Models: normalize Gemini 3 ids in runtime selection

Closes #795
This commit is contained in:
Shadow
2026-01-12 21:32:53 -06:00
parent 2467a103b2
commit ab993904d7
3 changed files with 28 additions and 4 deletions

View File

@@ -7,6 +7,7 @@
- Memory: allow custom OpenAI-compatible embedding endpoints for memory search (remote baseUrl/apiKey/headers). (#819 — thanks @mukhtharcm) - Memory: allow custom OpenAI-compatible embedding endpoints for memory search (remote baseUrl/apiKey/headers). (#819 — thanks @mukhtharcm)
### Fixes ### Fixes
- Models/Google: normalize Gemini 3 model ids to preview variants before runtime selection. (#795 — thanks @thewilloftheshadow)
- TUI: keep the last streamed response instead of replacing it with “(no output)”. (#747 — thanks @thewilloftheshadow) - TUI: keep the last streamed response instead of replacing it with “(no output)”. (#747 — thanks @thewilloftheshadow)
- Slack: accept slash commands with or without leading `/` for custom command configs. (#798 — thanks @thewilloftheshadow) - Slack: accept slash commands with or without leading `/` for custom command configs. (#798 — thanks @thewilloftheshadow)
- Onboarding/Configure: refuse to proceed with invalid configs; run `clawdbot doctor` first to avoid wiping custom fields. (#764 — thanks @mukhtharcm) - Onboarding/Configure: refuse to proceed with invalid configs; run `clawdbot doctor` first to avoid wiping custom fields. (#764 — thanks @mukhtharcm)

View File

@@ -107,6 +107,24 @@ describe("parseModelRef", () => {
model: "claude-opus-4-5", model: "claude-opus-4-5",
}); });
}); });
it("normalizes google gemini 3 models to preview ids", () => {
expect(parseModelRef("google/gemini-3-pro", "anthropic")).toEqual({
provider: "google",
model: "gemini-3-pro-preview",
});
expect(parseModelRef("google/gemini-3-flash", "anthropic")).toEqual({
provider: "google",
model: "gemini-3-flash-preview",
});
});
it("normalizes default-provider google models", () => {
expect(parseModelRef("gemini-3-pro", "google")).toEqual({
provider: "google",
model: "gemini-3-pro-preview",
});
});
}); });
describe("resolveHooksGmailModel", () => { describe("resolveHooksGmailModel", () => {

View File

@@ -1,5 +1,6 @@
import type { ClawdbotConfig } from "../config/config.js"; import type { ClawdbotConfig } from "../config/config.js";
import type { ModelCatalogEntry } from "./model-catalog.js"; import type { ModelCatalogEntry } from "./model-catalog.js";
import { normalizeGoogleModelId } from "./models-config.providers.js";
export type ModelRef = { export type ModelRef = {
provider: string; provider: string;
@@ -47,6 +48,12 @@ function normalizeAnthropicModelId(model: string): string {
return trimmed; return trimmed;
} }
function normalizeProviderModelId(provider: string, model: string): string {
if (provider === "anthropic") return normalizeAnthropicModelId(model);
if (provider === "google") return normalizeGoogleModelId(model);
return model;
}
export function parseModelRef( export function parseModelRef(
raw: string, raw: string,
defaultProvider: string, defaultProvider: string,
@@ -56,16 +63,14 @@ export function parseModelRef(
const slash = trimmed.indexOf("/"); const slash = trimmed.indexOf("/");
if (slash === -1) { if (slash === -1) {
const provider = normalizeProviderId(defaultProvider); const provider = normalizeProviderId(defaultProvider);
const model = const model = normalizeProviderModelId(provider, trimmed);
provider === "anthropic" ? normalizeAnthropicModelId(trimmed) : trimmed;
return { provider, model }; return { provider, model };
} }
const providerRaw = trimmed.slice(0, slash).trim(); const providerRaw = trimmed.slice(0, slash).trim();
const provider = normalizeProviderId(providerRaw); const provider = normalizeProviderId(providerRaw);
const model = trimmed.slice(slash + 1).trim(); const model = trimmed.slice(slash + 1).trim();
if (!provider || !model) return null; if (!provider || !model) return null;
const normalizedModel = const normalizedModel = normalizeProviderModelId(provider, model);
provider === "anthropic" ? normalizeAnthropicModelId(model) : model;
return { provider, model: normalizedModel }; return { provider, model: normalizedModel };
} }