Skip to content

Commit 21a5587

Browse files
committed
feat: chat record list
1 parent 5804068 commit 21a5587

File tree

6 files changed

+109
-71
lines changed

6 files changed

+109
-71
lines changed

backend/apps/chat/api/chat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ async def chats(session: SessionDep, current_user: CurrentUser):
2424

2525

2626
@router.get("/get/{chart_id}")
27-
async def list_chat(session: SessionDep, current_user: CurrentUser, chart_id: int):
27+
async def get_chat(session: SessionDep, current_user: CurrentUser, chart_id: int):
2828
try:
2929
return get_chat_with_records(chart_id=chart_id, session=session, current_user=current_user)
3030
except Exception as e:

backend/apps/chat/curd/chat.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from typing import List
33

44
from sqlalchemy import and_
5+
from sqlalchemy.orm import load_only
56

67
from apps.chat.models.chat_model import Chat, ChatRecord, CreateChat, ChatInfo, RenameChat, ChatQuestion
78
from apps.datasource.models.datasource import CoreDatasource
@@ -55,7 +56,11 @@ def get_chat_with_records(session: SessionDep, chart_id: int, current_user: Curr
5556
chat_info.datasource_exists = True
5657
chat_info.datasource_name = ds.name
5758

58-
record_list = session.query(ChatRecord).filter(
59+
record_list = session.query(ChatRecord).options(
60+
load_only(ChatRecord.id, ChatRecord.chat_id, ChatRecord.create_time, ChatRecord.finish_time,
61+
ChatRecord.question, ChatRecord.sql_answer, ChatRecord.sql, ChatRecord.data,
62+
ChatRecord.chart_answer, ChatRecord.chart, ChatRecord.finish, ChatRecord.error,
63+
ChatRecord.run_time)).filter(
5964
and_(Chat.create_by == current_user.id, ChatRecord.chat_id == chart_id)).order_by(ChatRecord.create_time).all()
6065

6166
chat_info.records = record_list
@@ -96,6 +101,9 @@ def create_chat(session: SessionDep, current_user: CurrentUser, create_chat_obj:
96101
chat_info.id = chat.id
97102
session.commit()
98103

104+
chat_info.datasource_exists = True
105+
chat_info.datasource_name = ds.name
106+
99107
return chat_info
100108

101109

@@ -214,6 +222,7 @@ def save_error_message(session: SessionDep, record_id: int, message: str) -> Cha
214222
record = session.query(ChatRecord).filter(ChatRecord.id == record_id).first()
215223
record.error = message
216224
record.finish = True
225+
record.finish_time = datetime.datetime.now()
217226

218227
result = ChatRecord(**record.model_dump())
219228

@@ -242,11 +251,13 @@ def save_sql_exec_data(session: SessionDep, record_id: int, data: str) -> ChatRe
242251

243252
return result
244253

254+
245255
def finish_record(session: SessionDep, record_id: int) -> ChatRecord:
246256
if not record_id:
247257
raise Exception("Record id cannot be None")
248258
record = session.query(ChatRecord).filter(ChatRecord.id == record_id).first()
249259
record.finish = True
260+
record.finish_time = datetime.datetime.now()
250261

251262
result = ChatRecord(**record.model_dump())
252263

@@ -257,4 +268,3 @@ def finish_record(session: SessionDep, record_id: int) -> ChatRecord:
257268
session.commit()
258269

259270
return result
260-

backend/apps/chat/models/chat_model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from sqlalchemy import Column, Text, BigInteger, DateTime, Integer, Identity, Boolean
33
from datetime import datetime
44
from pydantic import BaseModel
5-
from typing import List, Optional
5+
from typing import List, Optional, Any
66

77
from apps.template.generate_chart.generator import get_chart_template
88
from apps.template.generate_sql.generator import get_sql_template

backend/apps/datasource/crud/datasource.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ def get_table_obj_by_ds(session: SessionDep, ds: CoreDatasource) -> List[TableAn
225225
def get_table_schema(session: SessionDep, ds: CoreDatasource) -> str:
226226
schema_str = ""
227227
table_objs = get_table_obj_by_ds(session=session, ds=ds)
228+
if len(table_objs) == 0:
229+
return schema_str
228230
db_name = table_objs[0].schema
229231
schema_str += f"【DB_ID】 {db_name}\n【Schema】\n"
230232
for obj in table_objs:

frontend/src/api/chat.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface ChatMessage {
2020
role: 'user' | 'assistant'
2121
create_time?: Date | string
2222
content?: string | number
23+
record?: ChatRecord
2324
isTyping?: boolean
2425
isWelcome?: boolean
2526
}
@@ -28,24 +29,32 @@ export class ChatRecord {
2829
id?: number
2930
chat_id?: number
3031
create_time?: Date | string
31-
create_by?: number
32-
datasource?: number
33-
engine_type?: string
32+
finish_time?: Date | string
3433
question?: string
35-
answer?: string
34+
sql_answer?: string
35+
sql?: string
36+
data?: string
37+
chart_answer?: string
38+
chart?: string
39+
finish?: boolean = false
40+
error?: string
3641
run_time: number = 0
3742

3843
constructor()
39-
constructor(id: number, chat_id: number, create_time: Date | string, create_by: number, datasource: number, engine_type: string, question: string, answer: string, run_time: number)
40-
constructor(id?: number, chat_id?: number, create_time?: Date | string, create_by?: number, datasource?: number, engine_type?: string, question?: string, answer?: string, run_time?: number) {
44+
constructor(id: number, chat_id: number, create_time: Date | string, finish_time: Date | string | undefined, question: string, sql_answer: string | undefined, sql: string | undefined, data: string | undefined, chart_answer: string | undefined, chart: string | undefined, finish: boolean, error: string | undefined, run_time: number)
45+
constructor(id?: number, chat_id?: number, create_time?: Date | string, finish_time?: Date | string, question?: string, sql_answer?: string, sql?: string, data?: string, chart_answer?: string, chart?: string, finish?: boolean, error?: string, run_time?: number) {
4146
this.id = id
4247
this.chat_id = chat_id
4348
this.create_time = getDate(create_time)
44-
this.create_by = create_by
45-
this.datasource = datasource
46-
this.engine_type = engine_type
49+
this.finish_time = getDate(finish_time)
4750
this.question = question
48-
this.answer = answer
51+
this.sql_answer = sql_answer
52+
this.sql = sql
53+
this.data = data
54+
this.chart_answer = chart_answer
55+
this.chart = chart
56+
this.finish = finish
57+
this.error = error
4958
this.run_time = run_time ?? 0
5059
}
5160
}
@@ -111,7 +120,7 @@ function toChatRecord(data?: any): ChatRecord | undefined {
111120
if (!data) {
112121
return undefined
113122
}
114-
return new ChatRecord(data.id, data.number, data.create_time, data.create_by, data.datasource, data.engine_type, data.question, data.answer, data.run_time)
123+
return new ChatRecord(data.id, data.chat_id, data.create_time, data.finish_time, data.question, data.sql_answer, data.sql, data.data, data.chart_answer, data.chart, data.finish, data.error, data.run_time)
115124
}
116125

117126
function toChatRecordList(list: any = []): ChatRecord[] {

frontend/src/views/chat/index.vue

Lines changed: 73 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,31 @@
2020
<el-main class="chat-record-list">
2121
<el-scrollbar>
2222
<template v-for="(message, _index) in computedMessages" :key="_index">
23-
<ChatRow :current-chat="currentChat" v-model:datasource="currentChat.datasource" :msg="message"/>
23+
<ChatRow :current-chat="currentChat" v-model:datasource="currentChat.datasource" :msg="message">
24+
<template v-if="message.role === 'assistant'">
25+
<div v-if="message.isTyping">Thinking ...</div>
26+
<template v-if="message.record">
27+
<div>
28+
<div v-if="message.record.sql_answer">{{ message.record.sql_answer }}</div>
29+
<div v-if="message.record.sql">{{ message.record.sql }}</div>
30+
</div>
31+
<div>
32+
<div v-if="message.record.data">{{ message.record.data }}</div>
33+
</div>
34+
<div>
35+
<div v-if="message.record.chart_answer">{{ message.record.chart_answer }}</div>
36+
<div v-if="message.record.chart">{{ message.record.chart }}</div>
37+
</div>
38+
<div v-if="message.record.error" style="color: red">{{ message.record.error }}</div>
39+
</template>
40+
</template>
41+
</ChatRow>
2442
</template>
2543
</el-scrollbar>
2644
</el-main>
2745
<el-footer class="chat-footer">
2846
<div style="height: 24px;">
29-
<template v-if="currentChat.datasource">
47+
<template v-if="currentChat.datasource && currentChat.datasource_name">
3048
使用数据源:{{ currentChat.datasource_name }}
3149
</template>
3250
</div>
@@ -55,7 +73,7 @@
5573
</template>
5674

5775
<script setup lang="ts">
58-
import {ref, computed, nextTick, watch, onMounted} from 'vue'
76+
import {computed, nextTick, onMounted, ref} from 'vue'
5977
import {Plus, Position} from '@element-plus/icons-vue'
6078
import {Chat, chatApi, ChatInfo, type ChatMessage, ChatRecord, questionApi} from '@/api/chat'
6179
import ChatList from './ChatList.vue'
@@ -82,7 +100,6 @@ const computedMessages = computed<Array<ChatMessage>>(() => {
82100
if (currentChatId.value === undefined) {
83101
return messages
84102
}
85-
let appendThinking = false
86103
for (let i = 0; i < currentChat.value.records.length; i++) {
87104
const record = currentChat.value.records[i]
88105
if (record.question !== undefined) {
@@ -92,24 +109,16 @@ const computedMessages = computed<Array<ChatMessage>>(() => {
92109
content: record.question,
93110
})
94111
}
95-
if (record.answer !== undefined && record.answer !== '') {
96-
messages.push({
97-
role: 'assistant',
98-
create_time: record.create_time,
99-
content: record.answer,
100-
isTyping: i === currentChat.value.records.length - 1 && isTyping.value
101-
})
102-
} else {
103-
appendThinking = true
104-
}
105-
}
106-
if (isTyping.value && appendThinking) {
107112
messages.push({
108113
role: 'assistant',
109-
content: 'Thinking...',
110-
isTyping: true
114+
create_time: record.create_time,
115+
record: record,
116+
isTyping: i === currentChat.value.records.length - 1 && isTyping.value
111117
})
112118
}
119+
120+
console.log(messages)
121+
113122
return messages
114123
})
115124
@@ -138,6 +147,7 @@ function onClickHistory(chat: Chat) {
138147
chatApi.get(chat.id)
139148
.then((res) => {
140149
const info = chatApi.toChatInfo(res)
150+
console.log(info)
141151
if (info) {
142152
currentChat.value = info
143153
}
@@ -174,12 +184,6 @@ onMounted(() => {
174184
})
175185
176186
177-
const updateMessageContent = (content: string) => {
178-
if (currentChat.value) {
179-
currentChat.value.records[currentChat.value.records.length - 1].answer = content
180-
}
181-
}
182-
183187
const sendMessage = async () => {
184188
if (!inputMessage.value.trim()) return
185189
if (computedMessages.value[0].content === undefined) return
@@ -191,14 +195,17 @@ const sendMessage = async () => {
191195
currentRecord.create_time = new Date()
192196
currentRecord.chat_id = currentChatId.value
193197
currentRecord.question = inputMessage.value
194-
currentRecord.answer = ''
198+
currentRecord.sql_answer = ''
199+
currentRecord.sql = ''
200+
currentRecord.chart_answer = ''
201+
currentRecord.chart = ''
195202
196203
currentChat.value.records.push(currentRecord)
197204
inputMessage.value = ''
198205
199206
let error = false
200207
if (currentChatId.value === undefined) {
201-
chatApi.startChat({question: currentRecord.question.trim(), datasource: currentChat.value.datasource})
208+
await chatApi.startChat({question: currentRecord.question.trim(), datasource: currentChat.value.datasource})
202209
.then((res) => {
203210
const chat = chatApi.toChatInfo(res)
204211
if (chat !== undefined) {
@@ -228,50 +235,60 @@ const sendMessage = async () => {
228235
229236
while (true) {
230237
const {done, value} = await reader.read()
238+
console.log(done)
231239
if (done) {
232240
isTyping.value = false
233241
break
234242
}
235243
236244
const chunk = decoder.decode(value)
237-
const lines = chunk.split('\n\n')
238-
239-
for (const line of lines) {
240-
if (line.startsWith('data: ')) {
241-
const data = JSON.parse(line.replace('data:', '').trim())
242-
let realContent = data.content
243-
244-
switch (data.type) {
245-
case 'html':
246-
realContent = '\n' + data.content
247-
break
248-
249-
case 'error':
250-
realContent = '\nError: ' + data.content
251-
break
252-
253-
default:
254-
if (data.content) {
255-
const newContent = currentRecord.answer + realContent
256-
257-
updateMessageContent(newContent)
258-
await nextTick()
259-
}
260-
}
245+
console.log(chunk)
246+
const data = JSON.parse(chunk)
247+
248+
await nextTick(() => {
249+
switch (data.type) {
250+
case 'info':
251+
console.log(data.msg)
252+
break
253+
case 'error':
254+
currentRecord.error = data.content
255+
isTyping.value = false
256+
break
257+
case 'sql-result':
258+
currentRecord.sql_answer = currentRecord.sql_answer + data.content
259+
break
260+
case 'sql':
261+
currentRecord.sql = data.content
262+
break
263+
case 'sql-data':
264+
currentRecord.data = data.content
265+
break
266+
case 'chart-result':
267+
currentRecord.chart_answer = currentRecord.chart_answer + data.content
268+
break
269+
case 'chart':
270+
currentRecord.chart = data.content
271+
break
272+
case 'finish':
273+
isTyping.value = false
274+
break
261275
}
262-
}
276+
})
277+
263278
}
264279
} catch (error) {
265-
updateMessageContent(currentRecord.answer + '\nError: Failed to get response')
280+
if (!currentRecord.error) {
281+
currentRecord.error = ''
282+
}
283+
if (currentRecord.error.trim().length !== 0) {
284+
currentRecord.error = currentRecord.error + '\n'
285+
}
286+
currentRecord.error = currentRecord.error + 'Error:' + error
266287
console.error('Error:', error)
267288
isTyping.value = false
268289
}
269290
}
270291
271-
watch(() => currentChat.value?.records[currentChat.value.records.length - 1]?.answer, () => {
272-
//scrollToBottom()
273-
}, {deep: true})
274-
275292
//@ts-ignore
276293
const formatMessage = (content: string) => {
277294
if (!content) return ''

0 commit comments

Comments
 (0)