mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
Fix Antigravity
This commit is contained in:
+1
-1
@@ -56,6 +56,6 @@ cursor/*
|
|||||||
PUBLIC.md
|
PUBLIC.md
|
||||||
scripts/*
|
scripts/*
|
||||||
Thanks.md
|
Thanks.md
|
||||||
package.json
|
|
||||||
PUBLIC.en.md
|
PUBLIC.en.md
|
||||||
PR/*
|
PR/*
|
||||||
|
package-lock.json
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# v0.2.21 (2026-01-12)
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
- Update ReadMe
|
||||||
|
- Fix bug **antigravity**
|
||||||
|
|
||||||
@@ -2,6 +2,8 @@ import crypto from "crypto";
|
|||||||
import { BaseExecutor } from "./base.js";
|
import { BaseExecutor } from "./base.js";
|
||||||
import { PROVIDERS, OAUTH_ENDPOINTS } from "../config/constants.js";
|
import { PROVIDERS, OAUTH_ENDPOINTS } from "../config/constants.js";
|
||||||
|
|
||||||
|
const MAX_RETRY_AFTER_MS = 5000;
|
||||||
|
|
||||||
export class AntigravityExecutor extends BaseExecutor {
|
export class AntigravityExecutor extends BaseExecutor {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("antigravity", PROVIDERS.antigravity);
|
super("antigravity", PROVIDERS.antigravity);
|
||||||
@@ -26,6 +28,15 @@ export class AntigravityExecutor extends BaseExecutor {
|
|||||||
transformRequest(model, body, stream, credentials) {
|
transformRequest(model, body, stream, credentials) {
|
||||||
const projectId = credentials?.projectId || this.generateProjectId();
|
const projectId = credentials?.projectId || this.generateProjectId();
|
||||||
|
|
||||||
|
const transformedRequest = {
|
||||||
|
...body.request,
|
||||||
|
sessionId: body.request?.sessionId || this.generateSessionId(),
|
||||||
|
safetySettings: undefined,
|
||||||
|
toolConfig: body.request?.tools?.length > 0
|
||||||
|
? { functionCallingConfig: { mode: "VALIDATED" } }
|
||||||
|
: body.request?.toolConfig
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...body,
|
...body,
|
||||||
project: projectId,
|
project: projectId,
|
||||||
@@ -33,14 +44,7 @@ export class AntigravityExecutor extends BaseExecutor {
|
|||||||
userAgent: "antigravity",
|
userAgent: "antigravity",
|
||||||
requestType: "agent",
|
requestType: "agent",
|
||||||
requestId: `agent-${crypto.randomUUID()}`,
|
requestId: `agent-${crypto.randomUUID()}`,
|
||||||
request: {
|
request: transformedRequest
|
||||||
...body.request,
|
|
||||||
sessionId: body.request?.sessionId || this.generateSessionId(),
|
|
||||||
safetySettings: undefined,
|
|
||||||
toolConfig: body.request?.tools?.length > 0
|
|
||||||
? { functionCallingConfig: { mode: "VALIDATED" } }
|
|
||||||
: body.request?.toolConfig
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +89,105 @@ export class AntigravityExecutor extends BaseExecutor {
|
|||||||
generateSessionId() {
|
generateSessionId() {
|
||||||
return `-${Math.floor(Math.random() * 9_000_000_000_000_000_000)}`;
|
return `-${Math.floor(Math.random() * 9_000_000_000_000_000_000)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parseRetryHeaders(headers) {
|
||||||
|
console.log("🚀 ~ AntigravityExecutor ~ parseRetryHeaders ~ headers:", headers)
|
||||||
|
if (!headers?.get) return null;
|
||||||
|
|
||||||
|
const retryAfter = headers.get('retry-after');
|
||||||
|
if (retryAfter) {
|
||||||
|
const seconds = parseInt(retryAfter, 10);
|
||||||
|
if (!isNaN(seconds) && seconds > 0) return seconds * 1000;
|
||||||
|
|
||||||
|
const date = new Date(retryAfter);
|
||||||
|
if (!isNaN(date.getTime())) {
|
||||||
|
const diff = date.getTime() - Date.now();
|
||||||
|
return diff > 0 ? diff : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetAfter = headers.get('x-ratelimit-reset-after');
|
||||||
|
if (resetAfter) {
|
||||||
|
const seconds = parseInt(resetAfter, 10);
|
||||||
|
if (!isNaN(seconds) && seconds > 0) return seconds * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetTimestamp = headers.get('x-ratelimit-reset');
|
||||||
|
if (resetTimestamp) {
|
||||||
|
const ts = parseInt(resetTimestamp, 10) * 1000;
|
||||||
|
const diff = ts - Date.now();
|
||||||
|
return diff > 0 ? diff : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute({ model, body, stream, credentials, signal, log }) {
|
||||||
|
const fallbackCount = this.getFallbackCount();
|
||||||
|
let lastError = null;
|
||||||
|
let lastStatus = 0;
|
||||||
|
const MAX_AUTO_RETRIES = 2;
|
||||||
|
|
||||||
|
for (let urlIndex = 0; urlIndex < fallbackCount; urlIndex++) {
|
||||||
|
const url = this.buildUrl(model, stream, urlIndex);
|
||||||
|
const headers = this.buildHeaders(credentials, stream);
|
||||||
|
const transformedBody = this.transformRequest(model, body, stream, credentials);
|
||||||
|
let retryAttempts = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify(transformedBody),
|
||||||
|
signal
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status === 429 || response.status === 503) {
|
||||||
|
const retryMs = this.parseRetryHeaders(response.headers);
|
||||||
|
|
||||||
|
if (retryMs && retryMs <= MAX_RETRY_AFTER_MS) {
|
||||||
|
log?.debug?.("RETRY", `${response.status} with Retry-After: ${Math.ceil(retryMs/1000)}s, waiting...`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, retryMs));
|
||||||
|
urlIndex--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto retry only for 429 when retryMs is 0 or undefined
|
||||||
|
if (response.status === 429 && (!retryMs || retryMs === 0) && retryAttempts < MAX_AUTO_RETRIES) {
|
||||||
|
retryAttempts++;
|
||||||
|
log?.debug?.("RETRY", `429 auto retry ${retryAttempts}/${MAX_AUTO_RETRIES} after 1s`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
urlIndex--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
log?.debug?.("RETRY", `${response.status}, Retry-After ${retryMs ? `too long (${Math.ceil(retryMs/1000)}s)` : 'missing'}, trying fallback`);
|
||||||
|
lastStatus = response.status;
|
||||||
|
|
||||||
|
if (urlIndex + 1 < fallbackCount) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.shouldRetry(response.status, urlIndex)) {
|
||||||
|
log?.debug?.("RETRY", `${response.status} on ${url}, trying fallback ${urlIndex + 1}`);
|
||||||
|
lastStatus = response.status;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { response, url, headers, transformedBody };
|
||||||
|
} catch (error) {
|
||||||
|
lastError = error;
|
||||||
|
if (urlIndex + 1 < fallbackCount) {
|
||||||
|
log?.debug?.("RETRY", `Error on ${url}, trying fallback ${urlIndex + 1}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw lastError || new Error(`All ${fallbackCount} URLs failed with status ${lastStatus}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AntigravityExecutor;
|
export default AntigravityExecutor;
|
||||||
|
|||||||
@@ -252,6 +252,37 @@ function tryParseJSON(str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenAI -> Claude format for Antigravity (without system prompt modifications)
|
||||||
|
function openaiToClaudeRequestForAntigravity(model, body, stream) {
|
||||||
|
const result = openaiToClaudeRequest(model, body, stream);
|
||||||
|
|
||||||
|
// Remove Claude Code system prompt, keep only user's system messages
|
||||||
|
if (result.system && Array.isArray(result.system)) {
|
||||||
|
result.system = result.system.filter(block =>
|
||||||
|
!block.text || !block.text.includes("You are Claude Code")
|
||||||
|
);
|
||||||
|
if (result.system.length === 0) {
|
||||||
|
delete result.system;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Un-capitalize tool names (Antigravity doesn't require capitalized names)
|
||||||
|
if (result.tools && Array.isArray(result.tools)) {
|
||||||
|
result.tools = result.tools.map(tool => {
|
||||||
|
if (tool.name) {
|
||||||
|
const originalName = tool.name.charAt(0).toLowerCase() + tool.name.slice(1);
|
||||||
|
return { ...tool, name: originalName };
|
||||||
|
}
|
||||||
|
return tool;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export for use in other translators
|
||||||
|
export { openaiToClaudeRequestForAntigravity };
|
||||||
|
|
||||||
// Register
|
// Register
|
||||||
register(FORMATS.OPENAI, FORMATS.CLAUDE, openaiToClaudeRequest, null);
|
register(FORMATS.OPENAI, FORMATS.CLAUDE, openaiToClaudeRequest, null);
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { register } from "../index.js";
|
|||||||
import { FORMATS } from "../formats.js";
|
import { FORMATS } from "../formats.js";
|
||||||
import { DEFAULT_THINKING_GEMINI_SIGNATURE } from "../../config/defaultThinkingSignature.js";
|
import { DEFAULT_THINKING_GEMINI_SIGNATURE } from "../../config/defaultThinkingSignature.js";
|
||||||
import { ANTIGRAVITY_DEFAULT_SYSTEM } from "../../config/constants.js";
|
import { ANTIGRAVITY_DEFAULT_SYSTEM } from "../../config/constants.js";
|
||||||
|
import { openaiToClaudeRequestForAntigravity } from "./openai-to-claude.js";
|
||||||
|
|
||||||
function generateUUID() {
|
function generateUUID() {
|
||||||
return crypto.randomUUID();
|
return crypto.randomUUID();
|
||||||
@@ -84,7 +85,7 @@ function openaiToGeminiBase(model, body, stream) {
|
|||||||
}
|
}
|
||||||
} else if (role === "assistant") {
|
} else if (role === "assistant") {
|
||||||
const parts = [];
|
const parts = [];
|
||||||
|
|
||||||
if (content) {
|
if (content) {
|
||||||
const text = typeof content === "string" ? content : extractTextContent(content);
|
const text = typeof content === "string" ? content : extractTextContent(content);
|
||||||
if (text) {
|
if (text) {
|
||||||
@@ -96,7 +97,7 @@ function openaiToGeminiBase(model, body, stream) {
|
|||||||
const toolCallIds = [];
|
const toolCallIds = [];
|
||||||
for (const tc of msg.tool_calls) {
|
for (const tc of msg.tool_calls) {
|
||||||
if (tc.type !== "function") continue;
|
if (tc.type !== "function") continue;
|
||||||
|
|
||||||
const args = tryParseJSON(tc.function?.arguments || "{}");
|
const args = tryParseJSON(tc.function?.arguments || "{}");
|
||||||
parts.push({
|
parts.push({
|
||||||
thoughtSignature: DEFAULT_THINKING_GEMINI_SIGNATURE,
|
thoughtSignature: DEFAULT_THINKING_GEMINI_SIGNATURE,
|
||||||
@@ -113,37 +114,43 @@ function openaiToGeminiBase(model, body, stream) {
|
|||||||
result.contents.push({ role: "model", parts });
|
result.contents.push({ role: "model", parts });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append function responses
|
// Check if there are actual tool responses in the next messages
|
||||||
const toolParts = [];
|
const hasActualResponses = toolCallIds.some(fid => toolResponses[fid]);
|
||||||
for (const fid of toolCallIds) {
|
|
||||||
let name = tcID2Name[fid];
|
if (hasActualResponses) {
|
||||||
if (!name) {
|
const toolParts = [];
|
||||||
const idParts = fid.split("-");
|
for (const fid of toolCallIds) {
|
||||||
if (idParts.length > 2) {
|
if (!toolResponses[fid]) continue;
|
||||||
name = idParts.slice(0, -2).join("-");
|
|
||||||
} else {
|
let name = tcID2Name[fid];
|
||||||
name = fid;
|
if (!name) {
|
||||||
|
const idParts = fid.split("-");
|
||||||
|
if (idParts.length > 2) {
|
||||||
|
name = idParts.slice(0, -2).join("-");
|
||||||
|
} else {
|
||||||
|
name = fid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
let resp = toolResponses[fid];
|
||||||
let resp = toolResponses[fid] || "{}";
|
let parsedResp = tryParseJSON(resp);
|
||||||
let parsedResp = tryParseJSON(resp);
|
if (parsedResp === null) {
|
||||||
if (parsedResp === null) {
|
parsedResp = { result: resp };
|
||||||
parsedResp = { result: resp };
|
} else if (typeof parsedResp !== "object") {
|
||||||
} else if (typeof parsedResp !== "object") {
|
parsedResp = { result: parsedResp };
|
||||||
parsedResp = { result: parsedResp };
|
|
||||||
}
|
|
||||||
|
|
||||||
toolParts.push({
|
|
||||||
functionResponse: {
|
|
||||||
id: fid,
|
|
||||||
name: name,
|
|
||||||
response: { result: parsedResp }
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
toolParts.push({
|
||||||
if (toolParts.length > 0) {
|
functionResponse: {
|
||||||
result.contents.push({ role: "user", parts: toolParts });
|
id: fid,
|
||||||
|
name: name,
|
||||||
|
response: { result: parsedResp }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (toolParts.length > 0) {
|
||||||
|
result.contents.push({ role: "user", parts: toolParts });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (parts.length > 0) {
|
} else if (parts.length > 0) {
|
||||||
result.contents.push({ role: "model", parts });
|
result.contents.push({ role: "model", parts });
|
||||||
@@ -156,7 +163,16 @@ function openaiToGeminiBase(model, body, stream) {
|
|||||||
if (body.tools && Array.isArray(body.tools) && body.tools.length > 0) {
|
if (body.tools && Array.isArray(body.tools) && body.tools.length > 0) {
|
||||||
const functionDeclarations = [];
|
const functionDeclarations = [];
|
||||||
for (const t of body.tools) {
|
for (const t of body.tools) {
|
||||||
if (t.type === "function" && t.function) {
|
// Check if already in Anthropic/Claude format (no type field, direct name/description/input_schema)
|
||||||
|
if (t.name && t.input_schema) {
|
||||||
|
functionDeclarations.push({
|
||||||
|
name: t.name,
|
||||||
|
description: t.description || "",
|
||||||
|
parameters: t.input_schema || { type: "object", properties: {} }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// OpenAI format
|
||||||
|
else if (t.type === "function" && t.function) {
|
||||||
const fn = t.function;
|
const fn = t.function;
|
||||||
functionDeclarations.push({
|
functionDeclarations.push({
|
||||||
name: fn.name,
|
name: fn.name,
|
||||||
@@ -165,7 +181,7 @@ function openaiToGeminiBase(model, body, stream) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functionDeclarations.length > 0) {
|
if (functionDeclarations.length > 0) {
|
||||||
result.tools = [{ functionDeclarations }];
|
result.tools = [{ functionDeclarations }];
|
||||||
}
|
}
|
||||||
@@ -183,7 +199,7 @@ function openaiToGeminiRequest(model, body, stream) {
|
|||||||
function openaiToGeminiCLIRequest(model, body, stream) {
|
function openaiToGeminiCLIRequest(model, body, stream) {
|
||||||
const gemini = openaiToGeminiBase(model, body, stream);
|
const gemini = openaiToGeminiBase(model, body, stream);
|
||||||
const isClaude = model.toLowerCase().includes("claude");
|
const isClaude = model.toLowerCase().includes("claude");
|
||||||
|
|
||||||
// Add thinking config for CLI
|
// Add thinking config for CLI
|
||||||
if (body.reasoning_effort) {
|
if (body.reasoning_effort) {
|
||||||
const budgetMap = { low: 1024, medium: 8192, high: 32768 };
|
const budgetMap = { low: 1024, medium: 8192, high: 32768 };
|
||||||
@@ -207,12 +223,13 @@ function openaiToGeminiCLIRequest(model, body, stream) {
|
|||||||
for (const fn of gemini.tools[0].functionDeclarations) {
|
for (const fn of gemini.tools[0].functionDeclarations) {
|
||||||
if (fn.parameters) {
|
if (fn.parameters) {
|
||||||
const cleanedSchema = cleanJSONSchemaForAntigravity(fn.parameters);
|
const cleanedSchema = cleanJSONSchemaForAntigravity(fn.parameters);
|
||||||
if (isClaude) {
|
fn.parameters = cleanedSchema;
|
||||||
fn.parameters = cleanedSchema;
|
// if (isClaude) {
|
||||||
} else {
|
// fn.parameters = cleanedSchema;
|
||||||
fn.parametersJsonSchema = cleanedSchema;
|
// } else {
|
||||||
delete fn.parameters;
|
// fn.parametersJsonSchema = cleanedSchema;
|
||||||
}
|
// delete fn.parameters;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,7 +240,7 @@ function openaiToGeminiCLIRequest(model, body, stream) {
|
|||||||
// Wrap Gemini CLI format in Cloud Code wrapper
|
// Wrap Gemini CLI format in Cloud Code wrapper
|
||||||
function wrapInCloudCodeEnvelope(model, geminiCLI, credentials = null, isAntigravity = false) {
|
function wrapInCloudCodeEnvelope(model, geminiCLI, credentials = null, isAntigravity = false) {
|
||||||
const projectId = credentials?.projectId || generateProjectId();
|
const projectId = credentials?.projectId || generateProjectId();
|
||||||
|
|
||||||
const envelope = {
|
const envelope = {
|
||||||
project: projectId,
|
project: projectId,
|
||||||
model: model,
|
model: model,
|
||||||
@@ -241,7 +258,7 @@ function wrapInCloudCodeEnvelope(model, geminiCLI, credentials = null, isAntigra
|
|||||||
// Antigravity specific fields
|
// Antigravity specific fields
|
||||||
if (isAntigravity) {
|
if (isAntigravity) {
|
||||||
envelope.requestType = "agent";
|
envelope.requestType = "agent";
|
||||||
|
|
||||||
// Inject required default system prompt for Antigravity
|
// Inject required default system prompt for Antigravity
|
||||||
const defaultPart = { text: ANTIGRAVITY_DEFAULT_SYSTEM };
|
const defaultPart = { text: ANTIGRAVITY_DEFAULT_SYSTEM };
|
||||||
if (envelope.request.systemInstruction?.parts) {
|
if (envelope.request.systemInstruction?.parts) {
|
||||||
@@ -249,7 +266,7 @@ function wrapInCloudCodeEnvelope(model, geminiCLI, credentials = null, isAntigra
|
|||||||
} else {
|
} else {
|
||||||
envelope.request.systemInstruction = { role: "user", parts: [defaultPart] };
|
envelope.request.systemInstruction = { role: "user", parts: [defaultPart] };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add toolConfig for Antigravity
|
// Add toolConfig for Antigravity
|
||||||
if (geminiCLI.tools?.length > 0) {
|
if (geminiCLI.tools?.length > 0) {
|
||||||
envelope.request.toolConfig = {
|
envelope.request.toolConfig = {
|
||||||
@@ -264,8 +281,119 @@ function wrapInCloudCodeEnvelope(model, geminiCLI, credentials = null, isAntigra
|
|||||||
return envelope;
|
return envelope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrap Claude format in Cloud Code envelope for Antigravity
|
||||||
|
function wrapInCloudCodeEnvelopeForClaude(model, claudeRequest, credentials = null) {
|
||||||
|
const projectId = credentials?.projectId || generateProjectId();
|
||||||
|
|
||||||
|
const envelope = {
|
||||||
|
project: projectId,
|
||||||
|
model: model,
|
||||||
|
userAgent: "antigravity",
|
||||||
|
requestId: `agent-${generateUUID()}`,
|
||||||
|
requestType: "agent",
|
||||||
|
request: {
|
||||||
|
sessionId: generateSessionId(),
|
||||||
|
contents: [],
|
||||||
|
generationConfig: {
|
||||||
|
temperature: claudeRequest.temperature || 1,
|
||||||
|
maxOutputTokens: claudeRequest.max_tokens || 4096
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert Claude messages to Gemini contents
|
||||||
|
if (claudeRequest.messages && Array.isArray(claudeRequest.messages)) {
|
||||||
|
for (const msg of claudeRequest.messages) {
|
||||||
|
const parts = [];
|
||||||
|
|
||||||
|
if (Array.isArray(msg.content)) {
|
||||||
|
for (const block of msg.content) {
|
||||||
|
if (block.type === "text") {
|
||||||
|
parts.push({ text: block.text });
|
||||||
|
} else if (block.type === "tool_use") {
|
||||||
|
parts.push({
|
||||||
|
functionCall: {
|
||||||
|
id: block.id,
|
||||||
|
name: block.name,
|
||||||
|
args: block.input || {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (block.type === "tool_result") {
|
||||||
|
let content = block.content;
|
||||||
|
if (Array.isArray(content)) {
|
||||||
|
content = content.map(c => c.type === "text" ? c.text : JSON.stringify(c)).join("\n");
|
||||||
|
}
|
||||||
|
parts.push({
|
||||||
|
functionResponse: {
|
||||||
|
id: block.tool_use_id,
|
||||||
|
name: "unknown",
|
||||||
|
response: { result: tryParseJSON(content) || content }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (typeof msg.content === "string") {
|
||||||
|
parts.push({ text: msg.content });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts.length > 0) {
|
||||||
|
envelope.request.contents.push({
|
||||||
|
role: msg.role === "assistant" ? "model" : "user",
|
||||||
|
parts
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Claude tools to Gemini functionDeclarations
|
||||||
|
if (claudeRequest.tools && Array.isArray(claudeRequest.tools)) {
|
||||||
|
const functionDeclarations = [];
|
||||||
|
for (const tool of claudeRequest.tools) {
|
||||||
|
if (tool.name && tool.input_schema) {
|
||||||
|
const cleanedSchema = cleanJSONSchemaForAntigravity(tool.input_schema);
|
||||||
|
functionDeclarations.push({
|
||||||
|
name: tool.name,
|
||||||
|
description: tool.description || "",
|
||||||
|
parameters: cleanedSchema
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (functionDeclarations.length > 0) {
|
||||||
|
envelope.request.tools = [{ functionDeclarations }];
|
||||||
|
envelope.request.toolConfig = {
|
||||||
|
functionCallingConfig: { mode: "VALIDATED" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add system instruction (Antigravity default)
|
||||||
|
const defaultPart = { text: ANTIGRAVITY_DEFAULT_SYSTEM };
|
||||||
|
const systemParts = [defaultPart];
|
||||||
|
|
||||||
|
if (claudeRequest.system) {
|
||||||
|
if (Array.isArray(claudeRequest.system)) {
|
||||||
|
for (const block of claudeRequest.system) {
|
||||||
|
if (block.text) systemParts.push({ text: block.text });
|
||||||
|
}
|
||||||
|
} else if (typeof claudeRequest.system === "string") {
|
||||||
|
systemParts.push({ text: claudeRequest.system });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
envelope.request.systemInstruction = { role: "user", parts: systemParts };
|
||||||
|
|
||||||
|
return envelope;
|
||||||
|
}
|
||||||
|
|
||||||
// OpenAI -> Antigravity (Sandbox Cloud Code with wrapper)
|
// OpenAI -> Antigravity (Sandbox Cloud Code with wrapper)
|
||||||
function openaiToAntigravityRequest(model, body, stream, credentials = null) {
|
function openaiToAntigravityRequest(model, body, stream, credentials = null) {
|
||||||
|
const isClaude = model.toLowerCase().includes("claude");
|
||||||
|
|
||||||
|
if (isClaude) {
|
||||||
|
const claudeRequest = openaiToClaudeRequestForAntigravity(model, body, stream);
|
||||||
|
return wrapInCloudCodeEnvelopeForClaude(model, claudeRequest, credentials);
|
||||||
|
}
|
||||||
|
|
||||||
const geminiCLI = openaiToGeminiCLIRequest(model, body, stream);
|
const geminiCLI = openaiToGeminiCLIRequest(model, body, stream);
|
||||||
return wrapInCloudCodeEnvelope(model, geminiCLI, credentials, true);
|
return wrapInCloudCodeEnvelope(model, geminiCLI, credentials, true);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user