mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix(provider): normalize bare gemini-3 Pro model IDs for google-antigravity (#24145)
* fix(provider): normalize bare gemini-3 Pro model IDs for google-antigravity The Antigravity Cloud Code Assist API requires a thinking-tier suffix (-low or -high) for all Gemini 3 Pro variants. When a user configures a bare model ID like `gemini-3.1-pro`, the API returns a 404 because it only recognises `gemini-3.1-pro-low` or `gemini-3.1-pro-high`. Add `normalizeAntigravityModelId()` that appends `-low` (the default tier) to bare Pro model IDs, and apply it during provider normalisation for `google-antigravity`. Also refactor the per-provider model normalisation into a shared `normalizeProviderModels()` helper. Closes #24071 Co-authored-by: Cursor <cursoragent@cursor.com> * Tests: cover antigravity model ID normalization * Changelog: note antigravity pro tier normalization * Tests: type antigravity model helper inputs --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
@@ -71,6 +71,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Daemon/macOS launchd: forward proxy env vars into supervised service environments, keep LaunchAgent `KeepAlive=true` semantics, and harden restart sequencing to `print -> bootout -> wait old pid exit -> bootstrap -> kickstart`. (#27276) thanks @frankekn.
|
||||
- Gateway/macOS restart-loop hardening: detect OpenClaw-managed supervisor markers during SIGUSR1 restart handoff, clean stale gateway PIDs before `/restart` launchctl/systemctl triggers, and set LaunchAgent `ThrottleInterval=60` to bound launchd retry storms during lock-release races. Landed from contributor PRs #27655 (@taw0002), #27448 (@Sid-Qin), and #27650 (@kevinWangSheng). (#27605, #27590, #26904, #26736)
|
||||
- Models/MiniMax auth header defaults: set `authHeader: true` for both onboarding-generated MiniMax API providers and implicit built-in MiniMax (`minimax`, `minimax-portal`) provider templates so first requests no longer fail with MiniMax `401 authentication_error` due to missing `Authorization` header. Landed from contributor PRs #27622 by @riccoyuanft and #27631 by @kevinWangSheng. (#27600, #15303)
|
||||
- Models/Google Antigravity IDs: normalize bare `gemini-3-pro`, `gemini-3.1-pro`, and `gemini-3-1-pro` model IDs to the default `-low` thinking tier so provider requests no longer fail with 404 when the tier suffix is omitted. (#24145) Thanks @byungsker.
|
||||
- Auth/Auth profiles: normalize `auth-profiles.json` alias fields (`mode -> type`, `apiKey -> key`) before credential validation so entries copied from `openclaw.json` auth examples are no longer silently dropped. (#26950) thanks @byungsker.
|
||||
- Models/Google Gemini: treat `google` (Gemini API key auth profile) as a reasoning-tag provider to prevent `<think>` leakage, and add forward-compat model fallback for `google-gemini-cli` `gemini-3.1-pro*` / `gemini-3.1-flash*` IDs to avoid false unknown-model errors. (#26551, #26524) Thanks @byungsker.
|
||||
- Models/Profile suffix parsing: centralize trailing `@profile` parsing and only treat `@` as a profile separator when it appears after the final `/`, preserving model IDs like `openai/@cf/...` and `openrouter/@preset/...` across `/model` directive parsing and allowlist model resolution, with regression coverage.
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import { mkdtempSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
normalizeAntigravityModelId,
|
||||
normalizeProviders,
|
||||
type ProviderConfig,
|
||||
} from "./models-config.providers.js";
|
||||
|
||||
function buildModel(id: string): NonNullable<ProviderConfig["models"]>[number] {
|
||||
return {
|
||||
id,
|
||||
name: id,
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 1,
|
||||
maxTokens: 1,
|
||||
};
|
||||
}
|
||||
|
||||
function buildProvider(modelIds: string[]): ProviderConfig {
|
||||
return {
|
||||
baseUrl: "https://example.invalid/v1",
|
||||
api: "openai-completions",
|
||||
apiKey: "EXAMPLE_KEY",
|
||||
models: modelIds.map((id) => buildModel(id)),
|
||||
};
|
||||
}
|
||||
|
||||
describe("normalizeAntigravityModelId", () => {
|
||||
it.each(["gemini-3-pro", "gemini-3.1-pro", "gemini-3-1-pro"])(
|
||||
"adds default -low suffix to bare pro id: %s",
|
||||
(id) => {
|
||||
expect(normalizeAntigravityModelId(id)).toBe(`${id}-low`);
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
"gemini-3-pro-low",
|
||||
"gemini-3-pro-high",
|
||||
"gemini-3.1-flash",
|
||||
"claude-opus-4-6-thinking",
|
||||
])("keeps already-tiered and non-pro ids unchanged: %s", (id) => {
|
||||
expect(normalizeAntigravityModelId(id)).toBe(id);
|
||||
});
|
||||
});
|
||||
|
||||
describe("google-antigravity provider normalization", () => {
|
||||
it("normalizes bare gemini pro IDs only for google-antigravity providers", () => {
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const providers = {
|
||||
"google-antigravity": buildProvider([
|
||||
"gemini-3-pro",
|
||||
"gemini-3.1-pro",
|
||||
"gemini-3-1-pro",
|
||||
"gemini-3-pro-high",
|
||||
"claude-opus-4-6-thinking",
|
||||
]),
|
||||
openai: buildProvider(["gpt-5"]),
|
||||
};
|
||||
|
||||
const normalized = normalizeProviders({ providers, agentDir });
|
||||
|
||||
expect(normalized).not.toBe(providers);
|
||||
expect(normalized?.["google-antigravity"]?.models.map((model) => model.id)).toEqual([
|
||||
"gemini-3-pro-low",
|
||||
"gemini-3.1-pro-low",
|
||||
"gemini-3-1-pro-low",
|
||||
"gemini-3-pro-high",
|
||||
"claude-opus-4-6-thinking",
|
||||
]);
|
||||
expect(normalized?.openai).toBe(providers.openai);
|
||||
});
|
||||
|
||||
it("returns original providers object when no antigravity IDs need normalization", () => {
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const providers = {
|
||||
"google-antigravity": buildProvider(["gemini-3-pro-low", "claude-opus-4-6-thinking"]),
|
||||
};
|
||||
|
||||
const normalized = normalizeProviders({ providers, agentDir });
|
||||
|
||||
expect(normalized).toBe(providers);
|
||||
});
|
||||
});
|
||||
@@ -391,10 +391,22 @@ export function normalizeGoogleModelId(id: string): string {
|
||||
return id;
|
||||
}
|
||||
|
||||
function normalizeGoogleProvider(provider: ProviderConfig): ProviderConfig {
|
||||
const ANTIGRAVITY_BARE_PRO_IDS = new Set(["gemini-3-pro", "gemini-3.1-pro", "gemini-3-1-pro"]);
|
||||
|
||||
export function normalizeAntigravityModelId(id: string): string {
|
||||
if (ANTIGRAVITY_BARE_PRO_IDS.has(id)) {
|
||||
return `${id}-low`;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
function normalizeProviderModels(
|
||||
provider: ProviderConfig,
|
||||
normalizeId: (id: string) => string,
|
||||
): ProviderConfig {
|
||||
let mutated = false;
|
||||
const models = provider.models.map((model) => {
|
||||
const nextId = normalizeGoogleModelId(model.id);
|
||||
const nextId = normalizeId(model.id);
|
||||
if (nextId === model.id) {
|
||||
return model;
|
||||
}
|
||||
@@ -404,6 +416,14 @@ function normalizeGoogleProvider(provider: ProviderConfig): ProviderConfig {
|
||||
return mutated ? { ...provider, models } : provider;
|
||||
}
|
||||
|
||||
function normalizeGoogleProvider(provider: ProviderConfig): ProviderConfig {
|
||||
return normalizeProviderModels(provider, normalizeGoogleModelId);
|
||||
}
|
||||
|
||||
function normalizeAntigravityProvider(provider: ProviderConfig): ProviderConfig {
|
||||
return normalizeProviderModels(provider, normalizeAntigravityModelId);
|
||||
}
|
||||
|
||||
export function normalizeProviders(params: {
|
||||
providers: ModelsConfig["providers"];
|
||||
agentDir: string;
|
||||
@@ -470,6 +490,14 @@ export function normalizeProviders(params: {
|
||||
normalizedProvider = googleNormalized;
|
||||
}
|
||||
|
||||
if (normalizedKey === "google-antigravity") {
|
||||
const antigravityNormalized = normalizeAntigravityProvider(normalizedProvider);
|
||||
if (antigravityNormalized !== normalizedProvider) {
|
||||
mutated = true;
|
||||
}
|
||||
normalizedProvider = antigravityNormalized;
|
||||
}
|
||||
|
||||
next[key] = normalizedProvider;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user