summaryrefslogtreecommitdiff
path: root/src/config_file.zig
diff options
context:
space:
mode:
authort <t@tjp.lol>2026-06-15 16:14:55 -0600
committert <t@tjp.lol>2026-06-15 16:15:10 -0600
commitd9d4131cb321a9ce29b000cd7aed8ced9a18efc1 (patch)
tree5cc65cf6b0d226daa716f00aa6f667e8cf6dfa61 /src/config_file.zig
parent02b4c7a35ac0b714bc045d54fb4bb45d1ce4e490 (diff)
model registry sync via models.dev
Diffstat (limited to 'src/config_file.zig')
-rw-r--r--src/config_file.zig35
1 files changed, 33 insertions, 2 deletions
diff --git a/src/config_file.zig b/src/config_file.zig
index 8f6725c..a70a832 100644
--- a/src/config_file.zig
+++ b/src/config_file.zig
@@ -35,8 +35,10 @@
//! currently resolved. 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. `extra_headers` (a table of string headers) rides on the
-//! model request.
+//! providers. `model_catalog_name = <string>` optionally maps the
+//! provider to an external model catalog key (sync tooling otherwise
+//! falls back to the provider name). `extra_headers` (a table of string
+//! headers) rides on the model request.
//! - `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
@@ -84,6 +86,9 @@ pub const Provider = struct {
base_url: []const u8,
/// The `auth.<name>` session this provider draws its credential from.
auth_name: []const u8,
+ /// Optional external model-catalog provider key (e.g. `github-copilot`
+ /// on models.dev). Absent => sync tooling falls back to `name`.
+ model_catalog_name: ?[]const u8 = null,
/// anthropic_messages only. Place one advancing `cache_control`
/// breakpoint on each request. Defaults to the library default (true);
/// ignored for `openai_chat` providers.
@@ -97,6 +102,7 @@ pub const Provider = struct {
if (self.dialect) |d| alloc.free(d);
alloc.free(self.base_url);
alloc.free(self.auth_name);
+ if (self.model_catalog_name) |m| alloc.free(m);
// `extra_headers` lives in the Config auth arena; not freed here.
}
};
@@ -708,6 +714,8 @@ fn resolveProvider(
const auth_name = (val.get("auth") orelse return error.MissingProviderAuth).asString() orelse
return error.MissingProviderAuth;
+ const model_catalog_name = tomlStr(val, "model_catalog_name");
+
// anthropic_messages only; absent => library default (true).
const prompt_cache: bool = blk: {
if (val.get("prompt_cache")) |pc| {
@@ -724,6 +732,7 @@ fn resolveProvider(
.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),
+ .model_catalog_name = if (model_catalog_name) |m| try allocator.dupe(u8, m) else null,
.prompt_cache = prompt_cache,
.extra_headers = extra_headers,
};
@@ -1254,6 +1263,28 @@ test "resolve: provider extra_headers flow into the built provider config" {
try testing.expectEqualStrings("tok", pc.openai_chat.api_key);
}
+test "resolve: provider model_catalog_name is captured" {
+ const a = testing.allocator;
+ var env = emptyEnv(a);
+ defer env.deinit();
+ const src =
+ \\[providers.copilot]
+ \\style = "openai_chat"
+ \\base_url = "https://api.individual.githubcopilot.com"
+ \\auth = "k"
+ \\model_catalog_name = "github-copilot"
+ \\
+ \\[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.expectEqualStrings("github-copilot", cfg.provider("copilot").?.model_catalog_name.?);
+}
+
test "resolve: openai_responses codex dialect maps to internal API style" {
const a = testing.allocator;
var env = emptyEnv(a);