@@ -61,14 +61,19 @@ export async function generateAnswersWithChatgptWebApi(port, question, session,
61
61
session . parentMessageId = uuidv4 ( )
62
62
}
63
63
64
- const { controller, messageListener, disconnectListener } = setAbortController ( port , null , ( ) => {
65
- if ( session . autoClean ) deleteConversation ( accessToken , session . conversationId )
66
- } )
64
+ let ws
65
+ const { controller, cleanController } = setAbortController (
66
+ port ,
67
+ ( ) => {
68
+ if ( ws ) ws . close ( )
69
+ } ,
70
+ ( ) => {
71
+ if ( session . autoClean ) deleteConversation ( accessToken , session . conversationId )
72
+ if ( ws ) ws . close ( )
73
+ } ,
74
+ )
67
75
68
- const models = await getModels ( accessToken ) . catch ( ( ) => {
69
- port . onMessage . removeListener ( messageListener )
70
- port . onDisconnect . removeListener ( disconnectListener )
71
- } )
76
+ const models = await getModels ( accessToken ) . catch ( cleanController )
72
77
console . debug ( 'models' , models )
73
78
const config = await getUserConfig ( )
74
79
const selectedModel = Models [ session . modelName ] . value
@@ -116,7 +121,10 @@ export async function generateAnswersWithChatgptWebApi(port, question, session,
116
121
let answer = ''
117
122
let generationPrefixAnswer = ''
118
123
let generatedImageUrl = ''
119
- await fetchSSE ( `${ config . customChatGptWebApiUrl } ${ config . customChatGptWebApiPath } ` , {
124
+ let wss_url = ''
125
+
126
+ const url = `${ config . customChatGptWebApiUrl } ${ config . customChatGptWebApiPath } `
127
+ const options = {
120
128
method : 'POST' ,
121
129
signal : controller . signal ,
122
130
credentials : 'include' ,
@@ -152,12 +160,115 @@ export async function generateAnswersWithChatgptWebApi(port, question, session,
152
160
history_and_training_disabled : config . disableWebModeHistory ,
153
161
arkose_token : arkoseToken ,
154
162
} ) ,
163
+ }
164
+ await fetchSSE ( url , {
165
+ ...options ,
155
166
onMessage ( message ) {
156
- console . debug ( 'sse message' , message )
157
- if ( message . trim ( ) === '[DONE]' ) {
167
+ function handleMessage ( data ) {
168
+ if ( data . error ) {
169
+ if ( data . error . includes ( 'unusual activity' ) )
170
+ throw new Error (
171
+ "Please keep https://chat.openai.com open and try again. If it still doesn't work, type some characters in the input box of chatgpt web page and try again." ,
172
+ )
173
+ else throw new Error ( data . error )
174
+ }
175
+
176
+ if ( data . conversation_id ) session . conversationId = data . conversation_id
177
+ if ( data . message ?. id ) session . parentMessageId = data . message . id
178
+
179
+ const respAns = data . message ?. content ?. parts ?. [ 0 ]
180
+ const contentType = data . message ?. content ?. content_type
181
+ if ( contentType === 'text' && respAns ) {
182
+ answer =
183
+ generationPrefixAnswer +
184
+ ( generatedImageUrl && `\n\n\n\n` ) +
185
+ respAns
186
+ } else if ( contentType === 'code' && data . message ?. status === 'in_progress' ) {
187
+ const generationText = '\n\n' + t ( 'Generating...' )
188
+ if ( answer && ! answer . endsWith ( generationText ) ) generationPrefixAnswer = answer
189
+ answer = generationPrefixAnswer + generationText
190
+ } else if (
191
+ contentType === 'multimodal_text' &&
192
+ respAns ?. content_type === 'image_asset_pointer'
193
+ ) {
194
+ const imageAsset = respAns ?. asset_pointer || ''
195
+ if ( imageAsset ) {
196
+ fetch (
197
+ `${ config . customChatGptWebApiUrl } /backend-api/files/${ imageAsset . replace (
198
+ 'file-service://' ,
199
+ '' ,
200
+ ) } /download`,
201
+ {
202
+ credentials : 'include' ,
203
+ headers : {
204
+ Authorization : `Bearer ${ accessToken } ` ,
205
+ ...( cookie && { Cookie : cookie } ) ,
206
+ } ,
207
+ } ,
208
+ ) . then ( ( r ) => r . json ( ) . then ( ( json ) => ( generatedImageUrl = json ?. download_url ) ) )
209
+ }
210
+ }
211
+
212
+ if ( answer ) {
213
+ port . postMessage ( { answer : answer , done : false , session : null } )
214
+ }
215
+ }
216
+
217
+ function finishMessage ( ) {
158
218
pushRecord ( session , question , answer )
159
219
console . debug ( 'conversation history' , { content : session . conversationRecords } )
160
- port . postMessage ( { answer : null , done : true , session : session } )
220
+ port . postMessage ( { answer : answer , done : true , session : session } )
221
+ }
222
+
223
+ console . debug ( 'sse message' , message )
224
+ if ( message . trim ( ) === '[DONE]' ) {
225
+ if ( ! wss_url ) {
226
+ finishMessage ( )
227
+ } else {
228
+ ws = new WebSocket ( wss_url )
229
+ ws . onmessage = ( event ) => {
230
+ let wsData
231
+ try {
232
+ wsData = JSON . parse ( event . data )
233
+ } catch ( error ) {
234
+ console . debug ( 'json error' , error )
235
+ return
236
+ }
237
+ if ( wsData . type === 'http.response.body' ) {
238
+ let body
239
+ try {
240
+ body = atob ( wsData . body ) . replace ( / ^ d a t a : / , '' )
241
+ const data = JSON . parse ( body )
242
+ console . debug ( 'ws message' , data )
243
+ if ( wsData . conversation_id === session . conversationId ) {
244
+ handleMessage ( data )
245
+ }
246
+ } catch ( error ) {
247
+ if ( body && body . trim ( ) === '[DONE]' ) {
248
+ console . debug ( 'ws message' , '[DONE]' )
249
+ if ( wsData . conversation_id === session . conversationId ) {
250
+ finishMessage ( )
251
+ ws . close ( )
252
+ }
253
+ } else {
254
+ console . debug ( 'json error' , error )
255
+ }
256
+ }
257
+ }
258
+ }
259
+ ws . onopen = ( ) => {
260
+ // fetch(url, options)
261
+ }
262
+ ws . onclose = ( ) => {
263
+ port . postMessage ( { done : true } )
264
+ cleanController ( )
265
+ }
266
+ ws . onerror = ( event ) => {
267
+ console . debug ( 'ws error' , event )
268
+ port . postMessage ( { error : event } )
269
+ cleanController ( )
270
+ }
271
+ }
161
272
return
162
273
}
163
274
let data
@@ -167,65 +278,20 @@ export async function generateAnswersWithChatgptWebApi(port, question, session,
167
278
console . debug ( 'json error' , error )
168
279
return
169
280
}
170
- if ( data . error ) {
171
- if ( data . error . includes ( 'unusual activity' ) )
172
- throw new Error (
173
- "Please keep https://chat.openai.com open and try again. If it still doesn't work, type some characters in the input box of chatgpt web page and try again." ,
174
- )
175
- else throw new Error ( data . error )
176
- }
177
-
178
- if ( data . conversation_id ) session . conversationId = data . conversation_id
179
- if ( data . message ?. id ) session . parentMessageId = data . message . id
180
-
181
- const respAns = data . message ?. content ?. parts ?. [ 0 ]
182
- const contentType = data . message ?. content ?. content_type
183
- if ( contentType === 'text' && respAns ) {
184
- answer =
185
- generationPrefixAnswer +
186
- ( generatedImageUrl && `\n\n\n\n` ) +
187
- respAns
188
- } else if ( contentType === 'code' && data . message ?. status === 'in_progress' ) {
189
- const generationText = '\n\n' + t ( 'Generating...' )
190
- if ( answer && ! answer . endsWith ( generationText ) ) generationPrefixAnswer = answer
191
- answer = generationPrefixAnswer + generationText
192
- } else if (
193
- contentType === 'multimodal_text' &&
194
- respAns ?. content_type === 'image_asset_pointer'
195
- ) {
196
- const imageAsset = respAns ?. asset_pointer || ''
197
- if ( imageAsset ) {
198
- fetch (
199
- `${ config . customChatGptWebApiUrl } /backend-api/files/${ imageAsset . replace (
200
- 'file-service://' ,
201
- '' ,
202
- ) } /download`,
203
- {
204
- credentials : 'include' ,
205
- headers : {
206
- Authorization : `Bearer ${ accessToken } ` ,
207
- ...( cookie && { Cookie : cookie } ) ,
208
- } ,
209
- } ,
210
- ) . then ( ( r ) => r . json ( ) . then ( ( json ) => ( generatedImageUrl = json ?. download_url ) ) )
211
- }
212
- }
213
-
214
- if ( answer ) {
215
- port . postMessage ( { answer : answer , done : false , session : null } )
216
- }
281
+ if ( data . wss_url ) wss_url = data . wss_url
282
+ handleMessage ( data )
217
283
} ,
218
284
async onStart ( ) {
219
285
// sendModerations(accessToken, question, session.conversationId, session.messageId)
220
286
} ,
221
287
async onEnd ( ) {
222
- port . postMessage ( { done : true } )
223
- port . onMessage . removeListener ( messageListener )
224
- port . onDisconnect . removeListener ( disconnectListener )
288
+ if ( ! wss_url ) {
289
+ port . postMessage ( { done : true } )
290
+ cleanController ( )
291
+ }
225
292
} ,
226
293
async onError ( resp ) {
227
- port . onMessage . removeListener ( messageListener )
228
- port . onDisconnect . removeListener ( disconnectListener )
294
+ cleanController ( )
229
295
if ( resp instanceof Error ) throw resp
230
296
if ( resp . status === 403 ) {
231
297
throw new Error ( 'CLOUDFLARE' )
0 commit comments