fix: enable image input for Kimi K2.5 and refresh stale config model definitions

Kimi K2.5 supports vision but was declared text-only in both the synthetic
catalog and the moonshot provider.  More importantly, onboarding bakes the
full model catalog into openclaw.json, and the old merge logic let stale
config definitions shadow code updates — so even after fixing the catalog,
existing users would never see the change.

Fix mergeProviderModels so code-defined capability fields (input, reasoning,
contextWindow, maxTokens) override stale config entries while preserving
user-specific fields (cost, headers, compat, apiKey).

Follow-up to 5e635c9 (#4407).
This commit is contained in:
manikv12 2026-01-30 01:23:54 -06:00
parent 9025da2296
commit 8bca9fd828
3 changed files with 28 additions and 13 deletions

View File

@ -288,7 +288,7 @@ function buildMoonshotProvider(): ProviderConfig {
id: MOONSHOT_DEFAULT_MODEL_ID,
name: "Kimi K2.5",
reasoning: false,
input: ["text"],
input: ["text", "image"],
cost: MOONSHOT_DEFAULT_COST,
contextWindow: MOONSHOT_DEFAULT_CONTEXT_WINDOW,
maxTokens: MOONSHOT_DEFAULT_MAX_TOKENS,

View File

@ -29,18 +29,33 @@ function mergeProviderModels(implicit: ProviderConfig, explicit: ProviderConfig)
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;
}),
];
// Build a lookup of implicit (code-defined) models by ID so we can
// refresh stale config-written definitions with up-to-date capability
// fields (input, reasoning, contextWindow, maxTokens) while preserving
// any user-specific overrides (cost, headers, compat).
const implicitById = new Map(
implicitModels.map((m) => [getId(m), m] as const).filter(([id]) => id),
);
const seen = new Set<string>();
const mergedModels = explicitModels.map((explicitModel) => {
const id = getId(explicitModel);
if (id) seen.add(id);
const implicitModel = id ? implicitById.get(id) : undefined;
if (!implicitModel) return explicitModel;
// Merge: code-defined capability fields override stale config values,
// while config-only fields (cost, headers, compat) are preserved.
return { ...explicitModel, ...implicitModel };
});
// Append implicit models whose IDs are not present in the explicit list.
for (const model of implicitModels) {
const id = getId(model);
if (!id || seen.has(id)) continue;
seen.add(id);
mergedModels.push(model);
}
return {
...implicit,

View File

@ -103,7 +103,7 @@ export const SYNTHETIC_MODEL_CATALOG = [
id: "hf:moonshotai/Kimi-K2.5",
name: "Kimi K2.5",
reasoning: true,
input: ["text"],
input: ["text", "image"],
contextWindow: 256000,
maxTokens: 8192,
},