fix: add google provider model transform across all resolution paths#1917
fix: add google provider model transform across all resolution paths#1917feelsodev wants to merge 3 commits intocode-yeongyu:devfrom
Conversation
|
All contributors have signed the CLA. Thank you! ✅ |
7eaa45d to
dcfb82b
Compare
|
I have read the CLA Document and I hereby sign the CLA |
…ew suffix transformModelForProvider only handled github-copilot provider, leaving google provider models untransformed. This caused ProviderModelNotFoundError when google/gemini-3-flash was sent to the API (correct ID is gemini-3-flash-preview). Add google provider block with -preview suffix guard to prevent double transformation.
dcfb82b to
57188fa
Compare
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
transformModelForProvider only handled github-copilot provider, leaving google provider models untransformed. This caused ProviderModelNotFoundError when google/gemini-3-flash was sent to the API (correct ID is gemini-3-flash-preview). Changes: - Add google provider to transformModelForProvider with idempotent regex negative lookahead to prevent double -preview suffix - Fix category-default path in model-resolution-pipeline when availableModels is empty but connected provider exists - Fix getFirstFallbackModel first-run path that constructed raw model IDs without transformation - Fix github-copilot provider gemini transforms to also use idempotent regex (was vulnerable to double-transform) - Extract transformModelForProvider to shared module (single source of truth, imported by cli and shared layers) - Add 20 new test cases: unit tests for both providers, runtime integration tests for category-default and fallback-chain paths, double-transform prevention for both providers
db1b74d to
63d994f
Compare
There was a problem hiding this comment.
1 issue found across 5 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/cli/provider-model-id-transform.ts">
<violation number="1" location="src/cli/provider-model-id-transform.ts:1">
P1: GitHub Copilot provider missing suffix guard causing double-preview suffix bug</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| } | ||
| return model | ||
| } | ||
| export { transformModelForProvider } from "../shared/provider-model-id-transform" |
There was a problem hiding this comment.
P1: GitHub Copilot provider missing suffix guard causing double-preview suffix bug
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/cli/provider-model-id-transform.ts, line 1:
<comment>GitHub Copilot provider missing suffix guard causing double-preview suffix bug</comment>
<file context>
@@ -1,20 +1 @@
- }
- return model
-}
+export { transformModelForProvider } from "../shared/provider-model-id-transform"
</file context>
There was a problem hiding this comment.
Thanks for flagging this! This is actually a false positive though 😄
The old code in src/cli/provider-model-id-transform.ts used plain .replace() which was indeed vulnerable to double-transform (gemini-3-pro-preview → gemini-3-pro-preview-preview). But that's exactly what this PR fixes!
The implementation was moved to src/shared/provider-model-id-transform.ts, and it now uses idempotent regex with negative lookahead (?!-):
.replace(/gemini-3-pro(?!-)/g, "gemini-3-pro-preview")
.replace(/gemini-3-flash(?!-)/g, "gemini-3-flash-preview")This ensures:
gemini-3-pro→ matches (no-follows) →gemini-3-pro-preview✅gemini-3-pro-preview→ does NOT match (-followspro) → stays unchanged ✅
Both github-copilot and google providers use the same idempotent regex, and there are explicit test cases covering double-transform prevention for both providers (see provider-model-id-transform.test.ts lines 67-89, 129-151).
The CLI file is now just a re-export — the actual guard lives in the shared module. No issue here!
There was a problem hiding this comment.
@feelsodev I have started the AI code review. It will take a few minutes to complete.
There was a problem hiding this comment.
No issues found across 8 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Auto-approved: Comprehensive tests ensure correct model transformations with no regressions detected by AI review.
Summary
transformModelForProvider()only handled thegithub-copilotprovider, leavinggoogleprovider models untransformed. This causedProviderModelNotFoundErrorwhengoogle/gemini-3-flashwas sent to Google's API — the correct model ID isgemini-3-flash-preview.Problem
When the model resolution pipeline resolves
gemini-3-flashfor thegoogleprovider, it passes throughtransformModelForProvider()without transformation. Multiple code paths allow the raw model ID to reach the API:model-fallback.ts):resolveModelFromChain→transformModelForProvider("google", "gemini-3-flash")→ returns unchanged → writesgoogle/gemini-3-flashto configmodel-resolution-pipeline.ts): WhenavailableModelsis empty but connected provider exists,normalizedCategoryDefaultis returned as-is without transformationmodel-resolution.ts):getFirstFallbackModel()constructs${provider}/${entry.model}without callingtransformModelForProvider.replace()calls for gemini transforms were vulnerable to double-transformation (gemini-3-pro-preview→gemini-3-pro-preview-preview)Fix
1. Centralized transform function (
shared/provider-model-id-transform.ts)googleprovider handling with idempotent regex negative lookahead(?!-)to prevent double-previewsuffixgithub-copilotprovider gemini transforms to also use idempotent regex (was using plain.replace())2. Runtime category-default path (
shared/model-resolution-pipeline.ts)availableModels.size === 0and connected provider exists, now appliestransformModelForProviderbefore returning the category default model3. First-run fallback path (
agents/builtin-agents/model-resolution.ts)getFirstFallbackModel()now appliestransformModelForProviderwhen constructing the model ID on first run with no cacheTests
20 new test cases across 2 files:
Unit tests (
src/cli/provider-model-id-transform.test.ts) — 15 totalRuntime integration tests (
src/shared/model-resolver.test.ts) — 5 newgemini-3-pro→gemini-3-pro-preview)Existing tests
Files Changed
src/shared/provider-model-id-transform.tssrc/shared/model-resolution-pipeline.tssrc/agents/builtin-agents/model-resolution.tsgetFirstFallbackModel()first-run pathsrc/cli/provider-model-id-transform.tssrc/cli/provider-model-id-transform.test.tssrc/shared/model-resolver.test.tssrc/cli/__snapshots__/model-fallback.test.ts.snap