mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
@@ -98,6 +98,13 @@ async def chat_record_log(session: SessionDep, current_user: CurrentUser, chat_r
|
||||
|
||||
return await asyncio.to_thread(inner)
|
||||
|
||||
@router.get("/record/{chat_record_id}/usage", summary=f"{PLACEHOLDER_PREFIX}get_record_usage")
|
||||
async def chat_record_usage(session: SessionDep, current_user: CurrentUser, chat_record_id: int):
|
||||
def inner():
|
||||
return get_chat_log_history(session, chat_record_id, current_user, True)
|
||||
|
||||
return await asyncio.to_thread(inner)
|
||||
|
||||
|
||||
""" @router.post("/rename", response_model=str, summary=f"{PLACEHOLDER_PREFIX}rename_chat")
|
||||
@system_log(LogConfig(
|
||||
|
||||
@@ -514,7 +514,7 @@ def format_record(record: ChatRecordResult):
|
||||
return _dict
|
||||
|
||||
|
||||
def get_chat_log_history(session: SessionDep, chat_record_id: int, current_user: CurrentUser) -> ChatLogHistory:
|
||||
def get_chat_log_history(session: SessionDep, chat_record_id: int, current_user: CurrentUser, without_steps: bool = False) -> ChatLogHistory:
|
||||
"""
|
||||
获取ChatRecord的详细历史记录
|
||||
|
||||
@@ -522,6 +522,7 @@ def get_chat_log_history(session: SessionDep, chat_record_id: int, current_user:
|
||||
session: 数据库会话
|
||||
chat_record_id: ChatRecord的ID
|
||||
current_user: 当前用户
|
||||
without_steps
|
||||
|
||||
Returns:
|
||||
ChatLogHistory: 包含历史步骤和时间信息的对象
|
||||
@@ -536,7 +537,8 @@ def get_chat_log_history(session: SessionDep, chat_record_id: int, current_user:
|
||||
|
||||
# 2. 查询与该ChatRecord相关的所有ChatLog记录
|
||||
chat_logs = session.query(ChatLog).filter(
|
||||
ChatLog.pid == chat_record_id
|
||||
ChatLog.pid == chat_record_id,
|
||||
ChatLog.operate != OperationEnum.GENERATE_RECOMMENDED_QUESTIONS
|
||||
).order_by(ChatLog.start_time).all()
|
||||
|
||||
# 3. 计算总的时间和token信息
|
||||
@@ -544,15 +546,6 @@ def get_chat_log_history(session: SessionDep, chat_record_id: int, current_user:
|
||||
steps = []
|
||||
|
||||
for log in chat_logs:
|
||||
# 计算单条记录的耗时
|
||||
duration = None
|
||||
if log.start_time and log.finish_time:
|
||||
try:
|
||||
time_diff = log.finish_time - log.start_time
|
||||
duration = time_diff.total_seconds()
|
||||
except Exception:
|
||||
duration = None
|
||||
|
||||
# 计算单条记录的token消耗
|
||||
log_tokens = 0
|
||||
if log.token_usage is not None:
|
||||
@@ -567,16 +560,46 @@ def get_chat_log_history(session: SessionDep, chat_record_id: int, current_user:
|
||||
# 累加到总token消耗
|
||||
total_tokens += log_tokens
|
||||
|
||||
# 创建ChatLogHistoryItem
|
||||
history_item = ChatLogHistoryItem(
|
||||
start_time=log.start_time,
|
||||
finish_time=log.finish_time,
|
||||
duration=duration,
|
||||
total_tokens=log_tokens,
|
||||
operate=log.operate,
|
||||
local_operation=log.local_operation
|
||||
)
|
||||
steps.append(history_item)
|
||||
if not without_steps:
|
||||
# 计算单条记录的耗时
|
||||
duration = None
|
||||
if log.start_time and log.finish_time:
|
||||
try:
|
||||
time_diff = log.finish_time - log.start_time
|
||||
duration = round(time_diff.total_seconds(), 2)
|
||||
except Exception:
|
||||
duration = None
|
||||
|
||||
# 获取操作类型的枚举名称
|
||||
operate_name = None
|
||||
if log.operate:
|
||||
# 如果是OperationEnum枚举实例
|
||||
if isinstance(log.operate, OperationEnum):
|
||||
operate_name = log.operate.name
|
||||
# 如果是字符串,尝试从枚举值获取名称
|
||||
elif isinstance(log.operate, str):
|
||||
try:
|
||||
# 通过枚举值找到对应的枚举实例
|
||||
for enum_item in OperationEnum:
|
||||
if enum_item.value == log.operate:
|
||||
operate_name = enum_item.name
|
||||
break
|
||||
except Exception:
|
||||
operate_name = log.operate
|
||||
else:
|
||||
operate_name = str(log.operate)
|
||||
|
||||
# 创建ChatLogHistoryItem
|
||||
history_item = ChatLogHistoryItem(
|
||||
start_time=log.start_time,
|
||||
finish_time=log.finish_time,
|
||||
duration=duration,
|
||||
total_tokens=log_tokens,
|
||||
operate=operate_name,
|
||||
local_operation=log.local_operation
|
||||
)
|
||||
|
||||
steps.append(history_item)
|
||||
|
||||
# 4. 计算总耗时(使用ChatRecord的时间)
|
||||
total_duration = None
|
||||
|
||||
@@ -193,7 +193,7 @@ class ChatLogHistoryItem(BaseModel):
|
||||
finish_time: Optional[datetime] = None
|
||||
duration: Optional[float] = None # 耗时字段(单位:秒)
|
||||
total_tokens: Optional[int] = None # token总消耗
|
||||
operate: Optional[OperationEnum] = None
|
||||
operate: Optional[str] = None
|
||||
local_operation: Optional[bool] = False
|
||||
|
||||
class ChatLogHistory(BaseModel):
|
||||
|
||||
@@ -142,6 +142,7 @@
|
||||
"get_chart_data": "Get Chart Data",
|
||||
"get_chart_predict_data": "Get Chart Prediction Data",
|
||||
"get_record_log": "Get Chart Record Log",
|
||||
"get_record_usage": "Get Chart Record Token Usage & Duration",
|
||||
"rename_chat": "Rename Chat",
|
||||
"delete_chat": "Delete Chat",
|
||||
"start_chat": "Create Chat",
|
||||
|
||||
@@ -142,6 +142,7 @@
|
||||
"get_chart_data": "获取图表数据",
|
||||
"get_chart_predict_data": "获取图表预测数据",
|
||||
"get_record_log": "获取对话日志",
|
||||
"get_record_usage": "获取对话Token使用量及耗时",
|
||||
"rename_chat": "重命名对话",
|
||||
"delete_chat": "删除对话",
|
||||
"start_chat": "创建对话",
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { request } from '@/utils/request'
|
||||
import { getDate } from '@/utils/utils.ts'
|
||||
import { i18n } from '@/i18n'
|
||||
|
||||
const { t } = i18n.global
|
||||
|
||||
export const questionApi = {
|
||||
pager: (pageNumber: number, pageSize: number) =>
|
||||
@@ -320,7 +323,7 @@ export class ChatLogHistoryItem {
|
||||
this.finish_time = getDate(finish_time)
|
||||
this.duration = duration
|
||||
this.total_tokens = total_tokens
|
||||
this.operate = operate
|
||||
this.operate = t('chat.log.' + operate)
|
||||
this.local_operation = !!local_operation
|
||||
}
|
||||
}
|
||||
@@ -441,6 +444,9 @@ export const chatApi = {
|
||||
get_chart_log_history: (record_id?: number): Promise<any> => {
|
||||
return request.get(`/chat/record/${record_id}/log`)
|
||||
},
|
||||
get_chart_usage: (record_id?: number): Promise<any> => {
|
||||
return request.get(`/chat/record/${record_id}/usage`)
|
||||
},
|
||||
startChat: (data: any): Promise<ChatInfo> => {
|
||||
return request.post('/chat/start', data)
|
||||
},
|
||||
|
||||
@@ -709,7 +709,22 @@
|
||||
"exec-sql-err": "Execute SQL failed",
|
||||
"no_data": "No Data",
|
||||
"loading_data": "Loading ...",
|
||||
"show_error_detail": "Show error info"
|
||||
"show_error_detail": "Show error info",
|
||||
"log": {
|
||||
"GENERATE_SQL": "Generate SQL",
|
||||
"GENERATE_CHART": "Generate Chart Structure",
|
||||
"ANALYSIS": "Analyze",
|
||||
"PREDICT_DATA": "Predict",
|
||||
"GENERATE_SQL_WITH_PERMISSIONS": "Apply Row Permissions to SQL",
|
||||
"CHOOSE_DATASOURCE": "Match Datasource",
|
||||
"GENERATE_DYNAMIC_SQL": "Generate Dynamic SQL",
|
||||
"CHOOSE_TABLE": "Match Data Table (Schema)",
|
||||
"FILTER_TERMS": "Match Terms",
|
||||
"FILTER_SQL_EXAMPLE": "Match SQL Examples",
|
||||
"FILTER_CUSTOM_PROMPT": "Match Custom Prompts",
|
||||
"EXECUTE_SQL": "Execute SQL",
|
||||
"GENERATE_PICTURE": "Generate Picture"
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "About",
|
||||
@@ -874,4 +889,4 @@
|
||||
"to_doc": "View API",
|
||||
"trigger_limit": "Supports up to {0} API Keys"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -709,7 +709,22 @@
|
||||
"exec-sql-err": "SQL 실행 실패",
|
||||
"no_data": "데이터가 없습니다",
|
||||
"loading_data": "로딩 중 ...",
|
||||
"show_error_detail": "구체적인 정보 보기"
|
||||
"show_error_detail": "구체적인 정보 보기",
|
||||
"log": {
|
||||
"GENERATE_SQL": "SQL 생성",
|
||||
"GENERATE_CHART": "차트 구조 생성",
|
||||
"ANALYSIS": "분석",
|
||||
"PREDICT_DATA": "예측",
|
||||
"GENERATE_SQL_WITH_PERMISSIONS": "SQL에 행 권한 적용",
|
||||
"CHOOSE_DATASOURCE": "데이터 소스 매칭",
|
||||
"GENERATE_DYNAMIC_SQL": "동적 SQL 생성",
|
||||
"CHOOSE_TABLE": "데이터 테이블 매칭 (스키마)",
|
||||
"FILTER_TERMS": "용어 매칭",
|
||||
"FILTER_SQL_EXAMPLE": "SQL 예시 매칭",
|
||||
"FILTER_CUSTOM_PROMPT": "사용자 정의 프롬프트 매칭",
|
||||
"EXECUTE_SQL": "SQL 실행",
|
||||
"GENERATE_PICTURE": "이미지 생성"
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "정보",
|
||||
@@ -874,4 +889,4 @@
|
||||
"to_doc": "API 보기",
|
||||
"trigger_limit": "최대 {0}개의 API 키 생성 지원"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -709,7 +709,22 @@
|
||||
"exec-sql-err": "执行SQL失败",
|
||||
"no_data": "暂无数据",
|
||||
"loading_data": "加载中...",
|
||||
"show_error_detail": "查看具体信息"
|
||||
"show_error_detail": "查看具体信息",
|
||||
"log": {
|
||||
"GENERATE_SQL": "生成 SQL",
|
||||
"GENERATE_CHART": "生成图表结构",
|
||||
"ANALYSIS": "分析",
|
||||
"PREDICT_DATA": "预测",
|
||||
"GENERATE_SQL_WITH_PERMISSIONS": "拼接 SQL 行权限",
|
||||
"CHOOSE_DATASOURCE": "匹配数据源",
|
||||
"GENERATE_DYNAMIC_SQL": "生成动态 SQL",
|
||||
"CHOOSE_TABLE": "匹配数据表 (Schema)",
|
||||
"FILTER_TERMS": "匹配术语",
|
||||
"FILTER_SQL_EXAMPLE": "匹配 SQL 示例",
|
||||
"FILTER_CUSTOM_PROMPT": "匹配自定义提示词",
|
||||
"EXECUTE_SQL": "执行 SQL",
|
||||
"GENERATE_PICTURE": "生成图片"
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "关于",
|
||||
@@ -874,4 +889,4 @@
|
||||
"to_doc": "查看 API",
|
||||
"trigger_limit": "最多支持创建 {0} 个 API Key"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ const sendMessage = async () => {
|
||||
break
|
||||
case 'error':
|
||||
currentRecord.error = data.content
|
||||
emits('error')
|
||||
emits('error', currentRecord.id)
|
||||
break
|
||||
case 'analysis-result':
|
||||
analysis_answer += data.content
|
||||
|
||||
@@ -185,7 +185,7 @@ const sendMessage = async () => {
|
||||
break
|
||||
case 'error':
|
||||
currentRecord.error = data.content
|
||||
emits('error')
|
||||
emits('error', currentRecord.id)
|
||||
break
|
||||
case 'sql-result':
|
||||
sql_answer += data.reasoning_content
|
||||
|
||||
@@ -162,7 +162,7 @@ const sendMessage = async () => {
|
||||
break
|
||||
case 'error':
|
||||
currentRecord.error = data.content
|
||||
emits('error')
|
||||
emits('error', currentRecord.id)
|
||||
break
|
||||
case 'predict-result':
|
||||
predict_answer += data.reasoning_content
|
||||
@@ -171,7 +171,7 @@ const sendMessage = async () => {
|
||||
_currentChat.value.records[index.value].predict_content = predict_content
|
||||
break
|
||||
case 'predict-failed':
|
||||
emits('error')
|
||||
emits('error', currentRecord.id)
|
||||
break
|
||||
case 'predict-success':
|
||||
//currentChat.value.records[_index].predict_data = data.content
|
||||
|
||||
@@ -774,6 +774,7 @@ async function onChartAnswerFinish(id: number) {
|
||||
getRecommendQuestionsLoading.value = true
|
||||
loading.value = false
|
||||
isTyping.value = false
|
||||
getRecordUsage(id)
|
||||
getRecommendQuestions(id)
|
||||
}
|
||||
|
||||
@@ -781,10 +782,10 @@ const loadingOver = () => {
|
||||
getRecommendQuestionsLoading.value = false
|
||||
}
|
||||
|
||||
function onChartAnswerError() {
|
||||
function onChartAnswerError(id: number) {
|
||||
loading.value = false
|
||||
isTyping.value = false
|
||||
console.debug('onChartAnswerError')
|
||||
getRecordUsage(id)
|
||||
}
|
||||
|
||||
function onChatStop() {
|
||||
@@ -864,12 +865,13 @@ const analysisAnswerRef = ref()
|
||||
async function onAnalysisAnswerFinish(id: number) {
|
||||
loading.value = false
|
||||
isTyping.value = false
|
||||
console.debug(id)
|
||||
getRecordUsage(id)
|
||||
//await getRecommendQuestions(id)
|
||||
}
|
||||
function onAnalysisAnswerError() {
|
||||
function onAnalysisAnswerError(id: number) {
|
||||
loading.value = false
|
||||
isTyping.value = false
|
||||
getRecordUsage(id)
|
||||
}
|
||||
|
||||
function askAgain(message: ChatMessage) {
|
||||
@@ -931,17 +933,34 @@ async function clickAnalysis(id?: number) {
|
||||
return
|
||||
}
|
||||
|
||||
function getRecordUsage(recordId: any) {
|
||||
chatApi.get_chart_usage(recordId).then((res) => {
|
||||
const logHistory = chatApi.toChatLogHistory(res)
|
||||
if (logHistory) {
|
||||
currentChat.value.records.forEach((record) => {
|
||||
if (record.id === recordId) {
|
||||
record.duration = logHistory.duration
|
||||
record.finish_time = logHistory.finish_time
|
||||
record.total_tokens = logHistory.total_tokens
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const predictAnswerRef = ref()
|
||||
|
||||
async function onPredictAnswerFinish(id: number) {
|
||||
loading.value = false
|
||||
isTyping.value = false
|
||||
console.debug('onPredictAnswerFinish: ', id)
|
||||
// console.debug('onPredictAnswerFinish: ', id)
|
||||
getRecordUsage(id)
|
||||
//await getRecommendQuestions(id)
|
||||
}
|
||||
function onPredictAnswerError() {
|
||||
function onPredictAnswerError(id: number) {
|
||||
loading.value = false
|
||||
isTyping.value = false
|
||||
getRecordUsage(id)
|
||||
}
|
||||
|
||||
async function clickPredict(id?: number) {
|
||||
|
||||
Reference in New Issue
Block a user