feat: add model aliases + minimax shortlist

This commit is contained in:
Peter Steinberger
2025-12-26 23:26:14 +00:00
parent ae9a8ce34c
commit 5c8e1b6eef
7 changed files with 178 additions and 28 deletions

View File

@@ -50,4 +50,26 @@ describe("resolveConfiguredModelRef", () => {
model: DEFAULT_MODEL,
});
});
it("resolves agent.model aliases when configured", () => {
const cfg = {
agent: {
model: "Opus",
modelAliases: {
Opus: "anthropic/claude-opus-4-5",
},
},
} satisfies ClawdisConfig;
const resolved = resolveConfiguredModelRef({
cfg,
defaultProvider: DEFAULT_PROVIDER,
defaultModel: DEFAULT_MODEL,
});
expect(resolved).toEqual({
provider: "anthropic",
model: "claude-opus-4-5",
});
});
});

View File

@@ -6,6 +6,15 @@ export type ModelRef = {
model: string;
};
export type ModelAliasIndex = {
byAlias: Map<string, { alias: string; ref: ModelRef }>;
byKey: Map<string, string[]>;
};
function normalizeAliasKey(value: string): string {
return value.trim().toLowerCase();
}
export function modelKey(provider: string, model: string) {
return `${provider}/${model}`;
}
@@ -26,6 +35,49 @@ export function parseModelRef(
return { provider, model };
}
export function buildModelAliasIndex(params: {
cfg: ClawdisConfig;
defaultProvider: string;
}): ModelAliasIndex {
const rawAliases = params.cfg.agent?.modelAliases ?? {};
const byAlias = new Map<string, { alias: string; ref: ModelRef }>();
const byKey = new Map<string, string[]>();
for (const [aliasRaw, targetRaw] of Object.entries(rawAliases)) {
const alias = aliasRaw.trim();
if (!alias) continue;
const parsed = parseModelRef(String(targetRaw ?? ""), params.defaultProvider);
if (!parsed) continue;
const aliasKey = normalizeAliasKey(alias);
byAlias.set(aliasKey, { alias, ref: parsed });
const key = modelKey(parsed.provider, parsed.model);
const existing = byKey.get(key) ?? [];
existing.push(alias);
byKey.set(key, existing);
}
return { byAlias, byKey };
}
export function resolveModelRefFromString(params: {
raw: string;
defaultProvider: string;
aliasIndex?: ModelAliasIndex;
}): { ref: ModelRef; alias?: string } | null {
const trimmed = params.raw.trim();
if (!trimmed) return null;
if (!trimmed.includes("/")) {
const aliasKey = normalizeAliasKey(trimmed);
const aliasMatch = params.aliasIndex?.byAlias.get(aliasKey);
if (aliasMatch) {
return { ref: aliasMatch.ref, alias: aliasMatch.alias };
}
}
const parsed = parseModelRef(trimmed, params.defaultProvider);
if (!parsed) return null;
return { ref: parsed };
}
export function resolveConfiguredModelRef(params: {
cfg: ClawdisConfig;
defaultProvider: string;
@@ -34,10 +86,16 @@ export function resolveConfiguredModelRef(params: {
const rawModel = params.cfg.agent?.model?.trim() || "";
if (rawModel) {
const trimmed = rawModel.trim();
if (trimmed.includes("/")) {
const parsed = parseModelRef(trimmed, params.defaultProvider);
if (parsed) return parsed;
}
const aliasIndex = buildModelAliasIndex({
cfg: params.cfg,
defaultProvider: params.defaultProvider,
});
const resolved = resolveModelRefFromString({
raw: trimmed,
defaultProvider: params.defaultProvider,
aliasIndex,
});
if (resolved) return resolved.ref;
// TODO(steipete): drop this fallback once provider-less agent.model is fully deprecated.
return { provider: "anthropic", model: trimmed };
}