mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 09:08:38 +00:00
fix(models): synthesize antigravity Gemini 3.1 pro high/low models (#22899)
* Models: add antigravity Gemini 3.1 forward-compat * models: propagate availability to Gemini 3.1 dot IDs * test(models): format Gemini 3.1 forward-compat test * test(models): type Gemini 3.1 forward-compat fixtures * models: add changelog note for antigravity gemini 3.1 forward-compat --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
@@ -701,6 +701,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Agents/Context: apply configured model `contextWindow` overrides after provider discovery so `lookupContextTokens()` honors operator config values (including discovery-failure paths). (#17404) Thanks @michaelbship and @vignesh07.
|
- Agents/Context: apply configured model `contextWindow` overrides after provider discovery so `lookupContextTokens()` honors operator config values (including discovery-failure paths). (#17404) Thanks @michaelbship and @vignesh07.
|
||||||
- Agents/Context: derive `lookupContextTokens()` from auth-available model metadata and keep the smallest discovered context window for duplicate model ids, preventing cross-provider cache collisions from overestimating session context limits. (#17586) Thanks @githabideri and @vignesh07.
|
- Agents/Context: derive `lookupContextTokens()` from auth-available model metadata and keep the smallest discovered context window for duplicate model ids, preventing cross-provider cache collisions from overestimating session context limits. (#17586) Thanks @githabideri and @vignesh07.
|
||||||
- Agents/OpenAI: force `store=true` for direct OpenAI Responses/Codex runs to preserve multi-turn server-side conversation state, while leaving proxy/non-OpenAI endpoints unchanged. (#16803) Thanks @mark9232 and @vignesh07.
|
- Agents/OpenAI: force `store=true` for direct OpenAI Responses/Codex runs to preserve multi-turn server-side conversation state, while leaving proxy/non-OpenAI endpoints unchanged. (#16803) Thanks @mark9232 and @vignesh07.
|
||||||
|
- Models/Antigravity: synthesize Gemini 3.1 Pro high/low model IDs (dash and dot forms) from Gemini 3 templates so `models list` and `/model` remain usable while upstream catalogs catch up. (#22492) Thanks @Phineas1500.
|
||||||
- Memory/FTS: make `buildFtsQuery` Unicode-aware so non-ASCII queries (including CJK) produce keyword tokens instead of falling back to vector-only search. (#17672) Thanks @KinGP5471.
|
- Memory/FTS: make `buildFtsQuery` Unicode-aware so non-ASCII queries (including CJK) produce keyword tokens instead of falling back to vector-only search. (#17672) Thanks @KinGP5471.
|
||||||
- Auto-reply/Compaction: resolve `memory/YYYY-MM-DD.md` placeholders with timezone-aware runtime dates and append a `Current time:` line to memory-flush turns, preventing wrong-year memory filenames without making the system prompt time-variant. (#17603, #17633) Thanks @nicholaspapadam-wq and @vignesh07.
|
- Auto-reply/Compaction: resolve `memory/YYYY-MM-DD.md` placeholders with timezone-aware runtime dates and append a `Current time:` line to memory-flush turns, preventing wrong-year memory filenames without making the system prompt time-variant. (#17603, #17633) Thanks @nicholaspapadam-wq and @vignesh07.
|
||||||
- Auth/Cooldowns: auto-expire stale auth profile cooldowns when `cooldownUntil` or `disabledUntil` timestamps have passed, and reset `errorCount` so the next transient failure does not immediately escalate to a disproportionately long cooldown. Handles `cooldownUntil` and `disabledUntil` independently. (#3604) Thanks @nabbilkhan.
|
- Auth/Cooldowns: auto-expire stale auth profile cooldowns when `cooldownUntil` or `disabledUntil` timestamps have passed, and reset `errorCount` so the next transient failure does not immediately escalate to a disproportionately long cooldown. Handles `cooldownUntil` and `disabledUntil` independently. (#3604) Thanks @nabbilkhan.
|
||||||
|
|||||||
72
src/agents/model-forward-compat.antigravity-gemini31.test.ts
Normal file
72
src/agents/model-forward-compat.antigravity-gemini31.test.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import type { Api, Model } from "@mariozechner/pi-ai";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { resolveForwardCompatModel } from "./model-forward-compat.js";
|
||||||
|
import type { ModelRegistry } from "./pi-model-discovery.js";
|
||||||
|
|
||||||
|
function makeRegistry(): ModelRegistry {
|
||||||
|
const templates = new Map<string, Model<Api>>();
|
||||||
|
templates.set("google-antigravity/gemini-3-pro-high", {
|
||||||
|
id: "gemini-3-pro-high",
|
||||||
|
name: "Gemini 3 Pro High",
|
||||||
|
provider: "google-antigravity",
|
||||||
|
api: "google-antigravity",
|
||||||
|
input: ["text", "image"],
|
||||||
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||||
|
contextWindow: 200000,
|
||||||
|
maxTokens: 64000,
|
||||||
|
reasoning: true,
|
||||||
|
} as Model<Api>);
|
||||||
|
templates.set("google-antigravity/gemini-3-pro-low", {
|
||||||
|
id: "gemini-3-pro-low",
|
||||||
|
name: "Gemini 3 Pro Low",
|
||||||
|
provider: "google-antigravity",
|
||||||
|
api: "google-antigravity",
|
||||||
|
input: ["text", "image"],
|
||||||
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||||
|
contextWindow: 200000,
|
||||||
|
maxTokens: 64000,
|
||||||
|
reasoning: true,
|
||||||
|
} as Model<Api>);
|
||||||
|
|
||||||
|
const registry = {
|
||||||
|
find: (provider: string, modelId: string) => templates.get(`${provider}/${modelId}`) ?? null,
|
||||||
|
} as unknown as ModelRegistry;
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("resolveForwardCompatModel (google-antigravity Gemini 3.1)", () => {
|
||||||
|
it("resolves gemini-3-1-pro-high from gemini-3-pro-high template", () => {
|
||||||
|
const model = resolveForwardCompatModel(
|
||||||
|
"google-antigravity",
|
||||||
|
"gemini-3-1-pro-high",
|
||||||
|
makeRegistry(),
|
||||||
|
);
|
||||||
|
expect(model?.provider).toBe("google-antigravity");
|
||||||
|
expect(model?.id).toBe("gemini-3-1-pro-high");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves gemini-3-1-pro-low from gemini-3-pro-low template", () => {
|
||||||
|
const model = resolveForwardCompatModel(
|
||||||
|
"google-antigravity",
|
||||||
|
"gemini-3-1-pro-low",
|
||||||
|
makeRegistry(),
|
||||||
|
);
|
||||||
|
expect(model?.provider).toBe("google-antigravity");
|
||||||
|
expect(model?.id).toBe("gemini-3-1-pro-low");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("supports dot-notation model ids", () => {
|
||||||
|
const high = resolveForwardCompatModel(
|
||||||
|
"google-antigravity",
|
||||||
|
"gemini-3.1-pro-high",
|
||||||
|
makeRegistry(),
|
||||||
|
);
|
||||||
|
const low = resolveForwardCompatModel(
|
||||||
|
"google-antigravity",
|
||||||
|
"gemini-3.1-pro-low",
|
||||||
|
makeRegistry(),
|
||||||
|
);
|
||||||
|
expect(high?.id).toBe("gemini-3.1-pro-high");
|
||||||
|
expect(low?.id).toBe("gemini-3.1-pro-low");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -26,6 +26,12 @@ const ANTIGRAVITY_OPUS_THINKING_TEMPLATE_MODEL_IDS = [
|
|||||||
"claude-opus-4-5-thinking",
|
"claude-opus-4-5-thinking",
|
||||||
"claude-opus-4.5-thinking",
|
"claude-opus-4.5-thinking",
|
||||||
] as const;
|
] as const;
|
||||||
|
const ANTIGRAVITY_GEMINI_31_PRO_HIGH_MODEL_ID = "gemini-3-1-pro-high";
|
||||||
|
const ANTIGRAVITY_GEMINI_31_PRO_DOT_HIGH_MODEL_ID = "gemini-3.1-pro-high";
|
||||||
|
const ANTIGRAVITY_GEMINI_31_PRO_LOW_MODEL_ID = "gemini-3-1-pro-low";
|
||||||
|
const ANTIGRAVITY_GEMINI_31_PRO_DOT_LOW_MODEL_ID = "gemini-3.1-pro-low";
|
||||||
|
const ANTIGRAVITY_GEMINI_31_PRO_HIGH_TEMPLATE_MODEL_IDS = ["gemini-3-pro-high"] as const;
|
||||||
|
const ANTIGRAVITY_GEMINI_31_PRO_LOW_TEMPLATE_MODEL_IDS = ["gemini-3-pro-low"] as const;
|
||||||
|
|
||||||
export const ANTIGRAVITY_OPUS_46_FORWARD_COMPAT_CANDIDATES = [
|
export const ANTIGRAVITY_OPUS_46_FORWARD_COMPAT_CANDIDATES = [
|
||||||
{
|
{
|
||||||
@@ -34,10 +40,25 @@ export const ANTIGRAVITY_OPUS_46_FORWARD_COMPAT_CANDIDATES = [
|
|||||||
"google-antigravity/claude-opus-4-5-thinking",
|
"google-antigravity/claude-opus-4-5-thinking",
|
||||||
"google-antigravity/claude-opus-4.5-thinking",
|
"google-antigravity/claude-opus-4.5-thinking",
|
||||||
],
|
],
|
||||||
|
availabilityAliasIds: [] as const,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: ANTIGRAVITY_OPUS_46_MODEL_ID,
|
id: ANTIGRAVITY_OPUS_46_MODEL_ID,
|
||||||
templatePrefixes: ["google-antigravity/claude-opus-4-5", "google-antigravity/claude-opus-4.5"],
|
templatePrefixes: ["google-antigravity/claude-opus-4-5", "google-antigravity/claude-opus-4.5"],
|
||||||
|
availabilityAliasIds: [] as const,
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export const ANTIGRAVITY_GEMINI_31_FORWARD_COMPAT_CANDIDATES = [
|
||||||
|
{
|
||||||
|
id: ANTIGRAVITY_GEMINI_31_PRO_HIGH_MODEL_ID,
|
||||||
|
templatePrefixes: ["google-antigravity/gemini-3-pro-high"],
|
||||||
|
availabilityAliasIds: [ANTIGRAVITY_GEMINI_31_PRO_DOT_HIGH_MODEL_ID],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: ANTIGRAVITY_GEMINI_31_PRO_LOW_MODEL_ID,
|
||||||
|
templatePrefixes: ["google-antigravity/gemini-3-pro-low"],
|
||||||
|
availabilityAliasIds: [ANTIGRAVITY_GEMINI_31_PRO_DOT_LOW_MODEL_ID],
|
||||||
},
|
},
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
@@ -278,6 +299,40 @@ function resolveAntigravityOpus46ForwardCompatModel(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveAntigravityGemini31ForwardCompatModel(
|
||||||
|
provider: string,
|
||||||
|
modelId: string,
|
||||||
|
modelRegistry: ModelRegistry,
|
||||||
|
): Model<Api> | undefined {
|
||||||
|
const normalizedProvider = normalizeProviderId(provider);
|
||||||
|
if (normalizedProvider !== "google-antigravity") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trimmedModelId = modelId.trim();
|
||||||
|
const lower = trimmedModelId.toLowerCase();
|
||||||
|
const isGemini31High =
|
||||||
|
lower === ANTIGRAVITY_GEMINI_31_PRO_HIGH_MODEL_ID ||
|
||||||
|
lower === ANTIGRAVITY_GEMINI_31_PRO_DOT_HIGH_MODEL_ID;
|
||||||
|
const isGemini31Low =
|
||||||
|
lower === ANTIGRAVITY_GEMINI_31_PRO_LOW_MODEL_ID ||
|
||||||
|
lower === ANTIGRAVITY_GEMINI_31_PRO_DOT_LOW_MODEL_ID;
|
||||||
|
if (!isGemini31High && !isGemini31Low) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const templateIds = isGemini31High
|
||||||
|
? [...ANTIGRAVITY_GEMINI_31_PRO_HIGH_TEMPLATE_MODEL_IDS]
|
||||||
|
: [...ANTIGRAVITY_GEMINI_31_PRO_LOW_TEMPLATE_MODEL_IDS];
|
||||||
|
|
||||||
|
return cloneFirstTemplateModel({
|
||||||
|
normalizedProvider,
|
||||||
|
trimmedModelId,
|
||||||
|
templateIds,
|
||||||
|
modelRegistry,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveForwardCompatModel(
|
export function resolveForwardCompatModel(
|
||||||
provider: string,
|
provider: string,
|
||||||
modelId: string,
|
modelId: string,
|
||||||
@@ -288,6 +343,7 @@ export function resolveForwardCompatModel(
|
|||||||
resolveAnthropicOpus46ForwardCompatModel(provider, modelId, modelRegistry) ??
|
resolveAnthropicOpus46ForwardCompatModel(provider, modelId, modelRegistry) ??
|
||||||
resolveAnthropicSonnet46ForwardCompatModel(provider, modelId, modelRegistry) ??
|
resolveAnthropicSonnet46ForwardCompatModel(provider, modelId, modelRegistry) ??
|
||||||
resolveZaiGlm5ForwardCompatModel(provider, modelId, modelRegistry) ??
|
resolveZaiGlm5ForwardCompatModel(provider, modelId, modelRegistry) ??
|
||||||
resolveAntigravityOpus46ForwardCompatModel(provider, modelId, modelRegistry)
|
resolveAntigravityOpus46ForwardCompatModel(provider, modelId, modelRegistry) ??
|
||||||
|
resolveAntigravityGemini31ForwardCompatModel(provider, modelId, modelRegistry)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -389,6 +389,99 @@ describe("models list/status", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{
|
||||||
|
name: "high",
|
||||||
|
configuredModelId: "gemini-3-1-pro-high",
|
||||||
|
templateId: "gemini-3-pro-high",
|
||||||
|
templateName: "Gemini 3 Pro High",
|
||||||
|
expectedKey: "google-antigravity/gemini-3-1-pro-high",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "low",
|
||||||
|
configuredModelId: "gemini-3-1-pro-low",
|
||||||
|
templateId: "gemini-3-pro-low",
|
||||||
|
templateName: "Gemini 3 Pro Low",
|
||||||
|
expectedKey: "google-antigravity/gemini-3-1-pro-low",
|
||||||
|
},
|
||||||
|
] as const)(
|
||||||
|
"models list resolves antigravity gemini 3.1 $name from gemini 3 template",
|
||||||
|
async ({ configuredModelId, templateId, templateName, expectedKey }) => {
|
||||||
|
const payload = await runGoogleAntigravityListCase({
|
||||||
|
configuredModelId,
|
||||||
|
templateId,
|
||||||
|
templateName,
|
||||||
|
});
|
||||||
|
expectAntigravityModel(payload, {
|
||||||
|
key: expectedKey,
|
||||||
|
available: false,
|
||||||
|
includesTags: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{
|
||||||
|
name: "high",
|
||||||
|
configuredModelId: "gemini-3-1-pro-high",
|
||||||
|
templateId: "gemini-3-pro-high",
|
||||||
|
templateName: "Gemini 3 Pro High",
|
||||||
|
expectedKey: "google-antigravity/gemini-3-1-pro-high",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "low",
|
||||||
|
configuredModelId: "gemini-3-1-pro-low",
|
||||||
|
templateId: "gemini-3-pro-low",
|
||||||
|
templateName: "Gemini 3 Pro Low",
|
||||||
|
expectedKey: "google-antigravity/gemini-3-1-pro-low",
|
||||||
|
},
|
||||||
|
] as const)(
|
||||||
|
"models list marks synthesized antigravity gemini 3.1 $name as available when template is available",
|
||||||
|
async ({ configuredModelId, templateId, templateName, expectedKey }) => {
|
||||||
|
const payload = await runGoogleAntigravityListCase({
|
||||||
|
configuredModelId,
|
||||||
|
templateId,
|
||||||
|
templateName,
|
||||||
|
available: true,
|
||||||
|
});
|
||||||
|
expectAntigravityModel(payload, {
|
||||||
|
key: expectedKey,
|
||||||
|
available: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{
|
||||||
|
name: "high",
|
||||||
|
configuredModelId: "gemini-3.1-pro-high",
|
||||||
|
templateId: "gemini-3-pro-high",
|
||||||
|
templateName: "Gemini 3 Pro High",
|
||||||
|
expectedKey: "google-antigravity/gemini-3.1-pro-high",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "low",
|
||||||
|
configuredModelId: "gemini-3.1-pro-low",
|
||||||
|
templateId: "gemini-3-pro-low",
|
||||||
|
templateName: "Gemini 3 Pro Low",
|
||||||
|
expectedKey: "google-antigravity/gemini-3.1-pro-low",
|
||||||
|
},
|
||||||
|
] as const)(
|
||||||
|
"models list marks dot-notation antigravity gemini 3.1 $name as available when template is available",
|
||||||
|
async ({ configuredModelId, templateId, templateName, expectedKey }) => {
|
||||||
|
const payload = await runGoogleAntigravityListCase({
|
||||||
|
configuredModelId,
|
||||||
|
templateId,
|
||||||
|
templateName,
|
||||||
|
available: true,
|
||||||
|
});
|
||||||
|
expectAntigravityModel(payload, {
|
||||||
|
key: expectedKey,
|
||||||
|
available: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
it("models list prefers registry availability over provider auth heuristics", async () => {
|
it("models list prefers registry availability over provider auth heuristics", async () => {
|
||||||
const payload = await runGoogleAntigravityListCase({
|
const payload = await runGoogleAntigravityListCase({
|
||||||
configuredModelId: "claude-opus-4-6-thinking",
|
configuredModelId: "claude-opus-4-6-thinking",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
resolveEnvApiKey,
|
resolveEnvApiKey,
|
||||||
} from "../../agents/model-auth.js";
|
} from "../../agents/model-auth.js";
|
||||||
import {
|
import {
|
||||||
|
ANTIGRAVITY_GEMINI_31_FORWARD_COMPAT_CANDIDATES,
|
||||||
ANTIGRAVITY_OPUS_46_FORWARD_COMPAT_CANDIDATES,
|
ANTIGRAVITY_OPUS_46_FORWARD_COMPAT_CANDIDATES,
|
||||||
resolveForwardCompatModel,
|
resolveForwardCompatModel,
|
||||||
} from "../../agents/model-forward-compat.js";
|
} from "../../agents/model-forward-compat.js";
|
||||||
@@ -117,6 +118,9 @@ export async function loadModelRegistry(cfg: OpenClawConfig) {
|
|||||||
for (const synthesized of synthesizedForwardCompat) {
|
for (const synthesized of synthesizedForwardCompat) {
|
||||||
if (hasAvailableTemplate(availableKeys, synthesized.templatePrefixes)) {
|
if (hasAvailableTemplate(availableKeys, synthesized.templatePrefixes)) {
|
||||||
availableKeys.add(synthesized.key);
|
availableKeys.add(synthesized.key);
|
||||||
|
for (const aliasKey of synthesized.availabilityAliasKeys) {
|
||||||
|
availableKeys.add(aliasKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -137,6 +141,7 @@ export async function loadModelRegistry(cfg: OpenClawConfig) {
|
|||||||
type SynthesizedForwardCompat = {
|
type SynthesizedForwardCompat = {
|
||||||
key: string;
|
key: string;
|
||||||
templatePrefixes: readonly string[];
|
templatePrefixes: readonly string[];
|
||||||
|
availabilityAliasKeys: readonly string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
function appendAntigravityForwardCompatModels(
|
function appendAntigravityForwardCompatModels(
|
||||||
@@ -145,8 +150,12 @@ function appendAntigravityForwardCompatModels(
|
|||||||
): { models: Model<Api>[]; synthesizedForwardCompat: SynthesizedForwardCompat[] } {
|
): { models: Model<Api>[]; synthesizedForwardCompat: SynthesizedForwardCompat[] } {
|
||||||
const nextModels = [...models];
|
const nextModels = [...models];
|
||||||
const synthesizedForwardCompat: SynthesizedForwardCompat[] = [];
|
const synthesizedForwardCompat: SynthesizedForwardCompat[] = [];
|
||||||
|
const candidates = [
|
||||||
|
...ANTIGRAVITY_OPUS_46_FORWARD_COMPAT_CANDIDATES,
|
||||||
|
...ANTIGRAVITY_GEMINI_31_FORWARD_COMPAT_CANDIDATES,
|
||||||
|
];
|
||||||
|
|
||||||
for (const candidate of ANTIGRAVITY_OPUS_46_FORWARD_COMPAT_CANDIDATES) {
|
for (const candidate of candidates) {
|
||||||
const key = modelKey("google-antigravity", candidate.id);
|
const key = modelKey("google-antigravity", candidate.id);
|
||||||
const hasForwardCompat = nextModels.some((model) => modelKey(model.provider, model.id) === key);
|
const hasForwardCompat = nextModels.some((model) => modelKey(model.provider, model.id) === key);
|
||||||
if (hasForwardCompat) {
|
if (hasForwardCompat) {
|
||||||
@@ -162,6 +171,9 @@ function appendAntigravityForwardCompatModels(
|
|||||||
synthesizedForwardCompat.push({
|
synthesizedForwardCompat.push({
|
||||||
key,
|
key,
|
||||||
templatePrefixes: candidate.templatePrefixes,
|
templatePrefixes: candidate.templatePrefixes,
|
||||||
|
availabilityAliasKeys: candidate.availabilityAliasIds.map((id) =>
|
||||||
|
modelKey("google-antigravity", id),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user