Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions backend/apps/chat/api/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ def inner():

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(
Expand Down
65 changes: 44 additions & 21 deletions backend/apps/chat/curd/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,14 +514,15 @@ 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的详细历史记录

Args:
session: 数据库会话
chat_record_id: ChatRecord的ID
current_user: 当前用户
without_steps

Returns:
ChatLogHistory: 包含历史步骤和时间信息的对象
Expand All @@ -536,23 +537,15 @@ 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信息
total_tokens = 0
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:
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion backend/apps/chat/models/chat_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
1 change: 1 addition & 0 deletions backend/apps/swagger/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions backend/apps/swagger/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
"get_chart_data": "获取图表数据",
"get_chart_predict_data": "获取图表预测数据",
"get_record_log": "获取对话日志",
"get_record_usage": "获取对话Token使用量及耗时",
"rename_chat": "重命名对话",
"delete_chat": "删除对话",
"start_chat": "创建对话",
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/api/chat.ts
Original file line number Diff line number Diff line change
@@ -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) =>
Expand Down Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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)
},
Expand Down
19 changes: 17 additions & 2 deletions frontend/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -874,4 +889,4 @@
"to_doc": "View API",
"trigger_limit": "Supports up to {0} API Keys"
}
}
}
19 changes: 17 additions & 2 deletions frontend/src/i18n/ko-KR.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "정보",
Expand Down Expand Up @@ -874,4 +889,4 @@
"to_doc": "API 보기",
"trigger_limit": "최대 {0}개의 API 키 생성 지원"
}
}
}
19 changes: 17 additions & 2 deletions frontend/src/i18n/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "关于",
Expand Down Expand Up @@ -874,4 +889,4 @@
"to_doc": "查看 API",
"trigger_limit": "最多支持创建 {0} 个 API Key"
}
}
}
2 changes: 1 addition & 1 deletion frontend/src/views/chat/answer/AnalysisAnswer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/views/chat/answer/ChartAnswer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/views/chat/answer/PredictAnswer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
31 changes: 25 additions & 6 deletions frontend/src/views/chat/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -774,17 +774,18 @@ async function onChartAnswerFinish(id: number) {
getRecommendQuestionsLoading.value = true
loading.value = false
isTyping.value = false
getRecordUsage(id)
getRecommendQuestions(id)
}

const loadingOver = () => {
getRecommendQuestionsLoading.value = false
}

function onChartAnswerError() {
function onChartAnswerError(id: number) {
loading.value = false
isTyping.value = false
console.debug('onChartAnswerError')
getRecordUsage(id)
}

function onChatStop() {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down