(req: Request, res: Response)
| 1338 | * 而非 data: {"object":"chat.completion.chunk",...} 格式 |
| 1339 | */ |
| 1340 | export async function handleOpenAIResponses(req: Request, res: Response): Promise<void> { |
| 1341 | const body = req.body as Record<string, unknown>; |
| 1342 | const isStream = (body.stream as boolean) ?? true; |
| 1343 | const chatBody = responsesToChatCompletions(body); |
| 1344 | const log = createRequestLogger({ |
| 1345 | method: req.method, |
| 1346 | path: req.path, |
| 1347 | model: chatBody.model, |
| 1348 | stream: isStream, |
| 1349 | hasTools: (chatBody.tools?.length ?? 0) > 0, |
| 1350 | toolCount: chatBody.tools?.length ?? 0, |
| 1351 | messageCount: chatBody.messages?.length ?? 0, |
| 1352 | apiFormat: 'responses', |
| 1353 | }); |
| 1354 | log.startPhase('receive', '接收请求'); |
| 1355 | log.recordOriginalRequest(body); |
| 1356 | log.info('OpenAI', 'receive', '收到 OpenAI Responses 请求', { |
| 1357 | model: chatBody.model, |
| 1358 | stream: isStream, |
| 1359 | toolCount: chatBody.tools?.length ?? 0, |
| 1360 | messageCount: chatBody.messages?.length ?? 0, |
| 1361 | }); |
| 1362 | |
| 1363 | try { |
| 1364 | // Step 1: 转换请求格式 Responses → Chat Completions → Anthropic → Cursor |
| 1365 | log.startPhase('convert', '格式转换 (Responses→Chat→Anthropic)'); |
| 1366 | const anthropicReq = convertToAnthropicRequest(chatBody); |
| 1367 | const cursorReq = await convertToCursorRequest(anthropicReq); |
| 1368 | log.endPhase(); |
| 1369 | log.recordCursorRequest(cursorReq); |
| 1370 | |
| 1371 | // 身份探针拦截 |
| 1372 | if (isIdentityProbe(anthropicReq)) { |
| 1373 | log.intercepted('身份探针拦截 (Responses)'); |
| 1374 | const mockText = "I am Claude, an advanced AI programming assistant created by Anthropic. I am ready to help you write code, debug, and answer your technical questions."; |
| 1375 | if (isStream) { |
| 1376 | return handleResponsesStreamMock(res, body, mockText); |
| 1377 | } else { |
| 1378 | return handleResponsesNonStreamMock(res, body, mockText); |
| 1379 | } |
| 1380 | } |
| 1381 | |
| 1382 | if (isStream) { |
| 1383 | await handleResponsesStream(res, cursorReq, body, anthropicReq, log); |
| 1384 | } else { |
| 1385 | await handleResponsesNonStream(res, cursorReq, body, anthropicReq, log); |
| 1386 | } |
| 1387 | } catch (err: unknown) { |
| 1388 | const message = err instanceof Error ? err.message : String(err); |
| 1389 | log.fail(message); |
| 1390 | console.error(`[OpenAI] /v1/responses 处理失败:`, message); |
| 1391 | const status = err instanceof OpenAIRequestError ? err.status : 500; |
| 1392 | const type = err instanceof OpenAIRequestError ? err.type : 'server_error'; |
| 1393 | const code = err instanceof OpenAIRequestError ? err.code : 'internal_error'; |
| 1394 | res.status(status).json({ |
| 1395 | error: { message, type, code }, |
| 1396 | }); |
| 1397 | } |
no test coverage detected