core-agent-ide/codex-rs/codex-api
Josh McKinney bba5e5e0d4
fix(codex-api): handle Chat Completions DONE sentinel (#8708)
Context
- This code parses Server-Sent Events (SSE) from the legacy Chat
Completions streaming API (wire_api = "chat").
- The upstream protocol terminates a stream with a final sentinel event:
data: [DONE].
- Some of our test stubs/helpers historically end the stream with data:
DONE (no brackets).

How this was found
- GitHub Actions on Windows failed in codex-app-server integration tests
with wiremock verification errors (expected multiple POSTs, got 1).

Diagnosis
- The job logs included: codex_api::sse::chat: Failed to parse
ChatCompletions SSE event ... data: DONE.
- eventsource_stream surfaces the sentinel as a normal SSE event; it
does not automatically close the stream.
- The parser previously attempted to JSON-decode every data: payload.
The sentinel is not JSON, so we logged and skipped it, then continued
polling.
- On servers that keep the HTTP connection open after emitting the
sentinel (notably wiremock on Windows), skipping the sentinel meant we
never emitted ResponseEvent::Completed.
- Higher layers wait for completion before progressing (emitting
approval requests and issuing follow-up model calls), so the test never
reached the subsequent requests and wiremock panicked when its
expected-call count was not met.

Fix
- Treat both data: [DONE] and data: DONE as explicit end-of-stream
sentinels.
- When a sentinel is seen, flush any pending assistant/reasoning items
and emit ResponseEvent::Completed once.

Tests
- Add a regression unit test asserting we complete on the sentinel even
if the underlying connection is not closed.
2026-01-05 09:29:42 -08:00
..
src fix(codex-api): handle Chat Completions DONE sentinel (#8708) 2026-01-05 09:29:42 -08:00
tests Refresh on models etag mismatch (#8491) 2026-01-01 11:41:16 -08:00
Cargo.toml Add models endpoint (#7603) 2025-12-04 12:57:54 -08:00
README.md chore: proper client extraction (#6996) 2025-11-25 18:06:12 +00:00

codex-api

Typed clients for Codex/OpenAI APIs built on top of the generic transport in codex-client.

  • Hosts the request/response models and prompt helpers for Responses, Chat Completions, and Compact APIs.
  • Owns provider configuration (base URLs, headers, query params), auth header injection, retry tuning, and stream idle settings.
  • Parses SSE streams into ResponseEvent/ResponseStream, including rate-limit snapshots and API-specific error mapping.
  • Serves as the wire-level layer consumed by codex-core; higher layers handle auth refresh and business logic.

Core interface

The public interface of this crate is intentionally small and uniform:

  • Prompted endpoints (Chat + Responses)

    • Input: a single Prompt plus endpoint-specific options.
      • Prompt (re-exported as codex_api::Prompt) carries:
        • instructions: String the fully-resolved system prompt for this turn.
        • input: Vec<ResponseItem> conversation history and user/tool messages.
        • tools: Vec<serde_json::Value> JSON tools compatible with the target API.
        • parallel_tool_calls: bool.
        • output_schema: Option<Value> used to build text.format when present.
    • Output: a ResponseStream of ResponseEvent (both re-exported from common).
  • Compaction endpoint

    • Input: CompactionInput<'a> (re-exported as codex_api::CompactionInput):
      • model: &str.
      • input: &[ResponseItem] history to compact.
      • instructions: &str fully-resolved compaction instructions.
    • Output: Vec<ResponseItem>.
    • CompactClient::compact_input(&CompactionInput, extra_headers) wraps the JSON encoding and retry/telemetry wiring.

All HTTP details (URLs, headers, retry/backoff policies, SSE framing) are encapsulated in codex-api and codex-client. Callers construct prompts/inputs using protocol types and work with typed streams of ResponseEvent or compacted ResponseItem values.