diff options
| author | t <t@tjp.lol> | 2026-06-13 12:22:18 -0600 |
|---|---|---|
| committer | t <t@tjp.lol> | 2026-06-15 15:08:32 -0600 |
| commit | eb19fd812e15de5a6930daf2fcd0b2b0757687c7 (patch) | |
| tree | ace85c641e48836fe45b289141213fe8984ccb52 /src | |
| parent | 9f983c5d0ecc1c12f15eadf1add123e24995b660 (diff) | |
auth: openai_responses provider for Codex subscription (Tier 2)
Add the OpenAI Responses API wire dialect (openai_responses APIStyle +
OpenAIResponsesConfig), a request serializer (instructions/input items,
flat function tools, reasoning, store:false, include encrypted reasoning),
and a streaming state machine over the typed response.* SSE events mapping
to the shared block model. Wire dispatch, buildProviderConfig, the C-ABI
free switch, and the reasoning selectors. Ship a commented Codex example in
the default config.
Implemented from the Responses API docs + the open-source Codex client and
covered by fixture tests; live verification against a ChatGPT plan is still
required (documented).
Diffstat (limited to 'src')
| -rw-r--r-- | src/auth_manager.zig | 5 | ||||
| -rw-r--r-- | src/config_file.zig | 8 | ||||
| -rw-r--r-- | src/luarocks_runtime.zig | 24 | ||||
| -rw-r--r-- | src/tui_selectors.zig | 16 |
4 files changed, 48 insertions, 5 deletions
diff --git a/src/auth_manager.zig b/src/auth_manager.zig index 263ec54..87b20ae 100644 --- a/src/auth_manager.zig +++ b/src/auth_manager.zig @@ -189,6 +189,11 @@ fn patchCredential( c.base_url = base_url; c.extra_headers = merged; }, + .openai_responses => |*c| { + c.api_key = cred.api_key; + c.base_url = base_url; + c.extra_headers = merged; + }, } } diff --git a/src/config_file.zig b/src/config_file.zig index 1277661..cac003a 100644 --- a/src/config_file.zig +++ b/src/config_file.zig @@ -196,6 +196,14 @@ pub fn buildProviderConfig( .prompt_cache = prov.prompt_cache, .extra_headers = prov.extra_headers, } }, + .openai_responses => return .{ .openai_responses = .{ + .api_key = api_key, + .base_url = prov.base_url, + .model = wire_model, + .reasoning = reasoning, + .max_tokens = max_tokens, + .extra_headers = prov.extra_headers, + } }, } } diff --git a/src/luarocks_runtime.zig b/src/luarocks_runtime.zig index ac34ee0..3cfd02f 100644 --- a/src/luarocks_runtime.zig +++ b/src/luarocks_runtime.zig @@ -347,6 +347,30 @@ const default_base_config = \\# Editor-Plugin-Version = "copilot-chat/0.26.7" \\# Copilot-Integration-Id = "vscode-chat" \\ + \\# OpenAI Codex via a ChatGPT subscription (device login). Codex speaks the + \\# OpenAI Responses API, not Chat Completions, so style = "openai_responses". + \\# NOTE: this path is implemented but not yet verified against a live + \\# ChatGPT plan — see docs/oauth-provider-auth.md. + \\# + \\# [providers.codex] + \\# style = "openai_responses" + \\# base_url = "https://chatgpt.com/backend-api/codex" + \\# auth = "openai_codex" + \\# + \\# [providers.codex.extra_headers] + \\# OpenAI-Beta = "responses=experimental" + \\# originator = "codex_cli_rs" + \\# + \\# [auth.openai_codex] + \\# type = "oauth_device" + \\# dialect = "codex" + \\# client_id = "app_EMoamEEZ73f0CkXaXp7hrann" + \\# device_code_url = "https://auth.openai.com/api/accounts/deviceauth/usercode" + \\# device_poll_url = "https://auth.openai.com/api/accounts/deviceauth/token" + \\# verification_url = "https://auth.openai.com/codex/device" + \\# token_url = "https://auth.openai.com/oauth/token" + \\# account_id_jwt_claim = "https://api.openai.com/auth" + \\ \\# Tool/extension availability (glob patterns). Empty allow = allow all. \\# [tools] \\# allow = ["std.*"] diff --git a/src/tui_selectors.zig b/src/tui_selectors.zig index a37acb8..590d321 100644 --- a/src/tui_selectors.zig +++ b/src/tui_selectors.zig @@ -48,7 +48,10 @@ pub const ReasoningOption = struct { detail: []const u8, value: Value, - pub const Value = union(APIStyle) { + /// Two payload families, decoupled from `APIStyle`: `openai_chat` carries + /// a `reasoning` effort (shared by the `openai_chat` and `openai_responses` + /// styles), `anthropic_messages` carries a thinking selection. + pub const Value = union(enum) { /// OpenAI `reasoning` enum value. openai_chat: ReasoningEffort, /// Anthropic thinking selection: either disabled, or adaptive at an @@ -85,7 +88,8 @@ pub const ReasoningOption = struct { /// The reasoning options offered for a provider config's API style. pub fn forStyle(style: APIStyle) []const ReasoningOption { return switch (style) { - .openai_chat => &openai, + // The Responses style takes the same reasoning-effort options. + .openai_chat, .openai_responses => &openai, .anthropic_messages => &anthropic, }; } @@ -95,6 +99,7 @@ pub const ReasoningOption = struct { switch (self.value) { .openai_chat => |r| switch (cfg.*) { .openai_chat => |*c| c.reasoning = r, + .openai_responses => |*c| c.reasoning = r, .anthropic_messages => {}, }, .anthropic_messages => |sel| switch (cfg.*) { @@ -105,7 +110,7 @@ pub const ReasoningOption = struct { c.effort = e; }, }, - .openai_chat => {}, + .openai_chat, .openai_responses => {}, }, } } @@ -114,7 +119,8 @@ pub const ReasoningOption = struct { /// preselect the picker on the active value). pub fn matches(self: ReasoningOption, cfg: ProviderConfig) bool { switch (self.value) { - .openai_chat => |r| return cfg == .openai_chat and cfg.openai_chat.reasoning == r, + .openai_chat => |r| return (cfg == .openai_chat and cfg.openai_chat.reasoning == r) or + (cfg == .openai_responses and cfg.openai_responses.reasoning == r), .anthropic_messages => |sel| { if (cfg != .anthropic_messages) return false; const c = cfg.anthropic_messages; @@ -167,7 +173,7 @@ pub fn adaptiveFallback(cfg: *ProviderConfig) bool { c.thinking_budget_tokens = budget; return true; }, - .openai_chat => return false, + .openai_chat, .openai_responses => return false, } } |
