fix: Recommended optimization assistant problems (#554)

This commit is contained in:
王嘉豪
2025-12-03 10:06:35 +08:00
committed by GitHub
parent bf63d088b2
commit 4ca1adba25
6 changed files with 242 additions and 15 deletions
+5 -2
View File
@@ -42,13 +42,16 @@ def list_chats(session: SessionDep, current_user: CurrentUser) -> List[Chat]:
def list_recent_questions(session: SessionDep, current_user: CurrentUser, datasource_id: int) -> List[str]:
chat_records = (
session.query(ChatRecord.question)
session.query(
ChatRecord.question,
func.count(ChatRecord.question).label('question_count')
)
.filter(
ChatRecord.datasource == datasource_id,
ChatRecord.question.isnot(None)
)
.group_by(ChatRecord.question)
.order_by(desc(func.max(ChatRecord.create_time)))
.order_by(desc('question_count'), desc(func.max(ChatRecord.create_time)))
.limit(10)
.all()
)
+8 -5
View File
@@ -1,11 +1,11 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { onMounted, ref } from 'vue'
import icon_quick_question from '@/assets/svg/icon_quick_question.svg'
import icon_close from '@/assets/svg/operate/ope-close.svg'
import icon_replace_outlined from '@/assets/svg/icon_replace_outlined.svg'
import RecommendQuestion from '@/views/chat/RecommendQuestion.vue'
import { ChatInfo } from '@/api/chat.ts'
import RecentQuestion from '@/views/chat/RecentQuestion.vue'
import RecommendQuestionQuick from '@/views/chat/RecommendQuestionQuick.vue'
const activeName = ref('recommend')
const recommendQuestionRef = ref()
const recentQuestionRef = ref()
@@ -14,6 +14,7 @@ const getRecommendQuestions = () => {
recommendQuestionRef.value.getRecommendQuestions(10)
}
const questions = '[]'
const retrieveQuestions = () => {
getRecommendQuestions()
recentQuestionRef.value.getRecentQuestions()
@@ -37,6 +38,10 @@ const loadingOver = () => {
emits('loadingOver')
}
onMounted(() => {
getRecommendQuestions()
})
const emits = defineEmits(['quickAsk', 'loadingOver', 'stop'])
defineExpose({ getRecommendQuestions, id: () => props.recordId, stop })
@@ -45,7 +50,6 @@ const props = withDefaults(
recordId?: number
datasourceId?: number
currentChat?: ChatInfo
questions?: string
firstChat?: boolean
disabled?: boolean
}>(),
@@ -53,7 +57,6 @@ const props = withDefaults(
recordId: undefined,
datasourceId: undefined,
currentChat: () => new ChatInfo(),
questions: '[]',
firstChat: false,
disabled: false,
}
@@ -83,7 +86,7 @@ const props = withDefaults(
</el-tooltip>
<el-tabs v-model="activeName" class="quick_question_tab">
<el-tab-pane :label="$t('qa.recommend')" name="recommend">
<RecommendQuestion
<RecommendQuestionQuick
ref="recommendQuestionRef"
:current-chat="currentChat"
:record-id="recordId"
+11 -2
View File
@@ -36,14 +36,23 @@ const _currentChat = computed({
},
})
const computedQuestions = computed<string>(() => {
const computedQuestions = computed<string[]>(() => {
if (
props.questions &&
props.questions.length > 0 &&
startsWith(props.questions.trim(), '[') &&
endsWith(props.questions.trim(), ']')
) {
return JSON.parse(props.questions)
try {
const parsedQuestions = JSON.parse(props.questions)
if (Array.isArray(parsedQuestions)) {
return parsedQuestions.length > 4 ? parsedQuestions.slice(0, 4) : parsedQuestions
}
return []
} catch (error) {
console.error('Failed to parse questions:', error)
return []
}
}
return []
})
@@ -0,0 +1,209 @@
<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, ref } from 'vue'
import { endsWith, startsWith } from 'lodash-es'
import { chatApi } from '@/api/chat.ts'
const props = withDefaults(
defineProps<{
recordId?: number
disabled?: boolean
}>(),
{
recordId: undefined,
disabled: false,
}
)
const emits = defineEmits(['clickQuestion', 'stop', 'loadingOver'])
const loading = ref(false)
const questions = ref('[]')
const computedQuestions = computed<string>(() => {
if (
questions.value &&
questions.value.length > 0 &&
startsWith(questions.value.trim(), '[') &&
endsWith(questions.value.trim(), ']')
) {
return JSON.parse(questions.value)
}
return []
})
function clickQuestion(question: string): void {
if (!props.disabled) {
emits('clickQuestion', question)
}
}
const stopFlag = ref(false)
async function getRecommendQuestions(articles_number: number) {
stopFlag.value = false
loading.value = true
try {
const controller: AbortController = new AbortController()
const params = articles_number ? '?articles_number=' + articles_number : ''
const response = await chatApi.recommendQuestions(props.recordId, controller, params)
const reader = response.body.getReader()
const decoder = new TextDecoder('utf-8')
let tempResult = ''
while (true) {
if (stopFlag.value) {
controller.abort()
loading.value = false
break
}
const { done, value } = await reader.read()
if (done) {
break
}
let chunk = decoder.decode(value, { stream: true })
tempResult += chunk
const split = tempResult.match(/data:.*}\n\n/g)
if (split) {
chunk = split.join('')
tempResult = tempResult.replace(chunk, '')
} else {
continue
}
if (chunk && chunk.startsWith('data:{')) {
if (split) {
for (const str of split) {
let data
try {
data = JSON.parse(str.replace('data:{', '{'))
} catch (err) {
console.error('JSON string:', str)
throw err
}
if (data.code && data.code !== 200) {
ElMessage({
message: data.msg,
type: 'error',
showClose: true,
})
return
}
switch (data.type) {
case 'recommended_question':
if (
data.content &&
data.content.length > 0 &&
startsWith(data.content.trim(), '[') &&
endsWith(data.content.trim(), ']')
) {
questions.value = data.content
await nextTick()
}
}
}
}
}
}
} finally {
loading.value = false
emits('loadingOver')
}
}
function stop() {
stopFlag.value = true
loading.value = false
emits('stop')
}
onBeforeUnmount(() => {
stop()
})
defineExpose({ getRecommendQuestions, id: () => props.recordId, stop })
</script>
<template>
<div v-if="computedQuestions.length > 0 || loading" class="recommend-questions">
<div v-if="loading">
<el-button style="min-width: unset" type="primary" link loading />
</div>
<div v-else class="question-grid-input">
<div
v-for="(question, index) in computedQuestions"
:key="index"
class="question"
:class="{ disabled: disabled }"
@click="clickQuestion(question)"
>
{{ question }}
</div>
</div>
</div>
<div v-else class="recommend-questions-error">
{{ $t('qa.retrieve_error') }}
</div>
</template>
<style scoped lang="less">
.recommend-questions {
width: 100%;
font-size: 14px;
font-weight: 500;
line-height: 22px;
display: flex;
flex-direction: column;
gap: 4px;
.continue-ask {
color: rgba(100, 106, 115, 1);
font-weight: 400;
}
.question-grid-input {
display: grid;
grid-gap: 12px;
grid-template-columns: repeat(1, calc(100% - 6px));
}
.question-grid {
display: grid;
grid-gap: 12px;
grid-template-columns: repeat(2, calc(50% - 6px));
}
.question {
font-weight: 400;
cursor: pointer;
background: rgba(245, 246, 247, 1);
min-height: 32px;
border-radius: 6px;
padding: 5px 12px;
line-height: 22px;
&:hover {
background: rgba(31, 35, 41, 0.1);
}
&.disabled {
cursor: not-allowed;
background: rgba(245, 246, 247, 1);
}
}
}
.recommend-questions-error {
font-size: 12px;
font-weight: 500;
color: rgba(100, 106, 115, 1);
margin-top: 70px;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}
</style>
@@ -194,6 +194,12 @@ const sendMessage = async () => {
case 'chart':
_currentChat.value.records[index.value].chart = data.content
break
case 'datasource':
if (!_currentChat.value.datasource) {
_currentChat.value.datasource = data.id
}
_currentChat.value.records[index.value].chart = data.content
break
case 'finish':
emits('finish', currentRecord.id)
break
+3 -6
View File
@@ -1,8 +1,8 @@
<template>
<el-icon
v-if="assistantStore.assistant && !assistantStore.pageEmbedded && assistantStore.type != 4"
class="show-history_icon"
:class="{ 'embedded-history-hidden': embeddedHistoryHidden }"
v-if="assistantStore.assistant && !assistantStore.pageEmbedded && assistantStore.type != 4"
size="20"
@click="showFloatPopover"
>
@@ -374,13 +374,12 @@
</span>
</template>
</div>
<div v-if="computedMessages.length > 0" class="quick_question">
<div v-if="computedMessages.length > 0 && currentChat.datasource" class="quick_question">
<quick-question
ref="quickQuestionRef"
:datasource-id="currentChat.datasource"
:current-chat="currentChat"
:record-id="computedMessages[0].record?.id"
:questions="computedMessages[0].recommended_question"
:disabled="isTyping"
:first-chat="true"
@quick-ask="quickAsk"
@@ -701,9 +700,7 @@ const quickQuestionRef = ref()
function onChatCreated(chat: ChatInfo) {
if (chat.records.length === 1 && !chat.records[0].recommended_question) {
nextTick(() => {
quickQuestionRef.value.getRecommendQuestions()
})
// do nothing
}
}