mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-26 00:52:18 +03:00
fix(providers): eliminate Gemini 3.5 Flash
This commit is contained in:
@@ -1 +0,0 @@
|
|||||||
- fix(sse): mark gemini-3.5-flash as thinking-capable so reasoning_effort is no longer rejected with a spurious 400 (#10286)
|
|
||||||
@@ -6,7 +6,7 @@ lastUpdated: 2026-07-31
|
|||||||
|
|
||||||
# OmniRoute Antigravity (Google One AI) Onboarding Guide
|
# OmniRoute Antigravity (Google One AI) Onboarding Guide
|
||||||
|
|
||||||
> **What you get**: Access to Gemini 3.1 Pro, Gemini 3.5 Flash, Claude Sonnet 4.6, and other models through your Google One AI Pro subscription — routed through OmniRoute as a unified gateway.
|
> **What you get**: Access to Gemini 3.1 Pro, Gemini 3.7 Flash, Claude Sonnet 4.6, and other models through your Google One AI Pro subscription — routed through OmniRoute as a unified gateway.
|
||||||
|
|
||||||
**Official references**:
|
**Official references**:
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ Both providers share the **same Google backend** — identical OAuth client, tok
|
|||||||
|
|
||||||
**Why the model catalog differs**: Google's CLI is "optimized for speed and low overhead" and "co-optimized with Gemini models" (per Google's official blog). The Web/IDE product is "optimized for comprehensiveness." The CLI uses `:fetchAvailableModels` to dynamically discover models, while the IDE uses a static curated list.
|
**Why the model catalog differs**: Google's CLI is "optimized for speed and low overhead" and "co-optimized with Gemini models" (per Google's official blog). The Web/IDE product is "optimized for comprehensiveness." The CLI uses `:fetchAvailableModels` to dynamically discover models, while the IDE uses a static curated list.
|
||||||
|
|
||||||
**In practice**: Use `agy/` prefix for Gemini models (e.g. `agy/gemini-3.5-flash-high`). Use `antigravity/` for the static curated list. Both hit the same Google backend, but expose different model naming. The quota is shared — using either provider counts against the same Google account's limits.
|
**In practice**: Use `agy/` prefix for Gemini models (e.g. `agy/gemini-3.7-flash-high`). Use `antigravity/` for the static curated list. Both hit the same Google backend, but expose different model naming. The quota is shared — using either provider counts against the same Google account's limits.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ const AGY_RETIRED_MODEL_IDS = new Set([
|
|||||||
"gemini-3.6-flash-medium",
|
"gemini-3.6-flash-medium",
|
||||||
"gemini-3.6-flash-low",
|
"gemini-3.6-flash-low",
|
||||||
"gemini-3-flash-agent",
|
"gemini-3-flash-agent",
|
||||||
|
"gemini-3.5-flash",
|
||||||
"gemini-3.5-flash-extra-low",
|
"gemini-3.5-flash-extra-low",
|
||||||
"gemini-3.5-flash-low",
|
"gemini-3.5-flash-low",
|
||||||
"gemini-3.5-flash-high",
|
"gemini-3.5-flash-high",
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ const ANTIGRAVITY_RETIRED_MODEL_IDS = new Set([
|
|||||||
"gemini-3.6-flash-medium",
|
"gemini-3.6-flash-medium",
|
||||||
"gemini-3.6-flash-low",
|
"gemini-3.6-flash-low",
|
||||||
"gemini-3-flash-agent",
|
"gemini-3-flash-agent",
|
||||||
|
"gemini-3.5-flash",
|
||||||
"gemini-3.5-flash-extra-low",
|
"gemini-3.5-flash-extra-low",
|
||||||
"gemini-3.5-flash-low",
|
"gemini-3.5-flash-low",
|
||||||
"gemini-3.5-flash-high",
|
"gemini-3.5-flash-high",
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
"gemma-4-26b-it": { "rpm": 16000, "rpd": 14400, "tpm": 16000 },
|
"gemma-4-26b-it": { "rpm": 16000, "rpd": 14400, "tpm": 16000 },
|
||||||
"gemma-4-31b-it": { "rpm": 16000, "rpd": 14400, "tpm": 16000 },
|
"gemma-4-31b-it": { "rpm": 16000, "rpd": 14400, "tpm": 16000 },
|
||||||
"gemini-embedding-exp-03-07": { "rpm": 100, "rpd": 1000, "tpm": 30000 },
|
"gemini-embedding-exp-03-07": { "rpm": 100, "rpd": 1000, "tpm": 30000 },
|
||||||
"gemini-3.5-flash": { "rpm": 5, "rpd": 20, "tpm": 250000 },
|
|
||||||
"gemini-3.1-flash-lite": { "rpm": 15, "rpd": 500, "tpm": 250000 },
|
"gemini-3.1-flash-lite": { "rpm": 15, "rpd": 500, "tpm": 250000 },
|
||||||
"gemini-3.1-pro": { "rpm": 0, "rpd": 0, "tpm": 0 },
|
"gemini-3.1-pro": { "rpm": 0, "rpd": 0, "tpm": 0 },
|
||||||
"gemini-2.5-flash-lite": { "rpm": 10, "rpd": 20, "tpm": 250000 },
|
"gemini-2.5-flash-lite": { "rpm": 10, "rpd": 20, "tpm": 250000 },
|
||||||
|
|||||||
@@ -228,14 +228,14 @@ export const cursorProvider: RegistryEntry = {
|
|||||||
{ id: "gpt-5.1-low", name: "GPT-5.1 Low" },
|
{ id: "gpt-5.1-low", name: "GPT-5.1 Low" },
|
||||||
{ id: "gpt-5.1", name: "GPT-5.1" },
|
{ id: "gpt-5.1", name: "GPT-5.1" },
|
||||||
{ id: "gpt-5.1-high", name: "GPT-5.1 High" },
|
{ id: "gpt-5.1-high", name: "GPT-5.1 High" },
|
||||||
{ id: "gemini-3.5-flash", name: "Gemini 3.5 Flash" },
|
|
||||||
{ id: "claude-4-sonnet", name: "Sonnet 4" },
|
{ id: "claude-4-sonnet", name: "Sonnet 4" },
|
||||||
{ id: "claude-4-sonnet-thinking", name: "Sonnet 4 Thinking" },
|
{ id: "claude-4-sonnet-thinking", name: "Sonnet 4 Thinking" },
|
||||||
{ id: "gpt-5-mini", name: "GPT-5 Mini" },
|
{ id: "gpt-5-mini", name: "GPT-5 Mini" },
|
||||||
{ id: "kimi-k3-low", name: "Kimi K3 Low" },
|
{ id: "kimi-k3-low", name: "Kimi K3 Low" },
|
||||||
{ id: "kimi-k3-max", name: "Kimi K3" },
|
{ id: "kimi-k3-max", name: "Kimi K3" },
|
||||||
{ id: "glm-5.2-high", name: "GLM 5.2" },
|
{ id: "glm-5.2-high", name: "GLM 5.2" },
|
||||||
{ id: "glm-5.2-max", name: "GLM 5.2 Max" }, ],
|
{ id: "glm-5.2-max", name: "GLM 5.2 Max" },
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ const FALLBACK_MODEL_SEEDS: FallbackModelSeed[] = [
|
|||||||
|
|
||||||
/** Presets exposed by the web client's model picker (id → text/multimodal model). */
|
/** Presets exposed by the web client's model picker (id → text/multimodal model). */
|
||||||
export const CONOL_FALLBACK_MODEL_PRESETS: ConolModelPreset[] = [
|
export const CONOL_FALLBACK_MODEL_PRESETS: ConolModelPreset[] = [
|
||||||
{ id: "flash", text: "deepseek/deepseek-v4-flash", multimodal: "google/gemini-3.5-flash" },
|
{ id: "flash", text: "deepseek/deepseek-v4-flash", multimodal: "google/gemini-3.7-flash" },
|
||||||
{ id: "moderate", text: "deepseek/deepseek-v4-pro", multimodal: "claude-sonnet-5" },
|
{ id: "moderate", text: "deepseek/deepseek-v4-pro", multimodal: "claude-sonnet-5" },
|
||||||
{ id: "pro", text: "z-ai/glm-5.2", multimodal: "moonshotai/kimi-k3" },
|
{ id: "pro", text: "z-ai/glm-5.2", multimodal: "moonshotai/kimi-k3" },
|
||||||
{ id: "ultra", text: "claude-fable-5", multimodal: "claude-fable-5" },
|
{ id: "ultra", text: "claude-fable-5", multimodal: "claude-fable-5" },
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export interface PromptQlModel {
|
export interface PromptQlModel {
|
||||||
/** Client-facing id (model_reference slug, e.g. gemini-3.5-flash). */
|
/** Client-facing id (model_reference slug, e.g. gemini-3.7-flash). */
|
||||||
id: string;
|
id: string;
|
||||||
/** Friendly picker label. */
|
/** Friendly picker label. */
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ const IGNORED_METHODS = new Set([
|
|||||||
"asyncBatchEmbedContent",
|
"asyncBatchEmbedContent",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const RETIRED_GEMINI_MODEL_IDS = new Set(["gemini-3.5-flash"]);
|
||||||
|
|
||||||
export interface GeminiDiscoveryModel {
|
export interface GeminiDiscoveryModel {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -46,36 +48,38 @@ export interface GeminiDiscoveryModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function parseGeminiModelsList(data: any): GeminiDiscoveryModel[] {
|
export function parseGeminiModelsList(data: any): GeminiDiscoveryModel[] {
|
||||||
return (data?.models || []).map((m: Record<string, unknown>) => {
|
return (data?.models || [])
|
||||||
const methods: string[] = Array.isArray(m.supportedGenerationMethods)
|
.map((m: Record<string, unknown>) => {
|
||||||
? (m.supportedGenerationMethods as string[])
|
const methods: string[] = Array.isArray(m.supportedGenerationMethods)
|
||||||
: [];
|
? (m.supportedGenerationMethods as string[])
|
||||||
|
: [];
|
||||||
|
|
||||||
const endpoints = new Set<string>(
|
const endpoints = new Set<string>(
|
||||||
methods
|
methods
|
||||||
.filter((method) => !IGNORED_METHODS.has(method))
|
.filter((method) => !IGNORED_METHODS.has(method))
|
||||||
.map((method) => METHOD_TO_ENDPOINT[method] || "chat")
|
.map((method) => METHOD_TO_ENDPOINT[method] || "chat")
|
||||||
);
|
);
|
||||||
|
|
||||||
const id = ((m.name as string) || (m.id as string) || "").replace(/^models\//, "");
|
const id = ((m.name as string) || (m.id as string) || "").replace(/^models\//, "");
|
||||||
const lowerId = id.toLowerCase();
|
const lowerId = id.toLowerCase();
|
||||||
|
|
||||||
// Keep Veo models in the video bucket even when the method list is incomplete.
|
// Keep Veo models in the video bucket even when the method list is incomplete.
|
||||||
if (lowerId.includes("veo")) {
|
if (lowerId.includes("veo")) {
|
||||||
endpoints.add("video");
|
endpoints.add("video");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (endpoints.size === 0) endpoints.add("chat");
|
if (endpoints.size === 0) endpoints.add("chat");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...m,
|
...m,
|
||||||
id,
|
id,
|
||||||
name: (m.displayName as string) || id,
|
name: (m.displayName as string) || id,
|
||||||
supportedEndpoints: [...endpoints],
|
supportedEndpoints: [...endpoints],
|
||||||
...(typeof m.inputTokenLimit === "number" ? { inputTokenLimit: m.inputTokenLimit } : {}),
|
...(typeof m.inputTokenLimit === "number" ? { inputTokenLimit: m.inputTokenLimit } : {}),
|
||||||
...(typeof m.outputTokenLimit === "number" ? { outputTokenLimit: m.outputTokenLimit } : {}),
|
...(typeof m.outputTokenLimit === "number" ? { outputTokenLimit: m.outputTokenLimit } : {}),
|
||||||
...(typeof m.description === "string" ? { description: m.description } : {}),
|
...(typeof m.description === "string" ? { description: m.description } : {}),
|
||||||
...(m.thinking === true ? { supportsThinking: true } : {}),
|
...(m.thinking === true ? { supportsThinking: true } : {}),
|
||||||
} as GeminiDiscoveryModel;
|
} as GeminiDiscoveryModel;
|
||||||
});
|
})
|
||||||
|
.filter((model: GeminiDiscoveryModel) => !RETIRED_GEMINI_MODEL_IDS.has(model.id));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ import { normalizeDiscoveredModels } from "@/lib/providerModels/modelDiscovery";
|
|||||||
import {
|
import {
|
||||||
ANTIGRAVITY_MODEL_ALIASES,
|
ANTIGRAVITY_MODEL_ALIASES,
|
||||||
ANTIGRAVITY_REVERSE_MODEL_ALIASES,
|
ANTIGRAVITY_REVERSE_MODEL_ALIASES,
|
||||||
|
isDiscoverableAntigravityModelId,
|
||||||
} from "@omniroute/open-sse/config/antigravityModelAliases.ts";
|
} from "@omniroute/open-sse/config/antigravityModelAliases.ts";
|
||||||
|
import { isDiscoverableAgyModelId } from "@omniroute/open-sse/config/agyModels.ts";
|
||||||
import { filterChatSelectableModels } from "@omniroute/open-sse/services/modelEndpointPolicy.ts";
|
import { filterChatSelectableModels } from "@omniroute/open-sse/services/modelEndpointPolicy.ts";
|
||||||
import { filterSelectableModels } from "@omniroute/open-sse/services/modelLifecycle.ts";
|
import { filterSelectableModels } from "@omniroute/open-sse/services/modelLifecycle.ts";
|
||||||
import { isSelfHostedChatProvider } from "@/shared/constants/providers";
|
import { isSelfHostedChatProvider } from "@/shared/constants/providers";
|
||||||
@@ -254,14 +256,22 @@ export async function importManagedModels({
|
|||||||
const previousSyncedAvailableModels =
|
const previousSyncedAvailableModels =
|
||||||
previousSyncedAvailableModelsInput ??
|
previousSyncedAvailableModelsInput ??
|
||||||
(await getSyncedAvailableModelsForConnection(providerId, connectionId));
|
(await getSyncedAvailableModelsForConnection(providerId, connectionId));
|
||||||
|
const normalizedDiscoveredModels = normalizeDiscoveredModels(fetchedModels, providerId);
|
||||||
|
// Gemini 3.5 Flash elimination (ddf1bb760, carried from #11259): antigravity/
|
||||||
|
// agy discovery is restricted to each family's discoverable ids BEFORE any
|
||||||
|
// chat-selection filtering.
|
||||||
|
const providerFilteredModels =
|
||||||
|
providerId === "antigravity"
|
||||||
|
? normalizedDiscoveredModels.filter((model) => isDiscoverableAntigravityModelId(model.id))
|
||||||
|
: providerId === "agy"
|
||||||
|
? normalizedDiscoveredModels.filter((model) => isDiscoverableAgyModelId(model.id))
|
||||||
|
: normalizedDiscoveredModels;
|
||||||
// #11088 (option 1): self-hosted providers keep their non-chat models — chat
|
// #11088 (option 1): self-hosted providers keep their non-chat models — chat
|
||||||
// filtering happens at read time (resolveLocalSyncedEndpointRoute). Every other
|
// filtering happens at read time (resolveLocalSyncedEndpointRoute). Every other
|
||||||
// provider keeps the import-time chat filter: the read-time path is gated on
|
// provider keeps the import-time chat filter: the read-time path is gated on
|
||||||
// isSelfHostedChatProvider, so dropping it globally leaked image/video models
|
// isSelfHostedChatProvider, so dropping it globally leaked image/video models
|
||||||
// into OpenAI chat selections (#11271).
|
// into OpenAI chat selections (#11271).
|
||||||
const selectableModels = filterSelectableModels(
|
const selectableModels = filterSelectableModels(providerId, providerFilteredModels);
|
||||||
providerId,
|
|
||||||
normalizeDiscoveredModels(fetchedModels, providerId)
|
|
||||||
);
|
);
|
||||||
const discoveredModels = isSelfHostedChatProvider(providerId)
|
const discoveredModels = isSelfHostedChatProvider(providerId)
|
||||||
? selectableModels
|
? selectableModels
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ const GPT_5_6_MODEL_SPEC = {
|
|||||||
supportsVision: true,
|
supportsVision: true,
|
||||||
} satisfies ModelSpec;
|
} satisfies ModelSpec;
|
||||||
|
|
||||||
const GEMINI_35_FLASH_MODEL_SPEC = {
|
const GEMINI_36_FLASH_MODEL_SPEC = {
|
||||||
maxOutputTokens: 65536,
|
maxOutputTokens: 65536,
|
||||||
contextWindow: 1048576,
|
contextWindow: 1048576,
|
||||||
supportsThinking: false,
|
supportsThinking: false,
|
||||||
@@ -160,7 +160,7 @@ export const MODEL_SPECS: Record<string, ModelSpec> = {
|
|||||||
aliases: ["openai/gpt-4o"],
|
aliases: ["openai/gpt-4o"],
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Gemini 2.5 and provider-neutral 3.5 Flash series ─────────────
|
// ── Gemini 2.5 Flash ─────────────────────────────────────────────
|
||||||
"gemini-2.5-flash": {
|
"gemini-2.5-flash": {
|
||||||
maxOutputTokens: 65536,
|
maxOutputTokens: 65536,
|
||||||
contextWindow: 1048576,
|
contextWindow: 1048576,
|
||||||
@@ -171,16 +171,6 @@ export const MODEL_SPECS: Record<string, ModelSpec> = {
|
|||||||
supportsTools: true,
|
supportsTools: true,
|
||||||
supportsVision: true,
|
supportsVision: true,
|
||||||
},
|
},
|
||||||
"gemini-3.5-flash-extra-low": {
|
|
||||||
...GEMINI_35_FLASH_MODEL_SPEC,
|
|
||||||
thinkingBudgetCap: 0,
|
|
||||||
},
|
|
||||||
"gemini-3.5-flash-low": { ...GEMINI_35_FLASH_MODEL_SPEC },
|
|
||||||
"gemini-3-flash-agent": {
|
|
||||||
...GEMINI_35_FLASH_MODEL_SPEC,
|
|
||||||
thinkingBudgetCap: 0,
|
|
||||||
},
|
|
||||||
|
|
||||||
// ── Gemini 3.7 Flash (current Antigravity/AGY live tiers) ─────────
|
// ── Gemini 3.7 Flash (current Antigravity/AGY live tiers) ─────────
|
||||||
// The tier suffix configures the thinking budget passed to the upstream
|
// The tier suffix configures the thinking budget passed to the upstream
|
||||||
// gemini-3.7-flash-tiered backend (high: 24.5k, medium: 8k, low: 1k).
|
// gemini-3.7-flash-tiered backend (high: 24.5k, medium: 8k, low: 1k).
|
||||||
@@ -234,9 +224,9 @@ export const MODEL_SPECS: Record<string, ModelSpec> = {
|
|||||||
// Provider-neutral compatibility for providers that still serve Gemini 3.6.
|
// Provider-neutral compatibility for providers that still serve Gemini 3.6.
|
||||||
// Antigravity/AGY availability is governed by their own provider catalogs and
|
// Antigravity/AGY availability is governed by their own provider catalogs and
|
||||||
// retirement filters; these shared specs must not be treated as an allowlist.
|
// retirement filters; these shared specs must not be treated as an allowlist.
|
||||||
"gemini-3.6-flash-high": { ...GEMINI_35_FLASH_MODEL_SPEC },
|
"gemini-3.6-flash-high": { ...GEMINI_36_FLASH_MODEL_SPEC },
|
||||||
"gemini-3.6-flash-medium": { ...GEMINI_35_FLASH_MODEL_SPEC },
|
"gemini-3.6-flash-medium": { ...GEMINI_36_FLASH_MODEL_SPEC },
|
||||||
"gemini-3.6-flash-low": { ...GEMINI_35_FLASH_MODEL_SPEC },
|
"gemini-3.6-flash-low": { ...GEMINI_36_FLASH_MODEL_SPEC },
|
||||||
|
|
||||||
// ── Gemini 3 Flash series ───────────────────────────────────────
|
// ── Gemini 3 Flash series ───────────────────────────────────────
|
||||||
"gemini-3-flash": {
|
"gemini-3-flash": {
|
||||||
@@ -282,20 +272,6 @@ export const MODEL_SPECS: Record<string, ModelSpec> = {
|
|||||||
aliases: ["gemini-3-pro-low"],
|
aliases: ["gemini-3-pro-low"],
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Gemini 3.5 Flash ─────────────────────────────────────────────
|
|
||||||
// #10286: the base Google AI Studio model DOES support reasoning (it has
|
|
||||||
// an effort-tier alias gemini-3.5-flash-high) — override the shared spec's
|
|
||||||
// supportsThinking:false here only. Do NOT flip GEMINI_35_FLASH_MODEL_SPEC
|
|
||||||
// itself: it is also spread into the Antigravity flash-tier aliases
|
|
||||||
// (gemini-3.5-flash-low/-extra-low, gemini-3-flash-agent, gemini-3.6-flash-*)
|
|
||||||
// which reject client-supplied thinking params because the model id itself
|
|
||||||
// selects the reasoning tier upstream.
|
|
||||||
"gemini-3.5-flash": {
|
|
||||||
...GEMINI_35_FLASH_MODEL_SPEC,
|
|
||||||
supportsThinking: true,
|
|
||||||
aliases: ["gemini-3.5-flash-high"],
|
|
||||||
},
|
|
||||||
|
|
||||||
// ── Claude Opus 4.5 ─────────────────────────────────────────────
|
// ── Claude Opus 4.5 ─────────────────────────────────────────────
|
||||||
"claude-opus-4-5": {
|
"claude-opus-4-5": {
|
||||||
maxOutputTokens: 32768,
|
maxOutputTokens: 32768,
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ test("agy ships its own live callable model catalog", () => {
|
|||||||
assert.ok(!ids.includes("gemini-3.6-flash-low"));
|
assert.ok(!ids.includes("gemini-3.6-flash-low"));
|
||||||
assert.ok(!ids.includes("gemini-3.6-flash-medium"));
|
assert.ok(!ids.includes("gemini-3.6-flash-medium"));
|
||||||
assert.ok(!ids.includes("gemini-3.6-flash-high"));
|
assert.ok(!ids.includes("gemini-3.6-flash-high"));
|
||||||
|
assert.ok(!ids.includes("gemini-3.5-flash"));
|
||||||
assert.ok(!ids.includes("gemini-3.5-flash-extra-low"));
|
assert.ok(!ids.includes("gemini-3.5-flash-extra-low"));
|
||||||
assert.ok(!ids.includes("gemini-3.5-flash-low"));
|
assert.ok(!ids.includes("gemini-3.5-flash-low"));
|
||||||
assert.ok(!ids.includes("gemini-3-flash-agent"));
|
assert.ok(!ids.includes("gemini-3-flash-agent"));
|
||||||
@@ -87,6 +88,7 @@ test("agy model helpers resolve catalog ids and display names", () => {
|
|||||||
assert.equal(isUserCallableAgyModelId("gemini-3.6-flash-low"), false);
|
assert.equal(isUserCallableAgyModelId("gemini-3.6-flash-low"), false);
|
||||||
assert.equal(isUserCallableAgyModelId("gemini-3.6-flash-medium"), false);
|
assert.equal(isUserCallableAgyModelId("gemini-3.6-flash-medium"), false);
|
||||||
assert.equal(isUserCallableAgyModelId("gemini-3.6-flash-high"), false);
|
assert.equal(isUserCallableAgyModelId("gemini-3.6-flash-high"), false);
|
||||||
|
assert.equal(isUserCallableAgyModelId("gemini-3.5-flash"), false);
|
||||||
assert.equal(isUserCallableAgyModelId("gemini-3.5-flash-extra-low"), false);
|
assert.equal(isUserCallableAgyModelId("gemini-3.5-flash-extra-low"), false);
|
||||||
assert.equal(isUserCallableAgyModelId("gemini-3.5-flash-low"), false);
|
assert.equal(isUserCallableAgyModelId("gemini-3.5-flash-low"), false);
|
||||||
assert.equal(isUserCallableAgyModelId("gemini-3-flash-agent"), false);
|
assert.equal(isUserCallableAgyModelId("gemini-3-flash-agent"), false);
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ test("TDD S3: checkFallbackError extracts retry hint for oauth providers even if
|
|||||||
429,
|
429,
|
||||||
errorText,
|
errorText,
|
||||||
0,
|
0,
|
||||||
"gemini-3.5-flash",
|
"gemini-3.7-flash",
|
||||||
"antigravity", // which uses oauth provider profile (useUpstreamRetryHints: false)
|
"antigravity", // which uses oauth provider profile (useUpstreamRetryHints: false)
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const RETIRED_FLASH_IDS = [
|
|||||||
"gemini-3.6-flash-low",
|
"gemini-3.6-flash-low",
|
||||||
"gemini-3.6-flash-medium",
|
"gemini-3.6-flash-medium",
|
||||||
"gemini-3.6-flash-high",
|
"gemini-3.6-flash-high",
|
||||||
|
"gemini-3.5-flash",
|
||||||
"gemini-3.5-flash-extra-low",
|
"gemini-3.5-flash-extra-low",
|
||||||
"gemini-3.5-flash-low",
|
"gemini-3.5-flash-low",
|
||||||
"gemini-3-flash-agent",
|
"gemini-3-flash-agent",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ const RETIRED_PUBLIC_MODELS = [
|
|||||||
"gemini-3.6-flash-medium",
|
"gemini-3.6-flash-medium",
|
||||||
"gemini-3.6-flash-low",
|
"gemini-3.6-flash-low",
|
||||||
"gemini-3-flash-agent",
|
"gemini-3-flash-agent",
|
||||||
|
"gemini-3.5-flash",
|
||||||
"gemini-3.5-flash-low",
|
"gemini-3.5-flash-low",
|
||||||
"gemini-3.5-flash-extra-low",
|
"gemini-3.5-flash-extra-low",
|
||||||
"gemini-2.5-pro",
|
"gemini-2.5-pro",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
} from "../../open-sse/executors/conol-web.ts";
|
} from "../../open-sse/executors/conol-web.ts";
|
||||||
import {
|
import {
|
||||||
CONOL_FALLBACK_MODELS,
|
CONOL_FALLBACK_MODELS,
|
||||||
|
CONOL_FALLBACK_MODEL_PRESETS,
|
||||||
clampConolEffort,
|
clampConolEffort,
|
||||||
parseConolAgentServers,
|
parseConolAgentServers,
|
||||||
resolveConolModelSelection,
|
resolveConolModelSelection,
|
||||||
@@ -27,6 +28,11 @@ import { getResolvedModelCapabilities } from "../../src/lib/modelCapabilities.ts
|
|||||||
const SESSION_COOKIE_NAME = "__Secure-better-auth.session_token";
|
const SESSION_COOKIE_NAME = "__Secure-better-auth.session_token";
|
||||||
|
|
||||||
describe("Conol web provider", () => {
|
describe("Conol web provider", () => {
|
||||||
|
it("routes the Flash preset multimodal path to Gemini 3.7", () => {
|
||||||
|
const flashPreset = CONOL_FALLBACK_MODEL_PRESETS.find((preset) => preset.id === "flash");
|
||||||
|
assert.equal(flashPreset?.multimodal, "google/gemini-3.7-flash");
|
||||||
|
});
|
||||||
|
|
||||||
it("normalizes raw, full-header, JSON, and provider-data credentials", () => {
|
it("normalizes raw, full-header, JSON, and provider-data credentials", () => {
|
||||||
assert.equal(normalizeConolCookie("token-value"), `${SESSION_COOKIE_NAME}=token-value`);
|
assert.equal(normalizeConolCookie("token-value"), `${SESSION_COOKIE_NAME}=token-value`);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ describe("GithubExecutor — Gemini/Claude must never hit /responses (port 9rout
|
|||||||
|
|
||||||
it("routes registered Gemini Copilot models to chat/completions", () => {
|
it("routes registered Gemini Copilot models to chat/completions", () => {
|
||||||
const exec = new GithubExecutor();
|
const exec = new GithubExecutor();
|
||||||
for (const id of ["gemini-3.1-pro-preview", "gemini-3.5-flash"]) {
|
for (const id of ["gemini-3.1-pro-preview", "gemini-3.7-flash"]) {
|
||||||
assert.equal(exec.buildUrl(id, false), CHAT_URL, `${id} must route to chat/completions`);
|
assert.equal(exec.buildUrl(id, false), CHAT_URL, `${id} must route to chat/completions`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ function modelIds(): Set<string> {
|
|||||||
return new Set(cursorProvider.models.map((m) => m.id));
|
return new Set(cursorProvider.models.map((m) => m.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("cursor registry excludes retired Gemini 3.5 Flash", () => {
|
||||||
|
assert.equal(modelIds().has("gemini-3.5-flash"), false);
|
||||||
|
});
|
||||||
|
|
||||||
test("cursor registry includes Claude Opus 4.8 effort + thinking + fast variants", () => {
|
test("cursor registry includes Claude Opus 4.8 effort + thinking + fast variants", () => {
|
||||||
const ids = modelIds();
|
const ids = modelIds();
|
||||||
for (const effort of EFFORTS) {
|
for (const effort of EFFORTS) {
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ describe("PromptQl — registry consistency", () => {
|
|||||||
it("registers a model catalog via getModelsByProviderId", () => {
|
it("registers a model catalog via getModelsByProviderId", () => {
|
||||||
const catalog = getModelsByProviderId("promptql");
|
const catalog = getModelsByProviderId("promptql");
|
||||||
assert.ok(catalog.length >= 5);
|
assert.ok(catalog.length >= 5);
|
||||||
assert.ok(catalog.some((m) => m.id === "gemini-3.5-flash" || m.id.includes("gemini")));
|
assert.ok(catalog.some((m) => m.id === "gemini-3.7-flash" || m.id.includes("gemini")));
|
||||||
assert.ok(catalog.some((m) => m.id.includes("gpt-5.6") || m.id.includes("fable")));
|
assert.ok(catalog.some((m) => m.id.includes("gpt-5.6") || m.id.includes("fable")));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -209,7 +209,10 @@ describe("PromptQl — helpers", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("resolves model slugs and prefixes", () => {
|
it("resolves model slugs and prefixes", () => {
|
||||||
assert.equal(models.clientFacingPromptQlModelId("promptql/gemini-3.5-flash"), "gemini-3.5-flash");
|
assert.equal(
|
||||||
|
models.clientFacingPromptQlModelId("promptql/gemini-3.7-flash"),
|
||||||
|
"gemini-3.7-flash"
|
||||||
|
);
|
||||||
assert.equal(models.clientFacingPromptQlModelId("pql/gpt-5.6-sol"), "gpt-5.6-sol");
|
assert.equal(models.clientFacingPromptQlModelId("pql/gpt-5.6-sol"), "gpt-5.6-sol");
|
||||||
const r = models.resolvePromptQlModel("Claude Fable 5");
|
const r = models.resolvePromptQlModel("Claude Fable 5");
|
||||||
assert.ok(r);
|
assert.ok(r);
|
||||||
@@ -265,7 +268,7 @@ describe("PromptQlExecutor — auth / validation", () => {
|
|||||||
it("returns 401 when no token is supplied", async () => {
|
it("returns 401 when no token is supplied", async () => {
|
||||||
const executor = new mod.PromptQlExecutor();
|
const executor = new mod.PromptQlExecutor();
|
||||||
const result = await executor.execute({
|
const result = await executor.execute({
|
||||||
model: "gemini-3.5-flash",
|
model: "gemini-3.7-flash",
|
||||||
body: { messages: [{ role: "user", content: "hi" }] },
|
body: { messages: [{ role: "user", content: "hi" }] },
|
||||||
stream: false,
|
stream: false,
|
||||||
credentials: {},
|
credentials: {},
|
||||||
@@ -279,7 +282,7 @@ describe("PromptQlExecutor — auth / validation", () => {
|
|||||||
it("returns 400 when no user message is present", async () => {
|
it("returns 400 when no user message is present", async () => {
|
||||||
const executor = new mod.PromptQlExecutor();
|
const executor = new mod.PromptQlExecutor();
|
||||||
const result = await executor.execute({
|
const result = await executor.execute({
|
||||||
model: "gemini-3.5-flash",
|
model: "gemini-3.7-flash",
|
||||||
body: { messages: [{ role: "assistant", content: "hi" }] },
|
body: { messages: [{ role: "assistant", content: "hi" }] },
|
||||||
stream: false,
|
stream: false,
|
||||||
credentials: { apiKey: sampleJwt },
|
credentials: { apiKey: sampleJwt },
|
||||||
@@ -614,7 +617,7 @@ describe("PromptQlExecutor — mocked GraphQL turn", () => {
|
|||||||
try {
|
try {
|
||||||
const executor = new mod.PromptQlExecutor();
|
const executor = new mod.PromptQlExecutor();
|
||||||
const result = await executor.execute({
|
const result = await executor.execute({
|
||||||
model: "gemini-3.5-flash",
|
model: "gemini-3.7-flash",
|
||||||
body: { messages: [{ role: "user", content: "ping" }] },
|
body: { messages: [{ role: "user", content: "ping" }] },
|
||||||
stream: false,
|
stream: false,
|
||||||
credentials: { apiKey: sampleJwt },
|
credentials: { apiKey: sampleJwt },
|
||||||
@@ -628,7 +631,7 @@ describe("PromptQlExecutor — mocked GraphQL turn", () => {
|
|||||||
};
|
};
|
||||||
assert.equal(json.choices[0]!.message.content, "HELLO-PQL");
|
assert.equal(json.choices[0]!.message.content, "HELLO-PQL");
|
||||||
assert.equal(json.promptql_thread_id, "thread-1");
|
assert.equal(json.promptql_thread_id, "thread-1");
|
||||||
assert.equal(json.model, "gemini-3.5-flash");
|
assert.equal(json.model, "gemini-3.7-flash");
|
||||||
assert.ok(call >= 2);
|
assert.ok(call >= 2);
|
||||||
assert.equal(result.response.headers.get("X-PromptQL-Thread-Id"), "thread-1");
|
assert.equal(result.response.headers.get("X-PromptQL-Thread-Id"), "thread-1");
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
// Regression test for #10286: gemini-3.5-flash was incorrectly marked
|
|
||||||
// supportsThinking:false, causing a spurious pre-provider HTTP 400 for any
|
|
||||||
// request with reasoning_effort set, even though the base Google AI Studio
|
|
||||||
// model supports reasoning (it has an effort-tier alias gemini-3.5-flash-high).
|
|
||||||
import test from "node:test";
|
|
||||||
import assert from "node:assert/strict";
|
|
||||||
import fs from "node:fs";
|
|
||||||
import os from "node:os";
|
|
||||||
import path from "node:path";
|
|
||||||
|
|
||||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-repro-10286-"));
|
|
||||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
|
||||||
process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "test-repro-10286-secret";
|
|
||||||
|
|
||||||
const caps = await import("../../src/lib/modelCapabilities.ts");
|
|
||||||
const core = await import("../../src/lib/db/core.ts");
|
|
||||||
const rulesDb = await import("../../src/lib/db/reasoningRoutingRules.ts");
|
|
||||||
const policy = await import("../../src/lib/reasoningRouting/policy.ts");
|
|
||||||
|
|
||||||
async function resetStorage() {
|
|
||||||
core.resetDbInstance();
|
|
||||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
|
||||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
|
||||||
rulesDb.invalidateReasoningRoutingRuleCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
function ruleInput(patch: Record<string, unknown> = {}) {
|
|
||||||
return {
|
|
||||||
name: "Enable thinking on gemini-3.5-flash",
|
|
||||||
description: "",
|
|
||||||
scope: "global",
|
|
||||||
apiKeyId: null,
|
|
||||||
comboId: null,
|
|
||||||
connectionId: null,
|
|
||||||
modelPattern: "gemini-3.5-flash",
|
|
||||||
sourceEffort: "any",
|
|
||||||
requestTags: [],
|
|
||||||
tagMatchMode: "any",
|
|
||||||
effortMode: "inherit",
|
|
||||||
targetEffort: null,
|
|
||||||
targetKind: "keep",
|
|
||||||
targetModel: null,
|
|
||||||
targetComboId: null,
|
|
||||||
budgetAction: "preserve",
|
|
||||||
budgetTokens: null,
|
|
||||||
priority: 0,
|
|
||||||
enabled: true,
|
|
||||||
...patch,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
test.beforeEach(resetStorage);
|
|
||||||
test.after(async () => {
|
|
||||||
await resetStorage();
|
|
||||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
test("gemini-3.5-flash (AI Studio provider) resolves as thinking-capable", () => {
|
|
||||||
const resolved = caps.getResolvedModelCapabilities({
|
|
||||||
provider: "gemini",
|
|
||||||
model: "gemini-3.5-flash",
|
|
||||||
});
|
|
||||||
assert.equal(resolved.supportsThinking, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("reasoning_effort 'high' on gemini-3.5-flash is NOT rejected by routing policy", async () => {
|
|
||||||
await rulesDb.createReasoningRoutingRule(ruleInput());
|
|
||||||
const decision = await policy.resolveReasoningRoutingRule({
|
|
||||||
sourceModel: "gemini/gemini-3.5-flash",
|
|
||||||
sourceModelAliases: ["gemini-3.5-flash"],
|
|
||||||
sourceEffort: "high",
|
|
||||||
hasReasoningSignal: true,
|
|
||||||
});
|
|
||||||
assert.ok(decision, "a matching rule must produce a decision");
|
|
||||||
assert.equal(decision.capability, "supported");
|
|
||||||
});
|
|
||||||
@@ -71,7 +71,7 @@ test("OpenAI -> Gemini request strips encrypted from Codex collaboration tool pa
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = openaiToGeminiRequest("gemini-3.5-flash-low", body, false) as {
|
const result = openaiToGeminiRequest("gemini-3.7-flash-low", body, false) as {
|
||||||
tools?: Array<{ functionDeclarations?: Array<{ parameters: unknown }> }>;
|
tools?: Array<{ functionDeclarations?: Array<{ parameters: unknown }> }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,16 @@ const SAMPLE = {
|
|||||||
supportedGenerationMethods: ["generateContent", "countTokens", "batchGenerateContent"],
|
supportedGenerationMethods: ["generateContent", "countTokens", "batchGenerateContent"],
|
||||||
thinking: true,
|
thinking: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "models/gemini-3.5-flash",
|
||||||
|
displayName: "Gemini 3.5 Flash",
|
||||||
|
supportedGenerationMethods: ["generateContent"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "models/gemini-3.5-flash-lite",
|
||||||
|
displayName: "Gemini 3.5 Flash Lite",
|
||||||
|
supportedGenerationMethods: ["generateContent"],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "models/gemini-3-pro-image-preview",
|
name: "models/gemini-3-pro-image-preview",
|
||||||
displayName: "Gemini 3 Pro Image Preview",
|
displayName: "Gemini 3 Pro Image Preview",
|
||||||
@@ -48,6 +58,12 @@ test("parseGeminiModelsList strips the models/ prefix and maps display name", ()
|
|||||||
assert.deepEqual(flash!.supportedEndpoints, ["chat"]);
|
assert.deepEqual(flash!.supportedEndpoints, ["chat"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("parseGeminiModelsList excludes retired Gemini 3.5 Flash but keeps Flash Lite", () => {
|
||||||
|
const ids = parseGeminiModelsList(SAMPLE).map((model) => model.id);
|
||||||
|
assert.equal(ids.includes("gemini-3.5-flash"), false);
|
||||||
|
assert.equal(ids.includes("gemini-3.5-flash-lite"), true);
|
||||||
|
});
|
||||||
|
|
||||||
test("parseGeminiModelsList maps generateContent image models to the chat endpoint", () => {
|
test("parseGeminiModelsList maps generateContent image models to the chat endpoint", () => {
|
||||||
const models = parseGeminiModelsList(SAMPLE);
|
const models = parseGeminiModelsList(SAMPLE);
|
||||||
const proImage = models.find((m) => m.id === "gemini-3-pro-image-preview");
|
const proImage = models.find((m) => m.id === "gemini-3-pro-image-preview");
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ test("OpenAI -> Gemini request strips strict from OpenAI-style function tool par
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = openaiToGeminiRequest("gemini-3.5-flash-low", body, false) as {
|
const result = openaiToGeminiRequest("gemini-3.7-flash-low", body, false) as {
|
||||||
tools?: Array<{ functionDeclarations?: Array<{ parameters: unknown }> }>;
|
tools?: Array<{ functionDeclarations?: Array<{ parameters: unknown }> }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ test("buildUrl uses chat/completions endpoint for gemini models", () => {
|
|||||||
};
|
};
|
||||||
// Gemini has no native shim on Copilot — it stays on /chat/completions.
|
// Gemini has no native shim on Copilot — it stays on /chat/completions.
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
executor.buildUrl("gemini-3.5-flash", true, 0, credentials),
|
executor.buildUrl("gemini-3.7-flash", true, 0, credentials),
|
||||||
"https://ghe.company.com/chat/completions"
|
"https://ghe.company.com/chat/completions"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -279,6 +279,7 @@ test("antigravity sync dynamically builds and saves mitmAlias mappings", async (
|
|||||||
mode: "sync",
|
mode: "sync",
|
||||||
fetchedModels: [
|
fetchedModels: [
|
||||||
{ id: "gemini-3.5-flash", name: "Gemini 3.5 Flash" },
|
{ id: "gemini-3.5-flash", name: "Gemini 3.5 Flash" },
|
||||||
|
{ id: "gemini-3.7-flash-high", name: "Gemini 3.7 Flash High" },
|
||||||
{ id: "custom-antigravity-model", name: "Custom Antigravity Model" },
|
{ id: "custom-antigravity-model", name: "Custom Antigravity Model" },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@@ -289,8 +290,13 @@ test("antigravity sync dynamically builds and saves mitmAlias mappings", async (
|
|||||||
const mitmMappings = await modelsDb.getMitmAlias("antigravity");
|
const mitmMappings = await modelsDb.getMitmAlias("antigravity");
|
||||||
console.log("MITM MAPPINGS IN TEST:", mitmMappings);
|
console.log("MITM MAPPINGS IN TEST:", mitmMappings);
|
||||||
|
|
||||||
// Should contain standard mapping
|
// Retired models reported by upstream must not be imported or mapped.
|
||||||
assert.equal(mitmMappings["gemini-3.5-flash"], "antigravity/gemini-3.5-flash");
|
assert.equal(mitmMappings["gemini-3.5-flash"], undefined);
|
||||||
|
assert.equal(
|
||||||
|
models.some((model) => model.id === "gemini-3.5-flash"),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
assert.equal(mitmMappings["gemini-3.7-flash-high"], "antigravity/gemini-3.7-flash-high");
|
||||||
assert.equal(mitmMappings["custom-antigravity-model"], "antigravity/custom-antigravity-model");
|
assert.equal(mitmMappings["custom-antigravity-model"], "antigravity/custom-antigravity-model");
|
||||||
|
|
||||||
// Removed Antigravity 2.0 preview/agent aliases must not be reintroduced.
|
// Removed Antigravity 2.0 preview/agent aliases must not be reintroduced.
|
||||||
|
|||||||
@@ -154,21 +154,14 @@ test("unknown models keep maxOutputTokens null instead of using a generic defaul
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("provider-neutral Gemini 3.5 tier IDs retain their non-thinking capabilities", () => {
|
test("retired Gemini 3.5 Flash IDs have no provider-neutral model specs", () => {
|
||||||
for (const modelId of [
|
for (const modelId of [
|
||||||
|
"gemini-3.5-flash",
|
||||||
"gemini-3.5-flash-extra-low",
|
"gemini-3.5-flash-extra-low",
|
||||||
"gemini-3.5-flash-low",
|
"gemini-3.5-flash-low",
|
||||||
"gemini-3-flash-agent",
|
"gemini-3-flash-agent",
|
||||||
]) {
|
]) {
|
||||||
const spec = MODEL_SPECS[modelId];
|
assert.equal(MODEL_SPECS[modelId], undefined, modelId);
|
||||||
assert.ok(spec, `missing exact MODEL_SPECS entry for ${modelId}`);
|
|
||||||
const capabilities = modelCapabilities.getResolvedModelCapabilities(modelId);
|
|
||||||
assert.equal(capabilities.contextWindow, 1048576, modelId);
|
|
||||||
assert.equal(capabilities.maxOutputTokens, 65536, modelId);
|
|
||||||
// These ids encode the upstream reasoning tier and do not accept a client-supplied effort.
|
|
||||||
assert.equal(capabilities.supportsThinking, false, modelId);
|
|
||||||
assert.equal(capabilities.supportsTools, true, modelId);
|
|
||||||
assert.equal(capabilities.supportsVision, true, modelId);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -702,6 +702,7 @@ test("v1 models catalog exposes current Antigravity aliases without retired mode
|
|||||||
assert.equal(ids.has("antigravity/gemini-3.6-flash-high"), false);
|
assert.equal(ids.has("antigravity/gemini-3.6-flash-high"), false);
|
||||||
assert.equal(ids.has("antigravity/gemini-3.6-flash-medium"), false);
|
assert.equal(ids.has("antigravity/gemini-3.6-flash-medium"), false);
|
||||||
assert.equal(ids.has("antigravity/gemini-3.6-flash-low"), false);
|
assert.equal(ids.has("antigravity/gemini-3.6-flash-low"), false);
|
||||||
|
assert.equal(ids.has("antigravity/gemini-3.5-flash"), false);
|
||||||
assert.equal(ids.has("antigravity/gemini-3.5-flash-extra-low"), false);
|
assert.equal(ids.has("antigravity/gemini-3.5-flash-extra-low"), false);
|
||||||
assert.equal(ids.has("antigravity/gemini-3.5-flash-low"), false);
|
assert.equal(ids.has("antigravity/gemini-3.5-flash-low"), false);
|
||||||
assert.equal(ids.has("antigravity/gemini-3-flash-agent"), false);
|
assert.equal(ids.has("antigravity/gemini-3-flash-agent"), false);
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ test("buildOmniRouteResponseMetaHeaders keeps ASCII model header values unchange
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("buildOmniRouteResponseMetaHeaders percent-encodes non-ASCII model header values", () => {
|
test("buildOmniRouteResponseMetaHeaders percent-encodes non-ASCII model header values", () => {
|
||||||
const model = "free-mix/[假流式]gemini-3.5-flash";
|
const model = "free-mix/[假流式]gemini-3.7-flash";
|
||||||
const headers = buildOmniRouteResponseMetaHeaders({
|
const headers = buildOmniRouteResponseMetaHeaders({
|
||||||
provider: "openai",
|
provider: "openai",
|
||||||
model,
|
model,
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ test("hidden provider models are filtered from per-model quota rows", () => {
|
|||||||
});
|
});
|
||||||
const hidden = providerLimitUtils.collectHiddenQuotaModelIds("antigravity", {
|
const hidden = providerLimitUtils.collectHiddenQuotaModelIds("antigravity", {
|
||||||
models: [{ id: "antigravity/gpt-oss-120b-medium", isHidden: true }],
|
models: [{ id: "antigravity/gpt-oss-120b-medium", isHidden: true }],
|
||||||
modelCompatOverrides: [{ id: "gemini-3.5-flash", isHidden: true }],
|
modelCompatOverrides: [{ id: "gemini-3.7-flash", isHidden: true }],
|
||||||
});
|
});
|
||||||
const visible = providerLimitUtils.filterHiddenModelQuotas("antigravity", quotas, hidden);
|
const visible = providerLimitUtils.filterHiddenModelQuotas("antigravity", quotas, hidden);
|
||||||
|
|
||||||
|
|||||||
@@ -394,7 +394,7 @@ test("parseSSEToGeminiResponse extracts tool calls from textual format", () => {
|
|||||||
})}`,
|
})}`,
|
||||||
].join("\n");
|
].join("\n");
|
||||||
|
|
||||||
const parsed = parseSSEToGeminiResponse(rawSSE, "gemini-3.5-flash-low");
|
const parsed = parseSSEToGeminiResponse(rawSSE, "gemini-3.7-flash-low");
|
||||||
|
|
||||||
assert.ok(parsed);
|
assert.ok(parsed);
|
||||||
assert.equal(parsed.choices[0].finish_reason, "tool_calls");
|
assert.equal(parsed.choices[0].finish_reason, "tool_calls");
|
||||||
|
|||||||
@@ -232,14 +232,14 @@ test("createSSEStream passthrough converts textual tool-call content into struct
|
|||||||
id: "chatcmpl_textual_tool",
|
id: "chatcmpl_textual_tool",
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: 1,
|
created: 1,
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
choices: [{ index: 0, delta: { role: "assistant", content: toolText } }],
|
choices: [{ index: 0, delta: { role: "assistant", content: toolText } }],
|
||||||
})}\n\n`,
|
})}\n\n`,
|
||||||
`data: ${JSON.stringify({
|
`data: ${JSON.stringify({
|
||||||
id: "chatcmpl_textual_tool",
|
id: "chatcmpl_textual_tool",
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: 1,
|
created: 1,
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
||||||
})}\n\n`,
|
})}\n\n`,
|
||||||
],
|
],
|
||||||
@@ -247,7 +247,7 @@ test("createSSEStream passthrough converts textual tool-call content into struct
|
|||||||
mode: "passthrough",
|
mode: "passthrough",
|
||||||
sourceFormat: FORMATS.OPENAI,
|
sourceFormat: FORMATS.OPENAI,
|
||||||
provider: "antigravity",
|
provider: "antigravity",
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
body: {
|
body: {
|
||||||
messages: [{ role: "user", content: "inspect db" }],
|
messages: [{ role: "user", content: "inspect db" }],
|
||||||
},
|
},
|
||||||
@@ -284,21 +284,21 @@ test("createSSEStream passthrough converts split textual tool-call content at co
|
|||||||
id: "chatcmpl_split_textual_tool",
|
id: "chatcmpl_split_textual_tool",
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: 1,
|
created: 1,
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
choices: [{ index: 0, delta: { role: "assistant", content: chunks[0] } }],
|
choices: [{ index: 0, delta: { role: "assistant", content: chunks[0] } }],
|
||||||
})}\n\n`,
|
})}\n\n`,
|
||||||
`data: ${JSON.stringify({
|
`data: ${JSON.stringify({
|
||||||
id: "chatcmpl_split_textual_tool",
|
id: "chatcmpl_split_textual_tool",
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: 1,
|
created: 1,
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
choices: [{ index: 0, delta: { content: chunks[1] } }],
|
choices: [{ index: 0, delta: { content: chunks[1] } }],
|
||||||
})}\n\n`,
|
})}\n\n`,
|
||||||
`data: ${JSON.stringify({
|
`data: ${JSON.stringify({
|
||||||
id: "chatcmpl_split_textual_tool",
|
id: "chatcmpl_split_textual_tool",
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: 1,
|
created: 1,
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
||||||
})}\n\n`,
|
})}\n\n`,
|
||||||
],
|
],
|
||||||
@@ -306,7 +306,7 @@ test("createSSEStream passthrough converts split textual tool-call content at co
|
|||||||
mode: "passthrough",
|
mode: "passthrough",
|
||||||
sourceFormat: FORMATS.OPENAI,
|
sourceFormat: FORMATS.OPENAI,
|
||||||
provider: "antigravity",
|
provider: "antigravity",
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
body: { messages: [{ role: "user", content: "inspect db" }] },
|
body: { messages: [{ role: "user", content: "inspect db" }] },
|
||||||
onComplete(payload) {
|
onComplete(payload) {
|
||||||
onCompletePayload = payload;
|
onCompletePayload = payload;
|
||||||
@@ -340,28 +340,28 @@ test("createSSEStream passthrough handles textual tool-call content split inside
|
|||||||
id: "chatcmpl_split_prefix_textual_tool",
|
id: "chatcmpl_split_prefix_textual_tool",
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: 1,
|
created: 1,
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
choices: [{ index: 0, delta: { role: "assistant", content: chunks[0] } }],
|
choices: [{ index: 0, delta: { role: "assistant", content: chunks[0] } }],
|
||||||
})}\n\n`,
|
})}\n\n`,
|
||||||
`data: ${JSON.stringify({
|
`data: ${JSON.stringify({
|
||||||
id: "chatcmpl_split_prefix_textual_tool",
|
id: "chatcmpl_split_prefix_textual_tool",
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: 1,
|
created: 1,
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
choices: [{ index: 0, delta: { content: chunks[1] } }],
|
choices: [{ index: 0, delta: { content: chunks[1] } }],
|
||||||
})}\n\n`,
|
})}\n\n`,
|
||||||
`data: ${JSON.stringify({
|
`data: ${JSON.stringify({
|
||||||
id: "chatcmpl_split_prefix_textual_tool",
|
id: "chatcmpl_split_prefix_textual_tool",
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: 1,
|
created: 1,
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
choices: [{ index: 0, delta: { content: chunks[2] } }],
|
choices: [{ index: 0, delta: { content: chunks[2] } }],
|
||||||
})}\n\n`,
|
})}\n\n`,
|
||||||
`data: ${JSON.stringify({
|
`data: ${JSON.stringify({
|
||||||
id: "chatcmpl_split_prefix_textual_tool",
|
id: "chatcmpl_split_prefix_textual_tool",
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: 1,
|
created: 1,
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
||||||
})}\n\n`,
|
})}\n\n`,
|
||||||
],
|
],
|
||||||
@@ -369,7 +369,7 @@ test("createSSEStream passthrough handles textual tool-call content split inside
|
|||||||
mode: "passthrough",
|
mode: "passthrough",
|
||||||
sourceFormat: FORMATS.OPENAI,
|
sourceFormat: FORMATS.OPENAI,
|
||||||
provider: "antigravity",
|
provider: "antigravity",
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
body: { messages: [{ role: "user", content: "inspect db" }] },
|
body: { messages: [{ role: "user", content: "inspect db" }] },
|
||||||
onComplete(payload) {
|
onComplete(payload) {
|
||||||
onCompletePayload = payload;
|
onCompletePayload = payload;
|
||||||
@@ -515,14 +515,14 @@ Arguments: {"path":"/opt/OmniRoute/src","target":"files"}`;
|
|||||||
id: "chatcmpl_unknown_textual_tool",
|
id: "chatcmpl_unknown_textual_tool",
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: 1,
|
created: 1,
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
choices: [{ index: 0, delta: { role: "assistant", content: toolText } }],
|
choices: [{ index: 0, delta: { role: "assistant", content: toolText } }],
|
||||||
})}\n\n`,
|
})}\n\n`,
|
||||||
`data: ${JSON.stringify({
|
`data: ${JSON.stringify({
|
||||||
id: "chatcmpl_unknown_textual_tool",
|
id: "chatcmpl_unknown_textual_tool",
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: 1,
|
created: 1,
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
||||||
})}\n\n`,
|
})}\n\n`,
|
||||||
],
|
],
|
||||||
@@ -530,7 +530,7 @@ Arguments: {"path":"/opt/OmniRoute/src","target":"files"}`;
|
|||||||
mode: "passthrough",
|
mode: "passthrough",
|
||||||
sourceFormat: FORMATS.OPENAI,
|
sourceFormat: FORMATS.OPENAI,
|
||||||
provider: "antigravity",
|
provider: "antigravity",
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
body: {
|
body: {
|
||||||
messages: [{ role: "user", content: "inspect files" }],
|
messages: [{ role: "user", content: "inspect files" }],
|
||||||
tools: [
|
tools: [
|
||||||
@@ -561,14 +561,14 @@ test("createSSEStream passthrough suppresses malformed textual tool-call content
|
|||||||
id: "chatcmpl_malformed_textual_tool",
|
id: "chatcmpl_malformed_textual_tool",
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: 1,
|
created: 1,
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
choices: [{ index: 0, delta: { role: "assistant", content: malformedToolText } }],
|
choices: [{ index: 0, delta: { role: "assistant", content: malformedToolText } }],
|
||||||
})}\n\n`,
|
})}\n\n`,
|
||||||
`data: ${JSON.stringify({
|
`data: ${JSON.stringify({
|
||||||
id: "chatcmpl_malformed_textual_tool",
|
id: "chatcmpl_malformed_textual_tool",
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: 1,
|
created: 1,
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
||||||
})}\n\n`,
|
})}\n\n`,
|
||||||
],
|
],
|
||||||
@@ -576,7 +576,7 @@ test("createSSEStream passthrough suppresses malformed textual tool-call content
|
|||||||
mode: "passthrough",
|
mode: "passthrough",
|
||||||
sourceFormat: FORMATS.OPENAI,
|
sourceFormat: FORMATS.OPENAI,
|
||||||
provider: "antigravity",
|
provider: "antigravity",
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
body: { messages: [{ role: "user", content: "inspect db" }] },
|
body: { messages: [{ role: "user", content: "inspect db" }] },
|
||||||
onComplete(payload) {
|
onComplete(payload) {
|
||||||
onCompletePayload = payload;
|
onCompletePayload = payload;
|
||||||
@@ -617,7 +617,7 @@ test("createSSEStream suppresses malformed compact textual tool-call content", a
|
|||||||
targetFormat: FORMATS.ANTIGRAVITY,
|
targetFormat: FORMATS.ANTIGRAVITY,
|
||||||
sourceFormat: FORMATS.OPENAI,
|
sourceFormat: FORMATS.OPENAI,
|
||||||
provider: "antigravity",
|
provider: "antigravity",
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
body: { messages: [{ role: "user", content: "inspect files" }] },
|
body: { messages: [{ role: "user", content: "inspect files" }] },
|
||||||
onComplete(payload) {
|
onComplete(payload) {
|
||||||
onCompletePayload = payload;
|
onCompletePayload = payload;
|
||||||
@@ -1024,7 +1024,7 @@ Arguments: {"command":"systemctl status omniroute"}`;
|
|||||||
response: {
|
response: {
|
||||||
id: "resp_textual_tool",
|
id: "resp_textual_tool",
|
||||||
object: "response",
|
object: "response",
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
status: "completed",
|
status: "completed",
|
||||||
output: [],
|
output: [],
|
||||||
usage: { input_tokens: 10, output_tokens: 4, total_tokens: 14 },
|
usage: { input_tokens: 10, output_tokens: 4, total_tokens: 14 },
|
||||||
@@ -1038,7 +1038,7 @@ Arguments: {"command":"systemctl status omniroute"}`;
|
|||||||
sourceFormat: FORMATS.OPENAI_RESPONSES,
|
sourceFormat: FORMATS.OPENAI_RESPONSES,
|
||||||
clientResponseFormat: FORMATS.OPENAI_RESPONSES,
|
clientResponseFormat: FORMATS.OPENAI_RESPONSES,
|
||||||
provider: "antigravity",
|
provider: "antigravity",
|
||||||
model: "antigravity/gemini-3.5-flash-low",
|
model: "antigravity/gemini-3.7-flash-low",
|
||||||
body: {
|
body: {
|
||||||
input: "check service",
|
input: "check service",
|
||||||
tools: [{ type: "function", name: "terminal", parameters: { type: "object" } }],
|
tools: [{ type: "function", name: "terminal", parameters: { type: "object" } }],
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ test("T28: antigravity static catalog exposes only callable Gemini tier IDs", ()
|
|||||||
assert.ok(!staticIds.includes("gemini-3.6-flash-high"));
|
assert.ok(!staticIds.includes("gemini-3.6-flash-high"));
|
||||||
assert.ok(!staticIds.includes("gemini-3.6-flash-medium"));
|
assert.ok(!staticIds.includes("gemini-3.6-flash-medium"));
|
||||||
assert.ok(!staticIds.includes("gemini-3.6-flash-low"));
|
assert.ok(!staticIds.includes("gemini-3.6-flash-low"));
|
||||||
|
assert.ok(!staticIds.includes("gemini-3.5-flash"));
|
||||||
assert.ok(!staticIds.includes("gemini-3.5-flash-extra-low"));
|
assert.ok(!staticIds.includes("gemini-3.5-flash-extra-low"));
|
||||||
assert.ok(!staticIds.includes("gemini-3.5-flash-low"));
|
assert.ok(!staticIds.includes("gemini-3.5-flash-low"));
|
||||||
assert.ok(!staticIds.includes("gemini-3-flash-agent"));
|
assert.ok(!staticIds.includes("gemini-3-flash-agent"));
|
||||||
|
|||||||
@@ -607,7 +607,7 @@ test("OpenAI -> Antigravity wraps Gemini requests in a Cloud Code envelope", ()
|
|||||||
|
|
||||||
test("OpenAI -> Antigravity Gemini omits signature-less historical tool calls and keeps response context", () => {
|
test("OpenAI -> Antigravity Gemini omits signature-less historical tool calls and keeps response context", () => {
|
||||||
const result = openaiToAntigravityRequest(
|
const result = openaiToAntigravityRequest(
|
||||||
"gemini-3.5-flash-low",
|
"gemini-3.7-flash-low",
|
||||||
{
|
{
|
||||||
messages: [
|
messages: [
|
||||||
{ role: "user", content: "Update todo" },
|
{ role: "user", content: "Update todo" },
|
||||||
@@ -686,7 +686,7 @@ test("OpenAI -> Antigravity Gemini omits signature-less historical tool calls an
|
|||||||
|
|
||||||
test("OpenAI -> Antigravity preserves multiple signature-less historical tool responses as context", () => {
|
test("OpenAI -> Antigravity preserves multiple signature-less historical tool responses as context", () => {
|
||||||
const result = openaiToAntigravityRequest(
|
const result = openaiToAntigravityRequest(
|
||||||
"gemini-3.5-flash-low",
|
"gemini-3.7-flash-low",
|
||||||
{
|
{
|
||||||
messages: [
|
messages: [
|
||||||
{ role: "user", content: "Inspect OmniRoute config" },
|
{ role: "user", content: "Inspect OmniRoute config" },
|
||||||
@@ -747,7 +747,7 @@ test("OpenAI -> Antigravity preserves signed Gemini tool calls in native form",
|
|||||||
storeGeminiThoughtSignature(buildGeminiThoughtSignatureKey(ns, toolId), "SIG_AG_SIGNED_XYZ");
|
storeGeminiThoughtSignature(buildGeminiThoughtSignatureKey(ns, toolId), "SIG_AG_SIGNED_XYZ");
|
||||||
|
|
||||||
const result = openaiToAntigravityRequest(
|
const result = openaiToAntigravityRequest(
|
||||||
"gemini-3.5-flash-low",
|
"gemini-3.7-flash-low",
|
||||||
{
|
{
|
||||||
messages: [
|
messages: [
|
||||||
{ role: "user", content: "Read status" },
|
{ role: "user", content: "Read status" },
|
||||||
@@ -787,7 +787,7 @@ test("OpenAI -> Antigravity preserves signed Gemini tool calls in native form",
|
|||||||
|
|
||||||
test("OpenAI -> Antigravity escapes signature-less tool response context content", () => {
|
test("OpenAI -> Antigravity escapes signature-less tool response context content", () => {
|
||||||
const result = openaiToAntigravityRequest(
|
const result = openaiToAntigravityRequest(
|
||||||
"gemini-3.5-flash-low",
|
"gemini-3.7-flash-low",
|
||||||
{
|
{
|
||||||
messages: [
|
messages: [
|
||||||
{ role: "user", content: "Inspect previous output" },
|
{ role: "user", content: "Inspect previous output" },
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ test("Gemini stream: converts textual Tool call block to structured tool_calls",
|
|||||||
const result = geminiToOpenAIResponse(
|
const result = geminiToOpenAIResponse(
|
||||||
{
|
{
|
||||||
responseId: "resp-textual-tool",
|
responseId: "resp-textual-tool",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -386,7 +386,7 @@ test("Gemini stream: routes textual reasoning tags to reasoning_content before t
|
|||||||
const result = geminiToOpenAIResponse(
|
const result = geminiToOpenAIResponse(
|
||||||
{
|
{
|
||||||
responseId: "resp-textual-thought-tool",
|
responseId: "resp-textual-thought-tool",
|
||||||
modelVersion: "gemini-3.5-flash-high",
|
modelVersion: "gemini-3.7-flash-high",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -431,7 +431,7 @@ test("Gemini stream: keeps textual reasoning hidden across split chunks", () =>
|
|||||||
const first = geminiToOpenAIResponse(
|
const first = geminiToOpenAIResponse(
|
||||||
{
|
{
|
||||||
responseId: "resp-split-thought",
|
responseId: "resp-split-thought",
|
||||||
modelVersion: "gemini-3.5-flash-high",
|
modelVersion: "gemini-3.7-flash-high",
|
||||||
candidates: [{ content: { parts: [{ text: "§54§ <tho" }] } }],
|
candidates: [{ content: { parts: [{ text: "§54§ <tho" }] } }],
|
||||||
},
|
},
|
||||||
state
|
state
|
||||||
@@ -444,7 +444,7 @@ test("Gemini stream: keeps textual reasoning hidden across split chunks", () =>
|
|||||||
const second = geminiToOpenAIResponse(
|
const second = geminiToOpenAIResponse(
|
||||||
{
|
{
|
||||||
responseId: "resp-split-thought",
|
responseId: "resp-split-thought",
|
||||||
modelVersion: "gemini-3.5-flash-high",
|
modelVersion: "gemini-3.7-flash-high",
|
||||||
candidates: [{ content: { parts: [{ text: "ught\nNeed to inspect" }] } }],
|
candidates: [{ content: { parts: [{ text: "ught\nNeed to inspect" }] } }],
|
||||||
},
|
},
|
||||||
state
|
state
|
||||||
@@ -459,7 +459,7 @@ test("Gemini stream: keeps textual reasoning hidden across split chunks", () =>
|
|||||||
const third = geminiToOpenAIResponse(
|
const third = geminiToOpenAIResponse(
|
||||||
{
|
{
|
||||||
responseId: "resp-split-thought",
|
responseId: "resp-split-thought",
|
||||||
modelVersion: "gemini-3.5-flash-high",
|
modelVersion: "gemini-3.7-flash-high",
|
||||||
candidates: [{ content: { parts: [{ text: " more</tho" }] } }],
|
candidates: [{ content: { parts: [{ text: " more</tho" }] } }],
|
||||||
},
|
},
|
||||||
state
|
state
|
||||||
@@ -472,7 +472,7 @@ test("Gemini stream: keeps textual reasoning hidden across split chunks", () =>
|
|||||||
const fourth = geminiToOpenAIResponse(
|
const fourth = geminiToOpenAIResponse(
|
||||||
{
|
{
|
||||||
responseId: "resp-split-thought",
|
responseId: "resp-split-thought",
|
||||||
modelVersion: "gemini-3.5-flash-high",
|
modelVersion: "gemini-3.7-flash-high",
|
||||||
candidates: [{ content: { parts: [{ text: "ught>Visible answer" }] } }],
|
candidates: [{ content: { parts: [{ text: "ught>Visible answer" }] } }],
|
||||||
},
|
},
|
||||||
state
|
state
|
||||||
@@ -498,7 +498,7 @@ test("Gemini stream: converts prefixed textual Tool call block with zero-width c
|
|||||||
const result = geminiToOpenAIResponse(
|
const result = geminiToOpenAIResponse(
|
||||||
{
|
{
|
||||||
responseId: "resp-textual-tool-prefixed",
|
responseId: "resp-textual-tool-prefixed",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -612,7 +612,7 @@ test("Gemini stream: unwraps native functionCall args when emitted as JSON strin
|
|||||||
const result = geminiToOpenAIResponse(
|
const result = geminiToOpenAIResponse(
|
||||||
{
|
{
|
||||||
responseId: "resp-native-tool-json-string",
|
responseId: "resp-native-tool-json-string",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -648,7 +648,7 @@ test("Gemini stream: converts JSON-string encoded textual Tool call arguments",
|
|||||||
const result = geminiToOpenAIResponse(
|
const result = geminiToOpenAIResponse(
|
||||||
{
|
{
|
||||||
responseId: "resp-textual-tool-json-string",
|
responseId: "resp-textual-tool-json-string",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -685,7 +685,7 @@ test("Gemini stream: suppresses malformed textual Tool call marker", () => {
|
|||||||
const result = geminiToOpenAIResponse(
|
const result = geminiToOpenAIResponse(
|
||||||
{
|
{
|
||||||
responseId: "resp-textual-tool-malformed",
|
responseId: "resp-textual-tool-malformed",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -717,7 +717,7 @@ test("Gemini stream: handles textual Tool call block split across chunks", () =>
|
|||||||
const state = createStreamingState();
|
const state = createStreamingState();
|
||||||
const chunk1 = {
|
const chunk1 = {
|
||||||
responseId: "resp-split",
|
responseId: "resp-split",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -732,7 +732,7 @@ test("Gemini stream: handles textual Tool call block split across chunks", () =>
|
|||||||
};
|
};
|
||||||
const chunk2 = {
|
const chunk2 = {
|
||||||
responseId: "resp-split",
|
responseId: "resp-split",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -768,7 +768,7 @@ test("Gemini stream: does not swallow false positive textual tool call in backti
|
|||||||
const state = createStreamingState();
|
const state = createStreamingState();
|
||||||
const chunk1 = {
|
const chunk1 = {
|
||||||
responseId: "resp-false-positive",
|
responseId: "resp-false-positive",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -783,7 +783,7 @@ test("Gemini stream: does not swallow false positive textual tool call in backti
|
|||||||
};
|
};
|
||||||
const chunk2 = {
|
const chunk2 = {
|
||||||
responseId: "resp-false-positive",
|
responseId: "resp-false-positive",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -817,7 +817,7 @@ test("Gemini stream: does not swallow terminated trailing false positive textual
|
|||||||
const state = createStreamingState();
|
const state = createStreamingState();
|
||||||
const chunk1 = {
|
const chunk1 = {
|
||||||
responseId: "resp-false-positive-terminated",
|
responseId: "resp-false-positive-terminated",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -842,7 +842,7 @@ test("Gemini stream: flushes left part before textual tool call candidate and fl
|
|||||||
const state = createStreamingState() as any;
|
const state = createStreamingState() as any;
|
||||||
const chunk1 = {
|
const chunk1 = {
|
||||||
responseId: "resp-test-flush-left",
|
responseId: "resp-test-flush-left",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -884,7 +884,7 @@ test("Gemini stream: splits mid-stream partial candidate but preserves tool call
|
|||||||
const state = createStreamingState() as any;
|
const state = createStreamingState() as any;
|
||||||
const chunk1 = {
|
const chunk1 = {
|
||||||
responseId: "resp-test-split-candidate",
|
responseId: "resp-test-split-candidate",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -932,7 +932,7 @@ test("Gemini stream: index mismatch regression test with zero-width characters i
|
|||||||
const result = geminiToOpenAIResponse(
|
const result = geminiToOpenAIResponse(
|
||||||
{
|
{
|
||||||
responseId: "resp-textual-tool-index-mismatch",
|
responseId: "resp-textual-tool-index-mismatch",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -964,7 +964,7 @@ test("Gemini stream: partial tool call with (empty) prefix check at chunk end do
|
|||||||
const state = createStreamingState();
|
const state = createStreamingState();
|
||||||
const chunk1 = {
|
const chunk1 = {
|
||||||
responseId: "resp-empty-leak",
|
responseId: "resp-empty-leak",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -1011,7 +1011,7 @@ test("Gemini stream: parses textual tool call that starts in a subsequent chunk
|
|||||||
const state = createStreamingState() as any;
|
const state = createStreamingState() as any;
|
||||||
const chunk1 = {
|
const chunk1 = {
|
||||||
responseId: "resp-test-after-prose",
|
responseId: "resp-test-after-prose",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -1060,7 +1060,7 @@ test("Gemini stream: checks lastParen before lastBracket when identifying partia
|
|||||||
// Имитируем чанк, который кончается на частичный "(empty)[Tool call:" маркер, например "(em"
|
// Имитируем чанк, который кончается на частичный "(empty)[Tool call:" маркер, например "(em"
|
||||||
const chunk1 = {
|
const chunk1 = {
|
||||||
responseId: "resp-test-empty-partial",
|
responseId: "resp-test-empty-partial",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
@@ -1187,7 +1187,7 @@ test("Gemini stream: partial textual tool call survives a reasoning-only chunk",
|
|||||||
geminiToOpenAIResponse(
|
geminiToOpenAIResponse(
|
||||||
{
|
{
|
||||||
responseId: "resp-interleave",
|
responseId: "resp-interleave",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [
|
candidates: [
|
||||||
{ content: { parts: [{ text: '[Tool call: terminal]\nArguments: {"command":"ls' }] } },
|
{ content: { parts: [{ text: '[Tool call: terminal]\nArguments: {"command":"ls' }] } },
|
||||||
],
|
],
|
||||||
@@ -1200,7 +1200,7 @@ test("Gemini stream: partial textual tool call survives a reasoning-only chunk",
|
|||||||
geminiToOpenAIResponse(
|
geminiToOpenAIResponse(
|
||||||
{
|
{
|
||||||
responseId: "resp-interleave",
|
responseId: "resp-interleave",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [{ content: { parts: [{ text: "<thinking>pondering</thinking>" }] } }],
|
candidates: [{ content: { parts: [{ text: "<thinking>pondering</thinking>" }] } }],
|
||||||
},
|
},
|
||||||
state
|
state
|
||||||
@@ -1221,7 +1221,7 @@ test("Gemini stream: partial textual tool call survives a reasoning-only chunk",
|
|||||||
geminiToOpenAIResponse(
|
geminiToOpenAIResponse(
|
||||||
{
|
{
|
||||||
responseId: "resp-interleave",
|
responseId: "resp-interleave",
|
||||||
modelVersion: "gemini-3.5-flash-low",
|
modelVersion: "gemini-3.7-flash-low",
|
||||||
candidates: [{ content: { parts: [{ text: '"}' }] }, finishReason: "STOP" }],
|
candidates: [{ content: { parts: [{ text: '"}' }] }, finishReason: "STOP" }],
|
||||||
},
|
},
|
||||||
state
|
state
|
||||||
|
|||||||
Reference in New Issue
Block a user