diff options
| author | t <t@tjp.lol> | 2026-06-12 13:57:26 -0600 |
|---|---|---|
| committer | t <t@tjp.lol> | 2026-06-12 14:27:18 -0600 |
| commit | ecadcfa7a1e549d6ddb8611bdf44cac852064cea (patch) | |
| tree | 45b11c9bff35f49bdf1f7e14c89cd244453ace0c /src/config_file.zig | |
| parent | 25ddca9ef0de807e45be1f432f841336e4be29d2 (diff) | |
anthropic prompt caching
Diffstat (limited to 'src/config_file.zig')
| -rw-r--r-- | src/config_file.zig | 74 |
1 files changed, 73 insertions, 1 deletions
diff --git a/src/config_file.zig b/src/config_file.zig index f5ed79c..1858520 100644 --- a/src/config_file.zig +++ b/src/config_file.zig @@ -25,7 +25,10 @@ //! by `api_key_env_var`. A provider whose only key source is an absent //! env var is **silently dropped** — this is what lets the base layer //! ship default OpenAI/Anthropic providers that simply don't appear when -//! the user hasn't exported the corresponding key. +//! the user hasn't exported the corresponding key. An `anthropic_messages` +//! provider may also set `prompt_cache = <bool>` (default true) to control +//! the advancing `cache_control` breakpoint; it is ignored for +//! `openai_chat` providers. //! - `defaults.model` is parsed as `<provider_name>:<model_alias>`. //! - `tools` / `extensions` allow- and deny-lists are captured verbatim //! (as glob pattern lists). A pattern appearing in both allow and deny @@ -67,6 +70,10 @@ pub const Provider = struct { style: APIStyle, base_url: []const u8, api_key: []const u8, + /// anthropic_messages only. Place one advancing `cache_control` + /// breakpoint on each request. Defaults to the library default (true); + /// ignored for `openai_chat` providers. + prompt_cache: bool = true, pub fn deinit(self: Provider, alloc: Allocator) void { alloc.free(self.name); @@ -150,6 +157,7 @@ pub fn buildProviderConfig( .effort = if (def_opt) |d| d.effort else .medium, .thinking_budget_tokens = if (def_opt) |d| d.thinking_budget_tokens else 32_000, .thinking_interleaved = if (def_opt) |d| d.thinking_interleaved else false, + .prompt_cache = prov.prompt_cache, } }, } } @@ -545,11 +553,20 @@ fn resolveProvider( return null; }; + // anthropic_messages only; absent => library default (true). + const prompt_cache: bool = blk: { + if (val.get("prompt_cache")) |pc| { + if (pc.asBool()) |b| break :blk b; + } + break :blk true; + }; + return .{ .name = try allocator.dupe(u8, name), .style = style, .base_url = try allocator.dupe(u8, base_url), .api_key = try allocator.dupe(u8, api_key), + .prompt_cache = prompt_cache, }; } @@ -732,6 +749,61 @@ test "resolve: env-backed provider resolves when env var is set" { try testing.expectEqualStrings("sk-ant-xyz", p.api_key); } +test "resolve: prompt_cache defaults true and is parsed when set" { + const a = testing.allocator; + var env = emptyEnv(a); + defer env.deinit(); + + const src = + \\[providers.anthropic] + \\style = "anthropic_messages" + \\base_url = "https://api.anthropic.com" + \\api_key = "sk-ant" + \\ + \\[providers.anthropic_nocache] + \\style = "anthropic_messages" + \\base_url = "https://api.anthropic.com" + \\api_key = "sk-ant" + \\prompt_cache = false + ; + const doc = try parseDoc(a, src); + defer doc.deinit(); + + var cfg = try resolve(a, &env, doc.root); + defer cfg.deinit(); + + // Absent => library default (true). + try testing.expectEqual(true, cfg.provider("anthropic").?.prompt_cache); + // Explicit false is honoured. + try testing.expectEqual(false, cfg.provider("anthropic_nocache").?.prompt_cache); +} + +test "buildProviderConfig: prompt_cache flows from provider into anthropic config" { + const a = testing.allocator; + var env = emptyEnv(a); + defer env.deinit(); + + const src = + \\[providers.anthropic] + \\style = "anthropic_messages" + \\base_url = "https://api.anthropic.com" + \\api_key = "sk-ant" + \\prompt_cache = false + ; + const doc = try parseDoc(a, src); + defer doc.deinit(); + + var cfg = try resolve(a, &env, doc.root); + defer cfg.deinit(); + + var defs = models_toml.ModelRegistry.init(a); + defer defs.deinit(); + + const pc = try buildProviderConfig(&cfg, &defs, .{ .provider = "anthropic", .model = "sonnet" }); + try testing.expectEqual(APIStyle.anthropic_messages, pc.style()); + try testing.expectEqual(false, pc.anthropic_messages.prompt_cache); +} + test "resolve: inline api_key wins over env var" { const a = testing.allocator; var env = emptyEnv(a); |
