coder-agent 2.2.2 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.js +38 -10
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -236,8 +236,19 @@ function extractTextToolCalls(content) {
|
|
|
236
236
|
}
|
|
237
237
|
return calls;
|
|
238
238
|
}
|
|
239
|
-
// ─── Gemini API client
|
|
240
|
-
async function
|
|
239
|
+
// ─── Gemini API client with Auto-Rotation Fallback ────────────────────────────
|
|
240
|
+
async function callGeminiAPIWithRotation(apiKey, params, maxRetries = 3, initialDelayMs = 1500) {
|
|
241
|
+
const rotationList = [
|
|
242
|
+
"gemini-2.5-flash",
|
|
243
|
+
"gemini-2.5-pro",
|
|
244
|
+
"gemini-2.0-flash",
|
|
245
|
+
"gemini-2.0-pro-exp"
|
|
246
|
+
];
|
|
247
|
+
let currentModel = params.model;
|
|
248
|
+
let modelIndex = rotationList.indexOf(currentModel);
|
|
249
|
+
if (modelIndex === -1) {
|
|
250
|
+
modelIndex = 0; // Default if not in list
|
|
251
|
+
}
|
|
241
252
|
let attempts = 0;
|
|
242
253
|
while (attempts < maxRetries) {
|
|
243
254
|
try {
|
|
@@ -247,7 +258,7 @@ async function callGeminiAPI(apiKey, params, maxRetries = 3, initialDelayMs = 15
|
|
|
247
258
|
"Content-Type": "application/json",
|
|
248
259
|
"Authorization": `Bearer ${apiKey}`
|
|
249
260
|
},
|
|
250
|
-
body: JSON.stringify(params)
|
|
261
|
+
body: JSON.stringify({ ...params, model: currentModel })
|
|
251
262
|
});
|
|
252
263
|
if (!res.ok) {
|
|
253
264
|
const errText = await res.text();
|
|
@@ -255,20 +266,36 @@ async function callGeminiAPI(apiKey, params, maxRetries = 3, initialDelayMs = 15
|
|
|
255
266
|
err.status = res.status;
|
|
256
267
|
throw err;
|
|
257
268
|
}
|
|
258
|
-
|
|
269
|
+
const data = await res.json();
|
|
270
|
+
return { data, modelUsed: currentModel };
|
|
259
271
|
}
|
|
260
272
|
catch (err) {
|
|
261
273
|
attempts++;
|
|
262
274
|
const status = err?.status;
|
|
263
275
|
const isRetryableError = status === 429 || status === 503 || (status >= 500 && status < 600) || !status;
|
|
264
|
-
if (isRetryableError
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
276
|
+
if (isRetryableError) {
|
|
277
|
+
// Rotate model immediately if rate limit or service unavailable occurred
|
|
278
|
+
if ((status === 429 || status === 503) && modelIndex + 1 < rotationList.length) {
|
|
279
|
+
modelIndex++;
|
|
280
|
+
const nextModel = rotationList[modelIndex];
|
|
281
|
+
stopSpinner();
|
|
282
|
+
console.log(chalk.hex('#ff9f0a')('⚠') + ' ' + chalk.gray(`Rate limited on ${currentModel}. Rotating to ${nextModel}`));
|
|
283
|
+
startSpinner("thinking...");
|
|
284
|
+
currentModel = nextModel;
|
|
285
|
+
attempts = 0; // reset retry counter for the fresh model
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
// Otherwise do standard delay retry on same model
|
|
289
|
+
if (attempts < maxRetries) {
|
|
290
|
+
const delay = initialDelayMs * Math.pow(2, attempts - 1);
|
|
291
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
268
294
|
}
|
|
269
295
|
throw err;
|
|
270
296
|
}
|
|
271
297
|
}
|
|
298
|
+
throw new Error("Request failed after all fallback rotations.");
|
|
272
299
|
}
|
|
273
300
|
// ─── Agent Class ─────────────────────────────────────────────────────────────
|
|
274
301
|
export class Agent {
|
|
@@ -316,7 +343,6 @@ export class Agent {
|
|
|
316
343
|
contexts.push(`File contents of ${filePath} (around line ${diag.startLineNumber}):\n` + numberedLines.join("\n"));
|
|
317
344
|
}
|
|
318
345
|
catch (err) {
|
|
319
|
-
// If the absolute path doesn't exist directly (e.g. running in virtual workspaces), try to resolve it relative to current cwd
|
|
320
346
|
try {
|
|
321
347
|
const relativePath = path.relative("/", filePath).replace(/^[a-zA-Z]:/, "").replace(/^\\+|^[//]+/, "");
|
|
322
348
|
const localContent = await fs.readFile(relativePath, "utf-8");
|
|
@@ -342,7 +368,7 @@ export class Agent {
|
|
|
342
368
|
while (iterations < MAX_ITERATIONS) {
|
|
343
369
|
iterations++;
|
|
344
370
|
updateSpinner("thinking...");
|
|
345
|
-
const
|
|
371
|
+
const responseObj = await callGeminiAPIWithRotation(this.apiKey, {
|
|
346
372
|
model: this.model,
|
|
347
373
|
messages: this.memory.getAll(),
|
|
348
374
|
tools: TOOL_DEFINITIONS,
|
|
@@ -350,6 +376,8 @@ export class Agent {
|
|
|
350
376
|
temperature: 0.2,
|
|
351
377
|
});
|
|
352
378
|
stopSpinner();
|
|
379
|
+
this.model = responseObj.modelUsed; // Persist successful model rotation
|
|
380
|
+
const response = responseObj.data;
|
|
353
381
|
const choice = response.choices[0];
|
|
354
382
|
const msg = choice.message;
|
|
355
383
|
// Extract text-based tool calls if native tool_calls is empty
|