fix(models): explain missing provider model registration

This commit is contained in:
Peter Steinberger
2026-05-10 06:24:57 +01:00
parent 74dc2a6830
commit bf7cc278d2
5 changed files with 72 additions and 3 deletions

View File

@@ -66,6 +66,7 @@ Docs: https://docs.openclaw.ai
- Agents/Anthropic: report 1M session context for Claude Opus/Sonnet 4 models even when local model config still advertises 200k, matching model discovery and preventing premature status/UI overflow. Fixes #66766.
- Models/OpenRouter: hide missing-auth direct provider rows in `/model status` when they are only duplicated by a nested OpenRouter model id such as `openrouter/google/...`, while preserving explicitly configured direct providers. Fixes #62317.
- Models: preserve an explicitly selected provider/model such as `opencode-go/deepseek-v4-pro` when another provider owns the same bare model alias. Fixes #79325.
- Models/config: explain missing `models.providers.<provider>.models[]` registration when a model exists only in `agents.defaults.models`, instead of returning a bare unknown-model error. Fixes #80089.
- Kimi Code: use Kimi's stable `kimi-for-coding` API model id in bundled catalog, onboarding, and docs while normalizing legacy `kimi-code` and `k2p5` refs. Fixes #79965.
- Volcengine/Kimi: strip provider-unsupported tool schema length and item constraint keywords for direct and coding-plan models so hosted Kimi runs do not reject message tools with `minLength`. Fixes #38817.
- DeepSeek: backfill V4 `reasoning_content` replay fields for unowned OpenAI-compatible proxy providers, preventing follow-up request failures outside the bundled DeepSeek and OpenRouter routes. Fixes #79608.

View File

@@ -348,6 +348,8 @@ Many of the bundled provider plugins below already publish a default catalog. Us
Gateway model capability checks also read explicit `models.providers.<id>.models[]` metadata. If a custom or proxy model accepts images, set `input: ["text", "image"]` on that model so WebChat and node-origin attachment paths pass images as native model inputs instead of text-only media refs.
`agents.defaults.models["provider/model"]` only controls model visibility, aliases, and per-model metadata for agents. It does not register a new runtime model by itself. For custom provider models, also add `models.providers.<provider>.models[]` with at least the matching `id`.
### Moonshot AI (Kimi)
Moonshot ships as a bundled provider plugin. Use the built-in provider by default, and add an explicit `models.providers.moonshot` entry only when you need to override the base URL or model metadata:

View File

@@ -1146,6 +1146,30 @@ describe("resolveModel", () => {
expect(result.model?.input).toEqual(["text"]);
});
it("explains when an agent model entry is missing provider model registration", async () => {
const cfg = {
agents: {
defaults: {
models: {
"microsoft-foundry/Kimi-K2.6-1": {
contextWindow: 262144,
maxOutputTokens: 16384,
},
},
},
},
} as unknown as OpenClawConfig;
const result = await resolveModelAsync("microsoft-foundry", "Kimi-K2.6-1", "/tmp/agent", cfg, {
runtimeHooks: createRuntimeHooks(),
skipPiDiscovery: true,
});
expect(result.error).toBe(
'Unknown model: microsoft-foundry/Kimi-K2.6-1. Found agents.defaults.models["microsoft-foundry/Kimi-K2.6-1"], but no matching models.providers["microsoft-foundry"].models[] entry. Add { "id": "Kimi-K2.6-1" } to models.providers["microsoft-foundry"].models[] to register this provider model.',
);
});
it("repairs stale text-only Foundry fallback rows for GPT-family models", () => {
const cfg = {
models: {

View File

@@ -1223,6 +1223,14 @@ function buildUnknownModelError(params: {
return suppressed;
}
const base = `Unknown model: ${params.provider}/${params.modelId}`;
const registrationHint = buildMissingProviderModelRegistrationHint({
provider: params.provider,
modelId: params.modelId,
cfg: params.cfg,
});
if (registrationHint) {
return `${base}. ${registrationHint}`;
}
const runtimeHooks = params.runtimeHooks ?? DEFAULT_PROVIDER_RUNTIME_HOOKS;
const hint = runtimeHooks.buildProviderUnknownModelHintWithPlugin({
provider: params.provider,
@@ -1240,3 +1248,37 @@ function buildUnknownModelError(params: {
});
return hint ? `${base}. ${hint}` : base;
}
function buildMissingProviderModelRegistrationHint(params: {
provider: string;
modelId: string;
cfg?: OpenClawConfig;
}): string | undefined {
const configuredModels = params.cfg?.agents?.defaults?.models;
if (!configuredModels) {
return undefined;
}
const agentModelKey = modelKey(params.provider, params.modelId);
if (
!configuredModels[agentModelKey] &&
!configuredModels[`${params.provider}/${params.modelId}`]
) {
return undefined;
}
const providerConfig = findNormalizedProviderValue(
params.cfg?.models?.providers,
params.provider,
) as { models?: unknown } | undefined;
const providerModels = Array.isArray(providerConfig?.models) ? providerConfig.models : [];
const hasProviderModel = providerModels.some((entry) => {
if (!entry || typeof entry !== "object" || !("id" in entry)) {
return false;
}
const id = (entry as { id?: unknown }).id;
return typeof id === "string" && id === params.modelId;
});
if (hasProviderModel) {
return undefined;
}
return `Found agents.defaults.models["${agentModelKey}"], but no matching models.providers["${params.provider}"].models[] entry. Add { "id": "${params.modelId}" } to models.providers["${params.provider}"].models[] to register this provider model.`;
}

View File

@@ -515,7 +515,7 @@ describe("/model chat UX", () => {
},
},
},
} as OpenClawConfig,
} as unknown as OpenClawConfig,
allowedModelCatalog: [
{ provider: "anthropic", id: "claude-opus-4-6", name: "Claude Opus 4.5" },
{ provider: "openai", id: "gpt-4.1-mini", name: "GPT-4.1 mini" },
@@ -546,7 +546,7 @@ describe("/model chat UX", () => {
},
},
},
} as OpenClawConfig,
} as unknown as OpenClawConfig,
allowedModelCatalog: [
{ provider: "google", id: "gemini-3-flash-preview", name: "Gemini 3 Flash" },
{
@@ -584,7 +584,7 @@ describe("/model chat UX", () => {
},
},
},
} as OpenClawConfig,
} as unknown as OpenClawConfig,
allowedModelCatalog: [
{ provider: "google", id: "gemini-3-flash-preview", name: "Gemini 3 Flash" },
{