Skip to content

Commit e908439

Browse files
committed
fix(agents): resolve Gemini 3.1 forward-compat for all Google provider variants (#36134)
When sessions_spawn targets a Google Gemini model (e.g. google/gemini-3.1-flash-lite-preview), the provider is stored as "google" on the session. At runtime, resolveModel() could not find the model because the forward-compat fallback only checked "google-gemini-cli", causing a silent FailoverError that fell back to Anthropic — with modelApplied: true still reported. Fix: expand resolveGoogleGeminiCli31ForwardCompatModel to accept all Google provider variants (google, google-gemini-cli, google-vertex, google-antigravity). When the template model lives under a different variant in the pi-ai catalog, try all variants and re-stamp the provider to match the caller's request. Fixes #36134
1 parent 4dc0c66 commit e908439

File tree

2 files changed

+81
-11
lines changed

2 files changed

+81
-11
lines changed

src/agents/model-forward-compat.ts

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,29 @@ function resolveAnthropicSonnet46ForwardCompatModel(
168168
});
169169
}
170170

171-
// gemini-3.1-pro-preview / gemini-3.1-flash-preview are not present in pi-ai's built-in
172-
// google-gemini-cli catalog yet. Clone the nearest gemini-3 template so users don't get
173-
// "Unknown model" errors when Google Gemini CLI gains new minor-version models.
171+
// gemini-3.1-pro-preview / gemini-3.1-flash-preview / gemini-3.1-flash-lite-preview
172+
// are not present in pi-ai's built-in catalog for all Google provider variants.
173+
// Clone the nearest gemini-3 template so users don't get "Unknown model" errors
174+
// when Google releases new minor-version models.
175+
//
176+
// This must handle all Google provider variants ("google", "google-gemini-cli",
177+
// "google-vertex", "google-antigravity") so that model overrides via
178+
// sessions_spawn (which use the user-facing "google" provider) also resolve
179+
// correctly instead of silently falling back to the default provider (#36134).
180+
const GOOGLE_GEMINI_FORWARD_COMPAT_PROVIDERS = new Set([
181+
"google",
182+
"google-gemini-cli",
183+
"google-vertex",
184+
"google-antigravity",
185+
]);
186+
174187
function resolveGoogleGeminiCli31ForwardCompatModel(
175188
provider: string,
176189
modelId: string,
177190
modelRegistry: ModelRegistry,
178191
): Model<Api> | undefined {
179-
if (normalizeProviderId(provider) !== "google-gemini-cli") {
192+
const normalizedProvider = normalizeProviderId(provider);
193+
if (!GOOGLE_GEMINI_FORWARD_COMPAT_PROVIDERS.has(normalizedProvider)) {
180194
return undefined;
181195
}
182196
const trimmed = modelId.trim();
@@ -191,13 +205,30 @@ function resolveGoogleGeminiCli31ForwardCompatModel(
191205
return undefined;
192206
}
193207

194-
return cloneFirstTemplateModel({
195-
normalizedProvider: "google-gemini-cli",
196-
trimmedModelId: trimmed,
197-
templateIds: [...templateIds],
198-
modelRegistry,
199-
patch: { reasoning: true },
200-
});
208+
// Try the caller's provider first, then fall back to other Google variants.
209+
// The template model may live under a different Google provider in the
210+
// pi-ai built-in catalog (e.g. "google-gemini-cli") while the user
211+
// specified the bare "google" provider via sessions_spawn (#36134).
212+
const providersToTry = [
213+
normalizedProvider,
214+
...Array.from(GOOGLE_GEMINI_FORWARD_COMPAT_PROVIDERS).filter((p) => p !== normalizedProvider),
215+
];
216+
for (const candidateProvider of providersToTry) {
217+
const result = cloneFirstTemplateModel({
218+
normalizedProvider: candidateProvider,
219+
trimmedModelId: trimmed,
220+
templateIds: [...templateIds],
221+
modelRegistry,
222+
patch: { reasoning: true },
223+
});
224+
if (result) {
225+
// Re-stamp the provider to match what the caller requested so the
226+
// returned model routes through the correct auth/transport layer.
227+
(result as { provider: string }).provider = normalizedProvider;
228+
return result;
229+
}
230+
}
231+
return undefined;
201232
}
202233

203234
// Z.ai's GLM-5 may not be present in pi-ai's built-in model catalog yet.

src/agents/pi-embedded-runner/model.forward-compat.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,43 @@ describe("pi embedded model e2e smoke", () => {
8686
expect(result.model).toBeUndefined();
8787
expect(result.error).toBe("Unknown model: google-gemini-cli/gemini-4-unknown");
8888
});
89+
90+
it("builds a forward-compat fallback for google/gemini-3.1-flash-lite-preview (#36134)", () => {
91+
mockGoogleGeminiCliFlashTemplateModel();
92+
93+
const result = resolveModel("google", "gemini-3.1-flash-lite-preview", "/tmp/agent");
94+
expect(result.error).toBeUndefined();
95+
expect(result.model).toMatchObject({
96+
id: "gemini-3.1-flash-lite-preview",
97+
name: "gemini-3.1-flash-lite-preview",
98+
provider: "google",
99+
reasoning: true,
100+
});
101+
});
102+
103+
it("builds a forward-compat fallback for google/gemini-3.1-pro-preview (#36134)", () => {
104+
mockGoogleGeminiCliProTemplateModel();
105+
106+
const result = resolveModel("google", "gemini-3.1-pro-preview", "/tmp/agent");
107+
expect(result.error).toBeUndefined();
108+
expect(result.model).toMatchObject({
109+
id: "gemini-3.1-pro-preview",
110+
name: "gemini-3.1-pro-preview",
111+
provider: "google",
112+
reasoning: true,
113+
});
114+
});
115+
116+
it("builds a forward-compat fallback for google-vertex/gemini-3.1-flash-preview (#36134)", () => {
117+
mockGoogleGeminiCliFlashTemplateModel();
118+
119+
const result = resolveModel("google-vertex", "gemini-3.1-flash-preview", "/tmp/agent");
120+
expect(result.error).toBeUndefined();
121+
expect(result.model).toMatchObject({
122+
id: "gemini-3.1-flash-preview",
123+
name: "gemini-3.1-flash-preview",
124+
provider: "google-vertex",
125+
reasoning: true,
126+
});
127+
});
89128
});

0 commit comments

Comments
 (0)