Skip to content

Preserve Chat Completions for chat-native open-source agents #1467

Closed

Description

@dbc-hbin

Area

Proxy and routing

What are you trying to accomplish?

OpenAI Chat Completions-native open-source agents should be able to use OpenCodex routing without having their requests and responses translated through the Responses protocol when the selected upstream provider also speaks openai-chat.

This applies to generic third-party agents configured with POST /v1/chat/completions, not to Codex clients using Responses or Claude Code clients using Messages.

What prevents this today?

The inbound Chat Completions handler always converts the request into the internal Responses shape, runs the Responses pipeline, converts an openai-chat provider response into Responses events, and then converts those events back into Chat Completions.

For a Chat-native client routed to a Chat-native provider, the effective path is:

Chat client -> Chat request -> Responses AST -> Chat upstream
            <- Chat response <- Responses events <-

The double conversion adds work and can alter protocol details that the original client and provider both understand natively, including assistant reasoning fields, tool-call history, message roles, tool schemas, IDs, and streaming padding frames. A direct Chat client-to-provider configuration avoids those transformations.

What should OpenCodex do?

When an inbound /v1/chat/completions request resolves to an openai-chat target, OpenCodex should keep a Chat-native execution and output path instead of converting through Responses.

The path must still preserve OpenCodex behavior that is independent of wire format: admission authentication, model and combo routing, provider credential selection, API-key pools, pre-stream retry and failover, cancellation, usage accounting, request logging, and safe provider-specific policy.

If the selected target uses a different protocol, or if a feature requires the existing Responses bridge, OpenCodex should retain the current translation path. Existing Codex Responses and Claude Messages behavior must remain unchanged.

Example usage or interface

OpenAI-compatible open-source agent
  POST /v1/chat/completions
  model: synthetic/hf:moonshotai/Kimi-K3
              |
              v
OpenCodex admission + routing + key pool + logging
              |
              v
Synthetic POST /openai/v1/chat/completions

Expected protocol matrix:

Chat inbound + openai-chat target  -> Chat-native path
Chat inbound + non-Chat target     -> Existing translation bridge
Responses inbound + Chat target    -> Existing Responses-to-Chat adapter
Messages inbound                   -> Existing Claude-compatible path

Alternatives or workarounds

  • Configure the third-party agent directly with each provider endpoint and API key. This avoids conversion but loses OpenCodex model aggregation, credential isolation, key pools, combos, and logging.
  • Continue translating every Chat request through Responses and add provider-specific compatibility patches. This addresses individual regressions but retains the structural source of lossy round trips.
  • Relay raw upstream bytes immediately. This is too narrow unless Chat-specific inspection preserves usage, terminal state, cancellation, retry/failover, tool aliases, and request logs.

Additional context

This is a focused architectural follow-up to the external third-party agent gateway implemented after #357. The current documentation describes Chat Completions as translating into the internal Responses pipeline so all providers and sidecars share one lifecycle.

A safe implementation should reuse that lifecycle rather than duplicate it. One possible boundary is a Chat output mode in the shared core after route/auth/recovery decisions, with the existing bridge retained for protocol-mismatched targets and features that require it.

Checks

  • I searched existing issues and documentation.
  • This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
  • I removed secrets and personal data.

Activity

  1. added
    enhancementNew feature or request
    providerProvider adapters, OpenAI-compat presets, upstream API quirks
    proxyHTTP proxy, routing, reverse-proxy / management auth
    toolstool_calls, MCP, web-search / sidecar tools
    on Aug 11, 2026
  2. Repository owner deleted a comment from flyingsquirrel0419 on Aug 11, 2026
  3. Ingwannu commented on Aug 11, 2026

    @Ingwannu
    Owner

    Confirmed against current dev@20c513198: this limitation is real.

    src/server/chat-completions.ts calls chatCompletionsToResponsesBody() before route execution, then sends the translated request through handleResponses(). When the selected target is openai-chat, the adapter turns that internal Responses request back into Chat Completions upstream, and src/chat/outbound.ts converts the returned Responses events back into Chat Completions for the caller. The existing "native passthrough" logging path is for a native Responses transport; it is not a Chat-native client-to-provider path.

    The direction is valuable, but this should not be implemented as a raw-byte shortcut. A safe patch needs one shared execution lifecycle and must preserve admission, route/combo selection, credential and key-pool ownership, pre-stream retry/failover, cancellation, terminal status, usage accounting, request logs, provider policy, and the existing bridge whenever the selected wire or an enabled feature requires it.

    Before merge, I would require:

    1. a documented [Decision Log] for the shared lifecycle boundary and fallback rule;
    2. protocol-matrix tests for Chat-to-Chat native, Chat-to-non-Chat bridge, Responses-to-Chat bridge, and Messages unchanged;
    3. streaming and non-streaming coverage for tool calls, reasoning fields, structured output, usage, cancellation, retry/failover, and combo routes;
    4. proof that a retry cannot cross from a partially emitted native Chat stream into another target;
    5. exact-head full CI, because this changes shared routing and transport behavior.

    Keeping this open as a valid architectural enhancement. There is no merge-ready implementation attached yet.

  4. 1 remaining item

  5. lidge-jun commented on Aug 15, 2026

    @lidge-jun
    Owner

    Implemented on dev and verified at 02728d2 — closing.

    A Chat inbound request whose resolved target is openai-chat and needs nothing from the Responses lifecycle dispatches natively with the original Chat body (src/server/chat-native.ts:52 gates eligibility, src/server/chat-completions.ts:134 dispatches). Anything ineligible — or any non-Chat target — still goes through the existing bridge at src/server/chat-completions.ts:158 and :240, so nothing regressed for those clients.

    Smoke evidence on the remote validation host: bun test tests/chat-completions-endpoint.test.ts → 74 pass / 0 fail. That file covers both halves: native Chat fields surviving unchanged, and Chat inbound still succeeding against an openai-responses target through the bridge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestproviderProvider adapters, OpenAI-compat presets, upstream API quirksproxyHTTP proxy, routing, reverse-proxy / management authtoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions