Files
oh-my-opencode-free-fork/src/auth/antigravity/tools.ts
YeonGyu-Kim abd90bbc9c fix(antigravity): use loadCodeAssist project ID and add OpenAI message conversion
- Add message-converter.ts for OpenAI messages to Gemini contents conversion
- Use SKIP_THOUGHT_SIGNATURE_VALIDATOR as default signature (CLIProxyAPI approach)
- Restore loadCodeAssist API call to get user's actual project ID
- Improve debug logging for troubleshooting
- Fix tool normalization edge cases

🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
2025-12-13 04:24:24 +09:00

244 lines
6.3 KiB
TypeScript

/**
* Antigravity Tool Normalization
* Converts tools between OpenAI and Gemini formats.
*
* OpenAI format:
* { "type": "function", "function": { "name": "x", "description": "...", "parameters": {...} } }
*
* Gemini format:
* { "functionDeclarations": [{ "name": "x", "description": "...", "parameters": {...} }] }
*
* Note: This is for Gemini models ONLY. Claude models are not supported via Antigravity.
*/
/**
* OpenAI function tool format
*/
export interface OpenAITool {
type: string
function?: {
name: string
description?: string
parameters?: Record<string, unknown>
}
}
/**
* Gemini function declaration format
*/
export interface GeminiFunctionDeclaration {
name: string
description?: string
parameters?: Record<string, unknown>
}
/**
* Gemini tools format (array of functionDeclarations)
*/
export interface GeminiTools {
functionDeclarations: GeminiFunctionDeclaration[]
}
/**
* OpenAI tool call in response
*/
export interface OpenAIToolCall {
id: string
type: "function"
function: {
name: string
arguments: string
}
}
/**
* Gemini function call in response
*/
export interface GeminiFunctionCall {
name: string
args: Record<string, unknown>
}
/**
* Gemini function response format
*/
export interface GeminiFunctionResponse {
name: string
response: Record<string, unknown>
}
/**
* Gemini tool result containing function calls
*/
export interface GeminiToolResult {
functionCall?: GeminiFunctionCall
functionResponse?: GeminiFunctionResponse
}
/**
* Normalize OpenAI-format tools to Gemini format.
* Converts an array of OpenAI tools to Gemini's functionDeclarations format.
*
* - Handles `function` type tools with name, description, parameters
* - Logs warning for unsupported tool types (does NOT silently drop them)
* - Creates a single object with functionDeclarations array
*
* @param tools - Array of OpenAI-format tools
* @returns Gemini-format tools object with functionDeclarations, or undefined if no valid tools
*/
export function normalizeToolsForGemini(
tools: OpenAITool[]
): GeminiTools | undefined {
if (!tools || tools.length === 0) {
return undefined
}
const functionDeclarations: GeminiFunctionDeclaration[] = []
for (const tool of tools) {
if (!tool || typeof tool !== "object") {
continue
}
const toolType = tool.type ?? "function"
if (toolType === "function" && tool.function) {
const declaration: GeminiFunctionDeclaration = {
name: tool.function.name,
}
if (tool.function.description) {
declaration.description = tool.function.description
}
if (tool.function.parameters) {
declaration.parameters = tool.function.parameters
} else {
declaration.parameters = { type: "object", properties: {} }
}
functionDeclarations.push(declaration)
} else if (toolType !== "function" && process.env.ANTIGRAVITY_DEBUG === "1") {
console.warn(
`[antigravity-tools] Unsupported tool type: "${toolType}". Tool will be skipped.`
)
}
}
// Return undefined if no valid function declarations
if (functionDeclarations.length === 0) {
return undefined
}
return { functionDeclarations }
}
/**
* Convert Gemini tool results (functionCall) back to OpenAI tool_call format.
* Handles both functionCall (request) and functionResponse (result) formats.
*
* Gemini functionCall format:
* { "name": "tool_name", "args": { ... } }
*
* OpenAI tool_call format:
* { "id": "call_xxx", "type": "function", "function": { "name": "tool_name", "arguments": "..." } }
*
* @param results - Array of Gemini tool results containing functionCall or functionResponse
* @returns Array of OpenAI-format tool calls
*/
export function normalizeToolResultsFromGemini(
results: GeminiToolResult[]
): OpenAIToolCall[] {
if (!results || results.length === 0) {
return []
}
const toolCalls: OpenAIToolCall[] = []
let callCounter = 0
for (const result of results) {
// Handle functionCall (tool invocation from model)
if (result.functionCall) {
callCounter++
const toolCall: OpenAIToolCall = {
id: `call_${Date.now()}_${callCounter}`,
type: "function",
function: {
name: result.functionCall.name,
arguments: JSON.stringify(result.functionCall.args ?? {}),
},
}
toolCalls.push(toolCall)
}
}
return toolCalls
}
/**
* Convert a single Gemini functionCall to OpenAI tool_call format.
* Useful for streaming responses where each chunk may contain a function call.
*
* @param functionCall - Gemini function call
* @param id - Optional tool call ID (generates one if not provided)
* @returns OpenAI-format tool call
*/
export function convertFunctionCallToToolCall(
functionCall: GeminiFunctionCall,
id?: string
): OpenAIToolCall {
return {
id: id ?? `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
type: "function",
function: {
name: functionCall.name,
arguments: JSON.stringify(functionCall.args ?? {}),
},
}
}
/**
* Check if a tool array contains any function-type tools.
*
* @param tools - Array of OpenAI-format tools
* @returns true if there are function tools to normalize
*/
export function hasFunctionTools(tools: OpenAITool[]): boolean {
if (!tools || tools.length === 0) {
return false
}
return tools.some((tool) => tool.type === "function" && tool.function)
}
/**
* Extract function declarations from already-normalized Gemini tools.
* Useful when tools may already be in Gemini format.
*
* @param tools - Tools that may be in Gemini or OpenAI format
* @returns Array of function declarations
*/
export function extractFunctionDeclarations(
tools: unknown
): GeminiFunctionDeclaration[] {
if (!tools || typeof tools !== "object") {
return []
}
// Check if already in Gemini format
const geminiTools = tools as Record<string, unknown>
if (
Array.isArray(geminiTools.functionDeclarations) &&
geminiTools.functionDeclarations.length > 0
) {
return geminiTools.functionDeclarations as GeminiFunctionDeclaration[]
}
// Check if it's an array of OpenAI tools
if (Array.isArray(tools)) {
const normalized = normalizeToolsForGemini(tools as OpenAITool[])
return normalized?.functionDeclarations ?? []
}
return []
}