mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
fix(sse): use Gemini schema for Antigravity Claude (#2063)
Integrated into release/v3.8.0
This commit is contained in:
@@ -306,66 +306,64 @@ export class AntigravityExecutor extends BaseExecutor {
|
||||
? stripCloudCodeThinkingConfig(baseBody)
|
||||
: baseBody;
|
||||
|
||||
let transformedRequest;
|
||||
// Fix contents for Gemini-compatible Cloud Code requests via Antigravity.
|
||||
// Claude-branded Antigravity models use the same streamGenerateContent schema.
|
||||
const normalizedContents =
|
||||
normalizedBody.request?.contents?.map((c) => {
|
||||
let role = c.role;
|
||||
if (c.parts?.some((p) => p.functionResponse)) {
|
||||
role = "user";
|
||||
}
|
||||
|
||||
const hasFunctionCall = c.parts?.some((p) => p.functionCall) || false;
|
||||
|
||||
const parts =
|
||||
c.parts?.filter((p) => {
|
||||
if (typeof p.text === "string" && p.text === "") return false;
|
||||
if (p.functionCall && !p.functionCall.name) return false;
|
||||
|
||||
return !p.thought && (hasFunctionCall || !p.thoughtSignature);
|
||||
}) || [];
|
||||
return { ...c, role, parts };
|
||||
}) || [];
|
||||
|
||||
const contents = [];
|
||||
for (const c of normalizedContents) {
|
||||
if (!Array.isArray(c.parts) || c.parts.length === 0) continue;
|
||||
if (contents.length > 0 && contents[contents.length - 1].role === c.role) {
|
||||
contents[contents.length - 1].parts.push(...c.parts);
|
||||
} else {
|
||||
contents.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
const transformedRequest = {
|
||||
...normalizedBody.request,
|
||||
...(contents.length > 0 && { contents }),
|
||||
sessionId: normalizedBody.request?.sessionId || this.generateSessionId(),
|
||||
safetySettings: undefined,
|
||||
toolConfig:
|
||||
normalizedBody.request?.tools?.length > 0
|
||||
? { functionCallingConfig: { mode: "VALIDATED" } }
|
||||
: normalizedBody.request?.toolConfig,
|
||||
};
|
||||
|
||||
if (isClaude) {
|
||||
// Claude models on Vertex AI Cloud Code expect the native Anthropic payload
|
||||
// exactly as generated by openaiToClaudeRequestForAntigravity, without Gemini mappings.
|
||||
transformedRequest = {
|
||||
...normalizedBody.request,
|
||||
sessionId: normalizedBody.request?.sessionId || this.generateSessionId(),
|
||||
};
|
||||
} else {
|
||||
// Fix contents for Gemini models via Antigravity
|
||||
const normalizedContents =
|
||||
normalizedBody.request?.contents?.map((c) => {
|
||||
let role = c.role;
|
||||
if (c.parts?.some((p) => p.functionResponse)) {
|
||||
role = "user";
|
||||
}
|
||||
delete transformedRequest.messages;
|
||||
delete transformedRequest.system;
|
||||
delete transformedRequest.max_tokens;
|
||||
delete transformedRequest.stream;
|
||||
delete transformedRequest.temperature;
|
||||
}
|
||||
|
||||
const hasFunctionCall = c.parts?.some((p) => p.functionCall) || false;
|
||||
|
||||
const parts =
|
||||
c.parts?.filter((p) => {
|
||||
if (typeof p.text === "string" && p.text === "") return false;
|
||||
if (p.functionCall && !p.functionCall.name) return false;
|
||||
|
||||
return !p.thought && (hasFunctionCall || !p.thoughtSignature);
|
||||
}) || [];
|
||||
return { ...c, role, parts };
|
||||
}) || [];
|
||||
|
||||
const contents = [];
|
||||
for (const c of normalizedContents) {
|
||||
if (!Array.isArray(c.parts) || c.parts.length === 0) continue;
|
||||
if (contents.length > 0 && contents[contents.length - 1].role === c.role) {
|
||||
contents[contents.length - 1].parts.push(...c.parts);
|
||||
} else {
|
||||
contents.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
transformedRequest = {
|
||||
...normalizedBody.request,
|
||||
...(contents.length > 0 && { contents }),
|
||||
sessionId: normalizedBody.request?.sessionId || this.generateSessionId(),
|
||||
safetySettings: undefined,
|
||||
toolConfig:
|
||||
normalizedBody.request?.tools?.length > 0
|
||||
? { functionCallingConfig: { mode: "VALIDATED" } }
|
||||
: normalizedBody.request?.toolConfig,
|
||||
};
|
||||
|
||||
// Obfuscate sensitive client names in user content (e.g. "OpenCode", "Cursor")
|
||||
const requestContents = transformedRequest.contents;
|
||||
if (Array.isArray(requestContents)) {
|
||||
for (const msg of requestContents) {
|
||||
if (Array.isArray(msg.parts)) {
|
||||
for (const part of msg.parts) {
|
||||
if (typeof part.text === "string") {
|
||||
part.text = obfuscateSensitiveWords(part.text);
|
||||
}
|
||||
// Obfuscate sensitive client names in user content (e.g. "OpenCode", "Cursor")
|
||||
const requestContents = transformedRequest.contents;
|
||||
if (Array.isArray(requestContents)) {
|
||||
for (const msg of requestContents) {
|
||||
if (Array.isArray(msg.parts)) {
|
||||
for (const part of msg.parts) {
|
||||
if (typeof part.text === "string") {
|
||||
part.text = obfuscateSensitiveWords(part.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { FORMATS } from "../formats.ts";
|
||||
import { DEFAULT_THINKING_GEMINI_SIGNATURE } from "../../config/defaultThinkingSignature.ts";
|
||||
import { ANTIGRAVITY_DEFAULT_SYSTEM } from "../../config/constants.ts";
|
||||
import { resolveGeminiThoughtSignature } from "../../services/geminiThoughtSignatureStore.ts";
|
||||
import { openaiToClaudeRequestForAntigravity } from "./openai-to-claude.ts";
|
||||
import {
|
||||
capMaxOutputTokens,
|
||||
capThinkingBudget,
|
||||
@@ -481,62 +480,15 @@ function getAntigravityClaudeOutputTokens(body: Record<string, unknown>): number
|
||||
return ANTIGRAVITY_CLAUDE_MAX_OUTPUT_TOKENS;
|
||||
}
|
||||
|
||||
function wrapInCloudCodeEnvelopeForClaude(
|
||||
model,
|
||||
claudeRequest,
|
||||
credentials = null,
|
||||
sourceBody = {}
|
||||
) {
|
||||
let projectId = credentials?.projectId;
|
||||
|
||||
if (!projectId) {
|
||||
console.warn(
|
||||
`[OmniRoute] Antigravity/Claude account is missing projectId. ` +
|
||||
`Attempting request with empty project — reconnect OAuth to resolve.`
|
||||
);
|
||||
projectId = "";
|
||||
}
|
||||
|
||||
const cleanModel = model.includes("/") ? model.split("/").pop()! : model;
|
||||
|
||||
// Keep Antigravity's default and caller-provided system rules
|
||||
let systemText = ANTIGRAVITY_DEFAULT_SYSTEM;
|
||||
if (claudeRequest.system) {
|
||||
if (Array.isArray(claudeRequest.system)) {
|
||||
const texts = claudeRequest.system.map((b) => b.text).filter(Boolean);
|
||||
if (texts.length > 0) systemText += "\n" + texts.join("\n");
|
||||
} else if (typeof claudeRequest.system === "string") {
|
||||
systemText += "\n" + claudeRequest.system;
|
||||
}
|
||||
}
|
||||
|
||||
const envelope: CloudCodeEnvelope = {
|
||||
project: projectId,
|
||||
model: cleanModel,
|
||||
userAgent: "antigravity",
|
||||
requestId: `agent-${generateUUID()}`,
|
||||
requestType: "agent",
|
||||
request: {
|
||||
...claudeRequest,
|
||||
system: systemText,
|
||||
max_tokens: getAntigravityClaudeOutputTokens(sourceBody),
|
||||
sessionId: generateSessionId(),
|
||||
},
|
||||
};
|
||||
|
||||
return envelope;
|
||||
}
|
||||
|
||||
// OpenAI -> Antigravity (Sandbox Cloud Code with wrapper)
|
||||
export function openaiToAntigravityRequest(model, body, stream, credentials = null) {
|
||||
const isClaude = model.toLowerCase().includes("claude");
|
||||
const geminiCLI = openaiToGeminiCLIRequest(model, body, stream);
|
||||
|
||||
if (isClaude) {
|
||||
const claudeRequest = openaiToClaudeRequestForAntigravity(model, body, stream);
|
||||
return wrapInCloudCodeEnvelopeForClaude(model, claudeRequest, credentials, body);
|
||||
geminiCLI.generationConfig.maxOutputTokens = getAntigravityClaudeOutputTokens(body);
|
||||
}
|
||||
|
||||
const geminiCLI = openaiToGeminiCLIRequest(model, body, stream);
|
||||
return wrapInCloudCodeEnvelope(model, geminiCLI, credentials, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,26 +56,43 @@ test("AntigravityExecutor.transformRequest resolves alias models before dispatch
|
||||
{ projectId: "project-1" }
|
||||
);
|
||||
|
||||
if (result instanceof Response) throw new Error("Unexpected Response from transformRequest");
|
||||
assert.equal(result.model, "gemini-3.1-pro-high");
|
||||
});
|
||||
|
||||
test("AntigravityExecutor.transformRequest keeps Claude bridge output cap and strips unsupported thinking", async () => {
|
||||
test("AntigravityExecutor.transformRequest sends Claude through Gemini-compatible Cloud Code schema", async () => {
|
||||
const executor = new AntigravityExecutor();
|
||||
const bridged = openaiToAntigravityRequest(
|
||||
"claude-sonnet-4-6",
|
||||
"claude-opus-4-6-thinking",
|
||||
{
|
||||
messages: [{ role: "user", content: "Hello" }],
|
||||
max_completion_tokens: 32_000,
|
||||
temperature: 0.5,
|
||||
reasoning_effort: "high",
|
||||
},
|
||||
true,
|
||||
{ projectId: "project-1" } as any
|
||||
);
|
||||
|
||||
const result = await executor.transformRequest("antigravity/claude-sonnet-4-6", bridged, true, {
|
||||
projectId: "project-1",
|
||||
});
|
||||
const result = await executor.transformRequest(
|
||||
"antigravity/claude-opus-4-6-thinking",
|
||||
bridged,
|
||||
true,
|
||||
{
|
||||
projectId: "project-1",
|
||||
}
|
||||
);
|
||||
|
||||
assert.equal(result.request.max_tokens, 16_384);
|
||||
assert.equal(result.request.thinking, undefined);
|
||||
if (result instanceof Response) throw new Error("Unexpected Response from transformRequest");
|
||||
const request = result.request as any;
|
||||
assert.deepEqual(request.contents, [{ role: "user", parts: [{ text: "Hello" }] }]);
|
||||
assert.equal(request.generationConfig.maxOutputTokens, 16_384);
|
||||
assert.equal(request.generationConfig.temperature, 0.5);
|
||||
assert.equal(request.messages, undefined);
|
||||
assert.equal(request.system, undefined);
|
||||
assert.equal(request.max_tokens, undefined);
|
||||
assert.equal(request.stream, undefined);
|
||||
assert.equal(request.temperature, undefined);
|
||||
assert.equal(request.thinking, undefined);
|
||||
assert.equal(request.generationConfig.thinkingConfig, undefined);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import test from "node:test";
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { AntigravityExecutor } from "../../open-sse/executors/antigravity.ts";
|
||||
@@ -9,7 +9,11 @@ import {
|
||||
seedAntigravityVersionCache,
|
||||
} from "../../open-sse/services/antigravityVersion.ts";
|
||||
|
||||
async function withEnv(name, value, fn) {
|
||||
async function withEnv<T>(
|
||||
name: string,
|
||||
value: string | undefined,
|
||||
fn: () => T | Promise<T>
|
||||
): Promise<T> {
|
||||
const previous = process.env[name];
|
||||
if (value === undefined) {
|
||||
delete process.env[name];
|
||||
@@ -94,6 +98,7 @@ test("AntigravityExecutor.transformRequest normalizes model, project and content
|
||||
projectId: "project-1",
|
||||
});
|
||||
|
||||
if (result instanceof Response) throw new Error("Unexpected Response from transformRequest");
|
||||
assert.equal(result.project, "project-1");
|
||||
assert.equal(result.model, "gemini-3.1-pro-low");
|
||||
assert.deepEqual(Object.keys(result), [
|
||||
@@ -132,8 +137,12 @@ test("AntigravityExecutor.transformRequest strips thinking config for Cloud Code
|
||||
projectId: "project-1",
|
||||
});
|
||||
|
||||
if (result instanceof Response) throw new Error("Unexpected Response from transformRequest");
|
||||
const generationConfig = result.request.generationConfig as {
|
||||
thinkingConfig?: { thinkingBudget?: number; includeThoughts?: boolean };
|
||||
};
|
||||
assert.equal(result.reasoning_effort, undefined);
|
||||
assert.equal(result.request.generationConfig.thinkingConfig, undefined);
|
||||
assert.equal(generationConfig.thinkingConfig, undefined);
|
||||
});
|
||||
|
||||
test("AntigravityExecutor.transformRequest preserves thinking config for supported Gemini models", async () => {
|
||||
@@ -154,8 +163,12 @@ test("AntigravityExecutor.transformRequest preserves thinking config for support
|
||||
projectId: "project-1",
|
||||
});
|
||||
|
||||
assert.equal(result.request.generationConfig.thinkingConfig.thinkingBudget, 8192);
|
||||
assert.equal(result.request.generationConfig.thinkingConfig.includeThoughts, true);
|
||||
if (result instanceof Response) throw new Error("Unexpected Response from transformRequest");
|
||||
const generationConfig = result.request.generationConfig as {
|
||||
thinkingConfig: { thinkingBudget?: number; includeThoughts?: boolean };
|
||||
};
|
||||
assert.equal(generationConfig.thinkingConfig.thinkingBudget, 8192);
|
||||
assert.equal(generationConfig.thinkingConfig.includeThoughts, true);
|
||||
});
|
||||
|
||||
test("AntigravityExecutor.transformRequest tolerates a missing body when projectId is present", async () => {
|
||||
@@ -165,6 +178,7 @@ test("AntigravityExecutor.transformRequest tolerates a missing body when project
|
||||
projectId: "project-1",
|
||||
});
|
||||
|
||||
if (result instanceof Response) throw new Error("Unexpected Response from transformRequest");
|
||||
assert.equal(result.project, "project-1");
|
||||
assert.equal(result.model, "gemini-3.1-pro-low");
|
||||
assert.ok(result.request.sessionId);
|
||||
@@ -202,6 +216,7 @@ test("AntigravityExecutor.transformRequest allows body project overrides when th
|
||||
{ projectId: "credential-project" }
|
||||
);
|
||||
|
||||
if (result instanceof Response) throw new Error("Unexpected Response from transformRequest");
|
||||
assert.equal(result.project, "body-project");
|
||||
assert.equal(result.request.sessionId, "session-fixed");
|
||||
assert.equal(result.model, "gemini-2.5-pro");
|
||||
@@ -393,7 +408,7 @@ test("AntigravityExecutor.execute auto-retries short 429 responses and collects
|
||||
);
|
||||
};
|
||||
globalThis.setTimeout = ((callback) => {
|
||||
callback();
|
||||
(callback as () => void)();
|
||||
return 0;
|
||||
}) as typeof setTimeout;
|
||||
|
||||
@@ -504,7 +519,7 @@ test("AntigravityExecutor.execute applies CLI fingerprint when enabled", async (
|
||||
}
|
||||
});
|
||||
|
||||
test("AntigravityExecutor.transformRequest bypasses Gemini contents mapping for claude models", async () => {
|
||||
test("AntigravityExecutor.transformRequest maps Claude models through Gemini contents schema", async () => {
|
||||
const executor = new AntigravityExecutor();
|
||||
const body = {
|
||||
project: "project-1",
|
||||
@@ -513,12 +528,17 @@ test("AntigravityExecutor.transformRequest bypasses Gemini contents mapping for
|
||||
requestId: "agent-123",
|
||||
requestType: "agent",
|
||||
request: {
|
||||
messages: [{ role: "user", content: [{ type: "text", text: "Hello" }] }],
|
||||
system: [{ type: "text", text: "System prompt" }],
|
||||
contents: [{ role: "user", parts: [{ text: "Hello" }] }],
|
||||
systemInstruction: { role: "system", parts: [{ text: "System prompt" }] },
|
||||
generationConfig: {
|
||||
temperature: 1,
|
||||
maxOutputTokens: 16384,
|
||||
},
|
||||
messages: [{ role: "user", content: [{ type: "text", text: "Legacy Anthropic field" }] }],
|
||||
system: [{ type: "text", text: "Legacy system field" }],
|
||||
max_tokens: 16384,
|
||||
stream: true,
|
||||
temperature: 1,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -530,10 +550,19 @@ test("AntigravityExecutor.transformRequest bypasses Gemini contents mapping for
|
||||
assert.equal(result.model, "claude-sonnet-4-6");
|
||||
assert.equal(result.requestType, "agent");
|
||||
assert.ok(result.request.sessionId);
|
||||
assert.deepEqual(result.request.messages, [
|
||||
{ role: "user", content: [{ type: "text", text: "Hello" }] },
|
||||
]);
|
||||
assert.deepEqual(result.request.system, [{ type: "text", text: "System prompt" }]);
|
||||
assert.equal(result.request.contents, undefined);
|
||||
assert.deepEqual(result.request.contents, [{ role: "user", parts: [{ text: "Hello" }] }]);
|
||||
assert.deepEqual(result.request.systemInstruction, {
|
||||
role: "system",
|
||||
parts: [{ text: "System prompt" }],
|
||||
});
|
||||
assert.deepEqual(result.request.generationConfig, {
|
||||
temperature: 1,
|
||||
maxOutputTokens: 16384,
|
||||
});
|
||||
assert.equal(result.request.messages, undefined);
|
||||
assert.equal(result.request.system, undefined);
|
||||
assert.equal(result.request.max_tokens, undefined);
|
||||
assert.equal(result.request.stream, undefined);
|
||||
assert.equal(result.request.temperature, undefined);
|
||||
assert.equal(result.request.toolConfig, undefined);
|
||||
});
|
||||
|
||||
@@ -616,7 +616,7 @@ test("OpenAI -> Antigravity wraps Gemini requests in a Cloud Code envelope", ()
|
||||
});
|
||||
});
|
||||
|
||||
test("OpenAI -> Antigravity uses the Claude bridge for Claude-family models", () => {
|
||||
test("OpenAI -> Antigravity maps Claude-family models to Gemini-compatible schema", () => {
|
||||
const result = openaiToAntigravityRequest(
|
||||
"claude-3-7-sonnet",
|
||||
{
|
||||
@@ -659,28 +659,32 @@ test("OpenAI -> Antigravity uses the Claude bridge for Claude-family models", ()
|
||||
|
||||
assert.equal(result.project, "proj-claude");
|
||||
assert.equal(result.userAgent, "antigravity");
|
||||
assert.ok((result as any).request?.system.includes(ANTIGRAVITY_DEFAULT_SYSTEM));
|
||||
assert.ok((result as any).request?.system.includes("Project rules"));
|
||||
assert.equal((result as any).request?.max_tokens, 16384);
|
||||
assert.equal(result.request.systemInstruction.parts[0].text, ANTIGRAVITY_DEFAULT_SYSTEM);
|
||||
assert.equal(result.request.systemInstruction.parts[1].text, "Project rules");
|
||||
assert.equal((result as any).request?.generationConfig.maxOutputTokens, 16384);
|
||||
assert.equal((result as any).request?.messages, undefined);
|
||||
assert.equal((result as any).request?.system, undefined);
|
||||
assert.equal((result as any).request?.max_tokens, undefined);
|
||||
assert.equal((result as any).request?.stream, undefined);
|
||||
|
||||
const modelTurn = result.request.messages.find(
|
||||
(msg) => msg.role === "assistant" && msg.content.some((block) => block.type === "tool_use")
|
||||
const modelTurn = result.request.contents.find(
|
||||
(content) => content.role === "model" && content.parts.some((part) => part.functionCall)
|
||||
);
|
||||
assert.ok(modelTurn, "expected a Claude-bridged model turn");
|
||||
const bridgeFunctionCall = modelTurn.content.find((block) => block.type === "tool_use");
|
||||
assert.ok(modelTurn, "expected a Gemini-compatible model turn");
|
||||
const bridgeFunctionCall = getFunctionCall(modelTurn.parts[0]);
|
||||
assert.equal(bridgeFunctionCall.name, "read_file");
|
||||
assert.deepEqual(bridgeFunctionCall.input, { path: "/tmp/demo" });
|
||||
assert.deepEqual(bridgeFunctionCall.args, { path: "/tmp/demo" });
|
||||
|
||||
const toolTurn = result.request.messages.find(
|
||||
(msg) => msg.role === "user" && msg.content.some((block) => block.type === "tool_result")
|
||||
const toolTurn = result.request.contents.find(
|
||||
(content) => content.role === "user" && content.parts.some((part) => part.functionResponse)
|
||||
);
|
||||
assert.ok(toolTurn, "expected a Claude-bridged tool response turn");
|
||||
const toolResultBlock = toolTurn.content.find((block) => block.type === "tool_result");
|
||||
assert.equal(toolResultBlock.tool_use_id, "call_1");
|
||||
assert.equal((result as any).request?.tools[0].name, "read_file");
|
||||
assert.ok(toolTurn, "expected a Gemini-compatible tool response turn");
|
||||
const toolResultBlock = getFunctionResponse(toolTurn.parts[0]);
|
||||
assert.equal(toolResultBlock.id, "call_1");
|
||||
assert.equal((result as any).request?.tools[0].functionDeclarations[0].name, "read_file");
|
||||
});
|
||||
|
||||
test("OpenAI -> Antigravity Claude bridge preserves tool names (Claude supports longer names)", () => {
|
||||
test("OpenAI -> Antigravity Claude path sanitizes tool names for Gemini schema", () => {
|
||||
const longToolName =
|
||||
"ns:mcp__filesystem__read_multiple_files_with_validation_and_metadata_bundle";
|
||||
const result = openaiToAntigravityRequest(
|
||||
@@ -722,26 +726,31 @@ test("OpenAI -> Antigravity Claude bridge preserves tool names (Claude supports
|
||||
{ projectId: "proj-claude-map" } as any
|
||||
);
|
||||
|
||||
const sanitizedToolName = (result as any).request?.tools[0].name;
|
||||
assert.equal(sanitizedToolName, longToolName);
|
||||
const sanitizedToolName = (result as any).request?.tools[0].functionDeclarations[0].name;
|
||||
assert.notEqual(sanitizedToolName, longToolName);
|
||||
assert.match(
|
||||
sanitizedToolName,
|
||||
/^mcp_filesystem_read_multiple_files_with_validation_and__\w{8}$/
|
||||
);
|
||||
|
||||
const modelTurn = result.request.messages.find(
|
||||
(msg) => msg.role === "assistant" && msg.content.some((block) => block.type === "tool_use")
|
||||
const modelTurn = result.request.contents.find(
|
||||
(content) => content.role === "model" && content.parts.some((part) => part.functionCall)
|
||||
);
|
||||
assert.ok(modelTurn, "expected a model turn");
|
||||
const toolUseBlock = modelTurn.content.find((block) => block.type === "tool_use");
|
||||
const toolUseBlock = getFunctionCall(modelTurn.parts[0]);
|
||||
assert.equal(toolUseBlock.name, sanitizedToolName);
|
||||
|
||||
const toolTurn = result.request.messages.find(
|
||||
(msg) => msg.role === "user" && msg.content.some((block) => block.type === "tool_result")
|
||||
const toolTurn = result.request.contents.find(
|
||||
(content) => content.role === "user" && content.parts.some((part) => part.functionResponse)
|
||||
);
|
||||
assert.ok(toolTurn, "expected a tool response turn");
|
||||
const toolResultBlock = toolTurn.content.find((block) => block.type === "tool_result");
|
||||
assert.equal(toolResultBlock.tool_use_id, "call_long_2");
|
||||
assert.ok(toolResultBlock.content.includes("ok"));
|
||||
const toolResultBlock = getFunctionResponse(toolTurn.parts[0]);
|
||||
assert.equal(toolResultBlock.id, "call_long_2");
|
||||
assert.equal(toolResultBlock.name, sanitizedToolName);
|
||||
assert.deepEqual(toolResultBlock.response, { result: { ok: true } });
|
||||
});
|
||||
|
||||
test("OpenAI -> Antigravity Claude bridge applies Antigravity output cap but forwards thinking", () => {
|
||||
test("OpenAI -> Antigravity Claude path applies output cap in generationConfig", () => {
|
||||
const result = openaiToAntigravityRequest(
|
||||
"claude-3-7-sonnet",
|
||||
{
|
||||
@@ -753,11 +762,16 @@ test("OpenAI -> Antigravity Claude bridge applies Antigravity output cap but for
|
||||
{ projectId: "proj-claude-thinking" } as any
|
||||
);
|
||||
|
||||
assert.equal((result as any).request?.max_tokens, 16384);
|
||||
assert.deepEqual((result as any).request?.thinking, { type: "enabled", budget_tokens: 131072 });
|
||||
assert.equal((result as any).request?.generationConfig.maxOutputTokens, 16384);
|
||||
assert.deepEqual((result as any).request?.generationConfig.thinkingConfig, {
|
||||
thinkingBudget: 32768,
|
||||
includeThoughts: true,
|
||||
});
|
||||
assert.equal((result as any).request?.max_tokens, undefined);
|
||||
assert.equal((result as any).request?.thinking, undefined);
|
||||
});
|
||||
|
||||
test("OpenAI -> Antigravity Claude bridge preserves lower requested output despite reasoning effort", () => {
|
||||
test("OpenAI -> Antigravity Claude path preserves lower requested output", () => {
|
||||
const result = openaiToAntigravityRequest(
|
||||
"claude-3-7-sonnet",
|
||||
{
|
||||
@@ -769,6 +783,11 @@ test("OpenAI -> Antigravity Claude bridge preserves lower requested output despi
|
||||
{ projectId: "proj-claude-short" } as any
|
||||
);
|
||||
|
||||
assert.equal((result as any).request?.max_tokens, 1000);
|
||||
assert.deepEqual((result as any).request?.thinking, { type: "enabled", budget_tokens: 131072 });
|
||||
assert.equal((result as any).request?.generationConfig.maxOutputTokens, 1000);
|
||||
assert.deepEqual((result as any).request?.generationConfig.thinkingConfig, {
|
||||
thinkingBudget: 32768,
|
||||
includeThoughts: true,
|
||||
});
|
||||
assert.equal((result as any).request?.max_tokens, undefined);
|
||||
assert.equal((result as any).request?.thinking, undefined);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user