@@ -105,10 +105,26 @@ function readStreamTextDelta(
105105 throw new Error ( `${ providerName } stream error: ${ fieldName } must be a string` ) ;
106106}
107107
108+ function readAnthropicTextContent ( data : {
109+ content : Array < { type : string ; text ?: unknown } > ;
110+ } ) : string {
111+ const textBlocks = data . content . filter ( ( block ) => block . type === "text" ) ;
112+ if ( textBlocks . some ( ( block ) => typeof block . text !== "string" ) ) {
113+ throw new Error ( `LLM 响应格式异常: text content 必须是字符串 — ${ JSON . stringify ( data ) . slice ( 0 , 200 ) } ` ) ;
114+ }
115+
116+ const content = textBlocks . map ( ( block ) => block . text as string ) . join ( "" ) ;
117+ if ( content . length === 0 ) {
118+ throw new Error ( `LLM 响应格式异常: 缺少 text content 字段 — ${ JSON . stringify ( data ) . slice ( 0 , 200 ) } ` ) ;
119+ }
120+
121+ return content ;
122+ }
123+
108124/**
109125 * Sanitize string content in LLM request body to remove control characters
110126 * that may break JSON parsing in intermediate proxies.
111- */
127+ */
112128function sanitizeForJson ( obj : unknown ) : unknown {
113129 if ( typeof obj === 'string' ) {
114130 // Remove ASCII control chars (except \n \r \t) and Unicode replacement char
@@ -457,14 +473,7 @@ export class LLMRouter {
457473 }
458474
459475 // No tool use → extract text content as final answer
460- const textContent = data . content
461- . filter ( ( b ) : b is AnthropicTextBlock => b . type === "text" )
462- . map ( ( b ) => b . text )
463- . join ( "" ) ;
464-
465- if ( textContent . length === 0 ) {
466- throw new Error ( `LLM 响应格式异常: 缺少 text content 字段 — ${ JSON . stringify ( data ) . slice ( 0 , 200 ) } ` ) ;
467- }
476+ const textContent = readAnthropicTextContent ( data ) ;
468477 if ( onChunk && textContent ) onChunk ( textContent ) ;
469478 return {
470479 content : textContent ,
@@ -730,13 +739,7 @@ export class LLMRouter {
730739 if ( ! Array . isArray ( data . content ) ) {
731740 throw new Error ( `LLM 响应格式异常: 缺少 content 字段 — ${ JSON . stringify ( data ) . slice ( 0 , 200 ) } ` ) ;
732741 }
733- const content = data . content
734- . filter ( ( c ) => c . type === "text" )
735- . map ( ( c ) => c . text )
736- . join ( "" ) ;
737- if ( content . length === 0 ) {
738- throw new Error ( `LLM 响应格式异常: 缺少 text content 字段 — ${ JSON . stringify ( data ) . slice ( 0 , 200 ) } ` ) ;
739- }
742+ const content = readAnthropicTextContent ( data ) ;
740743 return {
741744 content,
742745 promptTokens : data . usage ?. input_tokens || 0 ,
0 commit comments