@corbat-tech/coco 2.23.0 → 2.24.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/README.md +110 -372
- package/dist/cli/index.js +601 -217
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +604 -205
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -13072,6 +13072,147 @@ function createKimiCodeProvider(config) {
|
|
|
13072
13072
|
|
|
13073
13073
|
// src/providers/openai.ts
|
|
13074
13074
|
init_errors();
|
|
13075
|
+
function getSingleBuilderKey(builders) {
|
|
13076
|
+
return builders.size === 1 ? Array.from(builders.keys())[0] ?? null : null;
|
|
13077
|
+
}
|
|
13078
|
+
function parseToolCallArguments(args, providerName) {
|
|
13079
|
+
try {
|
|
13080
|
+
return args ? JSON.parse(args) : {};
|
|
13081
|
+
} catch {
|
|
13082
|
+
try {
|
|
13083
|
+
if (args) {
|
|
13084
|
+
const repaired = jsonrepair(args);
|
|
13085
|
+
return JSON.parse(repaired);
|
|
13086
|
+
}
|
|
13087
|
+
} catch {
|
|
13088
|
+
console.error(`[${providerName}] Cannot parse tool arguments: ${args.slice(0, 200)}`);
|
|
13089
|
+
}
|
|
13090
|
+
return {};
|
|
13091
|
+
}
|
|
13092
|
+
}
|
|
13093
|
+
var ChatToolCallAssembler = class {
|
|
13094
|
+
builders = /* @__PURE__ */ new Map();
|
|
13095
|
+
lastBuilderKey = null;
|
|
13096
|
+
consume(delta) {
|
|
13097
|
+
const key = typeof delta.index === "number" ? `index:${delta.index}` : typeof delta.id === "string" && delta.id.length > 0 ? `id:${delta.id}` : getSingleBuilderKey(this.builders) ?? this.lastBuilderKey ?? `fallback:${this.builders.size}`;
|
|
13098
|
+
let started;
|
|
13099
|
+
if (!this.builders.has(key)) {
|
|
13100
|
+
const initialId = delta.id ?? "";
|
|
13101
|
+
const initialName = delta.function?.name ?? "";
|
|
13102
|
+
this.builders.set(key, { id: initialId, name: initialName, arguments: "" });
|
|
13103
|
+
started = {
|
|
13104
|
+
id: initialId || void 0,
|
|
13105
|
+
name: initialName || void 0
|
|
13106
|
+
};
|
|
13107
|
+
}
|
|
13108
|
+
const builder = this.builders.get(key);
|
|
13109
|
+
this.lastBuilderKey = key;
|
|
13110
|
+
if (delta.id) {
|
|
13111
|
+
builder.id = delta.id;
|
|
13112
|
+
}
|
|
13113
|
+
if (delta.function?.name) {
|
|
13114
|
+
builder.name = delta.function.name;
|
|
13115
|
+
}
|
|
13116
|
+
const text = delta.function?.arguments ?? "";
|
|
13117
|
+
if (!text) return { started };
|
|
13118
|
+
builder.arguments += text;
|
|
13119
|
+
return {
|
|
13120
|
+
started,
|
|
13121
|
+
argumentDelta: {
|
|
13122
|
+
id: builder.id,
|
|
13123
|
+
name: builder.name,
|
|
13124
|
+
text
|
|
13125
|
+
}
|
|
13126
|
+
};
|
|
13127
|
+
}
|
|
13128
|
+
finalizeAll(providerName) {
|
|
13129
|
+
const result = [];
|
|
13130
|
+
for (const builder of this.builders.values()) {
|
|
13131
|
+
result.push({
|
|
13132
|
+
id: builder.id,
|
|
13133
|
+
name: builder.name,
|
|
13134
|
+
input: parseToolCallArguments(builder.arguments, providerName)
|
|
13135
|
+
});
|
|
13136
|
+
}
|
|
13137
|
+
this.builders.clear();
|
|
13138
|
+
this.lastBuilderKey = null;
|
|
13139
|
+
return result;
|
|
13140
|
+
}
|
|
13141
|
+
};
|
|
13142
|
+
var ResponsesToolCallAssembler = class {
|
|
13143
|
+
builders = /* @__PURE__ */ new Map();
|
|
13144
|
+
outputIndexToBuilderKey = /* @__PURE__ */ new Map();
|
|
13145
|
+
onOutputItemAdded(event) {
|
|
13146
|
+
const item = event.item;
|
|
13147
|
+
if (!item || item.type !== "function_call") return null;
|
|
13148
|
+
const callId = item.call_id ?? "";
|
|
13149
|
+
const itemKey = item.id ?? callId;
|
|
13150
|
+
this.builders.set(itemKey, {
|
|
13151
|
+
callId,
|
|
13152
|
+
name: item.name ?? "",
|
|
13153
|
+
arguments: item.arguments ?? ""
|
|
13154
|
+
});
|
|
13155
|
+
if (typeof event.output_index === "number") {
|
|
13156
|
+
this.outputIndexToBuilderKey.set(event.output_index, itemKey);
|
|
13157
|
+
}
|
|
13158
|
+
return {
|
|
13159
|
+
id: callId,
|
|
13160
|
+
name: item.name ?? ""
|
|
13161
|
+
};
|
|
13162
|
+
}
|
|
13163
|
+
onArgumentsDelta(event) {
|
|
13164
|
+
const builderKey = this.resolveBuilderKey(event.item_id, event.output_index);
|
|
13165
|
+
if (!builderKey) return;
|
|
13166
|
+
const builder = this.builders.get(builderKey);
|
|
13167
|
+
if (!builder) return;
|
|
13168
|
+
builder.arguments += event.delta ?? "";
|
|
13169
|
+
}
|
|
13170
|
+
onArgumentsDone(event, providerName) {
|
|
13171
|
+
const builderKey = this.resolveBuilderKey(event.item_id, event.output_index);
|
|
13172
|
+
if (!builderKey) return null;
|
|
13173
|
+
const builder = this.builders.get(builderKey);
|
|
13174
|
+
if (!builder) return null;
|
|
13175
|
+
const toolCall = {
|
|
13176
|
+
id: builder.callId,
|
|
13177
|
+
name: builder.name,
|
|
13178
|
+
input: parseToolCallArguments(event.arguments ?? builder.arguments, providerName)
|
|
13179
|
+
};
|
|
13180
|
+
this.deleteBuilder(builderKey);
|
|
13181
|
+
return toolCall;
|
|
13182
|
+
}
|
|
13183
|
+
finalizeAll(providerName) {
|
|
13184
|
+
const calls = [];
|
|
13185
|
+
for (const builder of this.builders.values()) {
|
|
13186
|
+
calls.push({
|
|
13187
|
+
id: builder.callId,
|
|
13188
|
+
name: builder.name,
|
|
13189
|
+
input: parseToolCallArguments(builder.arguments, providerName)
|
|
13190
|
+
});
|
|
13191
|
+
}
|
|
13192
|
+
this.builders.clear();
|
|
13193
|
+
this.outputIndexToBuilderKey.clear();
|
|
13194
|
+
return calls;
|
|
13195
|
+
}
|
|
13196
|
+
resolveBuilderKey(itemId, outputIndex) {
|
|
13197
|
+
if (itemId && this.builders.has(itemId)) {
|
|
13198
|
+
return itemId;
|
|
13199
|
+
}
|
|
13200
|
+
if (typeof outputIndex === "number") {
|
|
13201
|
+
return this.outputIndexToBuilderKey.get(outputIndex) ?? null;
|
|
13202
|
+
}
|
|
13203
|
+
return getSingleBuilderKey(this.builders);
|
|
13204
|
+
}
|
|
13205
|
+
deleteBuilder(builderKey) {
|
|
13206
|
+
this.builders.delete(builderKey);
|
|
13207
|
+
for (const [idx, key] of this.outputIndexToBuilderKey.entries()) {
|
|
13208
|
+
if (key === builderKey) {
|
|
13209
|
+
this.outputIndexToBuilderKey.delete(idx);
|
|
13210
|
+
}
|
|
13211
|
+
}
|
|
13212
|
+
}
|
|
13213
|
+
};
|
|
13214
|
+
|
|
13215
|
+
// src/providers/openai.ts
|
|
13075
13216
|
var DEFAULT_MODEL2 = "gpt-5.4-codex";
|
|
13076
13217
|
var CONTEXT_WINDOWS2 = {
|
|
13077
13218
|
// OpenAI models
|
|
@@ -13411,7 +13552,7 @@ var OpenAIProvider = class {
|
|
|
13411
13552
|
const stream = await this.client.chat.completions.create(
|
|
13412
13553
|
requestParams
|
|
13413
13554
|
);
|
|
13414
|
-
const
|
|
13555
|
+
const toolCallAssembler = new ChatToolCallAssembler();
|
|
13415
13556
|
const streamTimeout = this.config.timeout ?? 12e4;
|
|
13416
13557
|
let lastActivityTime = Date.now();
|
|
13417
13558
|
const timeoutController = new AbortController();
|
|
@@ -13425,30 +13566,6 @@ var OpenAIProvider = class {
|
|
|
13425
13566
|
timeoutController.signal.addEventListener("abort", () => stream.controller.abort(), {
|
|
13426
13567
|
once: true
|
|
13427
13568
|
});
|
|
13428
|
-
const providerName = this.name;
|
|
13429
|
-
const parseArguments2 = (builder) => {
|
|
13430
|
-
let input = {};
|
|
13431
|
-
try {
|
|
13432
|
-
input = builder.arguments ? JSON.parse(builder.arguments) : {};
|
|
13433
|
-
} catch (error) {
|
|
13434
|
-
console.warn(
|
|
13435
|
-
`[${providerName}] Failed to parse tool call arguments for ${builder.name}: ${builder.arguments?.slice(0, 300)}`
|
|
13436
|
-
);
|
|
13437
|
-
try {
|
|
13438
|
-
if (builder.arguments) {
|
|
13439
|
-
const repaired = jsonrepair(builder.arguments);
|
|
13440
|
-
input = JSON.parse(repaired);
|
|
13441
|
-
console.log(`[${providerName}] \u2713 Successfully repaired JSON for ${builder.name}`);
|
|
13442
|
-
}
|
|
13443
|
-
} catch {
|
|
13444
|
-
console.error(
|
|
13445
|
-
`[${providerName}] Cannot repair JSON for ${builder.name}, using empty object`
|
|
13446
|
-
);
|
|
13447
|
-
console.error(`[${providerName}] Original error:`, error);
|
|
13448
|
-
}
|
|
13449
|
-
}
|
|
13450
|
-
return input;
|
|
13451
|
-
};
|
|
13452
13569
|
try {
|
|
13453
13570
|
let streamStopReason;
|
|
13454
13571
|
for await (const chunk of stream) {
|
|
@@ -13461,37 +13578,31 @@ var OpenAIProvider = class {
|
|
|
13461
13578
|
}
|
|
13462
13579
|
if (delta?.tool_calls) {
|
|
13463
13580
|
for (const toolCallDelta of delta.tool_calls) {
|
|
13464
|
-
const
|
|
13465
|
-
|
|
13466
|
-
|
|
13467
|
-
|
|
13468
|
-
name: toolCallDelta.function?.name ??
|
|
13469
|
-
arguments:
|
|
13470
|
-
}
|
|
13581
|
+
const consumed = toolCallAssembler.consume({
|
|
13582
|
+
index: toolCallDelta.index,
|
|
13583
|
+
id: toolCallDelta.id ?? void 0,
|
|
13584
|
+
function: {
|
|
13585
|
+
name: toolCallDelta.function?.name ?? void 0,
|
|
13586
|
+
arguments: toolCallDelta.function?.arguments ?? void 0
|
|
13587
|
+
}
|
|
13588
|
+
});
|
|
13589
|
+
if (consumed.started) {
|
|
13471
13590
|
yield {
|
|
13472
13591
|
type: "tool_use_start",
|
|
13473
13592
|
toolCall: {
|
|
13474
|
-
id:
|
|
13475
|
-
name:
|
|
13593
|
+
id: consumed.started.id,
|
|
13594
|
+
name: consumed.started.name
|
|
13476
13595
|
}
|
|
13477
13596
|
};
|
|
13478
13597
|
}
|
|
13479
|
-
|
|
13480
|
-
if (toolCallDelta.id) {
|
|
13481
|
-
builder.id = toolCallDelta.id;
|
|
13482
|
-
}
|
|
13483
|
-
if (toolCallDelta.function?.name) {
|
|
13484
|
-
builder.name = toolCallDelta.function.name;
|
|
13485
|
-
}
|
|
13486
|
-
if (toolCallDelta.function?.arguments) {
|
|
13487
|
-
builder.arguments += toolCallDelta.function.arguments;
|
|
13598
|
+
if (consumed.argumentDelta) {
|
|
13488
13599
|
yield {
|
|
13489
13600
|
type: "tool_use_delta",
|
|
13490
13601
|
toolCall: {
|
|
13491
|
-
id:
|
|
13492
|
-
name:
|
|
13602
|
+
id: consumed.argumentDelta.id,
|
|
13603
|
+
name: consumed.argumentDelta.name
|
|
13493
13604
|
},
|
|
13494
|
-
text:
|
|
13605
|
+
text: consumed.argumentDelta.text
|
|
13495
13606
|
};
|
|
13496
13607
|
}
|
|
13497
13608
|
}
|
|
@@ -13500,27 +13611,26 @@ var OpenAIProvider = class {
|
|
|
13500
13611
|
if (finishReason) {
|
|
13501
13612
|
streamStopReason = this.mapFinishReason(finishReason);
|
|
13502
13613
|
}
|
|
13503
|
-
if (finishReason
|
|
13504
|
-
for (const
|
|
13614
|
+
if (finishReason) {
|
|
13615
|
+
for (const toolCall of toolCallAssembler.finalizeAll(this.name)) {
|
|
13505
13616
|
yield {
|
|
13506
13617
|
type: "tool_use_end",
|
|
13507
13618
|
toolCall: {
|
|
13508
|
-
id:
|
|
13509
|
-
name:
|
|
13510
|
-
input:
|
|
13619
|
+
id: toolCall.id,
|
|
13620
|
+
name: toolCall.name,
|
|
13621
|
+
input: toolCall.input
|
|
13511
13622
|
}
|
|
13512
13623
|
};
|
|
13513
13624
|
}
|
|
13514
|
-
toolCallBuilders.clear();
|
|
13515
13625
|
}
|
|
13516
13626
|
}
|
|
13517
|
-
for (const
|
|
13627
|
+
for (const toolCall of toolCallAssembler.finalizeAll(this.name)) {
|
|
13518
13628
|
yield {
|
|
13519
13629
|
type: "tool_use_end",
|
|
13520
13630
|
toolCall: {
|
|
13521
|
-
id:
|
|
13522
|
-
name:
|
|
13523
|
-
input:
|
|
13631
|
+
id: toolCall.id,
|
|
13632
|
+
name: toolCall.name,
|
|
13633
|
+
input: toolCall.input
|
|
13524
13634
|
}
|
|
13525
13635
|
};
|
|
13526
13636
|
}
|
|
@@ -13957,7 +14067,7 @@ var OpenAIProvider = class {
|
|
|
13957
14067
|
toolCalls.push({
|
|
13958
14068
|
id: item.call_id,
|
|
13959
14069
|
name: item.name,
|
|
13960
|
-
input:
|
|
14070
|
+
input: parseToolCallArguments(item.arguments, this.name)
|
|
13961
14071
|
});
|
|
13962
14072
|
}
|
|
13963
14073
|
}
|
|
@@ -14063,7 +14173,7 @@ var OpenAIProvider = class {
|
|
|
14063
14173
|
const stream = await this.client.responses.create(
|
|
14064
14174
|
requestParams
|
|
14065
14175
|
);
|
|
14066
|
-
const
|
|
14176
|
+
const toolCallAssembler = new ResponsesToolCallAssembler();
|
|
14067
14177
|
const streamTimeout = this.config.timeout ?? 12e4;
|
|
14068
14178
|
let lastActivityTime = Date.now();
|
|
14069
14179
|
const timeoutController = new AbortController();
|
|
@@ -14087,57 +14197,66 @@ var OpenAIProvider = class {
|
|
|
14087
14197
|
yield { type: "text", text: event.delta };
|
|
14088
14198
|
break;
|
|
14089
14199
|
case "response.output_item.added":
|
|
14090
|
-
|
|
14091
|
-
const
|
|
14092
|
-
const
|
|
14093
|
-
|
|
14094
|
-
|
|
14095
|
-
|
|
14096
|
-
|
|
14200
|
+
{
|
|
14201
|
+
const item = event.item;
|
|
14202
|
+
const start = toolCallAssembler.onOutputItemAdded({
|
|
14203
|
+
output_index: event.output_index,
|
|
14204
|
+
item: {
|
|
14205
|
+
type: item.type,
|
|
14206
|
+
id: item.id,
|
|
14207
|
+
call_id: item.call_id,
|
|
14208
|
+
name: item.name,
|
|
14209
|
+
arguments: item.arguments
|
|
14210
|
+
}
|
|
14097
14211
|
});
|
|
14212
|
+
if (!start) break;
|
|
14098
14213
|
yield {
|
|
14099
14214
|
type: "tool_use_start",
|
|
14100
|
-
toolCall: { id:
|
|
14215
|
+
toolCall: { id: start.id, name: start.name }
|
|
14101
14216
|
};
|
|
14102
14217
|
}
|
|
14103
14218
|
break;
|
|
14104
14219
|
case "response.function_call_arguments.delta":
|
|
14105
|
-
{
|
|
14106
|
-
|
|
14107
|
-
|
|
14108
|
-
|
|
14109
|
-
|
|
14110
|
-
}
|
|
14220
|
+
toolCallAssembler.onArgumentsDelta({
|
|
14221
|
+
item_id: event.item_id,
|
|
14222
|
+
output_index: event.output_index,
|
|
14223
|
+
delta: event.delta
|
|
14224
|
+
});
|
|
14111
14225
|
break;
|
|
14112
14226
|
case "response.function_call_arguments.done":
|
|
14113
14227
|
{
|
|
14114
|
-
const
|
|
14115
|
-
|
|
14228
|
+
const toolCall = toolCallAssembler.onArgumentsDone(
|
|
14229
|
+
{
|
|
14230
|
+
item_id: event.item_id,
|
|
14231
|
+
output_index: event.output_index,
|
|
14232
|
+
arguments: event.arguments
|
|
14233
|
+
},
|
|
14234
|
+
this.name
|
|
14235
|
+
);
|
|
14236
|
+
if (toolCall) {
|
|
14116
14237
|
yield {
|
|
14117
14238
|
type: "tool_use_end",
|
|
14118
14239
|
toolCall: {
|
|
14119
|
-
id:
|
|
14120
|
-
name:
|
|
14121
|
-
input:
|
|
14240
|
+
id: toolCall.id,
|
|
14241
|
+
name: toolCall.name,
|
|
14242
|
+
input: toolCall.input
|
|
14122
14243
|
}
|
|
14123
14244
|
};
|
|
14124
|
-
fnCallBuilders.delete(event.item_id);
|
|
14125
14245
|
}
|
|
14126
14246
|
}
|
|
14127
14247
|
break;
|
|
14128
14248
|
case "response.completed":
|
|
14129
14249
|
{
|
|
14130
|
-
for (const
|
|
14250
|
+
for (const toolCall of toolCallAssembler.finalizeAll(this.name)) {
|
|
14131
14251
|
yield {
|
|
14132
14252
|
type: "tool_use_end",
|
|
14133
14253
|
toolCall: {
|
|
14134
|
-
id:
|
|
14135
|
-
name:
|
|
14136
|
-
input:
|
|
14254
|
+
id: toolCall.id,
|
|
14255
|
+
name: toolCall.name,
|
|
14256
|
+
input: toolCall.input
|
|
14137
14257
|
}
|
|
14138
14258
|
};
|
|
14139
14259
|
}
|
|
14140
|
-
fnCallBuilders.clear();
|
|
14141
14260
|
const hasToolCalls = event.response.output.some(
|
|
14142
14261
|
(i) => i.type === "function_call"
|
|
14143
14262
|
);
|
|
@@ -14257,24 +14376,6 @@ var OpenAIProvider = class {
|
|
|
14257
14376
|
strict: false
|
|
14258
14377
|
}));
|
|
14259
14378
|
}
|
|
14260
|
-
/**
|
|
14261
|
-
* Parse tool call arguments with jsonrepair fallback (Responses API)
|
|
14262
|
-
*/
|
|
14263
|
-
parseResponsesArguments(args) {
|
|
14264
|
-
try {
|
|
14265
|
-
return args ? JSON.parse(args) : {};
|
|
14266
|
-
} catch {
|
|
14267
|
-
try {
|
|
14268
|
-
if (args) {
|
|
14269
|
-
const repaired = jsonrepair(args);
|
|
14270
|
-
return JSON.parse(repaired);
|
|
14271
|
-
}
|
|
14272
|
-
} catch {
|
|
14273
|
-
console.error(`[${this.name}] Cannot parse tool arguments: ${args.slice(0, 200)}`);
|
|
14274
|
-
}
|
|
14275
|
-
return {};
|
|
14276
|
-
}
|
|
14277
|
-
}
|
|
14278
14379
|
};
|
|
14279
14380
|
function createKimiProvider(config) {
|
|
14280
14381
|
const provider = new OpenAIProvider("kimi", "Kimi (Moonshot)");
|
|
@@ -14322,21 +14423,6 @@ function extractAccountId(accessToken) {
|
|
|
14322
14423
|
const auth = claims["https://api.openai.com/auth"];
|
|
14323
14424
|
return claims["chatgpt_account_id"] || auth?.["chatgpt_account_id"] || claims["organizations"]?.[0]?.id;
|
|
14324
14425
|
}
|
|
14325
|
-
function parseArguments(args) {
|
|
14326
|
-
try {
|
|
14327
|
-
return args ? JSON.parse(args) : {};
|
|
14328
|
-
} catch {
|
|
14329
|
-
try {
|
|
14330
|
-
if (args) {
|
|
14331
|
-
const repaired = jsonrepair(args);
|
|
14332
|
-
return JSON.parse(repaired);
|
|
14333
|
-
}
|
|
14334
|
-
} catch {
|
|
14335
|
-
console.error(`[Codex] Cannot parse tool arguments: ${args.slice(0, 200)}`);
|
|
14336
|
-
}
|
|
14337
|
-
return {};
|
|
14338
|
-
}
|
|
14339
|
-
}
|
|
14340
14426
|
var CodexProvider = class {
|
|
14341
14427
|
id = "codex";
|
|
14342
14428
|
name = "OpenAI Codex (ChatGPT Plus/Pro)";
|
|
@@ -14636,7 +14722,7 @@ var CodexProvider = class {
|
|
|
14636
14722
|
let inputTokens = 0;
|
|
14637
14723
|
let outputTokens = 0;
|
|
14638
14724
|
const toolCalls = [];
|
|
14639
|
-
const
|
|
14725
|
+
const toolCallAssembler = new ResponsesToolCallAssembler();
|
|
14640
14726
|
await this.readSSEStream(response, (event) => {
|
|
14641
14727
|
if (event.id) responseId = event.id;
|
|
14642
14728
|
switch (event.type) {
|
|
@@ -14647,31 +14733,35 @@ var CodexProvider = class {
|
|
|
14647
14733
|
content = event.text ?? content;
|
|
14648
14734
|
break;
|
|
14649
14735
|
case "response.output_item.added": {
|
|
14650
|
-
|
|
14651
|
-
|
|
14652
|
-
|
|
14653
|
-
|
|
14654
|
-
callId: item.call_id,
|
|
14655
|
-
name: item.name,
|
|
14656
|
-
arguments: ""
|
|
14657
|
-
});
|
|
14658
|
-
}
|
|
14736
|
+
toolCallAssembler.onOutputItemAdded({
|
|
14737
|
+
output_index: event.output_index,
|
|
14738
|
+
item: event.item
|
|
14739
|
+
});
|
|
14659
14740
|
break;
|
|
14660
14741
|
}
|
|
14661
14742
|
case "response.function_call_arguments.delta": {
|
|
14662
|
-
|
|
14663
|
-
|
|
14743
|
+
toolCallAssembler.onArgumentsDelta({
|
|
14744
|
+
item_id: event.item_id,
|
|
14745
|
+
output_index: event.output_index,
|
|
14746
|
+
delta: event.delta
|
|
14747
|
+
});
|
|
14664
14748
|
break;
|
|
14665
14749
|
}
|
|
14666
14750
|
case "response.function_call_arguments.done": {
|
|
14667
|
-
const
|
|
14668
|
-
|
|
14751
|
+
const toolCall = toolCallAssembler.onArgumentsDone(
|
|
14752
|
+
{
|
|
14753
|
+
item_id: event.item_id,
|
|
14754
|
+
output_index: event.output_index,
|
|
14755
|
+
arguments: event.arguments
|
|
14756
|
+
},
|
|
14757
|
+
this.name
|
|
14758
|
+
);
|
|
14759
|
+
if (toolCall) {
|
|
14669
14760
|
toolCalls.push({
|
|
14670
|
-
id:
|
|
14671
|
-
name:
|
|
14672
|
-
input:
|
|
14761
|
+
id: toolCall.id,
|
|
14762
|
+
name: toolCall.name,
|
|
14763
|
+
input: toolCall.input
|
|
14673
14764
|
});
|
|
14674
|
-
fnCallBuilders.delete(event.item_id);
|
|
14675
14765
|
}
|
|
14676
14766
|
break;
|
|
14677
14767
|
}
|
|
@@ -14682,14 +14772,13 @@ var CodexProvider = class {
|
|
|
14682
14772
|
inputTokens = usage.input_tokens ?? 0;
|
|
14683
14773
|
outputTokens = usage.output_tokens ?? 0;
|
|
14684
14774
|
}
|
|
14685
|
-
for (const
|
|
14775
|
+
for (const toolCall of toolCallAssembler.finalizeAll(this.name)) {
|
|
14686
14776
|
toolCalls.push({
|
|
14687
|
-
id:
|
|
14688
|
-
name:
|
|
14689
|
-
input:
|
|
14777
|
+
id: toolCall.id,
|
|
14778
|
+
name: toolCall.name,
|
|
14779
|
+
input: toolCall.input
|
|
14690
14780
|
});
|
|
14691
14781
|
}
|
|
14692
|
-
fnCallBuilders.clear();
|
|
14693
14782
|
break;
|
|
14694
14783
|
}
|
|
14695
14784
|
}
|
|
@@ -14783,7 +14872,7 @@ var CodexProvider = class {
|
|
|
14783
14872
|
const reader = response.body.getReader();
|
|
14784
14873
|
const decoder = new TextDecoder();
|
|
14785
14874
|
let buffer = "";
|
|
14786
|
-
const
|
|
14875
|
+
const toolCallAssembler = new ResponsesToolCallAssembler();
|
|
14787
14876
|
let lastActivityTime = Date.now();
|
|
14788
14877
|
const timeoutController = new AbortController();
|
|
14789
14878
|
const timeoutInterval = setInterval(() => {
|
|
@@ -14816,55 +14905,58 @@ var CodexProvider = class {
|
|
|
14816
14905
|
yield { type: "text", text: event.delta ?? "" };
|
|
14817
14906
|
break;
|
|
14818
14907
|
case "response.output_item.added": {
|
|
14819
|
-
const
|
|
14820
|
-
|
|
14821
|
-
|
|
14822
|
-
|
|
14823
|
-
|
|
14824
|
-
name: item.name,
|
|
14825
|
-
arguments: ""
|
|
14826
|
-
});
|
|
14908
|
+
const start = toolCallAssembler.onOutputItemAdded({
|
|
14909
|
+
output_index: event.output_index,
|
|
14910
|
+
item: event.item
|
|
14911
|
+
});
|
|
14912
|
+
if (start) {
|
|
14827
14913
|
yield {
|
|
14828
14914
|
type: "tool_use_start",
|
|
14829
|
-
toolCall: { id:
|
|
14915
|
+
toolCall: { id: start.id, name: start.name }
|
|
14830
14916
|
};
|
|
14831
14917
|
}
|
|
14832
14918
|
break;
|
|
14833
14919
|
}
|
|
14834
14920
|
case "response.function_call_arguments.delta": {
|
|
14835
|
-
|
|
14836
|
-
|
|
14837
|
-
|
|
14838
|
-
|
|
14921
|
+
toolCallAssembler.onArgumentsDelta({
|
|
14922
|
+
item_id: event.item_id,
|
|
14923
|
+
output_index: event.output_index,
|
|
14924
|
+
delta: event.delta
|
|
14925
|
+
});
|
|
14839
14926
|
break;
|
|
14840
14927
|
}
|
|
14841
14928
|
case "response.function_call_arguments.done": {
|
|
14842
|
-
const
|
|
14843
|
-
|
|
14929
|
+
const toolCall = toolCallAssembler.onArgumentsDone(
|
|
14930
|
+
{
|
|
14931
|
+
item_id: event.item_id,
|
|
14932
|
+
output_index: event.output_index,
|
|
14933
|
+
arguments: event.arguments
|
|
14934
|
+
},
|
|
14935
|
+
this.name
|
|
14936
|
+
);
|
|
14937
|
+
if (toolCall) {
|
|
14844
14938
|
yield {
|
|
14845
14939
|
type: "tool_use_end",
|
|
14846
14940
|
toolCall: {
|
|
14847
|
-
id:
|
|
14848
|
-
name:
|
|
14849
|
-
input:
|
|
14941
|
+
id: toolCall.id,
|
|
14942
|
+
name: toolCall.name,
|
|
14943
|
+
input: toolCall.input
|
|
14850
14944
|
}
|
|
14851
14945
|
};
|
|
14852
|
-
fnCallBuilders.delete(event.item_id);
|
|
14853
14946
|
}
|
|
14854
14947
|
break;
|
|
14855
14948
|
}
|
|
14856
14949
|
case "response.completed": {
|
|
14857
|
-
for (const
|
|
14950
|
+
for (const toolCall of toolCallAssembler.finalizeAll(this.name)) {
|
|
14858
14951
|
yield {
|
|
14859
14952
|
type: "tool_use_end",
|
|
14860
14953
|
toolCall: {
|
|
14861
|
-
id:
|
|
14862
|
-
name:
|
|
14863
|
-
input:
|
|
14954
|
+
id: toolCall.id,
|
|
14955
|
+
name: toolCall.name,
|
|
14956
|
+
input: toolCall.input
|
|
14864
14957
|
}
|
|
14865
14958
|
};
|
|
14866
14959
|
}
|
|
14867
|
-
fnCallBuilders.clear();
|
|
14868
14960
|
const resp = event.response;
|
|
14869
14961
|
const output = resp?.output ?? [];
|
|
14870
14962
|
const hasToolCalls = output.some((i) => i.type === "function_call");
|
|
@@ -15222,8 +15314,8 @@ var GeminiProvider = class {
|
|
|
15222
15314
|
const { history, lastMessage } = this.convertMessages(messages);
|
|
15223
15315
|
const chat = model.startChat({ history });
|
|
15224
15316
|
const result = await chat.sendMessageStream(lastMessage);
|
|
15225
|
-
const emittedToolCalls = /* @__PURE__ */ new Set();
|
|
15226
15317
|
let streamStopReason;
|
|
15318
|
+
let streamToolCallCounter = 0;
|
|
15227
15319
|
for await (const chunk of result.stream) {
|
|
15228
15320
|
const text = chunk.text();
|
|
15229
15321
|
if (text) {
|
|
@@ -15238,30 +15330,23 @@ var GeminiProvider = class {
|
|
|
15238
15330
|
for (const part of candidate.content.parts) {
|
|
15239
15331
|
if ("functionCall" in part && part.functionCall) {
|
|
15240
15332
|
const funcCall = part.functionCall;
|
|
15241
|
-
|
|
15242
|
-
|
|
15243
|
-
|
|
15244
|
-
|
|
15245
|
-
|
|
15246
|
-
|
|
15247
|
-
|
|
15248
|
-
|
|
15249
|
-
|
|
15250
|
-
|
|
15251
|
-
|
|
15252
|
-
}
|
|
15253
|
-
|
|
15254
|
-
|
|
15255
|
-
|
|
15256
|
-
|
|
15257
|
-
|
|
15258
|
-
}
|
|
15259
|
-
};
|
|
15260
|
-
yield {
|
|
15261
|
-
type: "tool_use_end",
|
|
15262
|
-
toolCall
|
|
15263
|
-
};
|
|
15264
|
-
}
|
|
15333
|
+
streamToolCallCounter++;
|
|
15334
|
+
const toolCall = {
|
|
15335
|
+
id: `gemini_call_${streamToolCallCounter}`,
|
|
15336
|
+
name: funcCall.name,
|
|
15337
|
+
input: funcCall.args ?? {}
|
|
15338
|
+
};
|
|
15339
|
+
yield {
|
|
15340
|
+
type: "tool_use_start",
|
|
15341
|
+
toolCall: {
|
|
15342
|
+
id: toolCall.id,
|
|
15343
|
+
name: toolCall.name
|
|
15344
|
+
}
|
|
15345
|
+
};
|
|
15346
|
+
yield {
|
|
15347
|
+
type: "tool_use_end",
|
|
15348
|
+
toolCall
|
|
15349
|
+
};
|
|
15265
15350
|
}
|
|
15266
15351
|
}
|
|
15267
15352
|
}
|
|
@@ -15336,13 +15421,13 @@ var GeminiProvider = class {
|
|
|
15336
15421
|
* Convert messages to Gemini format
|
|
15337
15422
|
*/
|
|
15338
15423
|
convertMessages(messages) {
|
|
15424
|
+
const toolNameByUseId = this.buildToolUseNameMap(messages);
|
|
15425
|
+
const conversation = messages.filter((m) => m.role !== "system");
|
|
15339
15426
|
const history = [];
|
|
15340
15427
|
let lastUserMessage = "";
|
|
15341
|
-
for (
|
|
15342
|
-
|
|
15343
|
-
|
|
15344
|
-
}
|
|
15345
|
-
const parts = this.convertContent(msg.content);
|
|
15428
|
+
for (let i = 0; i < conversation.length; i++) {
|
|
15429
|
+
const msg = conversation[i];
|
|
15430
|
+
const isLastMessage = i === conversation.length - 1;
|
|
15346
15431
|
if (msg.role === "user") {
|
|
15347
15432
|
if (Array.isArray(msg.content) && msg.content[0]?.type === "tool_result") {
|
|
15348
15433
|
const functionResponses = [];
|
|
@@ -15351,23 +15436,49 @@ var GeminiProvider = class {
|
|
|
15351
15436
|
const toolResult = block;
|
|
15352
15437
|
functionResponses.push({
|
|
15353
15438
|
functionResponse: {
|
|
15354
|
-
name
|
|
15355
|
-
//
|
|
15439
|
+
// Gemini expects the function name in functionResponse.name.
|
|
15440
|
+
// Recover it from prior assistant tool_use blocks when possible.
|
|
15441
|
+
name: toolNameByUseId.get(toolResult.tool_use_id) ?? toolResult.tool_use_id,
|
|
15356
15442
|
response: { result: toolResult.content }
|
|
15357
15443
|
}
|
|
15358
15444
|
});
|
|
15359
15445
|
}
|
|
15360
15446
|
}
|
|
15361
|
-
|
|
15447
|
+
if (isLastMessage) {
|
|
15448
|
+
lastUserMessage = functionResponses;
|
|
15449
|
+
} else {
|
|
15450
|
+
history.push({ role: "user", parts: functionResponses });
|
|
15451
|
+
}
|
|
15362
15452
|
} else {
|
|
15363
|
-
|
|
15453
|
+
const parts = this.convertContent(msg.content);
|
|
15454
|
+
if (isLastMessage) {
|
|
15455
|
+
lastUserMessage = parts;
|
|
15456
|
+
} else {
|
|
15457
|
+
history.push({ role: "user", parts });
|
|
15458
|
+
}
|
|
15364
15459
|
}
|
|
15365
15460
|
} else if (msg.role === "assistant") {
|
|
15461
|
+
const parts = this.convertContent(msg.content);
|
|
15366
15462
|
history.push({ role: "model", parts });
|
|
15367
15463
|
}
|
|
15368
15464
|
}
|
|
15369
15465
|
return { history, lastMessage: lastUserMessage };
|
|
15370
15466
|
}
|
|
15467
|
+
/**
|
|
15468
|
+
* Build a map from tool_use IDs to function names from assistant history.
|
|
15469
|
+
*/
|
|
15470
|
+
buildToolUseNameMap(messages) {
|
|
15471
|
+
const map = /* @__PURE__ */ new Map();
|
|
15472
|
+
for (const msg of messages) {
|
|
15473
|
+
if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue;
|
|
15474
|
+
for (const block of msg.content) {
|
|
15475
|
+
if (block.type === "tool_use") {
|
|
15476
|
+
map.set(block.id, block.name);
|
|
15477
|
+
}
|
|
15478
|
+
}
|
|
15479
|
+
}
|
|
15480
|
+
return map;
|
|
15481
|
+
}
|
|
15371
15482
|
/**
|
|
15372
15483
|
* Convert content to Gemini parts
|
|
15373
15484
|
*/
|
|
@@ -15444,14 +15555,15 @@ var GeminiProvider = class {
|
|
|
15444
15555
|
let textContent = "";
|
|
15445
15556
|
const toolCalls = [];
|
|
15446
15557
|
if (candidate?.content?.parts) {
|
|
15558
|
+
let toolIndex = 0;
|
|
15447
15559
|
for (const part of candidate.content.parts) {
|
|
15448
15560
|
if ("text" in part && part.text) {
|
|
15449
15561
|
textContent += part.text;
|
|
15450
15562
|
}
|
|
15451
15563
|
if ("functionCall" in part && part.functionCall) {
|
|
15564
|
+
toolIndex++;
|
|
15452
15565
|
toolCalls.push({
|
|
15453
|
-
id:
|
|
15454
|
-
// Use name as ID for Gemini
|
|
15566
|
+
id: `gemini_call_${toolIndex}`,
|
|
15455
15567
|
name: part.functionCall.name,
|
|
15456
15568
|
input: part.functionCall.args ?? {}
|
|
15457
15569
|
});
|
|
@@ -15510,10 +15622,296 @@ var GeminiProvider = class {
|
|
|
15510
15622
|
|
|
15511
15623
|
// src/providers/circuit-breaker.ts
|
|
15512
15624
|
init_errors();
|
|
15625
|
+
var DEFAULT_CIRCUIT_BREAKER_CONFIG = {
|
|
15626
|
+
failureThreshold: 5,
|
|
15627
|
+
resetTimeout: 3e4,
|
|
15628
|
+
halfOpenRequests: 1
|
|
15629
|
+
};
|
|
15630
|
+
var CircuitOpenError = class extends ProviderError {
|
|
15631
|
+
remainingTime;
|
|
15632
|
+
constructor(provider, remainingTime) {
|
|
15633
|
+
super(`Circuit breaker is open for provider: ${provider}`, {
|
|
15634
|
+
provider,
|
|
15635
|
+
retryable: true
|
|
15636
|
+
});
|
|
15637
|
+
this.name = "CircuitOpenError";
|
|
15638
|
+
this.remainingTime = remainingTime;
|
|
15639
|
+
}
|
|
15640
|
+
};
|
|
15641
|
+
var CircuitBreaker = class {
|
|
15642
|
+
config;
|
|
15643
|
+
state = "closed";
|
|
15644
|
+
failureCount = 0;
|
|
15645
|
+
lastFailureTime = null;
|
|
15646
|
+
halfOpenSuccesses = 0;
|
|
15647
|
+
providerId;
|
|
15648
|
+
/**
|
|
15649
|
+
* Create a new circuit breaker
|
|
15650
|
+
*
|
|
15651
|
+
* @param config - Circuit breaker configuration
|
|
15652
|
+
* @param providerId - Provider identifier for error messages
|
|
15653
|
+
*/
|
|
15654
|
+
constructor(config, providerId = "unknown") {
|
|
15655
|
+
this.config = { ...DEFAULT_CIRCUIT_BREAKER_CONFIG, ...config };
|
|
15656
|
+
this.providerId = providerId;
|
|
15657
|
+
}
|
|
15658
|
+
/**
|
|
15659
|
+
* Get the current circuit state
|
|
15660
|
+
*/
|
|
15661
|
+
getState() {
|
|
15662
|
+
this.checkStateTransition();
|
|
15663
|
+
return this.state;
|
|
15664
|
+
}
|
|
15665
|
+
/**
|
|
15666
|
+
* Check if the circuit is currently open (blocking requests)
|
|
15667
|
+
*/
|
|
15668
|
+
isOpen() {
|
|
15669
|
+
this.checkStateTransition();
|
|
15670
|
+
return this.state === "open";
|
|
15671
|
+
}
|
|
15672
|
+
/**
|
|
15673
|
+
* Get the current failure count
|
|
15674
|
+
*/
|
|
15675
|
+
getFailureCount() {
|
|
15676
|
+
return this.failureCount;
|
|
15677
|
+
}
|
|
15678
|
+
/**
|
|
15679
|
+
* Record a successful request
|
|
15680
|
+
* Resets failure count in closed state, or counts toward closing in half-open
|
|
15681
|
+
*/
|
|
15682
|
+
recordSuccess() {
|
|
15683
|
+
if (this.state === "half-open") {
|
|
15684
|
+
this.halfOpenSuccesses++;
|
|
15685
|
+
if (this.halfOpenSuccesses >= this.config.halfOpenRequests) {
|
|
15686
|
+
this.close();
|
|
15687
|
+
}
|
|
15688
|
+
} else if (this.state === "closed") {
|
|
15689
|
+
this.failureCount = 0;
|
|
15690
|
+
}
|
|
15691
|
+
}
|
|
15692
|
+
/**
|
|
15693
|
+
* Record a failed request
|
|
15694
|
+
* Increments failure count and may open the circuit
|
|
15695
|
+
*/
|
|
15696
|
+
recordFailure() {
|
|
15697
|
+
this.lastFailureTime = Date.now();
|
|
15698
|
+
this.failureCount++;
|
|
15699
|
+
if (this.state === "half-open") {
|
|
15700
|
+
this.open();
|
|
15701
|
+
} else if (this.state === "closed" && this.failureCount >= this.config.failureThreshold) {
|
|
15702
|
+
this.open();
|
|
15703
|
+
}
|
|
15704
|
+
}
|
|
15705
|
+
/**
|
|
15706
|
+
* Execute a function with circuit breaker protection
|
|
15707
|
+
*
|
|
15708
|
+
* @param fn - Async function to execute
|
|
15709
|
+
* @returns Result of the function
|
|
15710
|
+
* @throws CircuitOpenError if circuit is open
|
|
15711
|
+
* @throws Original error if function fails
|
|
15712
|
+
*/
|
|
15713
|
+
async execute(fn) {
|
|
15714
|
+
this.checkStateTransition();
|
|
15715
|
+
if (this.state === "open") {
|
|
15716
|
+
const elapsed = Date.now() - (this.lastFailureTime ?? Date.now());
|
|
15717
|
+
const remaining = this.config.resetTimeout - elapsed;
|
|
15718
|
+
throw new CircuitOpenError(this.providerId, remaining);
|
|
15719
|
+
}
|
|
15720
|
+
try {
|
|
15721
|
+
const result = await fn();
|
|
15722
|
+
this.recordSuccess();
|
|
15723
|
+
return result;
|
|
15724
|
+
} catch (error) {
|
|
15725
|
+
this.recordFailure();
|
|
15726
|
+
throw error;
|
|
15727
|
+
}
|
|
15728
|
+
}
|
|
15729
|
+
/**
|
|
15730
|
+
* Manually reset the circuit breaker to closed state
|
|
15731
|
+
*/
|
|
15732
|
+
reset() {
|
|
15733
|
+
this.close();
|
|
15734
|
+
}
|
|
15735
|
+
/**
|
|
15736
|
+
* Check and perform state transitions based on time
|
|
15737
|
+
*/
|
|
15738
|
+
checkStateTransition() {
|
|
15739
|
+
if (this.state === "open" && this.lastFailureTime !== null) {
|
|
15740
|
+
const elapsed = Date.now() - this.lastFailureTime;
|
|
15741
|
+
if (elapsed >= this.config.resetTimeout) {
|
|
15742
|
+
this.halfOpen();
|
|
15743
|
+
}
|
|
15744
|
+
}
|
|
15745
|
+
}
|
|
15746
|
+
/**
|
|
15747
|
+
* Transition to closed state
|
|
15748
|
+
*/
|
|
15749
|
+
close() {
|
|
15750
|
+
this.state = "closed";
|
|
15751
|
+
this.failureCount = 0;
|
|
15752
|
+
this.halfOpenSuccesses = 0;
|
|
15753
|
+
this.lastFailureTime = null;
|
|
15754
|
+
}
|
|
15755
|
+
/**
|
|
15756
|
+
* Transition to open state
|
|
15757
|
+
*/
|
|
15758
|
+
open() {
|
|
15759
|
+
this.state = "open";
|
|
15760
|
+
this.halfOpenSuccesses = 0;
|
|
15761
|
+
}
|
|
15762
|
+
/**
|
|
15763
|
+
* Transition to half-open state
|
|
15764
|
+
*/
|
|
15765
|
+
halfOpen() {
|
|
15766
|
+
this.state = "half-open";
|
|
15767
|
+
this.halfOpenSuccesses = 0;
|
|
15768
|
+
}
|
|
15769
|
+
};
|
|
15513
15770
|
|
|
15514
15771
|
// src/providers/fallback.ts
|
|
15515
15772
|
init_errors();
|
|
15516
15773
|
|
|
15774
|
+
// src/providers/resilient.ts
|
|
15775
|
+
var DEFAULT_STREAM_RETRY = {
|
|
15776
|
+
maxRetries: 1,
|
|
15777
|
+
initialDelayMs: 500,
|
|
15778
|
+
maxDelayMs: 5e3,
|
|
15779
|
+
backoffMultiplier: 2,
|
|
15780
|
+
jitterFactor: 0.1
|
|
15781
|
+
};
|
|
15782
|
+
function sleep2(ms) {
|
|
15783
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
15784
|
+
}
|
|
15785
|
+
function computeRetryDelay(attempt, config) {
|
|
15786
|
+
const exp = config.initialDelayMs * Math.pow(config.backoffMultiplier, attempt);
|
|
15787
|
+
const capped = Math.min(exp, config.maxDelayMs);
|
|
15788
|
+
const jitter = capped * config.jitterFactor * (Math.random() * 2 - 1);
|
|
15789
|
+
return Math.max(0, Math.min(capped + jitter, config.maxDelayMs));
|
|
15790
|
+
}
|
|
15791
|
+
var ResilientProvider = class {
|
|
15792
|
+
id;
|
|
15793
|
+
name;
|
|
15794
|
+
provider;
|
|
15795
|
+
breaker;
|
|
15796
|
+
retryConfig;
|
|
15797
|
+
streamRetryConfig;
|
|
15798
|
+
constructor(provider, config = {}) {
|
|
15799
|
+
this.provider = provider;
|
|
15800
|
+
this.id = provider.id;
|
|
15801
|
+
this.name = provider.name;
|
|
15802
|
+
this.retryConfig = { ...DEFAULT_RETRY_CONFIG, ...config.retry };
|
|
15803
|
+
this.streamRetryConfig = { ...DEFAULT_STREAM_RETRY, ...config.streamRetry };
|
|
15804
|
+
this.breaker = new CircuitBreaker(
|
|
15805
|
+
{ ...DEFAULT_CIRCUIT_BREAKER_CONFIG, ...config.circuitBreaker },
|
|
15806
|
+
provider.id
|
|
15807
|
+
);
|
|
15808
|
+
}
|
|
15809
|
+
async initialize(config) {
|
|
15810
|
+
await this.provider.initialize(config);
|
|
15811
|
+
}
|
|
15812
|
+
async chat(messages, options) {
|
|
15813
|
+
return this.breaker.execute(
|
|
15814
|
+
() => withRetry(() => this.provider.chat(messages, options), this.retryConfig)
|
|
15815
|
+
);
|
|
15816
|
+
}
|
|
15817
|
+
async chatWithTools(messages, options) {
|
|
15818
|
+
return this.breaker.execute(
|
|
15819
|
+
() => withRetry(() => this.provider.chatWithTools(messages, options), this.retryConfig)
|
|
15820
|
+
);
|
|
15821
|
+
}
|
|
15822
|
+
async *stream(messages, options) {
|
|
15823
|
+
yield* this.streamWithPolicy(() => this.provider.stream(messages, options));
|
|
15824
|
+
}
|
|
15825
|
+
async *streamWithTools(messages, options) {
|
|
15826
|
+
yield* this.streamWithPolicy(() => this.provider.streamWithTools(messages, options));
|
|
15827
|
+
}
|
|
15828
|
+
countTokens(text) {
|
|
15829
|
+
return this.provider.countTokens(text);
|
|
15830
|
+
}
|
|
15831
|
+
getContextWindow() {
|
|
15832
|
+
return this.provider.getContextWindow();
|
|
15833
|
+
}
|
|
15834
|
+
async isAvailable() {
|
|
15835
|
+
try {
|
|
15836
|
+
return await this.breaker.execute(() => this.provider.isAvailable());
|
|
15837
|
+
} catch (error) {
|
|
15838
|
+
if (error instanceof CircuitOpenError) {
|
|
15839
|
+
return false;
|
|
15840
|
+
}
|
|
15841
|
+
return false;
|
|
15842
|
+
}
|
|
15843
|
+
}
|
|
15844
|
+
getCircuitState() {
|
|
15845
|
+
return this.breaker.getState();
|
|
15846
|
+
}
|
|
15847
|
+
resetCircuit() {
|
|
15848
|
+
this.breaker.reset();
|
|
15849
|
+
}
|
|
15850
|
+
async *streamWithPolicy(createStream) {
|
|
15851
|
+
let attempt = 0;
|
|
15852
|
+
while (attempt <= this.streamRetryConfig.maxRetries) {
|
|
15853
|
+
if (this.breaker.isOpen()) {
|
|
15854
|
+
throw new CircuitOpenError(this.id, 0);
|
|
15855
|
+
}
|
|
15856
|
+
let emittedChunk = false;
|
|
15857
|
+
try {
|
|
15858
|
+
for await (const chunk of createStream()) {
|
|
15859
|
+
emittedChunk = true;
|
|
15860
|
+
yield chunk;
|
|
15861
|
+
}
|
|
15862
|
+
this.breaker.recordSuccess();
|
|
15863
|
+
return;
|
|
15864
|
+
} catch (error) {
|
|
15865
|
+
this.breaker.recordFailure();
|
|
15866
|
+
const shouldRetry = !emittedChunk && attempt < this.streamRetryConfig.maxRetries && isRetryableError(error);
|
|
15867
|
+
if (!shouldRetry) {
|
|
15868
|
+
throw error;
|
|
15869
|
+
}
|
|
15870
|
+
const delay = computeRetryDelay(attempt, this.streamRetryConfig);
|
|
15871
|
+
await sleep2(delay);
|
|
15872
|
+
attempt++;
|
|
15873
|
+
}
|
|
15874
|
+
}
|
|
15875
|
+
}
|
|
15876
|
+
};
|
|
15877
|
+
function getDefaultResilienceConfig(providerId) {
|
|
15878
|
+
if (providerId === "ollama" || providerId === "lmstudio") {
|
|
15879
|
+
return {
|
|
15880
|
+
retry: {
|
|
15881
|
+
maxRetries: 1,
|
|
15882
|
+
initialDelayMs: 300,
|
|
15883
|
+
maxDelayMs: 1500
|
|
15884
|
+
},
|
|
15885
|
+
streamRetry: {
|
|
15886
|
+
maxRetries: 0
|
|
15887
|
+
},
|
|
15888
|
+
circuitBreaker: {
|
|
15889
|
+
failureThreshold: 3,
|
|
15890
|
+
resetTimeout: 1e4
|
|
15891
|
+
}
|
|
15892
|
+
};
|
|
15893
|
+
}
|
|
15894
|
+
return {
|
|
15895
|
+
retry: {
|
|
15896
|
+
maxRetries: 3,
|
|
15897
|
+
initialDelayMs: 1e3,
|
|
15898
|
+
maxDelayMs: 3e4
|
|
15899
|
+
},
|
|
15900
|
+
streamRetry: {
|
|
15901
|
+
maxRetries: 1,
|
|
15902
|
+
initialDelayMs: 500,
|
|
15903
|
+
maxDelayMs: 5e3
|
|
15904
|
+
},
|
|
15905
|
+
circuitBreaker: {
|
|
15906
|
+
failureThreshold: 5,
|
|
15907
|
+
resetTimeout: 3e4
|
|
15908
|
+
}
|
|
15909
|
+
};
|
|
15910
|
+
}
|
|
15911
|
+
function createResilientProvider(provider, config) {
|
|
15912
|
+
return new ResilientProvider(provider, getDefaultResilienceConfig(provider.id));
|
|
15913
|
+
}
|
|
15914
|
+
|
|
15517
15915
|
// src/providers/index.ts
|
|
15518
15916
|
init_copilot();
|
|
15519
15917
|
init_errors();
|
|
@@ -15546,12 +15944,10 @@ async function createProvider(type, config = {}) {
|
|
|
15546
15944
|
break;
|
|
15547
15945
|
case "kimi":
|
|
15548
15946
|
provider = createKimiProvider(mergedConfig);
|
|
15549
|
-
|
|
15550
|
-
return provider;
|
|
15947
|
+
break;
|
|
15551
15948
|
case "kimi-code":
|
|
15552
15949
|
provider = createKimiCodeProvider(mergedConfig);
|
|
15553
|
-
|
|
15554
|
-
return provider;
|
|
15950
|
+
break;
|
|
15555
15951
|
case "lmstudio":
|
|
15556
15952
|
provider = new OpenAIProvider("lmstudio", "LM Studio");
|
|
15557
15953
|
mergedConfig.baseUrl = mergedConfig.baseUrl ?? "http://localhost:1234/v1";
|
|
@@ -15596,7 +15992,10 @@ async function createProvider(type, config = {}) {
|
|
|
15596
15992
|
});
|
|
15597
15993
|
}
|
|
15598
15994
|
await provider.initialize(mergedConfig);
|
|
15599
|
-
|
|
15995
|
+
const resilienceEnabled = !["0", "false", "off"].includes(
|
|
15996
|
+
(process.env["COCO_PROVIDER_RESILIENCE"] ?? "1").toLowerCase()
|
|
15997
|
+
);
|
|
15998
|
+
return resilienceEnabled ? createResilientProvider(provider) : provider;
|
|
15600
15999
|
}
|
|
15601
16000
|
|
|
15602
16001
|
// src/orchestrator/orchestrator.ts
|