diff --git a/dist/providers/google-gemini-cli.js b/dist/providers/google-gemini-cli.js index 93aa26c..beb585e 100644 --- a/dist/providers/google-gemini-cli.js +++ b/dist/providers/google-gemini-cli.js @@ -248,6 +248,11 @@ export const streamGoogleGeminiCli = (model, context, options) => { break; // Success, exit retry loop } const errorText = await response.text(); + // Fail immediately on 429 for Antigravity to let callers rotate accounts. + // Antigravity rate limits can have very long retry delays (10+ minutes). + if (isAntigravity && response.status === 429) { + throw new Error(`Cloud Code Assist API error (${response.status}): ${errorText}`); + } // Check if retryable if (attempt < MAX_RETRIES && isRetryableError(response.status, errorText)) { // Use server-provided delay or exponential backoff diff --git a/dist/providers/openai-codex-responses.js b/dist/providers/openai-codex-responses.js index 188a829..4555c9f 100644 --- a/dist/providers/openai-codex-responses.js +++ b/dist/providers/openai-codex-responses.js @@ -515,7 +521,7 @@ function convertTools(tools) { name: tool.name, description: tool.description, parameters: tool.parameters, - strict: null, + strict: false, })); } function mapStopReason(status) { diff --git a/dist/providers/openai-completions.js b/dist/providers/openai-completions.js index 5d0813a..e0ef676 100644 --- a/dist/providers/openai-completions.js +++ b/dist/providers/openai-completions.js @@ -71,6 +71,18 @@ export const streamOpenAICompletions = (model, context, options) => { stream.push({ type: "start", partial: output }); let currentBlock = null; const blocks = output.content; + const pendingToolCalls = new Map(); + const isCompleteJsonObject = (text) => { + if (!text || text.trim() === "") + return false; + try { + JSON.parse(text); + return true; + } + catch { + return false; + } + }; const blockIndex = () => blocks.length - 1; const finishCurrentBlock = (block) => { if (block) { @@ -193,31 +205,41 @@ export const streamOpenAICompletions = (model, context, options) => { } if (choice?.delta?.tool_calls) { for (const toolCall of choice.delta.tool_calls) { + const index = typeof toolCall.index === "number" ? toolCall.index : 0; + const pending = pendingToolCalls.get(index) || { + type: "toolCall", + id: "", + name: "", + arguments: {}, + partialArgs: "", + }; + if (toolCall.id) + pending.id = toolCall.id; + if (toolCall.function?.name) + pending.name = toolCall.function.name; + let delta = ""; + if (toolCall.function && "arguments" in toolCall.function) { + delta = toolCall.function.arguments || ""; + pending.partialArgs += delta; + pending.arguments = parseStreamingJson(pending.partialArgs); + } + pendingToolCalls.set(index, pending); + // Delay emitting tool calls until the arguments JSON is complete. + // Some providers (e.g. LM Studio) stream an initial empty chunk. + if (!isCompleteJsonObject(pending.partialArgs)) { + continue; + } if (!currentBlock || currentBlock.type !== "toolCall" || - (toolCall.id && currentBlock.id !== toolCall.id)) { + (pending.id && currentBlock.id !== pending.id)) { finishCurrentBlock(currentBlock); - currentBlock = { - type: "toolCall", - id: toolCall.id || "", - name: toolCall.function?.name || "", - arguments: {}, - partialArgs: "", - }; + currentBlock = pending; output.content.push(currentBlock); stream.push({ type: "toolcall_start", contentIndex: blockIndex(), partial: output }); } if (currentBlock.type === "toolCall") { - if (toolCall.id) - currentBlock.id = toolCall.id; - if (toolCall.function?.name) - currentBlock.name = toolCall.function.name; - let delta = ""; - if (toolCall.function?.arguments) { - delta = toolCall.function.arguments; - currentBlock.partialArgs += toolCall.function.arguments; - currentBlock.arguments = parseStreamingJson(currentBlock.partialArgs); - } + currentBlock.partialArgs = pending.partialArgs; + currentBlock.arguments = pending.arguments; stream.push({ type: "toolcall_delta", contentIndex: blockIndex(),