Skip to content

Commit 1262801

Browse files
authored
Merge pull request #466 from NoeFabris/codex/gemini-3-1-support
Add Gemini 3.1 model support (prep for rollout)
2 parents eb2d9e4 + 9dd268a commit 1262801

8 files changed

Lines changed: 133 additions & 27 deletions

File tree

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ opencode run "Hello" --model=google/antigravity-claude-opus-4-6-thinking --varia
118118
| Model | Variants | Notes |
119119
|-------|----------|-------|
120120
| `antigravity-gemini-3-pro` | low, high | Gemini 3 Pro with thinking |
121+
| `antigravity-gemini-3.1-pro` | low, high | Gemini 3.1 Pro with thinking (rollout-dependent) |
121122
| `antigravity-gemini-3-flash` | minimal, low, medium, high | Gemini 3 Flash with thinking |
122123
| `antigravity-claude-sonnet-4-6` || Claude Sonnet 4.6 |
123124
| `antigravity-claude-opus-4-6-thinking` | low, max | Claude Opus 4.6 with extended thinking |
@@ -130,6 +131,8 @@ opencode run "Hello" --model=google/antigravity-claude-opus-4-6-thinking --varia
130131
| `gemini-2.5-pro` | Gemini 2.5 Pro |
131132
| `gemini-3-flash-preview` | Gemini 3 Flash (preview) |
132133
| `gemini-3-pro-preview` | Gemini 3 Pro (preview) |
134+
| `gemini-3.1-pro-preview` | Gemini 3.1 Pro (preview, rollout-dependent) |
135+
| `gemini-3.1-pro-preview-customtools` | Gemini 3.1 Pro Preview Custom Tools (preview, rollout-dependent) |
133136

134137
> **Routing Behavior:**
135138
> - **Antigravity-first (default):** Gemini models use Antigravity quota across accounts.
@@ -166,6 +169,15 @@ Add this to your `~/.config/opencode/opencode.json`:
166169
"high": { "thinkingLevel": "high" }
167170
}
168171
},
172+
"antigravity-gemini-3.1-pro": {
173+
"name": "Gemini 3.1 Pro (Antigravity)",
174+
"limit": { "context": 1048576, "output": 65535 },
175+
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] },
176+
"variants": {
177+
"low": { "thinkingLevel": "low" },
178+
"high": { "thinkingLevel": "high" }
179+
}
180+
},
169181
"antigravity-gemini-3-flash": {
170182
"name": "Gemini 3 Flash (Antigravity)",
171183
"limit": { "context": 1048576, "output": 65536 },
@@ -210,6 +222,16 @@ Add this to your `~/.config/opencode/opencode.json`:
210222
"name": "Gemini 3 Pro Preview (Gemini CLI)",
211223
"limit": { "context": 1048576, "output": 65535 },
212224
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }
225+
},
226+
"gemini-3.1-pro-preview": {
227+
"name": "Gemini 3.1 Pro Preview (Gemini CLI)",
228+
"limit": { "context": 1048576, "output": 65535 },
229+
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }
230+
},
231+
"gemini-3.1-pro-preview-customtools": {
232+
"name": "Gemini 3.1 Pro Preview Custom Tools (Gemini CLI)",
233+
"limit": { "context": 1048576, "output": 65535 },
234+
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }
213235
}
214236
}
215237
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "opencode-antigravity-auth",
3-
"version": "1.5.2",
3+
"version": "1.5.3",
44
"description": "Google Antigravity IDE OAuth auth plugin for Opencode - access Gemini 3 Pro and Claude 4.6 using Google credentials",
55
"main": "./dist/index.js",
66
"types": "./dist/index.d.ts",

src/plugin/config/models.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ describe("OPENCODE_MODEL_DEFINITIONS", () => {
1919
"antigravity-claude-sonnet-4-6",
2020
"antigravity-gemini-3-flash",
2121
"antigravity-gemini-3-pro",
22+
"antigravity-gemini-3.1-pro",
2223
"gemini-2.5-flash",
2324
"gemini-2.5-pro",
2425
"gemini-3-flash-preview",
2526
"gemini-3-pro-preview",
27+
"gemini-3.1-pro-preview",
28+
"gemini-3.1-pro-preview-customtools",
2629
]);
2730
});
2831

@@ -32,6 +35,11 @@ describe("OPENCODE_MODEL_DEFINITIONS", () => {
3235
high: { thinkingLevel: "high" },
3336
});
3437

38+
expect(getModel("antigravity-gemini-3.1-pro").variants).toEqual({
39+
low: { thinkingLevel: "low" },
40+
high: { thinkingLevel: "high" },
41+
});
42+
3543
expect(getModel("antigravity-gemini-3-flash").variants).toEqual({
3644
minimal: { thinkingLevel: "minimal" },
3745
low: { thinkingLevel: "low" },

src/plugin/config/models.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ export const OPENCODE_MODEL_DEFINITIONS: OpencodeModelDefinitions = {
4747
high: { thinkingLevel: "high" },
4848
},
4949
},
50+
"antigravity-gemini-3.1-pro": {
51+
name: "Gemini 3.1 Pro (Antigravity)",
52+
limit: { context: 1048576, output: 65535 },
53+
modalities: DEFAULT_MODALITIES,
54+
variants: {
55+
low: { thinkingLevel: "low" },
56+
high: { thinkingLevel: "high" },
57+
},
58+
},
5059
"antigravity-gemini-3-flash": {
5160
name: "Gemini 3 Flash (Antigravity)",
5261
limit: { context: 1048576, output: 65536 },
@@ -92,4 +101,14 @@ export const OPENCODE_MODEL_DEFINITIONS: OpencodeModelDefinitions = {
92101
limit: { context: 1048576, output: 65535 },
93102
modalities: DEFAULT_MODALITIES,
94103
},
104+
"gemini-3.1-pro-preview": {
105+
name: "Gemini 3.1 Pro Preview (Gemini CLI)",
106+
limit: { context: 1048576, output: 65535 },
107+
modalities: DEFAULT_MODALITIES,
108+
},
109+
"gemini-3.1-pro-preview-customtools": {
110+
name: "Gemini 3.1 Pro Preview Custom Tools (Gemini CLI)",
111+
limit: { context: 1048576, output: 65535 },
112+
modalities: DEFAULT_MODALITIES,
113+
},
95114
};

src/plugin/logger.ts

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
/**
22
* Structured Logger for Antigravity Plugin
33
*
4-
* Provides TUI-integrated logging that is silent by default.
5-
* Logs are only visible when:
6-
* 1. TUI client is available (logs to app log panel)
7-
* 2. OPENCODE_ANTIGRAVITY_CONSOLE_LOG=1 is set (logs to console)
8-
*
9-
* Ported from opencode-google-antigravity-auth/src/plugin/logger.ts
4+
* Logging behavior:
5+
* - debug disabled → no logs anywhere
6+
* - debug enabled → log files only (via debug.ts logWriter)
7+
* - debug enabled → log files + TUI log panel
8+
* - OPENCODE_ANTIGRAVITY_CONSOLE_LOG=1 → console output (independent of debug flags)
109
*/
1110

1211
import type { PluginClient } from "./types";
12+
import { isDebugEnabled } from "./debug";
1313

1414
type LogLevel = "debug" | "info" | "warn" | "error";
1515

@@ -65,18 +65,22 @@ export function createLogger(module: string): Logger {
6565
const service = `antigravity.${module}`;
6666

6767
const log = (level: LogLevel, message: string, extra?: Record<string, unknown>): void => {
68-
// Try TUI logging first
69-
const app = _client?.app;
70-
if (app && typeof app.log === "function") {
71-
app
72-
.log({
73-
body: { service, level, message, extra },
74-
})
75-
.catch(() => {
76-
// Silently ignore logging errors
77-
});
78-
} else if (isConsoleLogEnabled()) {
79-
// Fallback to console if env var is set
68+
// TUI logging: only when debug is enabled
69+
if (isDebugEnabled()) {
70+
const app = _client?.app;
71+
if (app && typeof app.log === "function") {
72+
app
73+
.log({
74+
body: { service, level, message, extra },
75+
})
76+
.catch(() => {
77+
// Silently ignore logging errors
78+
});
79+
}
80+
}
81+
82+
// Console fallback: when env var is set (independent of debug flags)
83+
if (isConsoleLogEnabled()) {
8084
const prefix = `[${service}]`;
8185
const args = extra ? [prefix, message, extra] : [prefix, message];
8286
switch (level) {

src/plugin/transform/gemini.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ describe("transform/gemini", () => {
7474
expect(isGemini3Model("gemini-3-flash")).toBe(true);
7575
});
7676

77+
it("returns true for gemini-3.1-pro", () => {
78+
expect(isGemini3Model("gemini-3.1-pro")).toBe(true);
79+
});
80+
7781
it("returns true for uppercase GEMINI-3-PRO", () => {
7882
expect(isGemini3Model("GEMINI-3-PRO")).toBe(true);
7983
});

src/plugin/transform/model-resolver.test.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ describe("resolveModelWithTier", () => {
3434
// All Gemini models now default to antigravity
3535
expect(result.quotaPreference).toBe("antigravity");
3636
});
37+
38+
it("gemini-3.1-pro-preview gets default thinkingLevel 'low' with antigravity quota", () => {
39+
const result = resolveModelWithTier("gemini-3.1-pro-preview");
40+
expect(result.actualModel).toBe("gemini-3.1-pro-preview");
41+
expect(result.thinkingLevel).toBe("low");
42+
expect(result.quotaPreference).toBe("antigravity");
43+
});
3744
});
3845

3946
describe("All Gemini models default to antigravity quota", () => {
@@ -103,6 +110,12 @@ describe("resolveModelWithTier", () => {
103110
expect(result.actualModel).toBe("gemini-3-flash");
104111
expect(result.thinkingLevel).toBe("medium");
105112
});
113+
114+
it("antigravity-gemini-3.1-pro gets default -low model", () => {
115+
const result = resolveModelWithTier("antigravity-gemini-3.1-pro");
116+
expect(result.actualModel).toBe("gemini-3.1-pro-low");
117+
expect(result.thinkingLevel).toBe("low");
118+
});
106119
});
107120

108121
describe("Claude thinking models default budget", () => {
@@ -259,6 +272,18 @@ describe("Issue #103: resolveModelForHeaderStyle", () => {
259272
expect(result.actualModel).toBe("gemini-3-pro-low");
260273
expect(result.quotaPreference).toBe("antigravity");
261274
});
275+
276+
it("transforms gemini-3.1-pro-preview to gemini-3.1-pro-low for antigravity", () => {
277+
const result = resolveModelForHeaderStyle("gemini-3.1-pro-preview", "antigravity");
278+
expect(result.actualModel).toBe("gemini-3.1-pro-low");
279+
expect(result.quotaPreference).toBe("antigravity");
280+
});
281+
282+
it("transforms gemini-3.1-pro-preview-customtools to gemini-3.1-pro-low for antigravity", () => {
283+
const result = resolveModelForHeaderStyle("gemini-3.1-pro-preview-customtools", "antigravity");
284+
expect(result.actualModel).toBe("gemini-3.1-pro-low");
285+
expect(result.quotaPreference).toBe("antigravity");
286+
});
262287
});
263288

264289
describe("quota fallback from antigravity to gemini-cli", () => {
@@ -273,6 +298,18 @@ describe("Issue #103: resolveModelForHeaderStyle", () => {
273298
expect(result.actualModel).toBe("gemini-3-pro-preview");
274299
expect(result.quotaPreference).toBe("gemini-cli");
275300
});
301+
302+
it("transforms gemini-3.1-pro-low to gemini-3.1-pro-preview for gemini-cli", () => {
303+
const result = resolveModelForHeaderStyle("gemini-3.1-pro-low", "gemini-cli");
304+
expect(result.actualModel).toBe("gemini-3.1-pro-preview");
305+
expect(result.quotaPreference).toBe("gemini-cli");
306+
});
307+
308+
it("keeps gemini-3.1-pro-preview-customtools unchanged for gemini-cli", () => {
309+
const result = resolveModelForHeaderStyle("gemini-3.1-pro-preview-customtools", "gemini-cli");
310+
expect(result.actualModel).toBe("gemini-3.1-pro-preview-customtools");
311+
expect(result.quotaPreference).toBe("gemini-cli");
312+
});
276313
});
277314

278315
describe("no transformation needed", () => {
@@ -289,5 +326,3 @@ describe("Issue #103: resolveModelForHeaderStyle", () => {
289326
});
290327
});
291328
});
292-
293-

src/plugin/transform/model-resolver.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ export const MODEL_ALIASES: Record<string, string> = {
4242
// For Antigravity, these are bypassed and full model name is kept
4343
"gemini-3-pro-low": "gemini-3-pro",
4444
"gemini-3-pro-high": "gemini-3-pro",
45+
"gemini-3.1-pro-low": "gemini-3.1-pro",
46+
"gemini-3.1-pro-high": "gemini-3.1-pro",
4547
"gemini-3-flash-low": "gemini-3-flash",
4648
"gemini-3-flash-medium": "gemini-3-flash",
4749
"gemini-3-flash-high": "gemini-3-flash",
@@ -59,6 +61,8 @@ export const MODEL_ALIASES: Record<string, string> = {
5961

6062
const TIER_REGEX = /-(minimal|low|medium|high)$/;
6163
const QUOTA_PREFIX_REGEX = /^antigravity-/i;
64+
const GEMINI_3_PRO_REGEX = /^gemini-3(?:\.\d+)?-pro/i;
65+
const GEMINI_3_FLASH_REGEX = /^gemini-3(?:\.\d+)?-flash/i;
6266

6367
// ANTIGRAVITY_ONLY_MODELS removed - all models now default to antigravity
6468

@@ -125,6 +129,14 @@ function isThinkingCapableModel(model: string): boolean {
125129
);
126130
}
127131

132+
function isGemini3ProModel(model: string): boolean {
133+
return GEMINI_3_PRO_REGEX.test(model);
134+
}
135+
136+
function isGemini3FlashModel(model: string): boolean {
137+
return GEMINI_3_FLASH_REGEX.test(model);
138+
}
139+
128140
/**
129141
* Resolves a model name with optional tier suffix and quota prefix to its actual API model name
130142
* and corresponding thinking configuration.
@@ -167,8 +179,8 @@ export function resolveModelWithTier(requestedModel: string, options: ModelResol
167179
// For Antigravity Gemini 3 Pro models without explicit tier, append default tier (-low)
168180
// Antigravity API: gemini-3-pro requires tier suffix (gemini-3-pro-low/high)
169181
// gemini-3-flash uses bare name + thinkingLevel param
170-
const isGemini3Pro = modelWithoutQuota.toLowerCase().startsWith("gemini-3-pro");
171-
const isGemini3Flash = modelWithoutQuota.toLowerCase().startsWith("gemini-3-flash");
182+
const isGemini3Pro = isGemini3ProModel(modelWithoutQuota);
183+
const isGemini3Flash = isGemini3FlashModel(modelWithoutQuota);
172184

173185
let antigravityModel = modelWithoutQuota;
174186
if (skipAlias) {
@@ -308,10 +320,11 @@ export function resolveModelForHeaderStyle(
308320

309321
if (headerStyle === "antigravity") {
310322
let transformedModel = requestedModel
323+
.replace(/-preview-customtools$/i, "")
311324
.replace(/-preview$/i, "")
312325
.replace(/^antigravity-/i, "");
313326

314-
const isGemini3Pro = transformedModel.toLowerCase().startsWith("gemini-3-pro");
327+
const isGemini3Pro = isGemini3ProModel(transformedModel);
315328
const hasTierSuffix = /-(low|medium|high)$/i.test(transformedModel);
316329
const isImageModel = IMAGE_GENERATION_MODELS.test(transformedModel);
317330

@@ -328,8 +341,9 @@ export function resolveModelForHeaderStyle(
328341
let transformedModel = requestedModel
329342
.replace(/^antigravity-/i, "")
330343
.replace(/-(low|medium|high)$/i, "");
331-
332-
if (!transformedModel.endsWith("-preview")) {
344+
345+
const hasPreviewSuffix = /-preview($|-)/i.test(transformedModel);
346+
if (!hasPreviewSuffix) {
333347
transformedModel = `${transformedModel}-preview`;
334348
}
335349

@@ -372,7 +386,7 @@ export function resolveModelWithVariant(
372386
if (isGemini3) {
373387
const level = budgetToGemini3Level(budget);
374388
const isAntigravityGemini3Pro = base.quotaPreference === "antigravity" &&
375-
base.actualModel.toLowerCase().startsWith("gemini-3-pro");
389+
isGemini3ProModel(base.actualModel);
376390

377391
let actualModel = base.actualModel;
378392
if (isAntigravityGemini3Pro) {

0 commit comments

Comments
 (0)