Skip to content

Commit 102624e

Browse files
refactor: 优化错误处理,使其兼容 OpenAI API
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
1 parent 9e91fa0 commit 102624e

File tree

3 files changed

+90
-33
lines changed

3 files changed

+90
-33
lines changed

packages/mcp-server/src/bridge/openai.ts

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { type Config } from '@google/gemini-cli-core';
33
import { createOpenAIStreamTransformer } from './stream-transformer.js';
44
import { GeminiApiClient } from '../gemini-client.js'; // <-- 引入新类
55
import { type OpenAIChatCompletionRequest } from '../types.js'; // <-- 引入新类型
6+
import { mapErrorToOpenAIError } from '../utils/error-mapper.js';
67

78
export function createOpenAIRouter(config: Config, debugMode = false): Router {
89
const router = Router();
@@ -75,44 +76,18 @@ export function createOpenAIRouter(config: Config, debugMode = false): Router {
7576
// --- 修正结束 ---
7677

7778
res.end();
78-
} catch (error) {
79-
console.error('[OpenAI Bridge] Error:', error);
80-
const errorMessage =
81-
error instanceof Error ? error.message : 'An unknown error occurred';
79+
} catch (e: unknown) {
80+
console.error('[OpenAI Bridge] Error:', e);
8281

83-
// [MODIFICATION] 检查错误类型并设置适当的状态码
84-
let statusCode = 500; // 默认为内部服务器错误
85-
if (error instanceof Error) {
86-
// 检查 gaxios 或类似 HTTP 客户端可能附加的 status 属性
87-
const status = (error as any).status;
88-
if (status === 429) {
89-
statusCode = 429;
90-
} else if (typeof status === 'number' && status >= 400 && status < 500) {
91-
statusCode = status;
92-
}
93-
// 也可以通过检查消息内容来增加健壮性
94-
else if (
95-
errorMessage.includes('429') ||
96-
errorMessage.toLowerCase().includes('quota')
97-
) {
98-
statusCode = 429;
99-
}
100-
}
82+
// 调用新的错误映射函数
83+
const { openAIError, statusCode } = mapErrorToOpenAIError(e);
10184

85+
// 使用映射后的状态码和错误对象进行响应
10286
if (!res.headersSent) {
103-
// 使用动态的状态码
104-
res.status(statusCode).json({
105-
error: {
106-
message: errorMessage,
107-
type: 'gemini_api_error',
108-
code: statusCode, // 在响应体中也反映出来
109-
},
110-
});
87+
res.status(statusCode).json(openAIError);
11188
} else {
11289
// 如果流已经开始,我们无法改变状态码,但可以在流中发送错误
113-
res.write(
114-
`data: ${JSON.stringify({ error: errorMessage, code: statusCode })}\n\n`,
115-
);
90+
res.write(`data: ${JSON.stringify({ error: openAIError.error })}\n\n`);
11691
res.end();
11792
}
11893
}

packages/mcp-server/src/types.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,24 @@ export type StreamChunk =
6161
| { type: 'text'; data: string }
6262
| { type: 'reasoning'; data: ReasoningData }
6363
| { type: 'tool_code'; data: { name: string; args: Record<string, unknown> } };
64+
65+
/**
66+
* 定义了 OpenAI API 兼容的错误对象结构。
67+
*/
68+
export interface OpenAIError {
69+
message: string;
70+
type:
71+
| 'invalid_request_error'
72+
| 'api_error'
73+
| 'authentication_error'
74+
| 'server_error';
75+
param: string | null;
76+
code: string | null;
77+
}
78+
79+
/**
80+
* 定义了完整的 OpenAI API 错误响应结构。
81+
*/
82+
export interface OpenAIErrorResponse {
83+
error: OpenAIError;
84+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { type OpenAIError, type OpenAIErrorResponse } from '../types.js';
2+
3+
/**
4+
* 将从 Gemini API 或身份验证流程中捕获的错误映射为标准的 OpenAI 错误对象和对应的 HTTP 状态码。
5+
* @param error 捕获到的未知错误。
6+
* @returns 一个包含标准 OpenAI 错误对象和建议的 HTTP 状态码的对象。
7+
*/
8+
export function mapErrorToOpenAIError(error: unknown): {
9+
openAIError: OpenAIErrorResponse;
10+
statusCode: number;
11+
} {
12+
let message = 'An unknown error occurred.';
13+
let type: OpenAIError['type'] = 'server_error';
14+
let code: string | null = 'internal_error';
15+
let statusCode = 500;
16+
17+
if (error instanceof Error) {
18+
message = error.message;
19+
20+
// 检查特定的错误类型或消息内容来确定更精确的错误码
21+
if (message.includes('Authentication failed')) {
22+
statusCode = 401;
23+
type = 'authentication_error';
24+
code = 'invalid_api_key';
25+
message =
26+
'Invalid authentication credentials. Please check your GCP_SERVICE_ACCOUNT.';
27+
} else if (
28+
message.includes('429') ||
29+
message.toLowerCase().includes('quota')
30+
) {
31+
statusCode = 429;
32+
type = 'server_error';
33+
code = 'rate_limit_exceeded';
34+
message =
35+
'You exceeded your current quota, please check your plan and billing details.';
36+
} else if (
37+
message.includes('400') ||
38+
message.toLowerCase().includes('invalid')
39+
) {
40+
statusCode = 400;
41+
type = 'invalid_request_error';
42+
code = 'invalid_request';
43+
} else if (message.includes('500')) {
44+
statusCode = 500;
45+
type = 'server_error';
46+
code = 'server_error';
47+
}
48+
// 可以根据需要添加更多针对 Gemini 特定错误的映射
49+
}
50+
51+
const openAIError: OpenAIErrorResponse = {
52+
error: {
53+
message,
54+
type,
55+
param: null, // 在这个场景下,我们通常不知道是哪个具体参数出错,所以设为 null
56+
code,
57+
},
58+
};
59+
60+
return { openAIError, statusCode };
61+
}

0 commit comments

Comments
 (0)