diff --git a/CHANGELOG.md b/CHANGELOG.md index 0894aca7fda..eeac2f8b820 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai - Docker: run the runtime image under `tini` so long-lived containers reap orphaned child processes and forward signals correctly. (#77885) Thanks @VintageAyu. - Google/Gemini: normalize retired `google/gemini-3-pro-preview` and `google-gemini-cli/gemini-3-pro-preview` selections to `google/gemini-3.1-pro-preview` before they are written to model config. - Google/Gemini: emit canonical `google/gemini-3.1-pro-preview` ids from configured provider catalog rows so model list and selection paths can test Gemini 3.1 instead of retired Gemini 3 Pro. +- Google/Gemini: normalize nested proxy-provider catalog ids like `google/gemini-3-pro-preview` to `google/gemini-3.1-pro-preview`, so Kilo-style configured catalogs test Gemini 3.1 instead of the retired Gemini 3 Pro id. - Amazon Bedrock: support `serviceTier` parameter for Bedrock models, configurable via `agents.defaults.params.serviceTier` or per-model in `agents.defaults.models`. Valid values: `default`, `flex`, `priority`, `reserved`. (#64512) Thanks @mobilinkd. - Control UI: read the Quick Settings exec policy badge from `tools.exec.security` instead of the non-schema `agents.defaults.exec.security` path, so configured `full`/`deny` values render accurately. Fixes #78311. Thanks @FriedBack. - Control UI/usage: add transcript-backed historical lineage rollups for rotated logical sessions, with current-instance vs historical-lineage scope controls and long-range presets so usage history stays visible after restarts and updates. Fixes #50701. Thanks @dev-gideon-llc and @BunsDev. diff --git a/docs/providers/kilocode.md b/docs/providers/kilocode.md index 217750c342c..f62225610c2 100644 --- a/docs/providers/kilocode.md +++ b/docs/providers/kilocode.md @@ -60,13 +60,13 @@ OpenClaw dynamically discovers available models from the Kilo Gateway at startup Any model available on the gateway can be used with the `kilocode/` prefix: -| Model ref | Notes | -| -------------------------------------- | ---------------------------------- | -| `kilocode/kilo/auto` | Default — smart routing | -| `kilocode/anthropic/claude-sonnet-4` | Anthropic via Kilo | -| `kilocode/openai/gpt-5.5` | OpenAI via Kilo | -| `kilocode/google/gemini-3-pro-preview` | Google via Kilo | -| ...and many more | Use `/models kilocode` to list all | +| Model ref | Notes | +| ---------------------------------------- | ---------------------------------- | +| `kilocode/kilo/auto` | Default — smart routing | +| `kilocode/anthropic/claude-sonnet-4` | Anthropic via Kilo | +| `kilocode/openai/gpt-5.5` | OpenAI via Kilo | +| `kilocode/google/gemini-3.1-pro-preview` | Google via Kilo | +| ...and many more | Use `/models kilocode` to list all | At startup, OpenClaw queries `GET https://api.kilo.ai/api/gateway/models` and merges diff --git a/extensions/kilocode/index.test.ts b/extensions/kilocode/index.test.ts index 1f7ce78b1bf..509f3083907 100644 --- a/extensions/kilocode/index.test.ts +++ b/extensions/kilocode/index.test.ts @@ -95,7 +95,7 @@ describe("kilocode provider plugin", () => { ).toEqual([ { provider: "kilocode", - id: "google/gemini-3-pro-preview", + id: "google/gemini-3.1-pro-preview", name: "Gemini 3 Pro Preview", input: ["text", "image"], reasoning: true, diff --git a/src/plugin-sdk/provider-catalog-shared.test.ts b/src/plugin-sdk/provider-catalog-shared.test.ts index 1a39fcfc56a..1eb68331314 100644 --- a/src/plugin-sdk/provider-catalog-shared.test.ts +++ b/src/plugin-sdk/provider-catalog-shared.test.ts @@ -59,7 +59,7 @@ describe("provider-catalog-shared native streaming usage compat", () => { }); describe("provider-catalog-shared configured catalog entries", () => { - it("preserves configured audio and video input modalities", () => { + it("preserves configured audio and video input modalities while normalizing nested Gemini ids", () => { expect( readConfiguredProviderCatalogEntries({ providerId: "kilocode", @@ -88,7 +88,7 @@ describe("provider-catalog-shared configured catalog entries", () => { ).toEqual([ { provider: "kilocode", - id: "google/gemini-3-pro-preview", + id: "google/gemini-3.1-pro-preview", name: "Gemini 3 Pro Preview", input: ["text", "image", "video", "audio"], reasoning: true, diff --git a/src/plugin-sdk/provider-catalog-shared.ts b/src/plugin-sdk/provider-catalog-shared.ts index 850178720e6..47c1bc121c8 100644 --- a/src/plugin-sdk/provider-catalog-shared.ts +++ b/src/plugin-sdk/provider-catalog-shared.ts @@ -13,6 +13,7 @@ import type { ModelCatalogModel, ModelCatalogTieredCost, } from "../model-catalog/types.js"; +import { normalizeGooglePreviewModelId } from "./provider-model-id-normalize.js"; import type { ModelProviderConfig } from "./provider-model-shared.js"; export type { ProviderCatalogContext, ProviderCatalogResult } from "../plugins/types.js"; @@ -158,6 +159,17 @@ function resolveConfiguredProviderModels( return Array.isArray(providerConfig.models) ? providerConfig.models : []; } +function normalizeConfiguredProviderCatalogModelId(id: string): string { + const trimmed = id.trim(); + const googlePrefix = "google/"; + if (!trimmed.startsWith(googlePrefix)) { + return trimmed; + } + const modelId = trimmed.slice(googlePrefix.length); + const normalizedModelId = normalizeGooglePreviewModelId(modelId); + return normalizedModelId === modelId ? trimmed : `${googlePrefix}${normalizedModelId}`; +} + export function readConfiguredProviderCatalogEntries(params: { config?: OpenClawConfig; providerId: string; @@ -174,7 +186,9 @@ export function readConfiguredProviderCatalogEntries(params: { if (!id) { continue; } - const name = (typeof model.name === "string" ? model.name : id).trim() || id; + const normalizedId = normalizeConfiguredProviderCatalogModelId(id); + const name = + (typeof model.name === "string" ? model.name : normalizedId).trim() || normalizedId; const contextWindow = typeof model.contextWindow === "number" && model.contextWindow > 0 ? model.contextWindow @@ -183,7 +197,7 @@ export function readConfiguredProviderCatalogEntries(params: { const input = normalizeConfiguredCatalogModelInput(model.input); entries.push({ provider, - id, + id: normalizedId, name, ...(contextWindow ? { contextWindow } : {}), ...(reasoning !== undefined ? { reasoning } : {}),