@@ -15,8 +15,10 @@ import type {
1515 UsageInfo ,
1616 ImageContent ,
1717} from './types.js' ;
18+ import { logger } from '../utils/core/logger.js' ;
1819import { addProxyToFetchOptions } from '../utils/core/proxyUtils.js' ;
1920import { saveUsageToFile } from '../utils/core/usageLogger.js' ;
21+ import { getVersionHeader } from '../utils/core/version.js' ;
2022
2123export type {
2224 ChatMessage ,
@@ -274,6 +276,7 @@ export interface StreamChunk {
274276 usage ?: UsageInfo ; // Token usage information
275277 reasoning_content ?: string ; // Complete reasoning content for DeepSeek R1 models
276278}
279+
277280/**
278281 * Parse Server-Sent Events (SSE) stream
279282 */
@@ -282,6 +285,8 @@ async function* parseSSEStream(
282285) : AsyncGenerator < any , void , unknown > {
283286 const decoder = new TextDecoder ( ) ;
284287 let buffer = '' ;
288+ let dataCount = 0 ; // 记录成功解析的数据块数量
289+ let lastEventType = '' ; // 记录最后一个事件类型
285290
286291 try {
287292 while ( true ) {
@@ -290,12 +295,18 @@ async function* parseSSEStream(
290295 if ( done ) {
291296 // ✅ 关键修复:检查buffer是否有残留数据
292297 if ( buffer . trim ( ) ) {
293- // 连接异常中断,抛出明确错误
298+ // 连接异常中断,抛出明确错误,包含更详细的断点信息
299+ const errorContext = {
300+ dataCount,
301+ lastEventType,
302+ bufferLength : buffer . length ,
303+ bufferPreview : buffer . substring ( 0 , 200 ) ,
304+ } ;
305+
306+ const errorMessage = `[API_ERROR] [RETRIABLE] OpenAI stream terminated unexpectedly with incomplete data` ;
307+ logger . error ( errorMessage , errorContext ) ;
294308 throw new Error (
295- `Stream terminated unexpectedly with incomplete data: ${ buffer . substring (
296- 0 ,
297- 100 ,
298- ) } ...`,
309+ `${ errorMessage } . Context: ${ JSON . stringify ( errorContext ) } ` ,
299310 ) ;
300311 }
301312 break ; // 正常结束
@@ -315,7 +326,10 @@ async function* parseSSEStream(
315326
316327 // Handle both "event: " and "event:" formats
317328 if ( trimmed . startsWith ( 'event:' ) ) {
318- // Event type, will be followed by data
329+ // 记录事件类型用于断点恢复
330+ lastEventType = trimmed . startsWith ( 'event: ' )
331+ ? trimmed . slice ( 7 )
332+ : trimmed . slice ( 6 ) ;
319333 continue ;
320334 }
321335
@@ -331,17 +345,27 @@ async function* parseSSEStream(
331345 } ) ;
332346
333347 if ( parseResult . success ) {
348+ dataCount ++ ;
334349 yield parseResult . data ;
335350 }
336351 }
337352 }
338353 }
339354 } catch ( error ) {
340355 const { logger} = await import ( '../utils/core/logger.js' ) ;
341- logger . error ( 'SSE stream parsing error:' , {
356+
357+ // 增强错误日志,包含断点状态
358+ const errorContext = {
342359 error : error instanceof Error ? error . message : 'Unknown error' ,
343- remainingBuffer : buffer . substring ( 0 , 200 ) ,
344- } ) ;
360+ dataCount,
361+ lastEventType,
362+ bufferLength : buffer . length ,
363+ bufferPreview : buffer . substring ( 0 , 200 ) ,
364+ } ;
365+ logger . error (
366+ '[API_ERROR] [RETRIABLE] OpenAI SSE stream parsing error with checkpoint context:' ,
367+ errorContext ,
368+ ) ;
345369 throw error ;
346370 }
347371}
@@ -458,13 +482,24 @@ export async function* createStreamingChatCompletion(
458482
459483 if ( ! response . ok ) {
460484 const errorText = await response . text ( ) ;
461- throw new Error (
462- `OpenAI API error: ${ response . status } ${ response . statusText } - ${ errorText } ` ,
463- ) ;
485+ const errorMsg = `[API_ERROR] OpenAI API HTTP ${ response . status } : ${ response . statusText } - ${ errorText } ` ;
486+ logger . error ( errorMsg , {
487+ status : response . status ,
488+ statusText : response . statusText ,
489+ url,
490+ model : requestBody . model ,
491+ } ) ;
492+ throw new Error ( errorMsg ) ;
464493 }
465494
466495 if ( ! response . body ) {
467- throw new Error ( 'No response body from OpenAI API' ) ;
496+ const errorMsg =
497+ '[API_ERROR] No response body from OpenAI API (empty response)' ;
498+ logger . error ( errorMsg , {
499+ url,
500+ model : requestBody . model ,
501+ } ) ;
502+ throw new Error ( errorMsg ) ;
468503 }
469504
470505 let contentBuffer = '' ;
0 commit comments