import { randomUUID } from 'node:crypto' import OpenAIResponsesProvider from './llm.openai_responses.js' const CODEX_RESPONSES_BASE_URL = 'https://chatgpt.com/backend-api/codex' /** ChatGPT Codex 内部 Responses 协议。认证由 composition root 通过 KeyLease 注入。 */ export default class CodexProvider extends OpenAIResponsesProvider { defaultBaseUrl() { return CODEX_RESPONSES_BASE_URL } responsesRequest(lease, _req, opts, _headers, body) { const requestId = randomUUID() const sessionId = opts.trace_ctx?.session_id ?? requestId const turnId = randomUUID() const headers = { accept: 'text/event-stream', authorization: `Bearer ${lease.api_key}`, 'chatgpt-account-id': lease.account_id, 'content-type': 'application/json', originator: 'codex-tui', 'x-client-request-id': requestId, 'session-id': sessionId, 'thread-id': sessionId, 'x-openai-internal-codex-responses-lite': 'true', } delete body.prompt_cache_options if (opts.trace_ctx?.session_id) body.prompt_cache_key = opts.trace_ctx.session_id for (const message of body.input) { for (const content of message.content) delete content.prompt_cache_breakpoint } delete body.max_output_tokens delete body.temperature for (const item of body.input) item.type = 'message' body.tools = [] body.tool_choice = 'auto' body.parallel_tool_calls = false body.reasoning = { effort: 'low', context: 'all_turns' } body.include = ['reasoning.encrypted_content'] body.text = { ...body.text, verbosity: 'low' } body.client_metadata = { session_id: sessionId, thread_id: sessionId, turn_id: turnId, } return { headers, body } } responseFormatHandlers() { return { json_object: (body) => { body.text = { format: { type: 'json_schema', name: 'json_object', strict: false, schema: { type: 'object', additionalProperties: true }, }, } }, json_schema: (body, _headers, response_format) => { body.text = { format: { type: 'json_schema', name: response_format.name ?? 'response', strict: true, schema: response_format.schema, }, } }, } } }