mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-08-03 21:51:11 -07:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dfa8394cad | |||
| 7059517a00 | |||
| 9519ba6f0e | |||
| 57f1c6912c | |||
| 0a11ce3e93 | |||
| 8acfe0c4ac | |||
| 5a0bee9016 | |||
| b545258c4c |
@@ -1,6 +1,6 @@
|
||||
# Latest stable release: v0.44.1
|
||||
# Latest stable release: v0.43.0
|
||||
|
||||
Released: May 28, 2026
|
||||
Released: May 22, 2026
|
||||
|
||||
For most users, our latest stable release is the recommended release. Install
|
||||
the latest stable version with:
|
||||
@@ -29,9 +29,6 @@ npm install -g @google/gemini-cli
|
||||
|
||||
## What's Changed
|
||||
|
||||
- fix(patch): cherry-pick bd53951 to release/v0.44.0-pr-27496 [CONFLICTS] by
|
||||
@gemini-cli-robot in
|
||||
[#27534](https://github.com/google-gemini/gemini-cli/pull/27534)
|
||||
- feat(core): steer model to use edit tool for surgical edits, fix a typo by
|
||||
@aishaneeshah in
|
||||
[#26480](https://github.com/google-gemini/gemini-cli/pull/26480)
|
||||
@@ -218,4 +215,4 @@ npm install -g @google/gemini-cli
|
||||
[#27256](https://github.com/google-gemini/gemini-cli/pull/27256)
|
||||
|
||||
**Full Changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.44.0...v0.44.1
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.42.0...v0.43.0
|
||||
|
||||
+200
-44
@@ -1,6 +1,6 @@
|
||||
# Preview release: v0.45.0-preview.0
|
||||
# Preview release: v0.44.0-preview.0
|
||||
|
||||
Released: May 27, 2026
|
||||
Released: May 22, 2026
|
||||
|
||||
Our preview release includes the latest, new, and experimental features. This
|
||||
release may not be as stable as our [latest weekly release](latest.md).
|
||||
@@ -13,52 +13,208 @@ npm install -g @google/gemini-cli@preview
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Context Simplification:** Completed major architectural work to simplify and
|
||||
optimize how the agent manages and processes session context.
|
||||
- **A2A Usage Metadata:** The Agent-to-Agent (A2A) interface now exposes
|
||||
detailed usage metadata, providing better visibility into resource
|
||||
consumption.
|
||||
- **Improved Routing:** Updated default auto-routing logic and added bypasses
|
||||
for certain routing classifiers to prevent orphaned function response errors.
|
||||
- **Terminal Stability:** Fixed several issues affecting terminal environments,
|
||||
including Termux relaunch loops and PTY resize errors.
|
||||
- **Security & Fixes:** Strengthened MCP list security and addressed issues with
|
||||
session resumption and PTY environment signals.
|
||||
- **Simplified Modes:** Merged existing Auto modes into a single, unified Auto
|
||||
mode for a more streamlined user experience.
|
||||
- **Enhanced Agent Registration:** Improved agent registration logic to
|
||||
prioritize project-specific agents using a first-wins strategy.
|
||||
- **New Developer Skills:** Introduced `agent-tui` and `tui-tester` skills to
|
||||
empower developers with better terminal UI testing and automation
|
||||
capabilities.
|
||||
- **Expanded Editor Support:** Added support for Sublime Text and Emacs Client,
|
||||
providing more flexibility for external editing tasks.
|
||||
- **Session Management:** Added new session invocation types
|
||||
(`LocalSessionInvocation`, `RemoteSessionInvocation`) and improved context
|
||||
recovery across sessions.
|
||||
|
||||
## What's Changed
|
||||
|
||||
- chore(release): bump version to 0.45.0-nightly.20260521.g854f811be by
|
||||
- chore(release): bump version to 0.44.0-nightly.20260512.g022e8baef by
|
||||
@gemini-cli-robot in
|
||||
[#27362](https://github.com/google-gemini/gemini-cli/pull/27362)
|
||||
- fix(cli): prevent Termux relaunch and resize remount loops by @saymanq in
|
||||
[#27110](https://github.com/google-gemini/gemini-cli/pull/27110)
|
||||
- Feat/a2a expose usage metadata by @jvargassanchez-dot in
|
||||
[#27288](https://github.com/google-gemini/gemini-cli/pull/27288)
|
||||
- feat(context): Complete simplification work. by @joshualitt in
|
||||
[#27345](https://github.com/google-gemini/gemini-cli/pull/27345)
|
||||
- fix(core): force update_topic tool to execute sequentially by
|
||||
@jvargassanchez-dot in
|
||||
[#27357](https://github.com/google-gemini/gemini-cli/pull/27357)
|
||||
- Changelog for v0.44.0-preview.0 by @gemini-cli-robot in
|
||||
[#27360](https://github.com/google-gemini/gemini-cli/pull/27360)
|
||||
- Changelog for v0.43.0 by @gemini-cli-robot in
|
||||
[#27361](https://github.com/google-gemini/gemini-cli/pull/27361)
|
||||
- Revert "fix(core): prevent SIGHUP kills in PTY environments" by @bbiggs in
|
||||
[#27401](https://github.com/google-gemini/gemini-cli/pull/27401)
|
||||
- fix(cli): filter internal session context from history during resumption by
|
||||
[#26957](https://github.com/google-gemini/gemini-cli/pull/26957)
|
||||
- Changelog for v0.42.0 by @gemini-cli-robot in
|
||||
[#26958](https://github.com/google-gemini/gemini-cli/pull/26958)
|
||||
- Refactor: Eliminate `no-unsafe-return` suppressions via strict type validation
|
||||
by @M-DEV-1 in
|
||||
[#20668](https://github.com/google-gemini/gemini-cli/pull/20668)
|
||||
- Changelog for v0.43.0-preview.0 by @gemini-cli-robot in
|
||||
[#26959](https://github.com/google-gemini/gemini-cli/pull/26959)
|
||||
- feat(core): change agent registration to first-wins and prioritize project by
|
||||
@adamfweidman in
|
||||
[#26953](https://github.com/google-gemini/gemini-cli/pull/26953)
|
||||
- feat(cli): merge Auto modes into a single Auto mode by @DavidAPierce in
|
||||
[#26714](https://github.com/google-gemini/gemini-cli/pull/26714)
|
||||
- fix(core): preserve OAuth refresh tokens during rotation and retrieval by
|
||||
@cocosheng-g in
|
||||
[#26924](https://github.com/google-gemini/gemini-cli/pull/26924)
|
||||
- fix(cli): allow keychain auth for --list-sessions and non-interactive mode by
|
||||
@cocosheng-g in
|
||||
[#26921](https://github.com/google-gemini/gemini-cli/pull/26921)
|
||||
- fix(core): handle EISDIR on virtual drives in memory discovery by @cocosheng-g
|
||||
in [#26985](https://github.com/google-gemini/gemini-cli/pull/26985)
|
||||
- fix(cli): auto-approve shell redirections in AUTO_EDIT mode by @cocosheng-g in
|
||||
[#27003](https://github.com/google-gemini/gemini-cli/pull/27003)
|
||||
- ci: suppress bot comments during standard triage maintenance by @cocosheng-g
|
||||
in [#27006](https://github.com/google-gemini/gemini-cli/pull/27006)
|
||||
- fix(core): refresh MCP OAuth token usage after re-auth by @sahilkirad in
|
||||
[#26312](https://github.com/google-gemini/gemini-cli/pull/26312)
|
||||
- fix(ui): clamped table column widths by @devr0306 in
|
||||
[#26991](https://github.com/google-gemini/gemini-cli/pull/26991)
|
||||
- fix(core): isolate subagent thread context by @akh64bit in
|
||||
[#26449](https://github.com/google-gemini/gemini-cli/pull/26449)
|
||||
- chore: add execution permission to scripts/review.sh by @scidomino in
|
||||
[#27009](https://github.com/google-gemini/gemini-cli/pull/27009)
|
||||
- fix(core): made context files append instead of replace by @devr0306 in
|
||||
[#26950](https://github.com/google-gemini/gemini-cli/pull/26950)
|
||||
- fix: add system PATH fallback for ripgrep resolution (#26777) by @cocosheng-g
|
||||
in [#26868](https://github.com/google-gemini/gemini-cli/pull/26868)
|
||||
- chore: clean up launched memory features by @SandyTao520 in
|
||||
[#26941](https://github.com/google-gemini/gemini-cli/pull/26941)
|
||||
- fix(core): throttle shell text output and bound live UI buffer by
|
||||
@emersonbusson in
|
||||
[#26955](https://github.com/google-gemini/gemini-cli/pull/26955)
|
||||
- fix(cli): don't crash when an @-mention captures a non-path blob by @ifitisit
|
||||
in [#25980](https://github.com/google-gemini/gemini-cli/pull/25980)
|
||||
- fix(core): ensure stable fallback for restricted preview models by @galz10 in
|
||||
[#26999](https://github.com/google-gemini/gemini-cli/pull/26999)
|
||||
- feat(core): expose RAG snippets to local log file for debugging by @spencer426
|
||||
in [#27016](https://github.com/google-gemini/gemini-cli/pull/27016)
|
||||
- fix(acp/auth): prevent conflicting credentials on enterprise gateways and
|
||||
support optional API keys natively by @sripasg in
|
||||
[#27021](https://github.com/google-gemini/gemini-cli/pull/27021)
|
||||
- fix(core): respect NO_PROXY for network-based MCP servers by @cocosheng-g in
|
||||
[#27012](https://github.com/google-gemini/gemini-cli/pull/27012)
|
||||
- fix(cli): resolve permission denied in sandbox on NixOS and other distros by
|
||||
@cocosheng-g in
|
||||
[#27004](https://github.com/google-gemini/gemini-cli/pull/27004)
|
||||
- fix(ui): preserve new line at the end of edit window by @devr0306 in
|
||||
[#27057](https://github.com/google-gemini/gemini-cli/pull/27057)
|
||||
- fix(core): ensure Vertex AI sets hasAccessToPreviewModels and remove
|
||||
aggressive 404 fallback revocation by @galz10 in
|
||||
[#27067](https://github.com/google-gemini/gemini-cli/pull/27067)
|
||||
- fix(core): ensure stable admin settings comparison across IPC to prevent
|
||||
restart loop by @DavidAPierce in
|
||||
[#27066](https://github.com/google-gemini/gemini-cli/pull/27066)
|
||||
- fix(deps): update vulnerable dependencies by @scidomino in
|
||||
[#27062](https://github.com/google-gemini/gemini-cli/pull/27062)
|
||||
- fix(core): resolve EISDIR errors during file processing (#21527) by @ProthamD
|
||||
in [#27041](https://github.com/google-gemini/gemini-cli/pull/27041)
|
||||
- docs(extensions): clarify env var sanitization policy for MCP and ext… by
|
||||
@galz10 in [#22854](https://github.com/google-gemini/gemini-cli/pull/22854)
|
||||
- fix(ui): add ENAMETOOLONG and ENOTDIR to exceptions for file parsing errors by
|
||||
@devr0306 in [#27069](https://github.com/google-gemini/gemini-cli/pull/27069)
|
||||
- fix(cli): explicitly clear entrypoint when spawning sandbox container by
|
||||
@cocosheng-g in
|
||||
[#27059](https://github.com/google-gemini/gemini-cli/pull/27059)
|
||||
- docs: update sandbox image command by @sjhddh in
|
||||
[#26774](https://github.com/google-gemini/gemini-cli/pull/26774)
|
||||
- fix(core): externalize https-proxy-agent to fix proxy support by @sotokisehiro
|
||||
in [#26361](https://github.com/google-gemini/gemini-cli/pull/26361)
|
||||
- security: update dependencies to fix critical and high vulnerabilities by
|
||||
@scidomino in [#27077](https://github.com/google-gemini/gemini-cli/pull/27077)
|
||||
- Fix/web fetch ctrl c abort by @ProthamD in
|
||||
[#24320](https://github.com/google-gemini/gemini-cli/pull/24320)
|
||||
- fix(core): add aliases and thinking config for gemini-3.1 models by
|
||||
@anishs1207 in
|
||||
[#27007](https://github.com/google-gemini/gemini-cli/pull/27007)
|
||||
- fix(core): use hasAccessToPreview for auto model resolution and fix
|
||||
disappearing models by @DavidAPierce in
|
||||
[#27112](https://github.com/google-gemini/gemini-cli/pull/27112)
|
||||
- feat(core): add adk.agentSessionSubagentEnabled flag by @adamfweidman in
|
||||
[#26947](https://github.com/google-gemini/gemini-cli/pull/26947)
|
||||
- fix(core): enforce compile-time exhaustiveness in content-utils by
|
||||
@adamfweidman in
|
||||
[#27207](https://github.com/google-gemini/gemini-cli/pull/27207)
|
||||
- feat(skills): add agent-tui and tui-tester skills by @adamfweidman in
|
||||
[#27121](https://github.com/google-gemini/gemini-cli/pull/27121)
|
||||
- fix(context): Fix snapshot recovery across sessions. by @joshualitt in
|
||||
[#26939](https://github.com/google-gemini/gemini-cli/pull/26939)
|
||||
- fix(core): add unit tests for stableStringify by @devr0306 in
|
||||
[#27212](https://github.com/google-gemini/gemini-cli/pull/27212)
|
||||
- fix(core): prefer pwsh.exe over Windows PowerShell 5.1 (#25859) by @kaluchi in
|
||||
[#25900](https://github.com/google-gemini/gemini-cli/pull/25900)
|
||||
- feat(core): add LocalSessionInvocation by @adamfweidman in
|
||||
[#26665](https://github.com/google-gemini/gemini-cli/pull/26665)
|
||||
- refactor: decouple auto model description and configuration from
|
||||
releaseChannel by @danielweis in
|
||||
[#27227](https://github.com/google-gemini/gemini-cli/pull/27227)
|
||||
- fix(core): prevent isBinary false-positive on Windows PTY streams by
|
||||
@TirthNaik-99 in
|
||||
[#26565](https://github.com/google-gemini/gemini-cli/pull/26565)
|
||||
- fix(cli): Prevent unmapped keys in Vim Normal mode from inserting text into
|
||||
prompt Input. by @Rajeshpatel07 in
|
||||
[#25139](https://github.com/google-gemini/gemini-cli/pull/25139)
|
||||
- fix(a2a-server): Implement default policy loading for parity with CLI by
|
||||
@kschaab in [#27073](https://github.com/google-gemini/gemini-cli/pull/27073)
|
||||
- feat(core): add RemoteSessionInvocation by @adamfweidman in
|
||||
[#26937](https://github.com/google-gemini/gemini-cli/pull/26937)
|
||||
- fix: allow configured MCP servers in non-interactive mode by @cocosheng-g in
|
||||
[#27215](https://github.com/google-gemini/gemini-cli/pull/27215)
|
||||
- fix(core): add exception handling to migrateFromFileStorage by @devr0306 in
|
||||
[#27229](https://github.com/google-gemini/gemini-cli/pull/27229)
|
||||
- fix(cli): bundle ink worker-entry.js by @rmedranollamas in
|
||||
[#27249](https://github.com/google-gemini/gemini-cli/pull/27249)
|
||||
- feat(core): wire AgentSession invocations into agent-tool by @adamfweidman in
|
||||
[#26948](https://github.com/google-gemini/gemini-cli/pull/26948)
|
||||
- fix(core): prevent path traversal in custome command file injection by
|
||||
@ompatel-aiml in
|
||||
[#27234](https://github.com/google-gemini/gemini-cli/pull/27234)
|
||||
- fix(core): respect NO_PROXY in global fetch dispatcher by @cocosheng-g in
|
||||
[#27216](https://github.com/google-gemini/gemini-cli/pull/27216)
|
||||
- fix(core): correctly handle nullable array types in MCP tools by @devr0306 in
|
||||
[#27228](https://github.com/google-gemini/gemini-cli/pull/27228)
|
||||
- fix(cli): preserve proxy-agent named exports in ESM bundle by @ashishch432 in
|
||||
[#27145](https://github.com/google-gemini/gemini-cli/pull/27145)
|
||||
- Proposal: deterministic encoding for child-process I/O by @kaluchi in
|
||||
[#27247](https://github.com/google-gemini/gemini-cli/pull/27247)
|
||||
- feat(cli): add Sublime Text and Emacs Client editors, improve error messages
|
||||
and documentation by @alberti42 in
|
||||
[#21090](https://github.com/google-gemini/gemini-cli/pull/21090)
|
||||
- Changelog for v0.43.0-preview.1 by @gemini-cli-robot in
|
||||
[#27297](https://github.com/google-gemini/gemini-cli/pull/27297)
|
||||
- fix(devtools): bundle devtools package to avoid resolution errors by
|
||||
@rmedranollamas in
|
||||
[#27391](https://github.com/google-gemini/gemini-cli/pull/27391)
|
||||
- Update default auto routing by @DavidAPierce in
|
||||
[#27071](https://github.com/google-gemini/gemini-cli/pull/27071)
|
||||
- fix(core): bypass routing classifiers to prevent orphaned function response
|
||||
errors by @danielweis in
|
||||
[#27389](https://github.com/google-gemini/gemini-cli/pull/27389)
|
||||
- fix(core): suppress PTY resize EBADF errors by @scidomino in
|
||||
[#27461](https://github.com/google-gemini/gemini-cli/pull/27461)
|
||||
- fix(core): prevent blacklist bypass in mcp list by @ompatel-aiml in
|
||||
[#27377](https://github.com/google-gemini/gemini-cli/pull/27377)
|
||||
- fix(cli): ignore unmapped vim normal keys by @MukundaKatta in
|
||||
[#27102](https://github.com/google-gemini/gemini-cli/pull/27102)
|
||||
[#27250](https://github.com/google-gemini/gemini-cli/pull/27250)
|
||||
- fix(cli): integrate PolicyEngine into ACP session to prevent deadlocks
|
||||
(#23507) by @cocosheng-g in
|
||||
[#27252](https://github.com/google-gemini/gemini-cli/pull/27252)
|
||||
- fix: robust ripgrep path resolution and 1p hermetic execution support by
|
||||
@cocosheng-g in
|
||||
[#27253](https://github.com/google-gemini/gemini-cli/pull/27253)
|
||||
- refactor: decouple stored session deletion from ChatRecordingService (#22920)
|
||||
by @yuvrajangadsingh in
|
||||
[#27039](https://github.com/google-gemini/gemini-cli/pull/27039)
|
||||
- fix(core): improve Alpine shell compatibility by @dibyx in
|
||||
[#26770](https://github.com/google-gemini/gemini-cli/pull/26770)
|
||||
- fix(core): generalize MCP compliance fix for tool results by @cocosheng-g in
|
||||
[#27045](https://github.com/google-gemini/gemini-cli/pull/27045)
|
||||
- fix(scripts): scrub CI env vars in dev to keep interactive mode by @Hashaam101
|
||||
in [#27159](https://github.com/google-gemini/gemini-cli/pull/27159)
|
||||
- fix(core): Added date field for the GCal MCP by @devr0306 in
|
||||
[#27251](https://github.com/google-gemini/gemini-cli/pull/27251)
|
||||
- fix(core): centralize path validation to prevent crashes from malformed
|
||||
prompts by @cocosheng-g in
|
||||
[#27211](https://github.com/google-gemini/gemini-cli/pull/27211)
|
||||
- fix(core): prevent SIGHUP kills in PTY environments (WSL2/Kitty/Alacritty) by
|
||||
@ProthamD in [#27267](https://github.com/google-gemini/gemini-cli/pull/27267)
|
||||
- fix(core): dynamic fallback routing for exhausted quota models by @cocosheng-g
|
||||
in [#27315](https://github.com/google-gemini/gemini-cli/pull/27315)
|
||||
- Auto detect pnpm global installation path for macOS and Windows by @tisonkun
|
||||
in [#22748](https://github.com/google-gemini/gemini-cli/pull/22748)
|
||||
- fix(windows): resolve interactive shell arrow-key navigation on Windows by
|
||||
@KumarADITHYA123 in
|
||||
[#23505](https://github.com/google-gemini/gemini-cli/pull/23505)
|
||||
- ci: robust stale issue lifecycle and consolidated triage labels by
|
||||
@cocosheng-g in
|
||||
[#27015](https://github.com/google-gemini/gemini-cli/pull/27015)
|
||||
- fix(context): Ensure last message is processed. by @joshualitt in
|
||||
[#27232](https://github.com/google-gemini/gemini-cli/pull/27232)
|
||||
- chore/release: bump version to 0.44.0-nightly.20260521.g57c42a5c4 by
|
||||
@gemini-cli-robot in
|
||||
[#27324](https://github.com/google-gemini/gemini-cli/pull/27324)
|
||||
- fix(ui): added volta to auto update check by @devr0306 in
|
||||
[#27353](https://github.com/google-gemini/gemini-cli/pull/27353)
|
||||
- perf: optimize issue triage and lifecycle management by @cocosheng-g in
|
||||
[#27346](https://github.com/google-gemini/gemini-cli/pull/27346)
|
||||
|
||||
**Full Changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.44.0-preview.0...v0.45.0-preview.0
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.43.0-preview.1...v0.44.0-preview.0
|
||||
|
||||
@@ -602,6 +602,12 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"model": "gemini-3.1-flash-lite"
|
||||
}
|
||||
},
|
||||
"gemini-3.5-flash": {
|
||||
"extends": "chat-base-3",
|
||||
"modelConfig": {
|
||||
"model": "gemini-3.5-flash"
|
||||
}
|
||||
},
|
||||
"gemma-4-31b-it": {
|
||||
"extends": "chat-base-3",
|
||||
"modelConfig": {
|
||||
@@ -626,6 +632,12 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"model": "gemini-3-flash-preview"
|
||||
}
|
||||
},
|
||||
"gemini-3.5-flash-base": {
|
||||
"extends": "base",
|
||||
"modelConfig": {
|
||||
"model": "gemini-3.5-flash"
|
||||
}
|
||||
},
|
||||
"classifier": {
|
||||
"extends": "base",
|
||||
"modelConfig": {
|
||||
@@ -868,6 +880,16 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"multimodalToolUse": true
|
||||
}
|
||||
},
|
||||
"gemini-3.5-flash": {
|
||||
"tier": "flash",
|
||||
"family": "gemini-3",
|
||||
"isPreview": false,
|
||||
"isVisible": true,
|
||||
"features": {
|
||||
"thinking": false,
|
||||
"multimodalToolUse": true
|
||||
}
|
||||
},
|
||||
"gemini-2.5-pro": {
|
||||
"tier": "pro",
|
||||
"family": "gemini-2.5",
|
||||
@@ -1020,9 +1042,46 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"hasAccessToPreview": false,
|
||||
"useGemini3_5Flash": true
|
||||
},
|
||||
"target": "gemini-3.5-flash"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"hasAccessToPreview": false,
|
||||
"useGemini3_5Flash": false
|
||||
},
|
||||
"target": "gemini-2.5-flash"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gemini-3.5-flash": {
|
||||
"default": "gemini-3.5-flash",
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_5Flash": false,
|
||||
"hasAccessToPreview": false
|
||||
},
|
||||
"target": "gemini-2.5-flash"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_5Flash": false
|
||||
},
|
||||
"target": "gemini-3-flash-preview"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gemini-2.5-flash": {
|
||||
"default": "gemini-2.5-flash",
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_5Flash": true
|
||||
},
|
||||
"target": "gemini-3.5-flash"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1104,6 +1163,12 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"flash": {
|
||||
"default": "gemini-3-flash-preview",
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_5Flash": true
|
||||
},
|
||||
"target": "gemini-3.5-flash"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"hasAccessToPreview": false
|
||||
@@ -1157,6 +1222,12 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"flash": {
|
||||
"default": "gemini-3-flash-preview",
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_5Flash": true
|
||||
},
|
||||
"target": "gemini-3.5-flash"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"hasAccessToPreview": false
|
||||
|
||||
+24
-4
@@ -76,10 +76,30 @@ export class LLMJudge {
|
||||
|
||||
for (const res of rawResults) {
|
||||
// Remove any punctuation the model might have appended
|
||||
const cleanRes = res.replace(/[^A-Z]/g, '');
|
||||
if (cleanRes.startsWith('YES')) yes++;
|
||||
else if (cleanRes.startsWith('NO')) no++;
|
||||
else other++;
|
||||
const cleanRes = res.replace(/[^A-Z ]/g, '');
|
||||
if (
|
||||
cleanRes.includes('THE ANSWER IS YES') ||
|
||||
cleanRes.includes('ANSWER IS YES') ||
|
||||
cleanRes.endsWith('YES')
|
||||
) {
|
||||
yes++;
|
||||
} else if (
|
||||
cleanRes.includes('THE ANSWER IS NO') ||
|
||||
cleanRes.includes('ANSWER IS NO') ||
|
||||
cleanRes.endsWith('NO')
|
||||
) {
|
||||
no++;
|
||||
} else if (cleanRes.trim() === 'YES') {
|
||||
yes++;
|
||||
} else if (cleanRes.trim() === 'NO') {
|
||||
no++;
|
||||
} else {
|
||||
// Fallback: look for YES or NO as standalone words or at the end
|
||||
const words = cleanRes.split(/\s+/);
|
||||
if (words.includes('YES')) yes++;
|
||||
else if (words.includes('NO')) no++;
|
||||
else other++;
|
||||
}
|
||||
}
|
||||
|
||||
// Pass if YES > NO and YES > OTHER (plurality)
|
||||
|
||||
Generated
+9
-9
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
@@ -18117,7 +18117,7 @@
|
||||
},
|
||||
"packages/a2a-server": {
|
||||
"name": "@google/gemini-cli-a2a-server",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"dependencies": {
|
||||
"@a2a-js/sdk": "0.3.11",
|
||||
"@google-cloud/storage": "^7.19.0",
|
||||
@@ -18246,7 +18246,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.16.1",
|
||||
@@ -18394,7 +18394,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@google/gemini-cli-core",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@a2a-js/sdk": "0.3.11",
|
||||
@@ -18674,7 +18674,7 @@
|
||||
},
|
||||
"packages/devtools": {
|
||||
"name": "@google/gemini-cli-devtools",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"ws": "^8.16.0"
|
||||
@@ -18689,7 +18689,7 @@
|
||||
},
|
||||
"packages/sdk": {
|
||||
"name": "@google/gemini-cli-sdk",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@google/gemini-cli-core": "file:../core",
|
||||
@@ -18720,7 +18720,7 @@
|
||||
},
|
||||
"packages/test-utils": {
|
||||
"name": "@google/gemini-cli-test-utils",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@google/gemini-cli-core": "file:../core",
|
||||
@@ -18752,7 +18752,7 @@
|
||||
},
|
||||
"packages/vscode-ide-companion": {
|
||||
"name": "gemini-cli-vscode-ide-companion",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"license": "LICENSE",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.23.0",
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
@@ -14,7 +14,7 @@
|
||||
"url": "git+https://github.com/google-gemini/gemini-cli.git"
|
||||
},
|
||||
"config": {
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.45.0-nightly.20260521.g854f811be"
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.45.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cross-env NODE_ENV=development node scripts/start.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-a2a-server",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"description": "Gemini CLI A2A Server",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"description": "Gemini CLI",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
@@ -27,7 +27,7 @@
|
||||
"dist"
|
||||
],
|
||||
"config": {
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.45.0-nightly.20260521.g854f811be"
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.45.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.16.1",
|
||||
|
||||
@@ -265,6 +265,7 @@ export function buildAvailableModels(
|
||||
const preferredModel = config.getModel() || GEMINI_MODEL_ALIAS_AUTO;
|
||||
const shouldShowPreviewModels = config.getHasAccessToPreviewModel();
|
||||
const useGemini31 = config.getGemini31LaunchedSync?.() ?? false;
|
||||
const useGemini3_5Flash = config.hasGemini35FlashGAAccess?.() ?? false;
|
||||
const selectedAuthType = settings.merged.security.auth.selectedType;
|
||||
const useCustomToolModel =
|
||||
useGemini31 && selectedAuthType === AuthType.USE_GEMINI;
|
||||
@@ -276,6 +277,7 @@ export function buildAvailableModels(
|
||||
) {
|
||||
const options = config.getModelConfigService().getAvailableModelOptions({
|
||||
useGemini3_1: useGemini31,
|
||||
useGemini3_5Flash,
|
||||
useCustomTools: useCustomToolModel,
|
||||
hasAccessToPreview: shouldShowPreviewModels,
|
||||
});
|
||||
@@ -294,6 +296,7 @@ export function buildAvailableModels(
|
||||
description: getAutoModelDescription(
|
||||
shouldShowPreviewModels,
|
||||
useGemini31,
|
||||
useGemini3_5Flash,
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -67,6 +67,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
|
||||
const shouldShowPreviewModels = config?.getHasAccessToPreviewModel() ?? false;
|
||||
const useGemini31 = config?.getGemini31LaunchedSync?.() ?? false;
|
||||
const useGemini3_5Flash = config?.hasGemini35FlashGAAccess?.() ?? false;
|
||||
const selectedAuthType = settings.merged.security.auth.selectedType;
|
||||
const useCustomToolModel =
|
||||
useGemini31 && selectedAuthType === AuthType.USE_GEMINI;
|
||||
@@ -129,6 +130,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
.getModelConfigService()
|
||||
.getAvailableModelOptions({
|
||||
useGemini3_1: useGemini31,
|
||||
useGemini3_5Flash,
|
||||
useCustomTools: useCustomToolModel,
|
||||
hasAccessToPreview: shouldShowPreviewModels,
|
||||
hasAccessToProModel,
|
||||
@@ -162,6 +164,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
description: getAutoModelDescription(
|
||||
shouldShowPreviewModels,
|
||||
useGemini31,
|
||||
useGemini3_5Flash,
|
||||
),
|
||||
key: GEMINI_MODEL_ALIAS_AUTO,
|
||||
},
|
||||
@@ -181,6 +184,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
shouldShowPreviewModels,
|
||||
manualModelSelected,
|
||||
useGemini31,
|
||||
useGemini3_5Flash,
|
||||
useCustomToolModel,
|
||||
hasAccessToProModel,
|
||||
]);
|
||||
@@ -195,6 +199,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
.getModelConfigService()
|
||||
.getAvailableModelOptions({
|
||||
useGemini3_1: useGemini31,
|
||||
useGemini3_5Flash,
|
||||
useCustomTools: useCustomToolModel,
|
||||
hasAccessToPreview: shouldShowPreviewModels,
|
||||
hasAccessToProModel,
|
||||
@@ -287,6 +292,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
}, [
|
||||
shouldShowPreviewModels,
|
||||
useGemini31,
|
||||
useGemini3_5Flash,
|
||||
useCustomToolModel,
|
||||
hasAccessToProModel,
|
||||
config,
|
||||
|
||||
@@ -353,6 +353,49 @@ describe('<ModelStatsDisplay />', () => {
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should resolve gemini-3-flash to gemini-3.5-flash via getDisplayString', async () => {
|
||||
const { lastFrame, unmount } = await renderWithMockedStats({
|
||||
models: {
|
||||
'gemini-3-flash': {
|
||||
api: { totalRequests: 1, totalErrors: 0, totalLatencyMs: 100 },
|
||||
tokens: {
|
||||
input: 5,
|
||||
prompt: 10,
|
||||
candidates: 20,
|
||||
total: 30,
|
||||
cached: 5,
|
||||
thoughts: 2,
|
||||
tool: 1,
|
||||
},
|
||||
roles: {},
|
||||
},
|
||||
},
|
||||
tools: {
|
||||
totalCalls: 0,
|
||||
totalSuccess: 0,
|
||||
totalFail: 0,
|
||||
totalDurationMs: 0,
|
||||
totalDecisions: {
|
||||
accept: 0,
|
||||
reject: 0,
|
||||
modify: 0,
|
||||
[ToolCallDecision.AUTO_ACCEPT]: 0,
|
||||
},
|
||||
byName: {},
|
||||
},
|
||||
files: {
|
||||
totalLinesAdded: 0,
|
||||
totalLinesRemoved: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const output = lastFrame();
|
||||
expect(output).toContain('gemini-3.5-flash');
|
||||
expect(output).not.toContain('gemini-3-flash');
|
||||
expect(output).toMatchSnapshot();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should handle models with long names (gemini-3-*-preview) without layout breaking', async () => {
|
||||
const { lastFrame, unmount } = await renderWithMockedStats(
|
||||
{
|
||||
|
||||
@@ -299,7 +299,7 @@ export const ModelStatsDisplay: React.FC<ModelStatsDisplayProps> = ({
|
||||
},
|
||||
...modelNames.map((name) => ({
|
||||
key: name,
|
||||
header: name,
|
||||
header: getDisplayString(name),
|
||||
flexGrow: 1,
|
||||
renderCell: (row: StatRowData) => {
|
||||
// Don't render anything for section headers in model columns
|
||||
|
||||
@@ -131,6 +131,33 @@ describe('<StatsDisplay />', () => {
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('resolves gemini-3-flash to gemini-3.5-flash in the model usage table', async () => {
|
||||
const metrics = createTestMetrics({
|
||||
models: {
|
||||
'gemini-3-flash': {
|
||||
api: { totalRequests: 5, totalErrors: 0, totalLatencyMs: 3000 },
|
||||
tokens: {
|
||||
input: 1000,
|
||||
prompt: 2000,
|
||||
candidates: 3000,
|
||||
total: 5000,
|
||||
cached: 500,
|
||||
thoughts: 100,
|
||||
tool: 50,
|
||||
},
|
||||
roles: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { lastFrame } = await renderWithMockedStats(metrics);
|
||||
const output = lastFrame();
|
||||
|
||||
expect(output).toContain('gemini-3.5-flash');
|
||||
expect(output).not.toContain('gemini-3-flash\u0020'); // Avoid matching parts of substrings if not intended
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders role breakdown correctly under models', async () => {
|
||||
const metrics = createTestMetrics({
|
||||
models: {
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
import { computeSessionStats } from '../utils/computeStats.js';
|
||||
import { useSettings } from '../contexts/SettingsContext.js';
|
||||
import type { QuotaStats } from '../types.js';
|
||||
import { LlmRole } from '@google/gemini-cli-core';
|
||||
import { LlmRole, getDisplayString } from '@google/gemini-cli-core';
|
||||
|
||||
// A more flexible and powerful StatRow component
|
||||
interface StatRowProps {
|
||||
@@ -101,7 +101,7 @@ const ModelUsageTable: React.FC<ModelUsageTableProps> = ({ models }) => {
|
||||
Object.entries(models).forEach(([name, metrics]) => {
|
||||
rows.push({
|
||||
name,
|
||||
displayName: name,
|
||||
displayName: getDisplayString(name),
|
||||
requests: metrics.api.totalRequests,
|
||||
cachedTokens: metrics.tokens.cached.toLocaleString(),
|
||||
inputTokens: metrics.tokens.prompt.toLocaleString(),
|
||||
|
||||
@@ -215,3 +215,26 @@ exports[`<ModelStatsDisplay /> > should render "no API calls" message when there
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<ModelStatsDisplay /> > should resolve gemini-3-flash to gemini-3.5-flash via getDisplayString 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ Model Stats For Nerds │
|
||||
│ │
|
||||
│ │
|
||||
│ Metric gemini-3.5-flash │
|
||||
│ ────────────────────────────────────────────────────────────────────────────────────────────── │
|
||||
│ API │
|
||||
│ Requests 1 │
|
||||
│ Errors 0 (0.0%) │
|
||||
│ Avg Latency 100ms │
|
||||
│ Tokens │
|
||||
│ Total 30 │
|
||||
│ ↳ Input 5 │
|
||||
│ ↳ Cache Reads 5 (50.0%) │
|
||||
│ ↳ Thoughts 2 │
|
||||
│ ↳ Tool 1 │
|
||||
│ ↳ Output 20 │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -292,3 +292,30 @@ exports[`<StatsDisplay /> > renders role breakdown correctly under models 1`] =
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<StatsDisplay /> > resolves gemini-3-flash to gemini-3.5-flash in the model usage table 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ Session Stats │
|
||||
│ │
|
||||
│ Interaction Summary │
|
||||
│ Session ID: test-session-id │
|
||||
│ Tool Calls: 0 ( ✓ 0 x 0 ) │
|
||||
│ Success Rate: 0.0% │
|
||||
│ │
|
||||
│ Performance │
|
||||
│ Wall Time: 1s │
|
||||
│ Agent Active: 3.0s │
|
||||
│ » API Time: 3.0s (100.0%) │
|
||||
│ » Tool Time: 0s (0.0%) │
|
||||
│ │
|
||||
│ │
|
||||
│ Model Usage │
|
||||
│ Use /model to view model quota information │
|
||||
│ │
|
||||
│ Model Reqs Input Tokens Cache Reads Output Tokens │
|
||||
│ ────────────────────────────────────────────────────────────────────────────────────────────── │
|
||||
│ gemini-3.5-flash 5 2,000 500 3,000 │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -77,10 +77,24 @@ describe('useBanner', () => {
|
||||
.update(defaultBannerData.defaultText)
|
||||
.digest('hex')]: 5,
|
||||
});
|
||||
});
|
||||
|
||||
const { result } = await renderHook(() => useBanner(defaultBannerData));
|
||||
it('should not hide banner if show count exceeds max limit (Legacy format) if it contains an Antigravity announcement', async () => {
|
||||
const antigravityBannerData = {
|
||||
defaultText: 'Antigravity is coming to town!',
|
||||
warningText: '',
|
||||
};
|
||||
|
||||
expect(result.current.bannerText).toBe('');
|
||||
mockedPersistentStateGet.mockReturnValue({
|
||||
[crypto
|
||||
.createHash('sha256')
|
||||
.update(antigravityBannerData.defaultText)
|
||||
.digest('hex')]: 5,
|
||||
});
|
||||
|
||||
const { result } = await renderHook(() => useBanner(antigravityBannerData));
|
||||
|
||||
expect(result.current.bannerText).toBe('Antigravity is coming to town!');
|
||||
});
|
||||
|
||||
it('should increment the persistent count when banner is shown', async () => {
|
||||
|
||||
@@ -41,7 +41,9 @@ export function useBanner(bannerData: BannerData) {
|
||||
const currentBannerCount = bannerCounts[hashedText] || 0;
|
||||
|
||||
const showBanner =
|
||||
activeText !== '' && currentBannerCount < DEFAULT_MAX_BANNER_SHOWN_COUNT;
|
||||
activeText !== '' &&
|
||||
(currentBannerCount < DEFAULT_MAX_BANNER_SHOWN_COUNT ||
|
||||
activeText.includes('Antigravity'));
|
||||
|
||||
const rawBannerText = showBanner ? activeText : '';
|
||||
const bannerText = rawBannerText.replace(/\\n/g, '\n');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-core",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"description": "Gemini CLI Core",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface ModelPolicyOptions {
|
||||
useGemini31?: boolean;
|
||||
useGemini31FlashLite?: boolean;
|
||||
useCustomToolModel?: boolean;
|
||||
useGemini3_5Flash?: boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_ACTIONS: ModelPolicyActionMap = {
|
||||
@@ -94,6 +95,9 @@ export function getModelPolicyChain(
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
options.useGemini31,
|
||||
options.useCustomToolModel,
|
||||
true,
|
||||
undefined,
|
||||
options.useGemini3_5Flash,
|
||||
);
|
||||
return [
|
||||
definePolicy({
|
||||
|
||||
@@ -54,6 +54,7 @@ export function resolvePolicyChain(
|
||||
const useGemini31 = config.getGemini31LaunchedSync?.() ?? false;
|
||||
const useCustomToolModel = config.getUseCustomToolModelSync?.() ?? false;
|
||||
const hasAccessToPreview = config.getHasAccessToPreviewModel?.() ?? false;
|
||||
const useGemini3_5Flash = config.hasGemini35FlashGAAccess?.() ?? false;
|
||||
|
||||
// Capture the original family intent before any normalization or early downgrade.
|
||||
const isOriginallyGemini3 = isGemini3Model(modelFromConfig, config);
|
||||
@@ -65,6 +66,7 @@ export function resolvePolicyChain(
|
||||
useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
),
|
||||
);
|
||||
const isAutoPreferred = normalizedPreferredModel
|
||||
@@ -82,6 +84,7 @@ export function resolvePolicyChain(
|
||||
const context = {
|
||||
useGemini3_1: useGemini31,
|
||||
useCustomTools: useCustomToolModel,
|
||||
useGemini3_5Flash,
|
||||
};
|
||||
|
||||
if (resolvedModel === DEFAULT_GEMINI_FLASH_LITE_MODEL) {
|
||||
@@ -136,6 +139,7 @@ export function resolvePolicyChain(
|
||||
userTier: config.getUserTier(),
|
||||
useGemini31,
|
||||
useCustomToolModel,
|
||||
useGemini3_5Flash,
|
||||
});
|
||||
} else {
|
||||
// User requested Gemini 3 but has no access. Proactively downgrade
|
||||
@@ -146,6 +150,7 @@ export function resolvePolicyChain(
|
||||
userTier: config.getUserTier(),
|
||||
useGemini31,
|
||||
useCustomToolModel,
|
||||
useGemini3_5Flash,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -19,6 +19,7 @@ export const ExperimentFlags = {
|
||||
GEMINI_3_1_PRO_LAUNCHED: 45760185,
|
||||
PRO_MODEL_NO_ACCESS: 45768879,
|
||||
DEFAULT_REQUEST_TIMEOUT: 45773134,
|
||||
GEMINI_3_5_FLASH_GA_LAUNCHED: 45780819,
|
||||
} as const;
|
||||
|
||||
export type ExperimentFlagName =
|
||||
|
||||
@@ -69,6 +69,7 @@ import {
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
PREVIEW_GEMINI_MODEL_AUTO,
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
} from './models.js';
|
||||
import { Storage } from './storage.js';
|
||||
import type { AgentLoopContext } from './agent-loop-context.js';
|
||||
@@ -4346,3 +4347,57 @@ describe('ADKSettings', () => {
|
||||
expect(config.getAgentSessionNoninteractiveEnabled()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasGemini35FlashGAAccess model setting', () => {
|
||||
const baseParams: ConfigParameters = {
|
||||
sessionId: 'test',
|
||||
targetDir: '.',
|
||||
debugMode: false,
|
||||
model: 'test-model',
|
||||
cwd: '.',
|
||||
};
|
||||
|
||||
it('should set DEFAULT_GEMINI_FLASH_MODEL to gemini-3.5-flash and PREVIEW_GEMINI_FLASH_MODEL to gemini-3-flash-preview if hasGemini35FlashGAAccess returns true and authType is USE_GEMINI', () => {
|
||||
const config = new Config(baseParams);
|
||||
config['contentGeneratorConfig'] = { authType: AuthType.USE_GEMINI };
|
||||
|
||||
// Set experiment to return true for GEMINI_3_5_FLASH_GA_LAUNCHED
|
||||
config.setExperiments({
|
||||
experimentIds: [],
|
||||
flags: {
|
||||
[ExperimentFlags.GEMINI_3_5_FLASH_GA_LAUNCHED]: {
|
||||
boolValue: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Call the method
|
||||
const result = config.hasGemini35FlashGAAccess();
|
||||
expect(result).toBe(true);
|
||||
|
||||
expect(DEFAULT_GEMINI_FLASH_MODEL).toBe('gemini-3.5-flash');
|
||||
expect(PREVIEW_GEMINI_FLASH_MODEL).toBe('gemini-3-flash-preview');
|
||||
});
|
||||
|
||||
it('should set DEFAULT_GEMINI_FLASH_MODEL and PREVIEW_GEMINI_FLASH_MODEL to gemini-3-flash if hasGemini35FlashGAAccess returns true and authType is not USE_GEMINI', () => {
|
||||
const config = new Config(baseParams);
|
||||
config['contentGeneratorConfig'] = { authType: AuthType.LOGIN_WITH_GOOGLE };
|
||||
|
||||
// Set experiment to return true for GEMINI_3_5_FLASH_GA_LAUNCHED
|
||||
config.setExperiments({
|
||||
experimentIds: [],
|
||||
flags: {
|
||||
[ExperimentFlags.GEMINI_3_5_FLASH_GA_LAUNCHED]: {
|
||||
boolValue: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Call the method
|
||||
const result = config.hasGemini35FlashGAAccess();
|
||||
expect(result).toBe(true);
|
||||
|
||||
expect(DEFAULT_GEMINI_FLASH_MODEL).toBe('gemini-3-flash');
|
||||
expect(PREVIEW_GEMINI_FLASH_MODEL).toBe('gemini-3-flash');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -86,6 +86,7 @@ import {
|
||||
isGemini2Model,
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
resolveModel,
|
||||
setFlashModels,
|
||||
} from './models.js';
|
||||
import { shouldAttemptBrowserLaunch } from '../utils/browser.js';
|
||||
import type { MCPOAuthConfig } from '../mcp/oauth-provider.js';
|
||||
@@ -2054,6 +2055,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
this.getUseCustomToolModelSync(),
|
||||
this.getHasAccessToPreviewModel(),
|
||||
this,
|
||||
this.hasGemini35FlashGAAccess(),
|
||||
);
|
||||
|
||||
const isPreview = isPreviewModel(primaryModel, this);
|
||||
@@ -2093,6 +2095,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
this.getUseCustomToolModelSync(),
|
||||
this.getHasAccessToPreviewModel(),
|
||||
this,
|
||||
this.hasGemini35FlashGAAccess(),
|
||||
);
|
||||
return this.modelQuotas.get(primaryModel)?.remaining;
|
||||
}
|
||||
@@ -2108,6 +2111,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
this.getUseCustomToolModelSync(),
|
||||
this.getHasAccessToPreviewModel(),
|
||||
this,
|
||||
this.hasGemini35FlashGAAccess(),
|
||||
);
|
||||
return this.modelQuotas.get(primaryModel)?.limit;
|
||||
}
|
||||
@@ -2123,6 +2127,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
this.getUseCustomToolModelSync(),
|
||||
this.getHasAccessToPreviewModel(),
|
||||
this,
|
||||
this.hasGemini35FlashGAAccess(),
|
||||
);
|
||||
return this.modelQuotas.get(primaryModel)?.resetTime;
|
||||
}
|
||||
@@ -3537,6 +3542,38 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether Gemini 3.5 Flash GA has been launched.
|
||||
*
|
||||
* Note: This method should only be called after startup, once experiments have been loaded.
|
||||
*/
|
||||
hasGemini35FlashGAAccess(): boolean {
|
||||
const authType = this.contentGeneratorConfig?.authType;
|
||||
const hasAccess = (() => {
|
||||
if (this.isGemini31LaunchedForAuthType(authType)) {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
this.experiments?.flags[ExperimentFlags.GEMINI_3_5_FLASH_GA_LAUNCHED]
|
||||
?.boolValue ?? false
|
||||
);
|
||||
})();
|
||||
// Used to set default flash models based on access
|
||||
// TODO: Remove once the experiment for 3_5 flash rollut can be cleaned up.
|
||||
if (hasAccess) {
|
||||
// Gemini API key users should have the ability to manually select the
|
||||
// old preview flash model.
|
||||
if (authType === AuthType.USE_GEMINI) {
|
||||
setFlashModels('gemini-3-flash-preview', 'gemini-3.5-flash');
|
||||
} else {
|
||||
setFlashModels('gemini-3-flash', 'gemini-3-flash');
|
||||
}
|
||||
} else {
|
||||
setFlashModels('gemini-3-flash-preview', 'gemini-2.5-flash');
|
||||
}
|
||||
return hasAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether Gemini 3.1 has been launched.
|
||||
*
|
||||
|
||||
@@ -113,6 +113,12 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
model: 'gemini-3.1-flash-lite',
|
||||
},
|
||||
},
|
||||
'gemini-3.5-flash': {
|
||||
extends: 'chat-base-3',
|
||||
modelConfig: {
|
||||
model: 'gemini-3.5-flash',
|
||||
},
|
||||
},
|
||||
'gemma-4-31b-it': {
|
||||
extends: 'chat-base-3',
|
||||
modelConfig: {
|
||||
@@ -139,6 +145,12 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
model: 'gemini-3-flash-preview',
|
||||
},
|
||||
},
|
||||
'gemini-3.5-flash-base': {
|
||||
extends: 'base',
|
||||
modelConfig: {
|
||||
model: 'gemini-3.5-flash',
|
||||
},
|
||||
},
|
||||
classifier: {
|
||||
extends: 'base',
|
||||
modelConfig: {
|
||||
@@ -346,6 +358,13 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
isVisible: true,
|
||||
features: { thinking: false, multimodalToolUse: true },
|
||||
},
|
||||
'gemini-3.5-flash': {
|
||||
tier: 'flash',
|
||||
family: 'gemini-3',
|
||||
isPreview: false,
|
||||
isVisible: true,
|
||||
features: { thinking: false, multimodalToolUse: true },
|
||||
},
|
||||
'gemini-2.5-pro': {
|
||||
tier: 'pro',
|
||||
family: 'gemini-2.5',
|
||||
@@ -451,11 +470,34 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
default: 'gemini-3-flash-preview',
|
||||
contexts: [
|
||||
{
|
||||
condition: { hasAccessToPreview: false },
|
||||
condition: { hasAccessToPreview: false, useGemini3_5Flash: true },
|
||||
target: 'gemini-3.5-flash',
|
||||
},
|
||||
{
|
||||
condition: { hasAccessToPreview: false, useGemini3_5Flash: false },
|
||||
target: 'gemini-2.5-flash',
|
||||
},
|
||||
],
|
||||
},
|
||||
'gemini-3.5-flash': {
|
||||
default: 'gemini-3.5-flash',
|
||||
contexts: [
|
||||
{
|
||||
condition: { useGemini3_5Flash: false, hasAccessToPreview: false },
|
||||
target: 'gemini-2.5-flash',
|
||||
},
|
||||
{
|
||||
condition: { useGemini3_5Flash: false },
|
||||
target: 'gemini-3-flash-preview',
|
||||
},
|
||||
],
|
||||
},
|
||||
'gemini-2.5-flash': {
|
||||
default: 'gemini-2.5-flash',
|
||||
contexts: [
|
||||
{ condition: { useGemini3_5Flash: true }, target: 'gemini-3.5-flash' },
|
||||
],
|
||||
},
|
||||
'gemini-3-pro-preview': {
|
||||
default: 'gemini-3-pro-preview',
|
||||
contexts: [
|
||||
@@ -504,6 +546,7 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
flash: {
|
||||
default: 'gemini-3-flash-preview',
|
||||
contexts: [
|
||||
{ condition: { useGemini3_5Flash: true }, target: 'gemini-3.5-flash' },
|
||||
{
|
||||
condition: { hasAccessToPreview: false },
|
||||
target: 'gemini-2.5-flash',
|
||||
@@ -535,6 +578,7 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
flash: {
|
||||
default: 'gemini-3-flash-preview',
|
||||
contexts: [
|
||||
{ condition: { useGemini3_5Flash: true }, target: 'gemini-3.5-flash' },
|
||||
{
|
||||
condition: { hasAccessToPreview: false },
|
||||
target: 'gemini-2.5-flash',
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_3_5_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
supportsMultimodalFunctionResponse,
|
||||
GEMINI_MODEL_ALIAS_PRO,
|
||||
@@ -744,4 +745,308 @@ describe('getAutoModelDescription', () => {
|
||||
expect(desc).toContain('gemini-3.1-pro-preview');
|
||||
expect(desc).toContain('gemini-3-flash-preview');
|
||||
});
|
||||
|
||||
it('should return Gemini 3.5 Flash description when hasAccessToPreview and useGemini3_5Flash are true', () => {
|
||||
const desc = getAutoModelDescription(true, true, true);
|
||||
expect(desc).toContain('gemini-3.1-pro-preview');
|
||||
expect(desc).toContain(DEFAULT_GEMINI_3_5_FLASH_MODEL);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveModel Gemini 3.5 Flash GA', () => {
|
||||
it('should resolve all but preview flash models to DEFAULT_GEMINI_FLASH_MODEL when useGemini3_5Flash is true (legacy)', () => {
|
||||
expect(
|
||||
resolveModel(
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
expect(
|
||||
resolveModel(
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
expect(
|
||||
resolveModel(
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe(PREVIEW_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should resolve all but preview flash models to gemini-3.5-flash when useGemini3_5Flash is true (dynamic)', () => {
|
||||
const mockDynamicConfig = {
|
||||
getExperimentalDynamicModelConfiguration: () => true,
|
||||
modelConfigService,
|
||||
} as unknown as Config;
|
||||
|
||||
expect(
|
||||
resolveModel(
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3.5-flash');
|
||||
expect(
|
||||
resolveModel(
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3.5-flash');
|
||||
expect(
|
||||
resolveModel(
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe(PREVIEW_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should NOT resolve flash models to DEFAULT_GEMINI_FLASH_MODEL when useGemini3_5Flash is false', () => {
|
||||
expect(
|
||||
resolveModel(
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
false,
|
||||
),
|
||||
).toBe(PREVIEW_GEMINI_FLASH_MODEL);
|
||||
expect(
|
||||
resolveModel(
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
false,
|
||||
),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
expect(
|
||||
resolveModel(
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
false,
|
||||
),
|
||||
).toBe(PREVIEW_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should resolve to DEFAULT_GEMINI_FLASH_MODEL when GA is false AND preview access is false (dynamic)', () => {
|
||||
const mockDynamicConfig = {
|
||||
getExperimentalDynamicModelConfiguration: () => true,
|
||||
modelConfigService,
|
||||
} as unknown as Config;
|
||||
|
||||
expect(
|
||||
resolveModel(
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
false, // No preview access
|
||||
mockDynamicConfig,
|
||||
false, // GA false
|
||||
),
|
||||
).toBe('gemini-2.5-flash');
|
||||
});
|
||||
|
||||
it('should resolve auto to DEFAULT_GEMINI_FLASH_MODEL when useGemini3_5Flash is true and classifier selects flash', () => {
|
||||
expect(
|
||||
resolveClassifierModel(
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should resolve auto to gemini-3.5-flash when useGemini3_5Flash is true and classifier selects flash (dynamic)', () => {
|
||||
const mockDynamicConfig = {
|
||||
getExperimentalDynamicModelConfiguration: () => true,
|
||||
modelConfigService,
|
||||
} as unknown as Config;
|
||||
|
||||
expect(
|
||||
resolveClassifierModel(
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3.5-flash');
|
||||
});
|
||||
|
||||
describe('Flash model promotion and manual override routing logic', () => {
|
||||
it('should resolve flash alias to DEFAULT_GEMINI_FLASH_MODEL when useGemini3_5Flash is true (static)', () => {
|
||||
expect(
|
||||
resolveModel(
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should resolve flash alias to gemini-3.5-flash when useGemini3_5Flash is true (dynamic)', () => {
|
||||
const mockDynamicConfig = {
|
||||
getExperimentalDynamicModelConfiguration: () => true,
|
||||
modelConfigService,
|
||||
} as unknown as Config;
|
||||
|
||||
expect(
|
||||
resolveModel(
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3.5-flash');
|
||||
});
|
||||
|
||||
it('should resolve manual selection of gemini-3-flash-preview to gemini-3-flash-preview when useGemini3_5Flash is true and has preview access (static)', () => {
|
||||
expect(
|
||||
resolveModel(
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3-flash-preview');
|
||||
});
|
||||
|
||||
it('should resolve manual selection of gemini-3-flash-preview to gemini-3-flash-preview when useGemini3_5Flash is true and has preview access (dynamic)', () => {
|
||||
const mockDynamicConfig = {
|
||||
getExperimentalDynamicModelConfiguration: () => true,
|
||||
modelConfigService,
|
||||
} as unknown as Config;
|
||||
|
||||
expect(
|
||||
resolveModel(
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3-flash-preview');
|
||||
});
|
||||
|
||||
it('should resolve manual selection of gemini-3-flash-preview to DEFAULT_GEMINI_FLASH_MODEL when useGemini3_5Flash is true but lacks preview access (static)', () => {
|
||||
expect(
|
||||
resolveModel(
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should resolve manual selection of gemini-3-flash-preview to gemini-3.5-flash when useGemini3_5Flash is true but lacks preview access (dynamic)', () => {
|
||||
const mockDynamicConfig = {
|
||||
getExperimentalDynamicModelConfiguration: () => true,
|
||||
modelConfigService,
|
||||
} as unknown as Config;
|
||||
|
||||
expect(
|
||||
resolveModel(
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3.5-flash');
|
||||
});
|
||||
|
||||
it('should resolve classifier-selected flash alias to DEFAULT_GEMINI_FLASH_MODEL when useGemini3_5Flash is true (static)', () => {
|
||||
expect(
|
||||
resolveClassifierModel(
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should resolve classifier-selected flash alias to gemini-3.5-flash when useGemini3_5Flash is true (dynamic)', () => {
|
||||
const mockDynamicConfig = {
|
||||
getExperimentalDynamicModelConfiguration: () => true,
|
||||
modelConfigService,
|
||||
} as unknown as Config;
|
||||
|
||||
expect(
|
||||
resolveClassifierModel(
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3.5-flash');
|
||||
});
|
||||
|
||||
it('should resolve auto to PREVIEW_GEMINI_MODEL when useGemini3_5Flash is true and has preview access', () => {
|
||||
expect(
|
||||
resolveModel(
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
false,
|
||||
false,
|
||||
true, // hasAccessToPreview
|
||||
undefined,
|
||||
true, // useGemini3_5Flash
|
||||
),
|
||||
).toBe(PREVIEW_GEMINI_MODEL);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
export interface ModelResolutionContext {
|
||||
useGemini3_1?: boolean;
|
||||
useGemini3_5Flash?: boolean;
|
||||
useCustomTools?: boolean;
|
||||
hasAccessToPreview?: boolean;
|
||||
requestedModel?: string;
|
||||
@@ -54,9 +55,29 @@ export const PREVIEW_GEMINI_MODEL = 'gemini-3-pro-preview';
|
||||
export const PREVIEW_GEMINI_3_1_MODEL = 'gemini-3.1-pro-preview';
|
||||
export const PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL =
|
||||
'gemini-3.1-pro-preview-customtools';
|
||||
export const PREVIEW_GEMINI_FLASH_MODEL = 'gemini-3-flash-preview';
|
||||
// TODO: set to none and const once the experiment for 3_5 flash rollut can be
|
||||
// cleaned up.
|
||||
export let PREVIEW_GEMINI_FLASH_MODEL = 'gemini-3-flash-preview';
|
||||
export const DEFAULT_GEMINI_MODEL = 'gemini-2.5-pro';
|
||||
export const DEFAULT_GEMINI_FLASH_MODEL = 'gemini-2.5-flash';
|
||||
// TODO: Set to const and update to 'gemini-3.5-flash' once the experiment for
|
||||
// 3_5 flash rollut can be cleaned up.
|
||||
// This is set to either the same as the DEFAULT_GEMINI_3_5_FLASH_MODEL const
|
||||
// OR the SECONDARY_GEMINI_3_5_FLASH_MODEL depending on which is needed for
|
||||
// the user's backend as determined by hasGemini35FlashGAAccess in
|
||||
// packages/core/src/config/config.ts
|
||||
export let DEFAULT_GEMINI_FLASH_MODEL = 'gemini-2.5-flash';
|
||||
export const DEFAULT_GEMINI_3_5_FLASH_MODEL = 'gemini-3.5-flash';
|
||||
// This is resolved to 3.5 flash in backends where it is used,
|
||||
// however those backends do not expect to see the string gemini-3.5-flash
|
||||
// so we need to provide this model as an alternative name in certain instances.
|
||||
export const SECONDARY_GEMINI_3_5_FLASH_MODEL = 'gemini-3-flash';
|
||||
|
||||
// Used to set default flash models based on access
|
||||
// TODO: Cleanup once the experiment for 3_5 flash rollut can be cleaned up.
|
||||
export function setFlashModels(preview: string, defaultFlash: string) {
|
||||
PREVIEW_GEMINI_FLASH_MODEL = preview;
|
||||
DEFAULT_GEMINI_FLASH_MODEL = defaultFlash;
|
||||
}
|
||||
export const DEFAULT_GEMINI_FLASH_LITE_MODEL = 'gemini-3.1-flash-lite';
|
||||
/** @deprecated Gemini 3.1 Flash Lite is now GA. Use DEFAULT_GEMINI_FLASH_LITE_MODEL. */
|
||||
export const PREVIEW_GEMINI_FLASH_LITE_MODEL = 'none';
|
||||
@@ -72,6 +93,8 @@ export const VALID_GEMINI_MODELS = new Set([
|
||||
PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_3_5_FLASH_MODEL,
|
||||
SECONDARY_GEMINI_3_5_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
|
||||
GEMMA_4_31B_IT_MODEL,
|
||||
@@ -97,6 +120,7 @@ export const DEFAULT_THINKING_MODE = 8192;
|
||||
export function getAutoModelDescription(
|
||||
hasAccessToPreview: boolean,
|
||||
useGemini3_1: boolean = false,
|
||||
useGemini3_5Flash: boolean = false,
|
||||
) {
|
||||
const proModel = hasAccessToPreview
|
||||
? useGemini3_1
|
||||
@@ -104,9 +128,11 @@ export function getAutoModelDescription(
|
||||
: PREVIEW_GEMINI_MODEL
|
||||
: DEFAULT_GEMINI_MODEL;
|
||||
const flashModel = hasAccessToPreview
|
||||
? PREVIEW_GEMINI_FLASH_MODEL
|
||||
? useGemini3_5Flash
|
||||
? DEFAULT_GEMINI_3_5_FLASH_MODEL
|
||||
: PREVIEW_GEMINI_FLASH_MODEL
|
||||
: DEFAULT_GEMINI_FLASH_MODEL;
|
||||
return `Let Gemini CLI decide the best model for the task: ${proModel}, ${flashModel}`;
|
||||
return `Let Gemini CLI decide the best model for the task: ${getDisplayString(proModel)}, ${getDisplayString(flashModel)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,6 +141,7 @@ export function getAutoModelDescription(
|
||||
*
|
||||
* @param requestedModel The model alias or concrete model name requested by the user.
|
||||
* @param useGemini3_1 Whether to use Gemini 3.1 Pro Preview for auto/pro aliases.
|
||||
* @param useGemini3_5Flash Whether to use Gemini 3.5 Flash GA.
|
||||
* @param hasAccessToPreview Whether the user has access to preview models.
|
||||
* @returns The resolved concrete model name.
|
||||
*/
|
||||
@@ -124,6 +151,7 @@ export function resolveModel(
|
||||
useCustomToolModel: boolean = false,
|
||||
hasAccessToPreview: boolean = true,
|
||||
config?: ModelCapabilityContext,
|
||||
useGemini3_5Flash: boolean = false,
|
||||
): string {
|
||||
// Defensive check against non-string inputs at runtime
|
||||
const normalizedModel = Array.isArray(requestedModel)
|
||||
@@ -137,6 +165,7 @@ export function resolveModel(
|
||||
useGemini3_1,
|
||||
useCustomTools: useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
useGemini3_5Flash,
|
||||
});
|
||||
|
||||
if (!hasAccessToPreview && isPreviewModel(resolved, config)) {
|
||||
@@ -179,7 +208,9 @@ export function resolveModel(
|
||||
break;
|
||||
}
|
||||
case GEMINI_MODEL_ALIAS_FLASH: {
|
||||
resolved = PREVIEW_GEMINI_FLASH_MODEL;
|
||||
resolved = useGemini3_5Flash
|
||||
? DEFAULT_GEMINI_FLASH_MODEL
|
||||
: PREVIEW_GEMINI_FLASH_MODEL;
|
||||
break;
|
||||
}
|
||||
case GEMINI_MODEL_ALIAS_FLASH_LITE: {
|
||||
@@ -196,6 +227,14 @@ export function resolveModel(
|
||||
return DEFAULT_GEMINI_FLASH_LITE_MODEL;
|
||||
}
|
||||
|
||||
if (
|
||||
useGemini3_5Flash &&
|
||||
isFlashModel(resolved) &&
|
||||
normalizedModel !== PREVIEW_GEMINI_FLASH_MODEL
|
||||
) {
|
||||
return DEFAULT_GEMINI_FLASH_MODEL;
|
||||
}
|
||||
|
||||
if (!hasAccessToPreview && isPreviewModel(resolved)) {
|
||||
// Downgrade to stable models if user lacks preview access.
|
||||
switch (resolved) {
|
||||
@@ -220,6 +259,17 @@ export function resolveModel(
|
||||
return resolved;
|
||||
}
|
||||
|
||||
function isFlashModel(model: string): boolean {
|
||||
return (
|
||||
model === DEFAULT_GEMINI_FLASH_MODEL ||
|
||||
model === PREVIEW_GEMINI_FLASH_MODEL ||
|
||||
model === DEFAULT_GEMINI_3_5_FLASH_MODEL ||
|
||||
model === SECONDARY_GEMINI_3_5_FLASH_MODEL ||
|
||||
model === 'flash' ||
|
||||
model.endsWith('flash')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the appropriate model based on the classifier's decision.
|
||||
*
|
||||
@@ -237,6 +287,7 @@ export function resolveClassifierModel(
|
||||
useCustomToolModel: boolean = false,
|
||||
hasAccessToPreview: boolean = true,
|
||||
config?: ModelCapabilityContext,
|
||||
useGemini3_5Flash: boolean = false,
|
||||
): string {
|
||||
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
|
||||
return config.modelConfigService.resolveClassifierModelId(
|
||||
@@ -246,6 +297,7 @@ export function resolveClassifierModel(
|
||||
useGemini3_1,
|
||||
useCustomTools: useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
useGemini3_5Flash,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -262,6 +314,9 @@ export function resolveClassifierModel(
|
||||
requestedModel === PREVIEW_GEMINI_MODEL ||
|
||||
requestedModel === GEMINI_MODEL_ALIAS_AUTO
|
||||
) {
|
||||
if (useGemini3_5Flash) {
|
||||
return DEFAULT_GEMINI_FLASH_MODEL;
|
||||
}
|
||||
return hasAccessToPreview
|
||||
? PREVIEW_GEMINI_FLASH_MODEL
|
||||
: DEFAULT_GEMINI_FLASH_MODEL;
|
||||
@@ -271,6 +326,8 @@ export function resolveClassifierModel(
|
||||
false,
|
||||
false,
|
||||
hasAccessToPreview,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
);
|
||||
}
|
||||
return resolveModel(
|
||||
@@ -279,6 +336,7 @@ export function resolveClassifierModel(
|
||||
useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -294,6 +352,8 @@ export function getDisplayString(
|
||||
}
|
||||
|
||||
switch (model) {
|
||||
case 'gemini-3-flash':
|
||||
return DEFAULT_GEMINI_3_5_FLASH_MODEL;
|
||||
case GEMINI_MODEL_ALIAS_AUTO:
|
||||
return 'Auto';
|
||||
case PREVIEW_GEMINI_MODEL_AUTO:
|
||||
|
||||
@@ -607,6 +607,7 @@ export class GeminiClient {
|
||||
false,
|
||||
this.config.getHasAccessToPreviewModel?.() ?? true,
|
||||
this.config,
|
||||
this.config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -221,6 +221,7 @@ export async function createContentGenerator(
|
||||
false,
|
||||
gcConfig.getHasAccessToPreviewModel?.() ?? true,
|
||||
gcConfig,
|
||||
gcConfig.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
const customHeadersEnv =
|
||||
process.env['GEMINI_CLI_CUSTOM_HEADERS'] || undefined;
|
||||
|
||||
@@ -159,6 +159,7 @@ describe('GeminiChat', () => {
|
||||
getTelemetryLogPromptsEnabled: () => true,
|
||||
getTelemetryTracesEnabled: () => false,
|
||||
getUsageStatisticsEnabled: () => true,
|
||||
hasGemini35FlashGAAccess: vi.fn().mockReturnValue(false),
|
||||
getDebugMode: () => false,
|
||||
getContentGeneratorConfig: vi.fn().mockImplementation(() => ({
|
||||
authType: 'oauth-personal',
|
||||
|
||||
@@ -721,7 +721,6 @@ export class GeminiChat {
|
||||
(await this.context.config.getGemini31Launched?.()) ?? false;
|
||||
const hasAccessToPreview =
|
||||
this.context.config.getHasAccessToPreviewModel?.() ?? true;
|
||||
|
||||
// Default to the last used model (which respects arguments/availability selection)
|
||||
let modelToUse = resolveModel(
|
||||
lastModelToUse,
|
||||
@@ -729,6 +728,7 @@ export class GeminiChat {
|
||||
false,
|
||||
hasAccessToPreview,
|
||||
this.context.config,
|
||||
this.context.config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
|
||||
// If the active model has changed (e.g. due to a fallback updating the config),
|
||||
@@ -740,6 +740,7 @@ export class GeminiChat {
|
||||
false,
|
||||
hasAccessToPreview,
|
||||
this.context.config,
|
||||
this.context.config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -802,6 +803,7 @@ export class GeminiChat {
|
||||
false,
|
||||
hasAccessToPreview,
|
||||
this.context.config,
|
||||
this.context.config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
lastModelToUse = modelToUse;
|
||||
// Re-evaluate contentsToUse based on the new model's feature support
|
||||
|
||||
@@ -98,6 +98,7 @@ describe('GeminiChat Network Retries', () => {
|
||||
getTelemetryLogPromptsEnabled: () => true,
|
||||
getTelemetryTracesEnabled: () => false,
|
||||
getUsageStatisticsEnabled: () => true,
|
||||
hasGemini35FlashGAAccess: vi.fn().mockReturnValue(false),
|
||||
getDebugMode: () => false,
|
||||
getContentGeneratorConfig: vi.fn().mockReturnValue({
|
||||
authType: 'oauth-personal',
|
||||
|
||||
@@ -76,6 +76,7 @@ export class PromptProvider {
|
||||
false,
|
||||
context.config.getHasAccessToPreviewModel?.() ?? true,
|
||||
context.config,
|
||||
context.config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
const isModernModel = supportsModernFeatures(desiredModel);
|
||||
const activeSnippets = isModernModel ? snippets : legacySnippets;
|
||||
@@ -299,6 +300,7 @@ export class PromptProvider {
|
||||
false,
|
||||
context.config.getHasAccessToPreviewModel?.() ?? true,
|
||||
context.config,
|
||||
context.config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
const isModernModel = supportsModernFeatures(desiredModel);
|
||||
const activeSnippets = isModernModel ? snippets : legacySnippets;
|
||||
|
||||
@@ -242,4 +242,22 @@ describe('ApprovalModeStrategy', () => {
|
||||
// Should resolve to Preview Flash (3.0) because resolveClassifierModel uses preview variants for Gemini 3
|
||||
expect(decision?.model).toBe(PREVIEW_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should route to DEFAULT_GEMINI_FLASH_MODEL when hasGemini35FlashGAAccess is true and plan is approved', async () => {
|
||||
vi.mocked(mockConfig.getModel).mockReturnValue(GEMINI_MODEL_ALIAS_AUTO);
|
||||
mockConfig.hasGemini35FlashGAAccess = vi.fn().mockReturnValue(true);
|
||||
|
||||
vi.mocked(mockConfig.getApprovalMode).mockReturnValue(ApprovalMode.DEFAULT);
|
||||
vi.mocked(mockConfig.getApprovedPlanPath).mockReturnValue(
|
||||
'/path/to/plan.md',
|
||||
);
|
||||
|
||||
const decision = await strategy.route(
|
||||
mockContext,
|
||||
mockConfig,
|
||||
mockBaseLlmClient,
|
||||
);
|
||||
|
||||
expect(decision?.model).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -54,6 +54,7 @@ export class ApprovalModeStrategy implements RoutingStrategy {
|
||||
config.getUseCustomToolModel(),
|
||||
config.getHasAccessToPreviewModel(),
|
||||
]);
|
||||
const useGemini3_5Flash = config.hasGemini35FlashGAAccess?.() ?? false;
|
||||
|
||||
// 1. Planning Phase: If ApprovalMode === PLAN, explicitly route to the Pro model.
|
||||
if (approvalMode === ApprovalMode.PLAN) {
|
||||
@@ -64,6 +65,7 @@ export class ApprovalModeStrategy implements RoutingStrategy {
|
||||
useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
);
|
||||
return {
|
||||
model: proModel,
|
||||
@@ -82,6 +84,7 @@ export class ApprovalModeStrategy implements RoutingStrategy {
|
||||
useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
);
|
||||
return {
|
||||
model: flashModel,
|
||||
|
||||
@@ -522,5 +522,27 @@ describe('ClassifierStrategy', () => {
|
||||
|
||||
expect(decision?.model).toBe(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL);
|
||||
});
|
||||
|
||||
it('should route to DEFAULT_GEMINI_FLASH_MODEL when hasGemini35FlashGAAccess is true', async () => {
|
||||
mockConfig.hasGemini35FlashGAAccess = vi.fn().mockReturnValue(true);
|
||||
vi.mocked(mockConfig.getModel).mockReturnValue(PREVIEW_GEMINI_MODEL_AUTO);
|
||||
|
||||
const mockApiResponse = {
|
||||
reasoning: 'Simple task',
|
||||
model_choice: 'flash',
|
||||
};
|
||||
vi.mocked(mockBaseLlmClient.generateJson).mockResolvedValue(
|
||||
mockApiResponse,
|
||||
);
|
||||
|
||||
const decision = await strategy.route(
|
||||
mockContext,
|
||||
mockConfig,
|
||||
mockBaseLlmClient,
|
||||
mockLocalLiteRtLmClient,
|
||||
);
|
||||
|
||||
expect(decision?.model).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -186,6 +186,7 @@ export class ClassifierStrategy implements RoutingStrategy {
|
||||
config.getGemini31Launched(),
|
||||
config.getUseCustomToolModel(),
|
||||
]);
|
||||
const useGemini3_5Flash = config.hasGemini35FlashGAAccess?.() ?? false;
|
||||
const selectedModel = normalizeModelId(
|
||||
resolveClassifierModel(
|
||||
normalizeModelId(model),
|
||||
@@ -194,6 +195,7 @@ export class ClassifierStrategy implements RoutingStrategy {
|
||||
useCustomToolModel,
|
||||
config.getHasAccessToPreviewModel?.() ?? true,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ export class DefaultStrategy implements TerminalStrategy {
|
||||
false,
|
||||
config.getHasAccessToPreviewModel?.() ?? true,
|
||||
config,
|
||||
config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
return {
|
||||
model: defaultModel,
|
||||
|
||||
@@ -31,6 +31,7 @@ export class FallbackStrategy implements RoutingStrategy {
|
||||
false,
|
||||
config.getHasAccessToPreviewModel?.() ?? true,
|
||||
config,
|
||||
config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
const service = config.getModelAvailabilityService();
|
||||
const snapshot = service.snapshot(resolvedModel);
|
||||
|
||||
@@ -12,6 +12,7 @@ import type { BaseLlmClient } from '../../core/baseLlmClient.js';
|
||||
import {
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
PREVIEW_GEMINI_MODEL_AUTO,
|
||||
} from '../../config/models.js';
|
||||
import type { Content } from '@google/genai';
|
||||
import { debugLogger } from '../../utils/debugLogger.js';
|
||||
@@ -323,4 +324,24 @@ second message
|
||||
|
||||
expect(lastTurn!.parts!.at(0)!.text).toEqual(expectedLastTurn);
|
||||
});
|
||||
|
||||
it('should route to DEFAULT_GEMINI_FLASH_MODEL when hasGemini35FlashGAAccess is true', async () => {
|
||||
mockConfig.hasGemini35FlashGAAccess = vi.fn().mockReturnValue(true);
|
||||
mockConfig.getModel = () => PREVIEW_GEMINI_MODEL_AUTO;
|
||||
|
||||
const mockApiResponse = {
|
||||
reasoning: 'Simple task',
|
||||
model_choice: 'flash',
|
||||
};
|
||||
mockGenerateJson.mockResolvedValue(mockApiResponse);
|
||||
|
||||
const decision = await strategy.route(
|
||||
mockContext,
|
||||
mockConfig,
|
||||
mockBaseLlmClient,
|
||||
mockLocalLiteRtLmClient,
|
||||
);
|
||||
|
||||
expect(decision?.model).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -216,6 +216,7 @@ ${formattedHistory}
|
||||
config.getUseCustomToolModel(),
|
||||
config.getHasAccessToPreviewModel(),
|
||||
]);
|
||||
const useGemini3_5Flash = config.hasGemini35FlashGAAccess?.() ?? false;
|
||||
|
||||
const selectedModel = resolveClassifierModel(
|
||||
context.requestedModel ?? config.getModel(),
|
||||
@@ -224,6 +225,7 @@ ${formattedHistory}
|
||||
useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
PREVIEW_GEMINI_MODEL_AUTO,
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
} from '../../config/models.js';
|
||||
import { promptIdContext } from '../../utils/promptIdContext.js';
|
||||
import type { Content } from '@google/genai';
|
||||
@@ -894,5 +895,27 @@ describe('NumericalClassifierStrategy', () => {
|
||||
|
||||
expect(decision?.model).toBe(PREVIEW_GEMINI_3_1_MODEL);
|
||||
});
|
||||
|
||||
it('should route to DEFAULT_GEMINI_FLASH_MODEL when hasGemini35FlashGAAccess is true', async () => {
|
||||
mockConfig.hasGemini35FlashGAAccess = vi.fn().mockReturnValue(true);
|
||||
vi.mocked(mockConfig.getModel).mockReturnValue(PREVIEW_GEMINI_MODEL_AUTO);
|
||||
|
||||
const mockApiResponse = {
|
||||
complexity_reasoning: 'Simple task',
|
||||
complexity_score: 10,
|
||||
};
|
||||
vi.mocked(mockBaseLlmClient.generateJson).mockResolvedValue(
|
||||
mockApiResponse,
|
||||
);
|
||||
|
||||
const decision = await strategy.route(
|
||||
mockContext,
|
||||
mockConfig,
|
||||
mockBaseLlmClient,
|
||||
mockLocalLiteRtLmClient,
|
||||
);
|
||||
|
||||
expect(decision?.model).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -184,6 +184,7 @@ export class NumericalClassifierStrategy implements RoutingStrategy {
|
||||
config.getGemini31Launched(),
|
||||
config.getUseCustomToolModel(),
|
||||
]);
|
||||
const useGemini3_5Flash = config.hasGemini35FlashGAAccess?.() ?? false;
|
||||
const selectedModel = normalizeModelId(
|
||||
resolveClassifierModel(
|
||||
normalizeModelId(model),
|
||||
@@ -192,6 +193,7 @@ export class NumericalClassifierStrategy implements RoutingStrategy {
|
||||
useCustomToolModel,
|
||||
config.getHasAccessToPreviewModel?.() ?? true,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ export class OverrideStrategy implements RoutingStrategy {
|
||||
false,
|
||||
config.getHasAccessToPreviewModel?.() ?? true,
|
||||
config,
|
||||
config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
),
|
||||
metadata: {
|
||||
source: this.name,
|
||||
|
||||
@@ -1044,6 +1044,79 @@ describe('ModelConfigService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// Resolves a model ID to a concrete model ID based on the provided context.
|
||||
describe('resolveModelId', () => {
|
||||
it('should resolve based on useGemini3_5Flash condition', () => {
|
||||
const config: ModelConfigServiceConfig = {
|
||||
modelIdResolutions: {
|
||||
flash: {
|
||||
default: 'gemini-2.0-flash',
|
||||
contexts: [
|
||||
{
|
||||
condition: { useGemini3_5Flash: true },
|
||||
target: 'gemini-3.5-flash',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const service = new ModelConfigService(config);
|
||||
|
||||
expect(service.resolveModelId('flash', { useGemini3_5Flash: true })).toBe(
|
||||
'gemini-3.5-flash',
|
||||
);
|
||||
expect(
|
||||
service.resolveModelId('flash', { useGemini3_5Flash: false }),
|
||||
).toBe('gemini-2.0-flash');
|
||||
expect(service.resolveModelId('flash', {})).toBe('gemini-2.0-flash');
|
||||
});
|
||||
|
||||
it('should resolve based on complex conditions including useGemini3_5Flash', () => {
|
||||
const config: ModelConfigServiceConfig = {
|
||||
modelIdResolutions: {
|
||||
'gemini-flash': {
|
||||
default: 'gemini-3-flash-preview',
|
||||
contexts: [
|
||||
{
|
||||
condition: {
|
||||
useGemini3_5Flash: false,
|
||||
hasAccessToPreview: false,
|
||||
},
|
||||
target: 'gemini-2.5-flash',
|
||||
},
|
||||
{
|
||||
condition: { useGemini3_5Flash: true },
|
||||
target: 'gemini-3.5-flash',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const service = new ModelConfigService(config);
|
||||
|
||||
// Case 1: GA Access granted
|
||||
expect(
|
||||
service.resolveModelId('gemini-flash', { useGemini3_5Flash: true }),
|
||||
).toBe('gemini-3.5-flash');
|
||||
|
||||
// Case 2: GA Access denied, but has preview access
|
||||
expect(
|
||||
service.resolveModelId('gemini-flash', {
|
||||
useGemini3_5Flash: false,
|
||||
hasAccessToPreview: true,
|
||||
}),
|
||||
).toBe('gemini-3-flash-preview');
|
||||
|
||||
// Case 3: GA Access denied AND no preview access
|
||||
expect(
|
||||
service.resolveModelId('gemini-flash', {
|
||||
useGemini3_5Flash: false,
|
||||
hasAccessToPreview: false,
|
||||
}),
|
||||
).toBe('gemini-2.5-flash');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAvailableModelOptions', () => {
|
||||
it('should filter out Pro models when hasAccessToProModel is false', () => {
|
||||
const config: ModelConfigServiceConfig = {
|
||||
|
||||
@@ -97,6 +97,7 @@ export interface ModelResolution {
|
||||
export interface ResolutionContext {
|
||||
useGemini3_1?: boolean;
|
||||
useGemini3_1FlashLite?: boolean;
|
||||
useGemini3_5Flash?: boolean;
|
||||
useCustomTools?: boolean;
|
||||
hasAccessToPreview?: boolean;
|
||||
hasAccessToProModel?: boolean;
|
||||
@@ -107,6 +108,7 @@ export interface ResolutionContext {
|
||||
export interface ResolutionCondition {
|
||||
useGemini3_1?: boolean;
|
||||
useGemini3_1FlashLite?: boolean;
|
||||
useGemini3_5Flash?: boolean;
|
||||
useCustomTools?: boolean;
|
||||
hasAccessToPreview?: boolean;
|
||||
/** Matches if the current model is in this list. */
|
||||
@@ -155,6 +157,7 @@ export class ModelConfigService {
|
||||
const definitions = this.config.modelDefinitions ?? {};
|
||||
const shouldShowPreviewModels = context.hasAccessToPreview ?? false;
|
||||
const useGemini31 = context.useGemini3_1 ?? false;
|
||||
const useGemini3_5Flash = context.useGemini3_5Flash ?? false;
|
||||
|
||||
const mainOptions = Object.entries(definitions)
|
||||
.filter(([_, m]) => {
|
||||
@@ -169,6 +172,7 @@ export class ModelConfigService {
|
||||
description = getAutoModelDescription(
|
||||
shouldShowPreviewModels,
|
||||
useGemini31,
|
||||
useGemini3_5Flash,
|
||||
);
|
||||
} else if (id === 'auto-gemini-3' && useGemini31) {
|
||||
description = description.replace('gemini-3-pro', 'gemini-3.1-pro');
|
||||
@@ -250,6 +254,8 @@ export class ModelConfigService {
|
||||
return value === context.useGemini3_1;
|
||||
case 'useGemini3_1FlashLite':
|
||||
return value === context.useGemini3_1FlashLite;
|
||||
case 'useGemini3_5Flash':
|
||||
return value === context.useGemini3_5Flash;
|
||||
case 'useCustomTools':
|
||||
return value === context.useCustomTools;
|
||||
case 'hasAccessToPreview':
|
||||
|
||||
@@ -145,6 +145,18 @@
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemini-3.5-flash": {
|
||||
"model": "gemini-3.5-flash",
|
||||
"generateContentConfig": {
|
||||
"temperature": 1,
|
||||
"topP": 0.95,
|
||||
"thinkingConfig": {
|
||||
"includeThoughts": true,
|
||||
"thinkingLevel": "HIGH"
|
||||
},
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemma-4-31b-it": {
|
||||
"model": "gemma-4-31b-it",
|
||||
"generateContentConfig": {
|
||||
@@ -183,6 +195,13 @@
|
||||
"topP": 1
|
||||
}
|
||||
},
|
||||
"gemini-3.5-flash-base": {
|
||||
"model": "gemini-3.5-flash",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1
|
||||
}
|
||||
},
|
||||
"classifier": {
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
|
||||
@@ -145,6 +145,18 @@
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemini-3.5-flash": {
|
||||
"model": "gemini-3.5-flash",
|
||||
"generateContentConfig": {
|
||||
"temperature": 1,
|
||||
"topP": 0.95,
|
||||
"thinkingConfig": {
|
||||
"includeThoughts": true,
|
||||
"thinkingLevel": "HIGH"
|
||||
},
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemma-4-31b-it": {
|
||||
"model": "gemma-4-31b-it",
|
||||
"generateContentConfig": {
|
||||
@@ -183,6 +195,13 @@
|
||||
"topP": 1
|
||||
}
|
||||
},
|
||||
"gemini-3.5-flash-base": {
|
||||
"model": "gemini-3.5-flash",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1
|
||||
}
|
||||
},
|
||||
"classifier": {
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
|
||||
@@ -1061,18 +1061,24 @@ export class ShellToolInvocation extends BaseToolInvocation<
|
||||
}
|
||||
signal.removeEventListener('abort', onAbort);
|
||||
timeoutController.signal.removeEventListener('abort', onAbort);
|
||||
if (tempFilePath) {
|
||||
try {
|
||||
await fsPromises.unlink(tempFilePath);
|
||||
} catch {
|
||||
// Ignore errors during unlink
|
||||
|
||||
// Only clean up if NOT running in background.
|
||||
// Background processes need the temp directory and PID file to remain
|
||||
// available until they exit.
|
||||
if (!this.params.is_background) {
|
||||
if (tempFilePath) {
|
||||
try {
|
||||
await fsPromises.unlink(tempFilePath);
|
||||
} catch {
|
||||
// Ignore errors during unlink
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tempDir) {
|
||||
try {
|
||||
await fsPromises.rm(tempDir, { recursive: true, force: true });
|
||||
} catch {
|
||||
// Ignore errors during rm
|
||||
if (tempDir) {
|
||||
try {
|
||||
await fsPromises.rm(tempDir, { recursive: true, force: true });
|
||||
} catch {
|
||||
// Ignore errors during rm
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-devtools",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"main": "dist/src/index.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-sdk",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"description": "Gemini CLI SDK",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-test-utils",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"private": true,
|
||||
"main": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "gemini-cli-vscode-ide-companion",
|
||||
"displayName": "Gemini CLI Companion",
|
||||
"description": "Enable Gemini CLI with direct access to your IDE workspace.",
|
||||
"version": "0.45.0-nightly.20260521.g854f811be",
|
||||
"version": "0.45.2",
|
||||
"publisher": "google",
|
||||
"icon": "assets/icon.png",
|
||||
"repository": {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user