fix(image): resolve custom provider model IDs

This commit is contained in:
Peter Steinberger
2026-04-22 20:25:52 +01:00
parent 81f247b1ae
commit 976398715f
4 changed files with 60 additions and 2 deletions

View File

@@ -145,5 +145,11 @@ export function resolveProviderVisionModelFromConfig(params: {
const models = providerCfg?.models ?? [];
const picked = models.find((m) => Boolean((m?.id ?? "").trim()) && m.input?.includes("image"));
const id = (picked?.id ?? "").trim();
return id ? `${params.provider}/${id}` : null;
if (!id) {
return null;
}
const slash = id.indexOf("/");
const idProvider = slash === -1 ? "" : normalizeLowercaseStringOrEmpty(id.slice(0, slash));
const selectedProvider = normalizeLowercaseStringOrEmpty(params.provider);
return idProvider && idProvider === selectedProvider ? id : `${params.provider}/${id}`;
}

View File

@@ -708,6 +708,35 @@ describe("image tool implicit imageModel config", () => {
});
});
it("does not double-prefix custom provider model IDs that already include the provider", async () => {
await withTempAgentDir(async (agentDir) => {
await writeAuthProfiles(agentDir, {
version: 1,
profiles: {
"kimchi:default": { type: "api_key", provider: "kimchi", key: "sk-test" },
},
});
const cfg: OpenClawConfig = {
agents: { defaults: { model: { primary: "kimchi/text-1" } } },
models: {
providers: {
kimchi: {
baseUrl: "https://example.com",
models: [
makeModelDefinition("kimchi/text-1", ["text"]),
makeModelDefinition("kimchi/vision-1", ["text", "image"]),
],
},
},
},
};
expect(resolveImageModelConfigForTool({ cfg, agentDir })).toEqual({
primary: "kimchi/vision-1",
});
});
});
it("pairs a provider when config uses an alias key", async () => {
await withTempAgentDir(async (agentDir) => {
await writeAuthProfiles(agentDir, {

View File

@@ -27,4 +27,21 @@ describe("resolveModelFromRegistry", () => {
}),
).toThrow("Unknown model: ollama/qwen3.5:397b-cloud");
});
it("falls back to provider-prefixed custom model IDs", () => {
const foundModel = { provider: "kimchi", id: "kimchi/claude-opus-4-6" };
const find = vi.fn().mockReturnValueOnce(null).mockReturnValueOnce(foundModel);
const result = resolveModelFromRegistry({
modelRegistry: { find },
provider: "kimchi",
modelId: "claude-opus-4-6",
});
expect(find.mock.calls).toEqual([
["kimchi", "claude-opus-4-6"],
["kimchi", "kimchi/claude-opus-4-6"],
]);
expect(result).toBe(foundModel);
});
});

View File

@@ -402,10 +402,16 @@ export function resolveModelFromRegistry(params: {
modelId: string;
}): Model<Api> {
const resolvedRef = normalizeModelRef(params.provider, params.modelId);
const model = params.modelRegistry.find(
let model = params.modelRegistry.find(
resolvedRef.provider,
resolvedRef.model,
) as Model<Api> | null;
if (!model && !resolvedRef.model.includes("/")) {
model = params.modelRegistry.find(
resolvedRef.provider,
`${resolvedRef.provider}/${resolvedRef.model}`,
) as Model<Api> | null;
}
if (!model) {
throw new Error(`Unknown model: ${resolvedRef.provider}/${resolvedRef.model}`);
}