diff options
| author | t <t@tjp.lol> | 2026-06-13 23:45:59 -0600 |
|---|---|---|
| committer | t <t@tjp.lol> | 2026-06-15 15:08:32 -0600 |
| commit | 02b4c7a35ac0b714bc045d54fb4bb45d1ce4e490 (patch) | |
| tree | 134196a82dd6fdd55b735be4c0cf38428c85038d /src/config_file.zig | |
| parent | 71643a5d69ffc40882c9fcde3cc8a3bcf02d7396 (diff) | |
Add Codex Responses support and session debugging
Teach provider config and auth resolution about the Codex Responses
dialect, including user-facing `style = "openai_responses"` with
`dialect = "codex"`. Serialize and parse Responses traffic with
provider-specific reasoning replay, assistant phase metadata, and robust
function-call assembly keyed by `output_index` so streamed tool inputs
survive proxy quirks and empty terminal payloads.
Also persist thinking origins and message metadata across sessions, add
the Anthropic interleaved-thinking header switch, write per-session
debug logs, and improve the TUI and scripts for inspecting tool output
and session costs.
Diffstat (limited to 'src/config_file.zig')
| -rw-r--r-- | src/config_file.zig | 69 |
1 files changed, 68 insertions, 1 deletions
diff --git a/src/config_file.zig b/src/config_file.zig index 1823177..8f6725c 100644 --- a/src/config_file.zig +++ b/src/config_file.zig @@ -77,6 +77,10 @@ pub const Provider = struct { /// the name used on the left of a `<provider>:<model>` reference. name: []const u8, style: APIStyle, + /// User-facing sub-dialect for styles that share a protocol family. + /// Currently only `style = "openai_responses"` supports `dialect = "codex"`, + /// which maps to internal `APIStyle.openai_codex_responses`. + dialect: ?[]const u8 = null, base_url: []const u8, /// The `auth.<name>` session this provider draws its credential from. auth_name: []const u8, @@ -90,6 +94,7 @@ pub const Provider = struct { pub fn deinit(self: Provider, alloc: Allocator) void { alloc.free(self.name); + if (self.dialect) |d| alloc.free(d); alloc.free(self.base_url); alloc.free(self.auth_name); // `extra_headers` lives in the Config auth arena; not freed here. @@ -213,6 +218,14 @@ pub fn buildProviderConfig( .max_tokens = max_tokens, .extra_headers = prov.extra_headers, } }, + .openai_codex_responses => return .{ .openai_codex_responses = .{ + .api_key = api_key, + .base_url = prov.base_url, + .model = wire_model, + .reasoning = reasoning, + .max_tokens = max_tokens, + .extra_headers = prov.extra_headers, + } }, } } @@ -677,7 +690,16 @@ fn resolveProvider( const style_str = (val.get("style") orelse return error.InvalidProvider).asString() orelse return error.InvalidProvider; - const style = std.meta.stringToEnum(APIStyle, style_str) orelse return error.InvalidProvider; + var style = std.meta.stringToEnum(APIStyle, style_str) orelse return error.InvalidProvider; + if (style == .openai_codex_responses) return error.InvalidProvider; + const dialect = if (tomlStr(val, "dialect")) |d| d else null; + if (dialect) |d| { + if (std.mem.eql(u8, style_str, "openai_responses") and std.mem.eql(u8, d, "codex")) { + style = .openai_codex_responses; + } else { + return error.InvalidProvider; + } + } const base_url = (val.get("base_url") orelse return error.InvalidProvider).asString() orelse return error.InvalidProvider; @@ -699,6 +721,7 @@ fn resolveProvider( return .{ .name = try allocator.dupe(u8, name), .style = style, + .dialect = if (dialect) |d| try allocator.dupe(u8, d) else null, .base_url = try allocator.dupe(u8, base_url), .auth_name = try allocator.dupe(u8, auth_name), .prompt_cache = prompt_cache, @@ -1231,6 +1254,50 @@ test "resolve: provider extra_headers flow into the built provider config" { try testing.expectEqualStrings("tok", pc.openai_chat.api_key); } +test "resolve: openai_responses codex dialect maps to internal API style" { + const a = testing.allocator; + var env = emptyEnv(a); + defer env.deinit(); + const src = + \\[providers.codex] + \\style = "openai_responses" + \\dialect = "codex" + \\base_url = "https://chatgpt.com/backend-api" + \\auth = "k" + \\ + \\[auth.k] + \\key = "tok" + ; + const doc = try parseDoc(a, src); + defer doc.deinit(); + var cfg = try resolve(a, &env, doc.root); + defer cfg.deinit(); + + try testing.expectEqual(APIStyle.openai_codex_responses, cfg.provider("codex").?.style); + var defs = models_toml.ModelRegistry.init(a); + defer defs.deinit(); + const pc = try buildProviderConfig(&cfg, &defs, .{ .provider = "codex", .model = "gpt-5.5" }); + try testing.expectEqual(APIStyle.openai_codex_responses, pc.style()); +} + +test "resolve: internal openai_codex_responses style is not user-facing" { + const a = testing.allocator; + var env = emptyEnv(a); + defer env.deinit(); + const src = + \\[providers.codex] + \\style = "openai_codex_responses" + \\base_url = "https://chatgpt.com/backend-api" + \\auth = "k" + \\ + \\[auth.k] + \\key = "tok" + ; + const doc = try parseDoc(a, src); + defer doc.deinit(); + try testing.expectError(error.InvalidProvider, resolve(a, &env, doc.root)); +} + test "resolve: prompt_cache defaults true and is parsed when set" { const a = testing.allocator; var env = emptyEnv(a); |
