122122 </div >
123123 </template >
124124 </div >
125+ <div v-if =" msg.streamWarning" class =" stream-warning" >
126+ <van-icon name =" warning-o" />
127+ <span >{{ msg.streamWarning }}</span >
128+ </div >
125129 <van-loading v-if =" msg.loading" size =" 18" />
126130 </div >
127131 <div v-if =" msg.role === 'assistant' && msg.content && !msg.loading" class =" bubble-tools" >
@@ -339,6 +343,8 @@ const COPY = {
339343 strategyPromptNeeded: ' 请先写一点策略想法' ,
340344 generateFailed: ' 生成失败' ,
341345 streamInterrupted: ' 连接中断,已保留当前内容,请重试。' ,
346+ streamIncomplete: ' 响应未正常结束,请重试。' ,
347+ outputLimit: ' 回答已达到输出上限,当前内容可能不完整。' ,
342348 desktopOnly: ' 手机端仅支持使用与监控,请在电脑端完成代码编辑或回测。' ,
343349 taskDiagnose: ' 诊断标的' ,
344350 taskDiagnoseDesc: ' 趋势、量能、支撑阻力和风险' ,
@@ -384,6 +390,8 @@ const COPY = {
384390 strategyPromptNeeded: ' 請先寫一點策略想法' ,
385391 generateFailed: ' 生成失敗' ,
386392 streamInterrupted: ' 連線中斷,已保留目前內容,請重試。' ,
393+ streamIncomplete: ' 回應未正常結束,請重試。' ,
394+ outputLimit: ' 回答已達到輸出上限,目前內容可能不完整。' ,
387395 desktopOnly: ' 手機端僅支援使用與監控,請在電腦端完成程式碼編輯或回測。' ,
388396 taskDiagnose: ' 診斷標的' ,
389397 taskDiagnoseDesc: ' 趨勢、量能、支撐阻力和風險' ,
@@ -429,6 +437,8 @@ const COPY = {
429437 strategyPromptNeeded: ' Write a short strategy idea first' ,
430438 generateFailed: ' Generation failed' ,
431439 streamInterrupted: ' The connection was interrupted. The current response was kept; please retry.' ,
440+ streamIncomplete: ' The response did not finish correctly. Please retry.' ,
441+ outputLimit: ' The response reached the output limit and may be incomplete.' ,
432442 desktopOnly: ' Mobile supports usage and monitoring only. Use desktop for code editing or backtesting.' ,
433443 taskDiagnose: ' Diagnose' ,
434444 taskDiagnoseDesc: ' Trend, volume, levels, and risk' ,
@@ -474,6 +484,8 @@ const COPY = {
474484 strategyPromptNeeded: ' まず戦略アイデアを入力してください' ,
475485 generateFailed: ' 生成に失敗しました' ,
476486 streamInterrupted: ' 接続が中断されました。現在の内容を保持しました。再試行してください。' ,
487+ streamIncomplete: ' 応答が正常に完了しませんでした。もう一度お試しください。' ,
488+ outputLimit: ' 出力上限に達したため、回答が不完全な可能性があります。' ,
477489 desktopOnly: ' モバイルは利用と監視専用です。コード編集やバックテストはデスクトップで行ってください。' ,
478490 taskDiagnose: ' 銘柄診断' ,
479491 taskDiagnoseDesc: ' トレンド、出来高、重要水準、リスク' ,
@@ -519,6 +531,8 @@ const COPY = {
519531 strategyPromptNeeded: ' 먼저 전략 아이디어를 입력하세요' ,
520532 generateFailed: ' 생성 실패' ,
521533 streamInterrupted: ' 연결이 중단되었습니다. 현재 내용을 유지했으니 다시 시도해 주세요.' ,
534+ streamIncomplete: ' 응답이 정상적으로 완료되지 않았습니다. 다시 시도해 주세요.' ,
535+ outputLimit: ' 출력 한도에 도달하여 답변이 불완전할 수 있습니다.' ,
522536 desktopOnly: ' 모바일은 사용 및 모니터링 전용입니다. 코드 편집과 백테스트는 데스크톱에서 진행하세요.' ,
523537 taskDiagnose: ' 종목 진단' ,
524538 taskDiagnoseDesc: ' 추세, 거래량, 레벨, 리스크' ,
@@ -893,9 +907,11 @@ export default {
893907 },
894908 async sendMessageStream (payload , pendingMsg ) {
895909 let hasContent = false
910+ let streamAccepted = false
896911 try {
897- await aiChatApi .streamMessage (payload, async (event , data ) => {
898- if (event === ' meta' ) {
912+ const streamResult = await aiChatApi .streamMessage (payload, async (event , data ) => {
913+ if (event === ' accepted' || event === ' meta' ) {
914+ streamAccepted = true
899915 this .sessionId = data? .session_id || this .sessionId
900916 return
901917 }
@@ -909,7 +925,29 @@ export default {
909925 await this .revealStreamText (pendingMsg, text)
910926 return
911927 }
928+ if (event === ' replace' ) {
929+ const text = this .extractStreamText (data)
930+ if (! text) throw new Error (this .text .generateFailed )
931+ hasContent = true
932+ this .updatePendingMessage (pendingMsg, {
933+ content: text,
934+ loading: false ,
935+ streamWarning: ' '
936+ })
937+ return
938+ }
939+ if (event === ' warning' ) {
940+ streamAccepted = true
941+ this .updatePendingMessage (pendingMsg, {
942+ loading: false ,
943+ streamWarning: data? .code === ' output_limit'
944+ ? this .text .outputLimit
945+ : (data? .msg || this .text .streamIncomplete )
946+ })
947+ return
948+ }
912949 if (event === ' done' ) {
950+ streamAccepted = true
913951 this .sessionId = data? .session_id || this .sessionId
914952 this .updatePendingMessage (pendingMsg, { id: data? .message_id || pendingMsg .id })
915953 const finalText = this .extractStreamText (data)
@@ -924,12 +962,19 @@ export default {
924962 return
925963 }
926964 if (event === ' error' ) {
927- throw new Error (data? .msg || data? .message || this .text .generateFailed )
965+ streamAccepted = true
966+ const streamError = new Error (data? .msg || data? .message || this .text .generateFailed )
967+ streamError .streamErrorType = data? .error_type || ' '
968+ throw streamError
928969 }
929970 })
971+ if (! streamResult? .completed ) {
972+ throw new Error (this .text .streamIncomplete )
973+ }
930974 } catch (error) {
931975 if (error && typeof error === ' object' ) {
932976 error .streamHasContent = hasContent
977+ error .streamAccepted = streamAccepted
933978 }
934979 throw error
935980 }
@@ -942,9 +987,17 @@ export default {
942987 await this .sendMessageStream (payload, pendingMsg)
943988 return
944989 } catch (error) {
945- if (error? .streamHasContent ) {
946- this .updatePendingMessage (pendingMsg, { loading: false })
947- showToast ({ message: this .text .streamInterrupted , type: ' fail' })
990+ if (error? .streamAccepted || error? .streamHasContent ) {
991+ const hasContent = Boolean (String (pendingMsg .content || ' ' ).trim ()) && pendingMsg .content !== this .text .sending
992+ this .updatePendingMessage (pendingMsg, {
993+ content: hasContent ? pendingMsg .content : (error? .message || this .text .generateFailed ),
994+ loading: false ,
995+ streamWarning: hasContent ? this .text .streamInterrupted : ' '
996+ })
997+ showToast ({
998+ message: hasContent ? this .text .streamInterrupted : (error? .message || this .text .generateFailed ),
999+ type: ' fail'
1000+ })
9481001 return
9491002 }
9501003 this .updatePendingMessage (pendingMsg, {
@@ -2008,6 +2061,20 @@ export default {
20082061 line- height: 1.72 ;
20092062}
20102063
2064+ .stream - warning {
2065+ display: flex;
2066+ align- items: flex- start;
2067+ gap: 6px ;
2068+ margin- top: 10px ;
2069+ padding: 8px 10px ;
2070+ border: 1px solid rgba (251 , 191 , 36 , 0.28 );
2071+ border- radius: 10px ;
2072+ color: #fbbf24;
2073+ background: rgba (251 , 191 , 36 , 0.08 );
2074+ font- size: 12px ;
2075+ line- height: 1.45 ;
2076+ }
2077+
20112078.markdown - body : deep (p ) {
20122079 margin: 0 0 10px ;
20132080}
0 commit comments