feat(gemini): add Gemini 3.6 Flash tier routing and 3.5 Flash Lite

Add gemini-3.6-flash tiered (high/medium/low) for Antigravity routing
via upstreamModelId "gemini-3.6-flash-tiered(level)" + thinkingLevel,
plus gemini-3.6-flash and gemini-3.5-flash-lite direct API models.

- getModelUpstreamId: split (level) suffix before lookup, re-append after
- Antigravity executor: preserve transformed body.model
- MITM extractModel: parse thinkingLevel for tiered model (default medium)
- Isolate Cloud Code endpoints: discovery (loadCodeAssist/onboardUser/
  quota) on PROD cloudcode-pa, chat transport on daily-cloudcode-pa
  to bypass prod 429
This commit is contained in:
Phuong Lambert
2026-07-23 16:33:03 +07:00
committed by decolua
parent 3c17d3406b
commit 783e271c16
15 changed files with 236 additions and 65 deletions

View File

@@ -134,10 +134,19 @@ export const ANTIGRAVITY_HEADERS = {
"User-Agent": ANTIGRAVITY_IDE_USER_AGENT
};
// Cloud Code Assist API
// Cloud Code Assist API endpoints differ by client ecosystem.
export const CLOUD_CODE_API = {
loadCodeAssist: "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist",
onboardUser: "https://cloudcode-pa.googleapis.com/v1internal:onboardUser",
"gemini-cli": {
loadCodeAssist: "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist",
onboardUser: "https://cloudcode-pa.googleapis.com/v1internal:onboardUser",
},
// Project discovery (loadCodeAssist/onboardUser) stays on PROD — the daily host
// rejects these auth/onboarding calls. Only chat traffic uses the daily host
// (see transport.apiEndpoint in registry/antigravity.js, set to bypass prod 429).
antigravity: {
loadCodeAssist: "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist",
onboardUser: "https://cloudcode-pa.googleapis.com/v1internal:onboardUser",
},
};
export const LOAD_CODE_ASSIST_HEADERS = {

View File

@@ -4,7 +4,6 @@ import REGISTRY from "../providers/registry/index.js";
import { PROVIDER_MODELS } from "../providers/index.js";
import { modelQuotaFamily, modelStrip, modelTargetFormat, normalizeModelId } from "../providers/models/schema.js";
import { CODEX_REVIEW_SUFFIX } from "../providers/models/helpers.js";
export { PROVIDER_MODELS };
@@ -70,8 +69,13 @@ export function getModelUpstreamId(aliasOrId, modelId) {
const baseId = suffix ? modelId.slice(0, sufMatch.index).trim() : modelId;
const models = PROVIDER_MODELS[aliasOrId];
const found = findModel(models, baseId, aliasOrId);
if (found?.upstreamModelId) return found.upstreamModelId + suffix;
if (found?.id) return found.id + suffix;
const resolvedId = found?.upstreamModelId || found?.id;
if (resolvedId) {
const presetMatch = resolvedId.match(/\([^()]+\)\s*$/);
const presetSuffix = presetMatch?.[0] || "";
const resolvedBase = presetSuffix ? resolvedId.slice(0, presetMatch.index).trim() : resolvedId;
return resolvedBase + (suffix || presetSuffix);
}
if (aliasOrId === "cx" && typeof baseId === "string" && baseId.endsWith(CODEX_REVIEW_SUFFIX)) {
return baseId.slice(0, -CODEX_REVIEW_SUFFIX.length) + suffix;
}

View File

@@ -264,7 +264,7 @@ export class AntigravityExecutor extends BaseExecutor {
return {
...body,
project: projectId,
model: model,
model: body.model || model,
userAgent: "antigravity",
requestType: "agent",
requestId: buildIdeRequestId({ body, request: transformedRequest, credentials, model, requestType: "agent" }),

View File

@@ -36,6 +36,7 @@ export default {
},
},
usage: {
// Discovery (quota/project) on PROD; daily host rejects these.
quotaApiUrl: "https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels",
loadProjectApiUrl: "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist",
tokenUrl: "https://oauth2.googleapis.com/token",
@@ -44,9 +45,9 @@ export default {
clientSecret: "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",
},
models: [
{ id: "gemini-3.6-flash-high", name: "Gemini 3.6 Flash (High)" },
{ id: "gemini-3.6-flash-medium", name: "Gemini 3.6 Flash (Medium)" },
{ id: "gemini-3.6-flash-low", name: "Gemini 3.6 Flash (Low)" },
{ id: "gemini-3.6-flash-high", name: "Gemini 3.6 Flash (High)", upstreamModelId: "gemini-3.6-flash-tiered(high)" },
{ id: "gemini-3.6-flash-medium", name: "Gemini 3.6 Flash (Medium)", upstreamModelId: "gemini-3.6-flash-tiered(medium)" },
{ id: "gemini-3.6-flash-low", name: "Gemini 3.6 Flash (Low)", upstreamModelId: "gemini-3.6-flash-tiered(low)" },
{ id: "gemini-3.5-flash-high", name: "Gemini 3.5 Flash (High)" },
{ id: "gemini-3-flash-agent", name: "Gemini 3.5 Flash (High)" },
{ id: "gemini-3.5-flash-low", name: "Gemini 3.5 Flash (Medium)" },
@@ -71,7 +72,7 @@ export default {
"https://www.googleapis.com/auth/cclog",
"https://www.googleapis.com/auth/experimentsandconfigs",
],
apiEndpoint: "https://cloudcode-pa.googleapis.com",
apiEndpoint: "https://daily-cloudcode-pa.googleapis.com",
apiVersion: "v1internal",
loadCodeAssistEndpoint: "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist",
onboardUserEndpoint: "https://cloudcode-pa.googleapis.com/v1internal:onboardUser",

View File

@@ -58,7 +58,7 @@ export const ANTHROPIC_COMPAT_BASE = "https://api.anthropic.com/v1";
// Keep this static even when 9router runs on Linux: the provider profile is
// intentionally matching the IDE client, not the server host.
export const ANTIGRAVITY_IDE_VERSION = "2.1.1";
export const ANTIGRAVITY_IDE_BASE_URL = "https://cloudcode-pa.googleapis.com";
export const ANTIGRAVITY_IDE_BASE_URL = "https://daily-cloudcode-pa.googleapis.com";
export const ANTIGRAVITY_IDE_USER_AGENT = `antigravity/ide/${ANTIGRAVITY_IDE_VERSION} darwin/arm64`;
// Antigravity OAuth client credentials (public CLI client — duplicated in usage.js + src/lib/oauth)

View File

@@ -83,7 +83,7 @@ startCacheCleanup();
* @param {string} accessToken - Valid OAuth access token
* @returns {Promise<string|null>} Real project ID or null
*/
export async function getProjectIdForConnection(connectionId, accessToken) {
export async function getProjectIdForConnection(connectionId, accessToken, provider = "gemini-cli") {
if (!connectionId || !accessToken) return null;
// Return cached value if still fresh
@@ -102,7 +102,7 @@ export async function getProjectIdForConnection(connectionId, accessToken) {
const promise = (async () => {
try {
const projectId = await fetchProjectId(accessToken, controller.signal);
const projectId = await fetchProjectId(accessToken, controller.signal, provider);
if (projectId) {
projectIdCache.set(connectionId, {projectId, fetchedAt: Date.now()});
return projectId;
@@ -155,8 +155,9 @@ export function removeConnection(connectionId) {
* @param {AbortSignal} signal
* @returns {Promise<string|null>}
*/
async function fetchProjectId(accessToken, signal) {
const response = await fetch(CLOUD_CODE_API.loadCodeAssist, {
async function fetchProjectId(accessToken, signal, provider) {
const endpoints = CLOUD_CODE_API[provider] || CLOUD_CODE_API["gemini-cli"];
const response = await fetch(endpoints.loadCodeAssist, {
method: "POST",
headers: { ...LOAD_CODE_ASSIST_HEADERS, "Authorization": `Bearer ${accessToken}` },
body: JSON.stringify({ metadata: LOAD_CODE_ASSIST_METADATA }),
@@ -185,7 +186,7 @@ async function fetchProjectId(accessToken, signal) {
}
}
return onboardUser(accessToken, tierID, signal);
return onboardUser(accessToken, tierID, signal, endpoints);
}
/**
@@ -196,7 +197,7 @@ async function fetchProjectId(accessToken, signal) {
* @param {AbortSignal} externalSignal – propagated from the connection's AbortController
* @returns {Promise<string|null>}
*/
async function onboardUser(accessToken, tierID, externalSignal) {
async function onboardUser(accessToken, tierID, externalSignal, endpoints) {
console.log(`[ProjectId] Onboarding user with tier: ${tierID}`);
const reqBody = { tierId: tierID, metadata: LOAD_CODE_ASSIST_METADATA };
@@ -213,7 +214,7 @@ async function onboardUser(accessToken, tierID, externalSignal) {
externalSignal?.addEventListener("abort", forwardAbort);
try {
const response = await fetch(CLOUD_CODE_API.onboardUser, {
const response = await fetch(endpoints.onboardUser, {
method: "POST",
headers: { ...LOAD_CODE_ASSIST_HEADERS, "Authorization": `Bearer ${accessToken}` },
body: JSON.stringify(reqBody),