llm.codex.js 2.31 KB
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,
          },
        }
      },
    }
  }
}