@@ -107,7 +107,9 @@ async function onConversation() {
107
107
scrollToBottom ()
108
108
109
109
try {
110
- let lastText = ' '
110
+ const magicSplit = ' t1h1i4s5i1s4a1s9i1l9l8y1s0plit'
111
+ let renderText = ' '
112
+ let firstTime = true
111
113
const fetchChatAPIOnce = async () => {
112
114
await fetchChatAPIProcess <Chat .ConversationResponse >({
113
115
prompt: message ,
@@ -117,42 +119,49 @@ async function onConversation() {
117
119
const xhr = event .target
118
120
const { responseText } = xhr
119
121
// Always process the final line
120
- const lastIndex = responseText .lastIndexOf (' \n ' , responseText .length - 2 )
121
- let chunk = responseText
122
- if (lastIndex !== - 1 )
123
- chunk = responseText .substring (lastIndex )
124
- try {
125
- const data = JSON .parse (chunk )
126
- updateChat (
127
- + uuid ,
128
- dataSources .value .length - 1 ,
129
- {
130
- dateTime: new Date ().toLocaleString (),
131
- text: lastText + data .text ?? ' ' ,
132
- inversion: false ,
133
- error: false ,
134
- loading: false ,
135
- conversationOptions: { conversationId: data .conversationId , parentMessageId: data .id },
136
- requestOptions: { prompt: message , options: { ... options } },
137
- },
138
- )
139
-
140
- if (openLongReply && data .detail .choices [0 ].finish_reason === ' length' ) {
141
- options .parentMessageId = data .id
142
- lastText = data .text
143
- message = ' '
144
- return fetchChatAPIOnce ()
145
- }
146
122
147
- scrollToBottomIfAtBottom ()
148
- }
149
- catch (error ) {
150
- //
123
+ const splitIndexBegin = responseText .search (magicSplit )
124
+ if (splitIndexBegin !== - 1 ) {
125
+ const splitIndexEnd = splitIndexBegin + magicSplit .length
126
+
127
+ const firstChunk = responseText .substring (0 , splitIndexBegin )
128
+ const deltaText = responseText .substring (splitIndexEnd )
129
+ try {
130
+ const data = JSON .parse (firstChunk )
131
+ if (firstTime ) {
132
+ firstTime = false
133
+ renderText = data .text ?? ' '
134
+ }
135
+ else {
136
+ renderText = deltaText ?? ' '
137
+ }
138
+ updateChat (
139
+ + uuid ,
140
+ dataSources .value .length - 1 ,
141
+ {
142
+ dateTime: new Date ().toLocaleString (),
143
+ text: renderText ,
144
+ inversion: false ,
145
+ error: false ,
146
+ loading: false ,
147
+ conversationOptions: { conversationId: data .conversationId , parentMessageId: data .id },
148
+ requestOptions: { prompt: message , ... options },
149
+ },
150
+ )
151
+
152
+ if (openLongReply && data .detail .choices [0 ].finish_reason === ' length' ) {
153
+ options .parentMessageId = data .id
154
+ message = ' '
155
+ return fetchChatAPIOnce ()
156
+ }
157
+ }
158
+ catch (error ) {
159
+ //
160
+ }
151
161
}
152
162
},
153
163
})
154
164
}
155
-
156
165
await fetchChatAPIOnce ()
157
166
}
158
167
catch (error : any ) {
@@ -237,7 +246,9 @@ async function onRegenerate(index: number) {
237
246
)
238
247
239
248
try {
240
- let lastText = ' '
249
+ const magicSplit = ' t1h1i4s5i1s4a1s9i1l9l8y1s0plit'
250
+ let renderText = ' '
251
+ let firstTime = true
241
252
const fetchChatAPIOnce = async () => {
242
253
await fetchChatAPIProcess <Chat .ConversationResponse >({
243
254
prompt: message ,
@@ -247,35 +258,45 @@ async function onRegenerate(index: number) {
247
258
const xhr = event .target
248
259
const { responseText } = xhr
249
260
// Always process the final line
250
- const lastIndex = responseText .lastIndexOf (' \n ' , responseText .length - 2 )
251
- let chunk = responseText
252
- if (lastIndex !== - 1 )
253
- chunk = responseText .substring (lastIndex )
254
- try {
255
- const data = JSON .parse (chunk )
256
- updateChat (
257
- + uuid ,
258
- index ,
259
- {
260
- dateTime: new Date ().toLocaleString (),
261
- text: lastText + data .text ?? ' ' ,
262
- inversion: false ,
263
- error: false ,
264
- loading: false ,
265
- conversationOptions: { conversationId: data .conversationId , parentMessageId: data .id },
266
- requestOptions: { prompt: message , ... options },
267
- },
268
- )
269
-
270
- if (openLongReply && data .detail .choices [0 ].finish_reason === ' length' ) {
271
- options .parentMessageId = data .id
272
- lastText = data .text
273
- message = ' '
274
- return fetchChatAPIOnce ()
261
+
262
+ const splitIndexBegin = responseText .search (magicSplit )
263
+ if (splitIndexBegin !== - 1 ) {
264
+ const splitIndexEnd = splitIndexBegin + magicSplit .length
265
+
266
+ const firstChunk = responseText .substring (0 , splitIndexBegin )
267
+ const deltaText = responseText .substring (splitIndexEnd )
268
+ try {
269
+ const data = JSON .parse (firstChunk )
270
+ if (firstTime ) {
271
+ firstTime = false
272
+ renderText = data .text ?? ' '
273
+ }
274
+ else {
275
+ renderText = deltaText ?? ' '
276
+ }
277
+ updateChat (
278
+ + uuid ,
279
+ index ,
280
+ {
281
+ dateTime: new Date ().toLocaleString (),
282
+ text: renderText ,
283
+ inversion: false ,
284
+ error: false ,
285
+ loading: false ,
286
+ conversationOptions: { conversationId: data .conversationId , parentMessageId: data .id },
287
+ requestOptions: { prompt: message , ... options },
288
+ },
289
+ )
290
+
291
+ if (openLongReply && data .detail .choices [0 ].finish_reason === ' length' ) {
292
+ options .parentMessageId = data .id
293
+ message = ' '
294
+ return fetchChatAPIOnce ()
295
+ }
296
+ }
297
+ catch (error ) {
298
+ //
275
299
}
276
- }
277
- catch (error ) {
278
- //
279
300
}
280
301
},
281
302
})
@@ -467,20 +488,13 @@ onUnmounted(() => {
467
488
<template >
468
489
<div class =" flex flex-col w-full h-full" >
469
490
<HeaderComponent
470
- v-if =" isMobile"
471
- :using-context =" usingContext"
472
- @export =" handleExport"
491
+ v-if =" isMobile" :using-context =" usingContext" @export =" handleExport"
473
492
@toggle-using-context =" toggleUsingContext"
474
493
/>
475
494
<main class =" flex-1 overflow-hidden" >
476
- <div
477
- id =" scrollRef"
478
- ref =" scrollRef"
479
- class =" h-full overflow-hidden overflow-y-auto"
480
- >
495
+ <div id =" scrollRef" ref =" scrollRef" class =" h-full overflow-hidden overflow-y-auto" >
481
496
<div
482
- id =" image-wrapper"
483
- class =" w-full max-w-screen-xl m-auto dark:bg-[#101014]"
497
+ id =" image-wrapper" class =" w-full max-w-screen-xl m-auto dark:bg-[#101014]"
484
498
:class =" [isMobile ? 'p-2' : 'p-4']"
485
499
>
486
500
<template v-if =" ! dataSources .length " >
@@ -492,14 +506,8 @@ onUnmounted(() => {
492
506
<template v-else >
493
507
<div >
494
508
<Message
495
- v-for =" (item, index) of dataSources"
496
- :key =" index"
497
- :date-time =" item.dateTime"
498
- :text =" item.text"
499
- :inversion =" item.inversion"
500
- :error =" item.error"
501
- :loading =" item.loading"
502
- @regenerate =" onRegenerate(index)"
509
+ v-for =" (item, index) of dataSources" :key =" index" :date-time =" item.dateTime" :text =" item.text"
510
+ :inversion =" item.inversion" :error =" item.error" :loading =" item.loading" @regenerate =" onRegenerate(index)"
503
511
@delete =" handleDelete(index)"
504
512
/>
505
513
<div class =" sticky bottom-0 left-0 flex justify-center" >
@@ -536,15 +544,9 @@ onUnmounted(() => {
536
544
<NAutoComplete v-model:value =" prompt" :options =" searchOptions" :render-label =" renderOption" >
537
545
<template #default =" { handleInput , handleBlur , handleFocus } " >
538
546
<NInput
539
- ref =" inputRef"
540
- v-model:value =" prompt"
541
- type =" textarea"
542
- :placeholder =" placeholder"
543
- :autosize =" { minRows: 1, maxRows: isMobile ? 4 : 8 }"
544
- @input =" handleInput"
545
- @focus =" handleFocus"
546
- @blur =" handleBlur"
547
- @keypress =" handleEnter"
547
+ ref =" inputRef" v-model:value =" prompt" type =" textarea" :placeholder =" placeholder"
548
+ :autosize =" { minRows: 1, maxRows: isMobile ? 4 : 8 }" @input =" handleInput" @focus =" handleFocus"
549
+ @blur =" handleBlur" @keypress =" handleEnter"
548
550
/>
549
551
</template >
550
552
</NAutoComplete >
0 commit comments