fix: canonicalize nested gemini catalog ids

This commit is contained in:
Peter Steinberger
2026-05-08 21:32:07 +01:00
parent 9bc8237f7b
commit 70723b306d
5 changed files with 27 additions and 12 deletions

View File

@@ -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.

View File

@@ -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 |
<Tip>
At startup, OpenClaw queries `GET https://api.kilo.ai/api/gateway/models` and merges

View File

@@ -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,

View File

@@ -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,

View File

@@ -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 } : {}),