跳转到内容

架构

opencodex 是单个 Bun 进程。一个请求以 OpenAI Responses 格式进入,被规范化为内部模型,经过路由,通过 adapter 发送给某个 provider,再被桥接回 Responses SSE。端到端流程参见 工作原理

src/
├── cli.ts # ocx command dispatch
├── index.ts # public entry
├── server.ts # Bun.serve: /v1/* proxy + /api/* management API
├── router.ts # model id → provider + adapter
├── config.ts # ~/.opencodex/config.json, defaults, PID, env resolution
├── service.ts # launchd / systemd / Task Scheduler background service
├── init.ts # interactive setup wizard
├── bridge.ts # AdapterEvent stream → Responses SSE
├── codex-inject.ts # $CODEX_HOME/config.toml injection + restore
├── codex-catalog.ts # routed-model catalog merge + subagent ranking
├── reasoning-effort.ts # reasoning-effort translation, clamping, and catalog levels
├── model-cache.ts # per-provider /models TTL cache
├── types.ts # core interfaces + helpers (modelInList, namespacedToolName)
├── responses/
│ ├── parser.ts # Responses request → OcxParsedRequest
│ └── schema.ts # Zod validation
├── adapters/ # base + openai-chat, openai-responses, anthropic, google, azure, image
├── oauth/ # OAuth providers, API-key catalog, token store/refresh
├── web-search/ # web-search sidecar (synthetic tool, loop, executor, parser)
└── vision/ # vision sidecar (describe + plan)

responses/parser.ts 使用 responses/schema.ts(Zod)校验传入的请求,然后构建一个 OcxParsedRequest:

  • 消息(Messages) —— input 条目会变成规范化的 OcxMessage[]:user / developer / assistant / toolResult。reasoning 条目变成思考块;function_callcustom_tool_calltool_search_call 条目变成工具调用;它们对应的 *_output 条目变成工具结果。
  • 工具(Tools) —— function 工具直接透传;带命名空间的(MCP)工具会被扁平化namespace__name(并在返回时还原);**自由格式(freeform)**工具(例如 apply_patch)和 tool_search 发现类工具会被打上标记;托管工具(hosted tools)(web_search、图像生成……)会被丢弃,仅当某个 sidecar 会处理它时才重新注入。
  • 图像(Images) —— 作为真实的内容部分保留(data URL 或远程 https),绝不内联为文本。
  • 功能标志(Feature flags) —— _webSearch(请求了托管的网络搜索)和 _structuredOutput(text.format 为 json_schema / json_object)。

bridge.ts 将 adapter 的内部 AdapterEvent 流转换回 Codex 能理解的 Responses SSE:

AdapterEvent发出的 Responses SSE
text_deltaresponse.output_text.delta…doneresponse.content_part.doneresponse.output_item.done
thinking_deltaresponse.reasoning_summary_text.delta…done、item close
tool_call_startresponse.output_item.added(type:function_call / custom_tool_call / tool_search_call)
tool_call_deltaresponse.function_call_arguments.delta(对 freeform / tool_search 跳过)
tool_call_endresponse.function_call_arguments.doneresponse.output_item.done
doneresponse.completed(带 usage)
errorresponse.failed(带 last_error)

桥接器还运行一个心跳保活(RC3):在上游沉默期间,每 2 秒发出一个解析器会忽略的 response.heartbeat SSE 事件,以重置 Codex 的空闲定时器。停滞截止时间为 150 个 tick (默认 2 秒间隔下为 5 分钟),如果 provider 始终不恢复,则中止上游并关闭流 —— 防止挂起的 连接无限期阻塞 Codex。

工具调用会借助解析器捕获的命名空间映射、freeform 集合和 tool-search 集合,被消歧为三种 Responses item 类型 —— 因此 MCP 命名空间、apply_patch 风格的 freeform 工具,以及由客户端执行的 tool_search 都能完整往返。一个 buildResponseJSON() 变体会从同一批事件中生成单个非流式的响应对象。

  • model-cache.ts 为每个 provider 维护一个内存中的 TTL 缓存,缓存实时 /models 的结果(默认 5 分钟,与 Codex 自身的缓存一致),并在请求失败时提供陈旧回退(stale-fallback)。
  • codex-catalog.ts 将已路由的模型作为带命名空间的条目合并到 Codex 的目录中,将精选的 subagent 模型 排在前面,过滤掉 disabledModels,并能从一次性备份中完全恢复原始目录。

reasoning-effort.ts 将 Codex 的推理标签翻译为每个 provider 的线上值。Codex 目录只广告 Codex 自身接受的标签(low / medium / high / xhigh),但上游 provider 可能使用不同的名称(如 max)或支持更小的子集。该模块:

  • 定义了标准的 CODEX_REASONING_LEVELS 及其排序顺序。
  • 当精确级别不可用时,将请求的 effort 钳位到最接近的受支持层级。
  • 解析每个模型和 provider 的 reasoningEffortMap 覆盖,用于自定义线上映射。
  • 对列在 noReasoningModels 中的模型完全丢弃 effort。

内部模型位于 types.ts 中:OcxParsedRequestOcxContextOcxMessage 联合类型、OcxContentPart(text / image)、OcxToolCallOcxToolAdapterEvent,以及配置类型(OcxConfigOcxProviderConfig)。有两个被广泛使用的辅助函数:namespacedToolName()modelInList()(为 noVisionModels / noReasoningModels 提供可容忍 :size 标签的匹配)。