mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 01:50:43 +00:00
fix: normalize gemini 3 pro preview config
This commit is contained in:
@@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Changes
|
||||
|
||||
- Google/Gemini: normalize retired `google/gemini-3-pro-preview` catalog selections to `google/gemini-3.1-pro-preview` before they are written to model config.
|
||||
- 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.
|
||||
- Agents/failover: harden state-aware lane suspension by persisting quota resume transitions, restoring configured lane concurrency, preserving non-quota failure reasons, and exporting model failover events through diagnostics OTLP. Thanks @BunsDev.
|
||||
|
||||
@@ -2,6 +2,14 @@ import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
type GoogleManifest = {
|
||||
modelIdNormalization?: {
|
||||
providers?: Record<
|
||||
string,
|
||||
{
|
||||
aliases?: Record<string, string>;
|
||||
}
|
||||
>;
|
||||
};
|
||||
modelCatalog?: {
|
||||
suppressions?: Array<{
|
||||
provider?: string;
|
||||
@@ -83,4 +91,14 @@ describe("google manifest model catalog", () => {
|
||||
expect(suppressionRefs).not.toContain("google/gemini-2.5-pro");
|
||||
expect(suppressionRefs).not.toContain("google/gemini-3.1-pro-preview");
|
||||
});
|
||||
|
||||
it("normalizes retired Gemini 3 Pro aliases for all Google chat providers", () => {
|
||||
const manifest = loadManifest();
|
||||
|
||||
for (const provider of GOOGLE_CHAT_PROVIDERS) {
|
||||
expect(manifest.modelIdNormalization?.providers?.[provider]?.aliases).toMatchObject({
|
||||
"gemini-3-pro": "gemini-3.1-pro-preview",
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,6 +18,16 @@
|
||||
"gemini-3.1-flash-preview": "gemini-3-flash-preview"
|
||||
}
|
||||
},
|
||||
"google-gemini-cli": {
|
||||
"aliases": {
|
||||
"gemini-3-pro": "gemini-3.1-pro-preview",
|
||||
"gemini-3-flash": "gemini-3-flash-preview",
|
||||
"gemini-3.1-pro": "gemini-3.1-pro-preview",
|
||||
"gemini-3.1-flash-lite": "gemini-3.1-flash-lite-preview",
|
||||
"gemini-3.1-flash": "gemini-3-flash-preview",
|
||||
"gemini-3.1-flash-preview": "gemini-3-flash-preview"
|
||||
}
|
||||
},
|
||||
"google-vertex": {
|
||||
"aliases": {
|
||||
"gemini-3-pro": "gemini-3.1-pro-preview",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { normalizeGooglePreviewModelId } from "../plugin-sdk/provider-model-id-normalize.js";
|
||||
import { normalizeProviderModelIdWithManifest } from "../plugins/manifest-model-id-normalization.js";
|
||||
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
|
||||
@@ -32,19 +33,27 @@ export function normalizeStaticProviderModelId(
|
||||
manifestPlugins?: readonly Pick<PluginManifestRecord, "modelIdNormalization">[];
|
||||
} = {},
|
||||
): string {
|
||||
const normalizedProvider = normalizeProviderId(provider);
|
||||
if (options.allowManifestNormalization === false) {
|
||||
return model;
|
||||
return normalizeBuiltInProviderModelId(normalizedProvider, model);
|
||||
}
|
||||
return (
|
||||
const manifestModelId =
|
||||
normalizeProviderModelIdWithManifest({
|
||||
provider,
|
||||
provider: normalizedProvider,
|
||||
plugins: options.manifestPlugins,
|
||||
context: {
|
||||
provider,
|
||||
provider: normalizedProvider,
|
||||
modelId: model,
|
||||
},
|
||||
}) ?? model
|
||||
);
|
||||
}) ?? model;
|
||||
return normalizeBuiltInProviderModelId(normalizedProvider, manifestModelId);
|
||||
}
|
||||
|
||||
function normalizeBuiltInProviderModelId(provider: string, model: string): string {
|
||||
if (provider === "google" || provider === "google-gemini-cli" || provider === "google-vertex") {
|
||||
return normalizeGooglePreviewModelId(model);
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
function parseStaticModelRef(raw: string, defaultProvider: string): StaticModelRef | null {
|
||||
|
||||
@@ -311,6 +311,18 @@ describe("model-selection", () => {
|
||||
defaultProvider: "google",
|
||||
expected: { provider: "google", model: "gemini-3-flash-preview" },
|
||||
},
|
||||
{
|
||||
name: "normalizes retired google gemini 3 pro preview ids",
|
||||
variants: ["google/gemini-3-pro-preview", "gemini-3-pro-preview"],
|
||||
defaultProvider: "google",
|
||||
expected: { provider: "google", model: "gemini-3.1-pro-preview" },
|
||||
},
|
||||
{
|
||||
name: "normalizes retired gemini cli 3 pro preview ids",
|
||||
variants: ["google-gemini-cli/gemini-3-pro-preview"],
|
||||
defaultProvider: "google",
|
||||
expected: { provider: "google-gemini-cli", model: "gemini-3.1-pro-preview" },
|
||||
},
|
||||
{
|
||||
name: "normalizes gemini 3.1 flash-lite ids",
|
||||
variants: ["google/gemini-3.1-flash-lite", "gemini-3.1-flash-lite"],
|
||||
|
||||
@@ -303,11 +303,38 @@ describe("promptDefaultModel", () => {
|
||||
expect(optionValues).toEqual([
|
||||
"openai/gpt-5.5",
|
||||
"anthropic/claude-sonnet-4-6",
|
||||
"google/gemini-3-pro-preview",
|
||||
"google/gemini-3.1-pro-preview",
|
||||
"openai-codex/gpt-5.5",
|
||||
]);
|
||||
});
|
||||
|
||||
it("normalizes retired Google Gemini catalog rows before saving config", async () => {
|
||||
loadModelCatalog.mockResolvedValue([
|
||||
{ provider: "google", id: "gemini-3-pro-preview", name: "Gemini 3 Pro" },
|
||||
]);
|
||||
|
||||
const select = vi.fn(async (params) => params.options[0]?.value as never);
|
||||
const prompter = makePrompter({ select });
|
||||
|
||||
const result = await promptDefaultModel({
|
||||
config: { agents: { defaults: {} } } as OpenClawConfig,
|
||||
prompter,
|
||||
allowKeep: false,
|
||||
includeManual: false,
|
||||
ignoreAllowlist: true,
|
||||
});
|
||||
|
||||
expect(result.model).toBe("google/gemini-3.1-pro-preview");
|
||||
expect(select.mock.calls[0]?.[0]?.options).toEqual([
|
||||
expect.objectContaining({ value: "google/gemini-3.1-pro-preview" }),
|
||||
]);
|
||||
expect(runProviderModelSelectedHook).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
model: "google/gemini-3.1-pro-preview",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("uses configured provider models for default picker without loading the full catalog in replace mode", async () => {
|
||||
loadModelCatalog.mockResolvedValue([
|
||||
{ provider: "openai", id: "gpt-5.5", name: "GPT-5.5" },
|
||||
@@ -1268,7 +1295,7 @@ describe("runtime model picker visibility", () => {
|
||||
expect(optionValues).toEqual([
|
||||
"openai/gpt-5.5",
|
||||
"anthropic/claude-sonnet-4-6",
|
||||
"google/gemini-3-pro-preview",
|
||||
"google/gemini-3.1-pro-preview",
|
||||
]);
|
||||
expect(call?.initialValues).toEqual(["openai/gpt-5.5"]);
|
||||
});
|
||||
@@ -1504,7 +1531,7 @@ describe("applyModelFallbacksFromSelection", () => {
|
||||
});
|
||||
expect(next.agents?.defaults?.model).toEqual({
|
||||
primary: "anthropic/claude-opus-4-6",
|
||||
fallbacks: ["google/gemini-3-pro-preview"],
|
||||
fallbacks: ["google/gemini-3.1-pro-preview"],
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
buildModelAliasIndex,
|
||||
type ModelAliasIndex,
|
||||
modelKey,
|
||||
normalizeModelRef,
|
||||
normalizeProviderId,
|
||||
resolveConfiguredModelRef,
|
||||
resolveModelRefFromString,
|
||||
@@ -231,11 +232,12 @@ function addModelSelectOption(params: {
|
||||
hasAuth: (provider: string) => boolean;
|
||||
literalPrefixProviders: Set<string>;
|
||||
}) {
|
||||
const key = modelKey(params.entry.provider, params.entry.id);
|
||||
const normalizedRef = normalizeModelRef(params.entry.provider, params.entry.id);
|
||||
const key = modelKey(normalizedRef.provider, normalizedRef.model);
|
||||
if (
|
||||
params.seen.has(key) ||
|
||||
HIDDEN_ROUTER_MODELS.has(key) ||
|
||||
!isModelPickerVisibleProvider(params.entry.provider)
|
||||
!isModelPickerVisibleProvider(normalizedRef.provider)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -253,15 +255,15 @@ function addModelSelectOption(params: {
|
||||
if (aliases?.length) {
|
||||
hints.push(`alias: ${aliases.join(", ")}`);
|
||||
}
|
||||
const routeHint = resolveModelRouteHint(params.entry.provider);
|
||||
const routeHint = resolveModelRouteHint(normalizedRef.provider);
|
||||
if (routeHint) {
|
||||
hints.push(routeHint);
|
||||
}
|
||||
if (!params.hasAuth(params.entry.provider)) {
|
||||
if (!params.hasAuth(normalizedRef.provider)) {
|
||||
return;
|
||||
}
|
||||
const label = params.literalPrefixProviders.has(normalizeProviderId(params.entry.provider))
|
||||
? `${params.entry.provider}/${params.entry.id}`
|
||||
const label = params.literalPrefixProviders.has(normalizeProviderId(normalizedRef.provider))
|
||||
? formatLiteralProviderPrefixedModelRef(normalizedRef.provider, key)
|
||||
: key;
|
||||
params.options.push({
|
||||
value: key,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { normalizeGooglePreviewModelId } from "./provider-model-id-normalize.js"
|
||||
describe("provider model id normalization", () => {
|
||||
it("routes bare Gemini 3 Pro to the current Gemini 3.1 Pro preview", () => {
|
||||
expect(normalizeGooglePreviewModelId("gemini-3-pro")).toBe("gemini-3.1-pro-preview");
|
||||
expect(normalizeGooglePreviewModelId("gemini-3-pro-preview")).toBe("gemini-3.1-pro-preview");
|
||||
expect(normalizeGooglePreviewModelId("gemini-3.1-pro")).toBe("gemini-3.1-pro-preview");
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const ANTIGRAVITY_BARE_PRO_IDS = new Set(["gemini-3-pro", "gemini-3.1-pro", "gemini-3-1-pro"]);
|
||||
|
||||
export function normalizeGooglePreviewModelId(id: string): string {
|
||||
if (id === "gemini-3-pro") {
|
||||
if (id === "gemini-3-pro" || id === "gemini-3-pro-preview") {
|
||||
return "gemini-3.1-pro-preview";
|
||||
}
|
||||
if (id === "gemini-3-flash") {
|
||||
|
||||
Reference in New Issue
Block a user