Skip to content

Commit dc49963

Browse files
Jinyu XuJinyu Xu
authored andcommitted
fix: avoid duplicate fallback after partial stream
1 parent d8a25f0 commit dc49963

2 files changed

Lines changed: 48 additions & 30 deletions

File tree

src/views/ai-hub/index.vue

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ const COPY = {
338338
promptNeeded: '请输入问题或上传图片',
339339
strategyPromptNeeded: '请先写一点策略想法',
340340
generateFailed: '生成失败',
341+
streamInterrupted: '连接中断,已保留当前内容,请重试。',
341342
desktopOnly: '手机端仅支持使用与监控,请在电脑端完成代码编辑或回测。',
342343
taskDiagnose: '诊断标的',
343344
taskDiagnoseDesc: '趋势、量能、支撑阻力和风险',
@@ -382,6 +383,7 @@ const COPY = {
382383
promptNeeded: '請輸入問題或上傳圖片',
383384
strategyPromptNeeded: '請先寫一點策略想法',
384385
generateFailed: '生成失敗',
386+
streamInterrupted: '連線中斷,已保留目前內容,請重試。',
385387
desktopOnly: '手機端僅支援使用與監控,請在電腦端完成程式碼編輯或回測。',
386388
taskDiagnose: '診斷標的',
387389
taskDiagnoseDesc: '趨勢、量能、支撐阻力和風險',
@@ -426,6 +428,7 @@ const COPY = {
426428
promptNeeded: 'Enter a question or upload an image',
427429
strategyPromptNeeded: 'Write a short strategy idea first',
428430
generateFailed: 'Generation failed',
431+
streamInterrupted: 'The connection was interrupted. The current response was kept; please retry.',
429432
desktopOnly: 'Mobile supports usage and monitoring only. Use desktop for code editing or backtesting.',
430433
taskDiagnose: 'Diagnose',
431434
taskDiagnoseDesc: 'Trend, volume, levels, and risk',
@@ -470,6 +473,7 @@ const COPY = {
470473
promptNeeded: '質問を入力するか画像をアップロードしてください',
471474
strategyPromptNeeded: 'まず戦略アイデアを入力してください',
472475
generateFailed: '生成に失敗しました',
476+
streamInterrupted: '接続が中断されました。現在の内容を保持しました。再試行してください。',
473477
desktopOnly: 'モバイルは利用と監視専用です。コード編集やバックテストはデスクトップで行ってください。',
474478
taskDiagnose: '銘柄診断',
475479
taskDiagnoseDesc: 'トレンド、出来高、重要水準、リスク',
@@ -514,6 +518,7 @@ const COPY = {
514518
promptNeeded: '질문을 입력하거나 이미지를 업로드하세요',
515519
strategyPromptNeeded: '먼저 전략 아이디어를 입력하세요',
516520
generateFailed: '생성 실패',
521+
streamInterrupted: '연결이 중단되었습니다. 현재 내용을 유지했으니 다시 시도해 주세요.',
517522
desktopOnly: '모바일은 사용 및 모니터링 전용입니다. 코드 편집과 백테스트는 데스크톱에서 진행하세요.',
518523
taskDiagnose: '종목 진단',
519524
taskDiagnoseDesc: '추세, 거래량, 레벨, 리스크',
@@ -888,39 +893,46 @@ export default {
888893
},
889894
async sendMessageStream(payload, pendingMsg) {
890895
let hasContent = false
891-
await aiChatApi.streamMessage(payload, async (event, data) => {
892-
if (event === 'meta') {
893-
this.sessionId = data?.session_id || this.sessionId
894-
return
895-
}
896-
if (['delta', 'message', 'content'].includes(event)) {
897-
const text = this.extractStreamText(data)
898-
if (!text) return
899-
if (!hasContent) {
900-
this.updatePendingMessage(pendingMsg, { content: '', loading: false })
901-
hasContent = true
896+
try {
897+
await aiChatApi.streamMessage(payload, async (event, data) => {
898+
if (event === 'meta') {
899+
this.sessionId = data?.session_id || this.sessionId
900+
return
902901
}
903-
await this.revealStreamText(pendingMsg, text)
904-
return
905-
}
906-
if (event === 'done') {
907-
this.sessionId = data?.session_id || this.sessionId
908-
this.updatePendingMessage(pendingMsg, { id: data?.message_id || pendingMsg.id })
909-
const finalText = this.extractStreamText(data)
910-
if (finalText && !hasContent) {
911-
this.updatePendingMessage(pendingMsg, { content: '', loading: false })
912-
hasContent = true
913-
await this.revealStreamText(pendingMsg, finalText)
902+
if (['delta', 'message', 'content'].includes(event)) {
903+
const text = this.extractStreamText(data)
904+
if (!text) return
905+
if (!hasContent) {
906+
this.updatePendingMessage(pendingMsg, { content: '', loading: false })
907+
hasContent = true
908+
}
909+
await this.revealStreamText(pendingMsg, text)
910+
return
911+
}
912+
if (event === 'done') {
913+
this.sessionId = data?.session_id || this.sessionId
914+
this.updatePendingMessage(pendingMsg, { id: data?.message_id || pendingMsg.id })
915+
const finalText = this.extractStreamText(data)
916+
if (finalText && !hasContent) {
917+
this.updatePendingMessage(pendingMsg, { content: '', loading: false })
918+
hasContent = true
919+
await this.revealStreamText(pendingMsg, finalText)
920+
}
921+
if (data?.actions) {
922+
this.updatePendingMessage(pendingMsg, { actions: this.filterMobileActions(data.actions || []) })
923+
}
924+
return
914925
}
915-
if (data?.actions) {
916-
this.updatePendingMessage(pendingMsg, { actions: this.filterMobileActions(data.actions || []) })
926+
if (event === 'error') {
927+
throw new Error(data?.msg || data?.message || this.text.generateFailed)
917928
}
918-
return
919-
}
920-
if (event === 'error') {
921-
throw new Error(data?.msg || data?.message || this.text.generateFailed)
929+
})
930+
} catch (error) {
931+
if (error && typeof error === 'object') {
932+
error.streamHasContent = hasContent
922933
}
923-
})
934+
throw error
935+
}
924936
if (!hasContent && pendingMsg.content === this.text.sending) {
925937
throw new Error(this.text.generateFailed)
926938
}
@@ -929,7 +941,12 @@ export default {
929941
try {
930942
await this.sendMessageStream(payload, pendingMsg)
931943
return
932-
} catch (_) {
944+
} catch (error) {
945+
if (error?.streamHasContent) {
946+
this.updatePendingMessage(pendingMsg, { loading: false })
947+
showToast({ message: this.text.streamInterrupted, type: 'fail' })
948+
return
949+
}
933950
this.updatePendingMessage(pendingMsg, {
934951
content: this.text.sending,
935952
loading: true,

tests/unit/mobile-usage-boundary.test.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ test('AI composer stays at the bottom and uses a text send action', () => {
206206
assert.match(api, /sendMessage: \(payload\) => http\.post\('\/api\/ai\/chat\/message', payload, \{ timeout: 600000 \}\)/)
207207
assert.match(api, /streamMessage:[\s\S]*\/api\/ai\/chat\/message\/stream/)
208208
assert.match(api, /if \(await handlePart\(part\) === 'done'\)[\s\S]*await reader\.cancel\(\)/)
209+
assert.match(aiHub, /error\.streamHasContent = hasContent[\s\S]*if \(error\?\.streamHasContent\)[\s\S]*return/)
209210
})
210211

211212
test('signal chart supports mobile history navigation and candle inspection', () => {

0 commit comments

Comments
 (0)