fix(models): preserve implicit vision models

This commit is contained in:
Peter Steinberger
2026-01-12 19:08:53 +00:00
parent bb7397c636
commit a0a7e74a62
2 changed files with 65 additions and 6 deletions

View File

@@ -487,9 +487,14 @@ describe("models config", () => {
const modelPath = path.join(resolveClawdbotAgentDir(), "models.json");
const raw = await fs.readFile(modelPath, "utf8");
const parsed = JSON.parse(raw) as {
providers: Record<string, { apiKey?: string }>;
providers: Record<
string,
{ apiKey?: string; models?: Array<{ id: string }> }
>;
};
expect(parsed.providers.minimax?.apiKey).toBe("MINIMAX_API_KEY");
const ids = parsed.providers.minimax?.models?.map((model) => model.id);
expect(ids).toContain("MiniMax-VL-01");
} finally {
if (prevKey === undefined) delete process.env.MINIMAX_API_KEY;
else process.env.MINIMAX_API_KEY = prevKey;

View File

@@ -25,6 +25,57 @@ function isRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value && typeof value === "object" && !Array.isArray(value));
}
function mergeProviderModels(
implicit: ProviderConfig,
explicit: ProviderConfig,
): ProviderConfig {
const implicitModels = Array.isArray(implicit.models) ? implicit.models : [];
const explicitModels = Array.isArray(explicit.models) ? explicit.models : [];
if (implicitModels.length === 0) return { ...implicit, ...explicit };
const getId = (model: unknown): string => {
if (!model || typeof model !== "object") return "";
const id = (model as { id?: unknown }).id;
return typeof id === "string" ? id.trim() : "";
};
const seen = new Set(explicitModels.map(getId).filter(Boolean));
const mergedModels = [
...explicitModels,
...implicitModels.filter((model) => {
const id = getId(model);
if (!id) return false;
if (seen.has(id)) return false;
seen.add(id);
return true;
}),
];
return {
...implicit,
...explicit,
models: mergedModels,
};
}
function mergeProviders(params: {
implicit?: Record<string, ProviderConfig> | null;
explicit?: Record<string, ProviderConfig> | null;
}): Record<string, ProviderConfig> {
const out: Record<string, ProviderConfig> = params.implicit
? { ...params.implicit }
: {};
for (const [key, explicit] of Object.entries(params.explicit ?? {})) {
const providerKey = key.trim();
if (!providerKey) continue;
const implicit = out[providerKey];
out[providerKey] = implicit
? mergeProviderModels(implicit, explicit)
: explicit;
}
return out;
}
async function readJson(pathname: string): Promise<unknown> {
try {
const raw = await fs.readFile(pathname, "utf8");
@@ -101,12 +152,15 @@ export async function ensureClawdbotModelsJson(
? agentDirOverride.trim()
: resolveClawdbotAgentDir();
const explicitProviders = cfg.models?.providers ?? {};
const explicitProviders = (cfg.models?.providers ?? {}) as Record<
string,
ProviderConfig
>;
const implicitProviders = resolveImplicitProviders({ agentDir });
const providers: Record<string, ProviderConfig> = {
...implicitProviders,
...explicitProviders,
};
const providers: Record<string, ProviderConfig> = mergeProviders({
implicit: implicitProviders,
explicit: explicitProviders,
});
const implicitCopilot = await maybeBuildCopilotProvider({ agentDir });
if (implicitCopilot && !providers["github-copilot"]) {
providers["github-copilot"] = implicitCopilot;