From 8b88b886346460b1ab29edb03ad85bc3bb565a45 Mon Sep 17 00:00:00 2001 From: t Date: Tue, 2 Jun 2026 15:00:44 -0600 Subject: compaction --- src/config_file.zig | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) (limited to 'src/config_file.zig') diff --git a/src/config_file.zig b/src/config_file.zig index f7589fe..f1ae65f 100644 --- a/src/config_file.zig +++ b/src/config_file.zig @@ -158,11 +158,19 @@ pub const Config = struct { default_model_ref: ?ModelRef, tools: Policy, extensions: Policy, + /// `[compaction]` settings. `compaction_keep_verbatim` is the kept- + /// suffix token budget (null = use libpanto's default). `compaction_model` + /// is an owned `:` reference string for the optional + /// compaction-model override; `compaction_model_ref` slices into it. + compaction_keep_verbatim: ?u32 = null, + compaction_model: ?[]const u8 = null, + compaction_model_ref: ?ModelRef = null, pub fn deinit(self: *Config) void { for (self.providers) |p| p.deinit(self.allocator); self.allocator.free(self.providers); if (self.default_model) |m| self.allocator.free(m); + if (self.compaction_model) |m| self.allocator.free(m); self.tools.deinit(self.allocator); self.extensions.deinit(self.allocator); } @@ -440,6 +448,25 @@ fn resolve( } } + // `[compaction]` settings. + var compaction_keep_verbatim: ?u32 = null; + var compaction_model: ?[]const u8 = null; + errdefer if (compaction_model) |m| allocator.free(m); + var compaction_model_ref: ?ModelRef = null; + if (root.get("compaction")) |compaction_tbl| { + if (compaction_tbl.get("keep_verbatim")) |kv| { + if (kv.* == .integer and kv.integer > 0) { + compaction_keep_verbatim = @intCast(kv.integer); + } + } + if (compaction_tbl.get("model")) |model_v| { + if (model_v.* == .string) { + compaction_model = try allocator.dupe(u8, model_v.string); + compaction_model_ref = try parseModelRef(compaction_model.?); + } + } + } + const providers_slice = try providers.toOwnedSlice(allocator); errdefer { for (providers_slice) |p| p.deinit(allocator); @@ -458,6 +485,18 @@ fn resolve( if (!found) return error.UnknownDefaultProvider; } + // Validate the compaction-model override names a provider that resolved. + if (compaction_model_ref) |ref| { + var found = false; + for (providers_slice) |p| { + if (std.mem.eql(u8, p.name, ref.provider)) { + found = true; + break; + } + } + if (!found) return error.UnknownDefaultProvider; + } + return .{ .allocator = allocator, .providers = providers_slice, @@ -465,6 +504,9 @@ fn resolve( .default_model_ref = default_model_ref, .tools = tools, .extensions = extensions, + .compaction_keep_verbatim = compaction_keep_verbatim, + .compaction_model = compaction_model, + .compaction_model_ref = compaction_model_ref, }; } @@ -977,3 +1019,73 @@ test "loadFromPaths: missing files are skipped" { try testing.expectEqual(@as(usize, 0), cfg.providers.len); try testing.expect(cfg.default_model == null); } + +test "resolve: [compaction] keep_verbatim and model parse" { + const a = testing.allocator; + var env = emptyEnv(a); + defer env.deinit(); + try env.put("ANTHROPIC_API_KEY", "sk-ant-xyz"); + + const src = + \\[providers.anthropic] + \\style = "anthropic_messages" + \\base_url = "https://api.anthropic.com" + \\api_key_env_var = "ANTHROPIC_API_KEY" + \\ + \\[compaction] + \\keep_verbatim = 12345 + \\model = "anthropic:haiku" + ; + const doc = try parseDoc(a, src); + defer doc.deinit(); + + var cfg = try resolve(a, &env, doc.root); + defer cfg.deinit(); + try testing.expectEqual(@as(?u32, 12345), cfg.compaction_keep_verbatim); + try testing.expectEqualStrings("anthropic:haiku", cfg.compaction_model.?); + try testing.expectEqualStrings("anthropic", cfg.compaction_model_ref.?.provider); + try testing.expectEqualStrings("haiku", cfg.compaction_model_ref.?.model); +} + +test "resolve: [compaction] absent leaves defaults null" { + const a = testing.allocator; + var env = emptyEnv(a); + defer env.deinit(); + try env.put("ANTHROPIC_API_KEY", "sk-ant-xyz"); + + const src = + \\[providers.anthropic] + \\style = "anthropic_messages" + \\base_url = "https://api.anthropic.com" + \\api_key_env_var = "ANTHROPIC_API_KEY" + ; + const doc = try parseDoc(a, src); + defer doc.deinit(); + + var cfg = try resolve(a, &env, doc.root); + defer cfg.deinit(); + try testing.expect(cfg.compaction_keep_verbatim == null); + try testing.expect(cfg.compaction_model == null); + try testing.expect(cfg.compaction_model_ref == null); +} + +test "resolve: [compaction] model naming an unresolved provider errors" { + const a = testing.allocator; + var env = emptyEnv(a); + defer env.deinit(); + try env.put("ANTHROPIC_API_KEY", "sk-ant-xyz"); + + const src = + \\[providers.anthropic] + \\style = "anthropic_messages" + \\base_url = "https://api.anthropic.com" + \\api_key_env_var = "ANTHROPIC_API_KEY" + \\ + \\[compaction] + \\model = "openai:gpt-4o" + ; + const doc = try parseDoc(a, src); + defer doc.deinit(); + + try testing.expectError(error.UnknownDefaultProvider, resolve(a, &env, doc.root)); +} -- cgit v1.3