File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 1- # Anything Analyzer v3.6.28
1+ # Anything Analyzer v3.6.29
22
33## 修复
44
5- - ** OpenAI Chat 非流式响应格式校验 ** — 避免兼容服务缺少 ` message.content ` 时被误判为空成功结果
6- - 普通 Chat Completions 请求现在会明确拒绝缺少 ` message.content ` 的 malformed choice
7- - 工具调用最终文本轮次复用同一校验,保留工具调用中 ` content ` 为空的合法场景
5+ - ** LLM 非对象 JSON 响应诊断 ** — 避免兼容服务返回 ` null ` 等合法但非对象 JSON 时抛出内部属性访问错误
6+ - ` safeParseJson ` 现在会将非对象 JSON 归一为空对象,由各调用路径继续给出明确格式错误
7+ - OpenAI Chat Completions 回归测试覆盖 ` null ` 响应,确保诊断保持为缺少 ` choices ` 字段
88
99## 下载
1010
1111| 平台 | 文件 |
1212| ------| ------|
13- | Windows | Anything-Analyzer-Setup-3.6.28 .exe |
14- | macOS (Apple Silicon) | Anything-Analyzer-3.6.28 -arm64.dmg |
15- | macOS (Intel) | Anything-Analyzer-3.6.28 -x64.dmg |
16- | Linux | Anything-Analyzer-3.6.28 .AppImage |
13+ | Windows | Anything-Analyzer-Setup-3.6.29 .exe |
14+ | macOS (Apple Silicon) | Anything-Analyzer-3.6.29 -arm64.dmg |
15+ | macOS (Intel) | Anything-Analyzer-3.6.29 -x64.dmg |
16+ | Linux | Anything-Analyzer-3.6.29 .AppImage |
Original file line number Diff line number Diff line change 11{
22 "name" : " anything-analyzer" ,
3- "version" : " 3.6.28 " ,
3+ "version" : " 3.6.29 " ,
44 "description" : " Universal web protocol analyzer with embedded browser and AI-powered analysis" ,
55 "packageManager" : " pnpm@10.24.0" ,
66 "main" : " ./out/main/index.js" ,
Original file line number Diff line number Diff line change @@ -135,8 +135,10 @@ export class LLMRouter {
135135 throw new Error ( `LLM 返回了非 JSON 响应 (${ response . status } ): ${ preview } ` ) ;
136136 }
137137
138- // Anthropic error format: { type: "error", error: { type, message } }
139- const obj = data as Record < string , unknown > ;
138+ // Anthropic error format: { type: "error", error: { type, message } }
139+ const obj = data !== null && typeof data === "object"
140+ ? data as Record < string , unknown >
141+ : { } ;
140142 if ( obj . type === 'error' && typeof obj . error === 'object' && obj . error !== null ) {
141143 const err = obj . error as Record < string , unknown > ;
142144 throw new Error ( `LLM API 错误: ${ err . type ?? 'unknown' } — ${ err . message ?? JSON . stringify ( err ) } ` ) ;
@@ -153,8 +155,8 @@ export class LLMRouter {
153155 throw new Error ( `LLM API 错误: ${ err . message ?? JSON . stringify ( err ) } ` ) ;
154156 }
155157
156- return data as T ;
157- }
158+ return obj as T ;
159+ }
158160
159161 async complete (
160162 messages : ChatMessage [ ] ,
Original file line number Diff line number Diff line change @@ -46,10 +46,10 @@ function createRawSSEResponse(body: string): Response {
4646// Helper: create a mock JSON Response
4747function createJSONResponse ( body : unknown ) : Response {
4848 return new Response ( JSON . stringify ( body ) , {
49- status : 200 ,
50- headers : { "content-type" : "application/json" } ,
51- } ) ;
52- }
49+ status : 200 ,
50+ headers : { "content-type" : "application/json" } ,
51+ } ) ;
52+ }
5353
5454const baseConfig : LLMProviderConfig = {
5555 name : "openai" ,
@@ -261,6 +261,16 @@ describe("LLMRouter", () => {
261261 ) . rejects . toThrow ( "LLM 响应格式异常: 缺少 choices 字段" ) ;
262262 } ) ;
263263
264+ it ( "should reject non-object OpenAI completion JSON with a clear format error" , async ( ) => {
265+ fetchSpy . mockResolvedValueOnce ( createJSONResponse ( null ) ) ;
266+
267+ const router = new LLMRouter ( baseConfig ) ;
268+
269+ await expect (
270+ router . complete ( [ { role : "user" , content : "test" } ] ) ,
271+ ) . rejects . toThrow ( "LLM 响应格式异常: 缺少 choices 字段" ) ;
272+ } ) ;
273+
264274 it ( "should reject OpenAI completion choices without message content" , async ( ) => {
265275 fetchSpy . mockResolvedValueOnce (
266276 createJSONResponse ( {
You can’t perform that action at this time.
0 commit comments