@jeffusion/bungee-llms 3.3.0 → 4.0.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/index.js +844 -633
- package/dist/plugin-api.js +3924 -3713
- package/dist/protocol-converters/anthropic-to-openai.converter.d.ts +22 -18
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2678,8 +2678,9 @@ function mapOpenAIFinishReasonToAnthropic(finishReason) {
|
|
|
2678
2678
|
case "length":
|
|
2679
2679
|
return "max_tokens";
|
|
2680
2680
|
case "content_filter":
|
|
2681
|
-
return "
|
|
2681
|
+
return "end_turn";
|
|
2682
2682
|
case "tool_calls":
|
|
2683
|
+
case "function_call":
|
|
2683
2684
|
return "tool_use";
|
|
2684
2685
|
default:
|
|
2685
2686
|
return "end_turn";
|
|
@@ -2853,7 +2854,11 @@ class AnthropicToGeminiConverter {
|
|
|
2853
2854
|
if (block.type === "text" && block.text) {
|
|
2854
2855
|
parts.push({ text: block.text });
|
|
2855
2856
|
} else if (block.type === "thinking" && block.thinking) {
|
|
2856
|
-
|
|
2857
|
+
const thinkingPart = { text: block.thinking, thought: true };
|
|
2858
|
+
if (block.thought_signature) {
|
|
2859
|
+
thinkingPart.thoughtSignature = block.thought_signature;
|
|
2860
|
+
}
|
|
2861
|
+
parts.push(thinkingPart);
|
|
2857
2862
|
} else if (block.type === "image" && block.source) {
|
|
2858
2863
|
parts.push({
|
|
2859
2864
|
inlineData: {
|
|
@@ -3057,10 +3062,14 @@ class AnthropicToGeminiConverter {
|
|
|
3057
3062
|
for (const part of parts) {
|
|
3058
3063
|
if (part.text) {
|
|
3059
3064
|
if (part.thought) {
|
|
3060
|
-
|
|
3065
|
+
const thinkingBlock = {
|
|
3061
3066
|
type: "thinking",
|
|
3062
3067
|
thinking: part.text
|
|
3063
|
-
}
|
|
3068
|
+
};
|
|
3069
|
+
if (part.thoughtSignature) {
|
|
3070
|
+
thinkingBlock.thought_signature = part.thoughtSignature;
|
|
3071
|
+
}
|
|
3072
|
+
content.push(thinkingBlock);
|
|
3064
3073
|
} else {
|
|
3065
3074
|
content.push({
|
|
3066
3075
|
type: "text",
|
|
@@ -3137,6 +3146,10 @@ class AnthropicToGeminiConverter {
|
|
|
3137
3146
|
name: part.functionCall.name,
|
|
3138
3147
|
input: {}
|
|
3139
3148
|
};
|
|
3149
|
+
if (part.thoughtSignature) {
|
|
3150
|
+
contentBlock.thought_signature = part.thoughtSignature;
|
|
3151
|
+
ctx.streamState.set(`part_${partIdx}_sig`, part.thoughtSignature);
|
|
3152
|
+
}
|
|
3140
3153
|
} else if (part.thought !== undefined) {
|
|
3141
3154
|
contentBlock = {
|
|
3142
3155
|
type: "thinking",
|
|
@@ -3159,6 +3172,9 @@ class AnthropicToGeminiConverter {
|
|
|
3159
3172
|
const argsStr = JSON.stringify(part.functionCall.args || {});
|
|
3160
3173
|
const lastArgsLength = Number(ctx.streamState.get(argsKey) ?? 0);
|
|
3161
3174
|
const currentArgsLength = argsStr.length;
|
|
3175
|
+
if (part.thoughtSignature && !ctx.streamState.has(`part_${partIdx}_sig`)) {
|
|
3176
|
+
ctx.streamState.set(`part_${partIdx}_sig`, part.thoughtSignature);
|
|
3177
|
+
}
|
|
3162
3178
|
if (currentArgsLength > lastArgsLength) {
|
|
3163
3179
|
const argsDelta = argsStr.substring(lastArgsLength);
|
|
3164
3180
|
events.push({
|
|
@@ -3175,6 +3191,9 @@ class AnthropicToGeminiConverter {
|
|
|
3175
3191
|
const thinkingKey = `part_${partIdx}_thinking_length`;
|
|
3176
3192
|
const lastLength = Number(ctx.streamState.get(thinkingKey) ?? 0);
|
|
3177
3193
|
const currentLength = part.thought.length;
|
|
3194
|
+
if (part.thoughtSignature && !ctx.streamState.has(`part_${partIdx}_sig`)) {
|
|
3195
|
+
ctx.streamState.set(`part_${partIdx}_sig`, part.thoughtSignature);
|
|
3196
|
+
}
|
|
3178
3197
|
if (currentLength > lastLength) {
|
|
3179
3198
|
const delta = part.thought.substring(lastLength);
|
|
3180
3199
|
events.push({
|
|
@@ -3205,10 +3224,15 @@ class AnthropicToGeminiConverter {
|
|
|
3205
3224
|
}
|
|
3206
3225
|
}
|
|
3207
3226
|
if (hasFinish) {
|
|
3208
|
-
|
|
3227
|
+
const stopEvent = {
|
|
3209
3228
|
type: "content_block_stop",
|
|
3210
3229
|
index: partIdx
|
|
3211
|
-
}
|
|
3230
|
+
};
|
|
3231
|
+
const sig = ctx.streamState.get(`part_${partIdx}_sig`);
|
|
3232
|
+
if (sig) {
|
|
3233
|
+
stopEvent.thought_signature = sig;
|
|
3234
|
+
}
|
|
3235
|
+
events.push(stopEvent);
|
|
3212
3236
|
}
|
|
3213
3237
|
}
|
|
3214
3238
|
return events;
|
|
@@ -3230,18 +3254,6 @@ class AnthropicToOpenAIConverter {
|
|
|
3230
3254
|
from = "anthropic";
|
|
3231
3255
|
to = "openai";
|
|
3232
3256
|
configuredApiMode;
|
|
3233
|
-
protocolConversion = new OpenAIProtocolConversion;
|
|
3234
|
-
runtime = new LLMSRuntime;
|
|
3235
|
-
constructor() {
|
|
3236
|
-
this.runtime.registerAdapter(new AnthropicAdapter, {
|
|
3237
|
-
provider: "anthropic",
|
|
3238
|
-
displayName: "Anthropic"
|
|
3239
|
-
});
|
|
3240
|
-
this.runtime.registerAdapter(new OpenAIAdapter, {
|
|
3241
|
-
provider: "openai",
|
|
3242
|
-
displayName: "OpenAI"
|
|
3243
|
-
});
|
|
3244
|
-
}
|
|
3245
3257
|
setApiMode(mode) {
|
|
3246
3258
|
if (typeof mode !== "string") {
|
|
3247
3259
|
this.configuredApiMode = undefined;
|
|
@@ -3262,21 +3274,6 @@ class AnthropicToOpenAIConverter {
|
|
|
3262
3274
|
const value = options;
|
|
3263
3275
|
this.setApiMode(value.anthropicToOpenAIApiMode);
|
|
3264
3276
|
}
|
|
3265
|
-
parseIntegerOption(value) {
|
|
3266
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
3267
|
-
return Math.trunc(value);
|
|
3268
|
-
}
|
|
3269
|
-
if (typeof value === "string" && value.trim().length > 0) {
|
|
3270
|
-
const parsed = parseInt(value, 10);
|
|
3271
|
-
if (Number.isFinite(parsed)) {
|
|
3272
|
-
return parsed;
|
|
3273
|
-
}
|
|
3274
|
-
}
|
|
3275
|
-
return;
|
|
3276
|
-
}
|
|
3277
|
-
resolveReasoningMaxCompletionTokens(maxTokens) {
|
|
3278
|
-
return this.parseIntegerOption(maxTokens);
|
|
3279
|
-
}
|
|
3280
3277
|
resolveTargetApiMode() {
|
|
3281
3278
|
if (this.configuredApiMode) {
|
|
3282
3279
|
return this.configuredApiMode;
|
|
@@ -3301,26 +3298,6 @@ class AnthropicToOpenAIConverter {
|
|
|
3301
3298
|
}
|
|
3302
3299
|
if (ctx.url.pathname === "/v1/messages" || ctx.url.pathname === "/messages") {
|
|
3303
3300
|
const targetApiMode = this.resolveTargetApiMode();
|
|
3304
|
-
if (this.canUseRuntimeRequestConversion(body, targetApiMode)) {
|
|
3305
|
-
ctx.url.pathname = "/v1/chat/completions";
|
|
3306
|
-
let runtimeRequestBody;
|
|
3307
|
-
try {
|
|
3308
|
-
runtimeRequestBody = this.runtime.convertRequest("anthropic", "openai", body, { pathname: "/v1/chat/completions" });
|
|
3309
|
-
} catch {
|
|
3310
|
-
ctx.body = this.buildOpenAIRequest(body);
|
|
3311
|
-
return;
|
|
3312
|
-
}
|
|
3313
|
-
if (!runtimeRequestBody || typeof runtimeRequestBody !== "object" || Array.isArray(runtimeRequestBody)) {
|
|
3314
|
-
ctx.body = this.buildOpenAIRequest(body);
|
|
3315
|
-
return;
|
|
3316
|
-
}
|
|
3317
|
-
const model = runtimeRequestBody.model;
|
|
3318
|
-
if (typeof model !== "string" || model.trim().length === 0) {
|
|
3319
|
-
runtimeRequestBody.model = "gpt-4";
|
|
3320
|
-
}
|
|
3321
|
-
ctx.body = runtimeRequestBody;
|
|
3322
|
-
return;
|
|
3323
|
-
}
|
|
3324
3301
|
if (targetApiMode === "responses") {
|
|
3325
3302
|
ctx.url.pathname = "/v1/responses";
|
|
3326
3303
|
ctx.body = this.buildOpenAIResponsesRequest(body);
|
|
@@ -3330,43 +3307,6 @@ class AnthropicToOpenAIConverter {
|
|
|
3330
3307
|
}
|
|
3331
3308
|
}
|
|
3332
3309
|
}
|
|
3333
|
-
canUseRuntimeRequestConversion(anthropicBody, targetApiMode) {
|
|
3334
|
-
if (targetApiMode !== "chat_completions") {
|
|
3335
|
-
return false;
|
|
3336
|
-
}
|
|
3337
|
-
if (!anthropicBody || typeof anthropicBody !== "object" || Array.isArray(anthropicBody)) {
|
|
3338
|
-
return false;
|
|
3339
|
-
}
|
|
3340
|
-
const body = anthropicBody;
|
|
3341
|
-
if (typeof body.system !== "string" && body.system !== undefined) {
|
|
3342
|
-
return false;
|
|
3343
|
-
}
|
|
3344
|
-
if (body.tools !== undefined || body.tool_choice !== undefined || body.thinking !== undefined || body.output_config !== undefined || body.top_k !== undefined) {
|
|
3345
|
-
return false;
|
|
3346
|
-
}
|
|
3347
|
-
if (!Array.isArray(body.messages)) {
|
|
3348
|
-
return false;
|
|
3349
|
-
}
|
|
3350
|
-
if (body.messages.length === 0) {
|
|
3351
|
-
const hasNonEmptySystem = typeof body.system === "string" && body.system.trim().length > 0;
|
|
3352
|
-
if (!hasNonEmptySystem) {
|
|
3353
|
-
return false;
|
|
3354
|
-
}
|
|
3355
|
-
}
|
|
3356
|
-
for (const message of body.messages) {
|
|
3357
|
-
if (!message || typeof message !== "object" || Array.isArray(message)) {
|
|
3358
|
-
return false;
|
|
3359
|
-
}
|
|
3360
|
-
const typedMessage = message;
|
|
3361
|
-
if (typedMessage.role !== "user" && typedMessage.role !== "assistant") {
|
|
3362
|
-
return false;
|
|
3363
|
-
}
|
|
3364
|
-
if (typeof typedMessage.content !== "string") {
|
|
3365
|
-
return false;
|
|
3366
|
-
}
|
|
3367
|
-
}
|
|
3368
|
-
return true;
|
|
3369
|
-
}
|
|
3370
3310
|
isAnthropicCountTokensPath(pathname) {
|
|
3371
3311
|
return pathname === "/v1/messages/count_tokens" || pathname === "/messages/count_tokens" || pathname.endsWith("/messages/count_tokens");
|
|
3372
3312
|
}
|
|
@@ -3387,10 +3327,12 @@ class AnthropicToOpenAIConverter {
|
|
|
3387
3327
|
}
|
|
3388
3328
|
buildOpenAIRequest(anthropicBody) {
|
|
3389
3329
|
const openaiBody = {};
|
|
3390
|
-
|
|
3330
|
+
if (typeof anthropicBody.model === "string") {
|
|
3331
|
+
openaiBody.model = anthropicBody.model;
|
|
3332
|
+
}
|
|
3391
3333
|
const messages = [];
|
|
3392
3334
|
messages.push(...this.convertSystemMessages(anthropicBody.system));
|
|
3393
|
-
if (anthropicBody.messages) {
|
|
3335
|
+
if (Array.isArray(anthropicBody.messages)) {
|
|
3394
3336
|
for (const msg of anthropicBody.messages) {
|
|
3395
3337
|
if (msg.role === "user") {
|
|
3396
3338
|
this.convertUserMessage(msg, messages);
|
|
@@ -3399,13 +3341,18 @@ class AnthropicToOpenAIConverter {
|
|
|
3399
3341
|
}
|
|
3400
3342
|
}
|
|
3401
3343
|
}
|
|
3402
|
-
|
|
3344
|
+
this.normalizeOpenAISystemMessages(messages);
|
|
3345
|
+
openaiBody.messages = messages;
|
|
3403
3346
|
if (!Array.isArray(openaiBody.messages) || openaiBody.messages.length === 0) {
|
|
3404
3347
|
openaiBody.messages = [{ role: "user", content: "" }];
|
|
3405
3348
|
}
|
|
3406
|
-
const maxTokens = anthropicBody.max_tokens
|
|
3349
|
+
const maxTokens = anthropicBody.max_tokens ?? anthropicBody.max_tokens_to_sample;
|
|
3407
3350
|
if (maxTokens !== undefined) {
|
|
3408
|
-
openaiBody.
|
|
3351
|
+
if (typeof openaiBody.model === "string" && this.isOpenAINumericOSeriesModel(openaiBody.model)) {
|
|
3352
|
+
openaiBody.max_completion_tokens = maxTokens;
|
|
3353
|
+
} else {
|
|
3354
|
+
openaiBody.max_tokens = maxTokens;
|
|
3355
|
+
}
|
|
3409
3356
|
}
|
|
3410
3357
|
if (anthropicBody.temperature !== undefined) {
|
|
3411
3358
|
openaiBody.temperature = anthropicBody.temperature;
|
|
@@ -3414,156 +3361,289 @@ class AnthropicToOpenAIConverter {
|
|
|
3414
3361
|
openaiBody.top_p = anthropicBody.top_p;
|
|
3415
3362
|
}
|
|
3416
3363
|
if (anthropicBody.stop_sequences !== undefined) {
|
|
3417
|
-
openaiBody.stop =
|
|
3364
|
+
openaiBody.stop = anthropicBody.stop_sequences;
|
|
3418
3365
|
}
|
|
3419
3366
|
if (anthropicBody.stream !== undefined) {
|
|
3420
3367
|
openaiBody.stream = anthropicBody.stream;
|
|
3421
3368
|
}
|
|
3422
3369
|
if (Array.isArray(anthropicBody.tools)) {
|
|
3423
|
-
openaiBody.tools = anthropicBody.tools.filter((tool) =>
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3370
|
+
openaiBody.tools = anthropicBody.tools.filter((tool) => tool?.type !== "BatchTool").map((tool) => {
|
|
3371
|
+
const mappedTool = {
|
|
3372
|
+
type: "function",
|
|
3373
|
+
function: {
|
|
3374
|
+
name: typeof tool?.name === "string" ? tool.name : "",
|
|
3375
|
+
description: tool?.description,
|
|
3376
|
+
parameters: this.cleanOpenAIToolSchema(tool?.input_schema ?? {})
|
|
3377
|
+
}
|
|
3378
|
+
};
|
|
3379
|
+
if (tool.cache_control !== undefined) {
|
|
3380
|
+
mappedTool.cache_control = tool.cache_control;
|
|
3429
3381
|
}
|
|
3430
|
-
|
|
3382
|
+
return mappedTool;
|
|
3383
|
+
});
|
|
3431
3384
|
if (openaiBody.tools.length === 0) {
|
|
3432
3385
|
delete openaiBody.tools;
|
|
3433
3386
|
}
|
|
3434
3387
|
}
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
openaiBody.tool_choice = toolChoice;
|
|
3388
|
+
if (anthropicBody.tool_choice !== undefined) {
|
|
3389
|
+
openaiBody.tool_choice = anthropicBody.tool_choice;
|
|
3438
3390
|
}
|
|
3439
|
-
const
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
openaiBody.reasoning_effort = effort;
|
|
3447
|
-
}
|
|
3448
|
-
const maxCompletionTokens = this.resolveReasoningMaxCompletionTokens(openaiBody.max_tokens);
|
|
3449
|
-
if (maxCompletionTokens !== undefined) {
|
|
3450
|
-
openaiBody.max_completion_tokens = maxCompletionTokens;
|
|
3451
|
-
delete openaiBody.max_tokens;
|
|
3452
|
-
}
|
|
3391
|
+
const promptCacheKey = this.resolvePromptCacheKey(anthropicBody);
|
|
3392
|
+
if (promptCacheKey) {
|
|
3393
|
+
openaiBody.prompt_cache_key = promptCacheKey;
|
|
3394
|
+
}
|
|
3395
|
+
const reasoningEffort = this.resolveAnthropicReasoningEffort(anthropicBody);
|
|
3396
|
+
if (reasoningEffort && this.supportsReasoningEffort(openaiBody.model)) {
|
|
3397
|
+
openaiBody.reasoning_effort = reasoningEffort;
|
|
3453
3398
|
}
|
|
3454
3399
|
return openaiBody;
|
|
3455
3400
|
}
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
}
|
|
3460
|
-
const rawType = typeof thinking.type === "string" ? thinking.type.trim().toLowerCase() : "";
|
|
3461
|
-
return rawType === "enabled" || rawType === "adaptive";
|
|
3401
|
+
isOpenAINumericOSeriesModel(model) {
|
|
3402
|
+
const normalized = model.trim().toLowerCase();
|
|
3403
|
+
return normalized.length > 1 && normalized[0] === "o" && /\d/.test(normalized[1]);
|
|
3462
3404
|
}
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3405
|
+
buildOpenAIResponsesRequest(anthropicBody) {
|
|
3406
|
+
const responsesBody = {};
|
|
3407
|
+
if (typeof anthropicBody.model === "string") {
|
|
3408
|
+
responsesBody.model = anthropicBody.model;
|
|
3409
|
+
}
|
|
3410
|
+
const instructions = this.convertAnthropicSystemToInstructions(anthropicBody.system);
|
|
3411
|
+
if (instructions) {
|
|
3412
|
+
responsesBody.instructions = instructions;
|
|
3466
3413
|
}
|
|
3467
|
-
const
|
|
3468
|
-
if (
|
|
3469
|
-
|
|
3414
|
+
const input = this.convertAnthropicMessagesToResponsesInput(anthropicBody.messages);
|
|
3415
|
+
if (input.length > 0) {
|
|
3416
|
+
responsesBody.input = input;
|
|
3470
3417
|
}
|
|
3471
|
-
|
|
3472
|
-
|
|
3418
|
+
const maxTokens = anthropicBody.max_tokens ?? anthropicBody.max_tokens_to_sample;
|
|
3419
|
+
if (maxTokens !== undefined) {
|
|
3420
|
+
responsesBody.max_output_tokens = maxTokens;
|
|
3473
3421
|
}
|
|
3474
|
-
if (
|
|
3475
|
-
|
|
3422
|
+
if (anthropicBody.temperature !== undefined) {
|
|
3423
|
+
responsesBody.temperature = anthropicBody.temperature;
|
|
3476
3424
|
}
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
resolveOutputConfigEffort(outputConfig) {
|
|
3480
|
-
if (!outputConfig || typeof outputConfig !== "object") {
|
|
3481
|
-
return;
|
|
3425
|
+
if (anthropicBody.top_p !== undefined) {
|
|
3426
|
+
responsesBody.top_p = anthropicBody.top_p;
|
|
3482
3427
|
}
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
return rawEffort;
|
|
3428
|
+
if (anthropicBody.stream !== undefined) {
|
|
3429
|
+
responsesBody.stream = anthropicBody.stream;
|
|
3486
3430
|
}
|
|
3487
|
-
if (
|
|
3488
|
-
|
|
3431
|
+
if (Array.isArray(anthropicBody.tools)) {
|
|
3432
|
+
const tools = anthropicBody.tools.filter((tool) => tool?.type !== "BatchTool").map((tool) => ({
|
|
3433
|
+
type: "function",
|
|
3434
|
+
name: tool.name || "",
|
|
3435
|
+
description: tool.description,
|
|
3436
|
+
parameters: this.normalizeToolParametersSchema(tool.input_schema)
|
|
3437
|
+
}));
|
|
3438
|
+
if (tools.length > 0) {
|
|
3439
|
+
responsesBody.tools = tools;
|
|
3440
|
+
}
|
|
3489
3441
|
}
|
|
3490
|
-
|
|
3491
|
-
|
|
3442
|
+
const toolChoice = this.mapAnthropicToolChoiceToResponses(anthropicBody.tool_choice);
|
|
3443
|
+
if (toolChoice !== undefined) {
|
|
3444
|
+
responsesBody.tool_choice = toolChoice;
|
|
3492
3445
|
}
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3446
|
+
const reasoningEffort = this.resolveAnthropicReasoningEffort(anthropicBody);
|
|
3447
|
+
if (reasoningEffort && this.supportsReasoningEffort(responsesBody.model)) {
|
|
3448
|
+
responsesBody.reasoning = {
|
|
3449
|
+
effort: reasoningEffort
|
|
3450
|
+
};
|
|
3498
3451
|
}
|
|
3499
|
-
|
|
3500
|
-
|
|
3452
|
+
const promptCacheKey = this.resolvePromptCacheKey(anthropicBody);
|
|
3453
|
+
if (promptCacheKey) {
|
|
3454
|
+
responsesBody.prompt_cache_key = promptCacheKey;
|
|
3501
3455
|
}
|
|
3502
|
-
return
|
|
3456
|
+
return responsesBody;
|
|
3503
3457
|
}
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
return
|
|
3458
|
+
convertAnthropicSystemToInstructions(system) {
|
|
3459
|
+
if (typeof system === "string") {
|
|
3460
|
+
const cleaned = this.sanitizeSystemText(system).trim();
|
|
3461
|
+
return cleaned || undefined;
|
|
3508
3462
|
}
|
|
3509
|
-
if (
|
|
3510
|
-
return
|
|
3463
|
+
if (!Array.isArray(system)) {
|
|
3464
|
+
return;
|
|
3511
3465
|
}
|
|
3512
|
-
|
|
3513
|
-
|
|
3466
|
+
const texts = [];
|
|
3467
|
+
for (const block of system) {
|
|
3468
|
+
if (typeof block === "string") {
|
|
3469
|
+
const cleaned = this.sanitizeSystemText(block).trim();
|
|
3470
|
+
if (cleaned)
|
|
3471
|
+
texts.push(cleaned);
|
|
3472
|
+
} else if (block && typeof block === "object" && block.type === "text" && typeof block.text === "string") {
|
|
3473
|
+
const cleaned = this.sanitizeSystemText(block.text).trim();
|
|
3474
|
+
if (cleaned)
|
|
3475
|
+
texts.push(cleaned);
|
|
3476
|
+
}
|
|
3514
3477
|
}
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3478
|
+
return texts.length > 0 ? texts.join(`
|
|
3479
|
+
|
|
3480
|
+
`) : undefined;
|
|
3481
|
+
}
|
|
3482
|
+
convertAnthropicMessagesToResponsesInput(messages) {
|
|
3483
|
+
const input = [];
|
|
3484
|
+
if (!Array.isArray(messages)) {
|
|
3485
|
+
return input;
|
|
3486
|
+
}
|
|
3487
|
+
for (const msg of messages) {
|
|
3488
|
+
if (!msg || typeof msg !== "object")
|
|
3489
|
+
continue;
|
|
3490
|
+
const role = msg.role === "assistant" ? "assistant" : "user";
|
|
3491
|
+
const content = msg.content;
|
|
3492
|
+
if (typeof content === "string") {
|
|
3493
|
+
input.push({
|
|
3494
|
+
role,
|
|
3495
|
+
content: [{ type: role === "assistant" ? "output_text" : "input_text", text: content }]
|
|
3496
|
+
});
|
|
3497
|
+
continue;
|
|
3498
|
+
}
|
|
3499
|
+
if (Array.isArray(content)) {
|
|
3500
|
+
let contentItems = [];
|
|
3501
|
+
const flushContentItems = () => {
|
|
3502
|
+
if (contentItems.length > 0) {
|
|
3503
|
+
input.push({ role, content: contentItems });
|
|
3504
|
+
contentItems = [];
|
|
3505
|
+
}
|
|
3506
|
+
};
|
|
3507
|
+
for (const block of content) {
|
|
3508
|
+
if (!block || typeof block !== "object")
|
|
3509
|
+
continue;
|
|
3510
|
+
switch (block.type) {
|
|
3511
|
+
case "text":
|
|
3512
|
+
if (typeof block.text === "string") {
|
|
3513
|
+
contentItems.push({
|
|
3514
|
+
type: role === "assistant" ? "output_text" : "input_text",
|
|
3515
|
+
text: block.text
|
|
3516
|
+
});
|
|
3517
|
+
}
|
|
3518
|
+
break;
|
|
3519
|
+
case "image":
|
|
3520
|
+
if (role !== "assistant" && block.source) {
|
|
3521
|
+
const imageUrl = this.convertAnthropicImageSourceToUrl(block.source);
|
|
3522
|
+
if (imageUrl) {
|
|
3523
|
+
contentItems.push({ type: "input_image", image_url: imageUrl });
|
|
3524
|
+
}
|
|
3525
|
+
}
|
|
3526
|
+
break;
|
|
3527
|
+
case "tool_use":
|
|
3528
|
+
flushContentItems();
|
|
3529
|
+
input.push({
|
|
3530
|
+
type: "function_call",
|
|
3531
|
+
call_id: block.id || "",
|
|
3532
|
+
name: block.name || "",
|
|
3533
|
+
arguments: typeof block.input === "object" ? JSON.stringify(block.input) : "{}"
|
|
3534
|
+
});
|
|
3535
|
+
break;
|
|
3536
|
+
case "tool_result":
|
|
3537
|
+
flushContentItems();
|
|
3538
|
+
input.push({
|
|
3539
|
+
type: "function_call_output",
|
|
3540
|
+
call_id: block.tool_use_id || "",
|
|
3541
|
+
output: this.normalizeToolResultContent(block.content)
|
|
3542
|
+
});
|
|
3543
|
+
break;
|
|
3544
|
+
case "thinking":
|
|
3545
|
+
break;
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
flushContentItems();
|
|
3549
|
+
continue;
|
|
3520
3550
|
}
|
|
3551
|
+
input.push({ role });
|
|
3521
3552
|
}
|
|
3522
|
-
return
|
|
3553
|
+
return input;
|
|
3523
3554
|
}
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
input: responsesInput.input
|
|
3530
|
-
};
|
|
3531
|
-
if (responsesInput.instructions) {
|
|
3532
|
-
responsesBody.instructions = responsesInput.instructions;
|
|
3555
|
+
convertAnthropicImageSourceToUrl(source) {
|
|
3556
|
+
if (!source || typeof source !== "object")
|
|
3557
|
+
return null;
|
|
3558
|
+
if (source.type === "base64" && source.media_type && source.data) {
|
|
3559
|
+
return `data:${source.media_type};base64,${source.data}`;
|
|
3533
3560
|
}
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3561
|
+
return null;
|
|
3562
|
+
}
|
|
3563
|
+
mapAnthropicToolChoiceToResponses(toolChoice) {
|
|
3564
|
+
if (toolChoice === undefined || toolChoice === null) {
|
|
3565
|
+
return;
|
|
3537
3566
|
}
|
|
3538
|
-
if (
|
|
3539
|
-
|
|
3567
|
+
if (typeof toolChoice === "string") {
|
|
3568
|
+
return toolChoice;
|
|
3540
3569
|
}
|
|
3541
|
-
if (
|
|
3542
|
-
|
|
3570
|
+
if (typeof toolChoice !== "object" || Array.isArray(toolChoice)) {
|
|
3571
|
+
return;
|
|
3543
3572
|
}
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
}
|
|
3573
|
+
const choice = toolChoice;
|
|
3574
|
+
const type = typeof choice.type === "string" ? choice.type.trim().toLowerCase() : "";
|
|
3575
|
+
if (type === "any") {
|
|
3576
|
+
return "required";
|
|
3549
3577
|
}
|
|
3550
|
-
if (
|
|
3551
|
-
|
|
3578
|
+
if (type === "auto") {
|
|
3579
|
+
return "auto";
|
|
3552
3580
|
}
|
|
3553
|
-
if (
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3581
|
+
if (type === "none") {
|
|
3582
|
+
return "none";
|
|
3583
|
+
}
|
|
3584
|
+
if (type === "tool") {
|
|
3585
|
+
return {
|
|
3586
|
+
type: "function",
|
|
3587
|
+
name: typeof choice.name === "string" ? choice.name : ""
|
|
3557
3588
|
};
|
|
3558
3589
|
}
|
|
3559
|
-
|
|
3560
|
-
|
|
3590
|
+
return toolChoice;
|
|
3591
|
+
}
|
|
3592
|
+
resolveAnthropicReasoningEffort(anthropicBody) {
|
|
3593
|
+
if (anthropicBody.output_config && typeof anthropicBody.output_config === "object") {
|
|
3594
|
+
const effort = typeof anthropicBody.output_config.effort === "string" ? anthropicBody.output_config.effort.trim().toLowerCase() : "";
|
|
3595
|
+
if (effort === "low" || effort === "medium" || effort === "high") {
|
|
3596
|
+
return effort;
|
|
3597
|
+
}
|
|
3598
|
+
if (effort === "max") {
|
|
3599
|
+
return "xhigh";
|
|
3600
|
+
}
|
|
3561
3601
|
}
|
|
3562
|
-
|
|
3602
|
+
if (anthropicBody.thinking && typeof anthropicBody.thinking === "object") {
|
|
3603
|
+
const thinkingType = typeof anthropicBody.thinking.type === "string" ? anthropicBody.thinking.type.trim().toLowerCase() : "";
|
|
3604
|
+
const thinkingEffort = typeof anthropicBody.thinking.effort === "string" ? anthropicBody.thinking.effort.trim().toLowerCase() : "";
|
|
3605
|
+
if (thinkingEffort === "low" || thinkingEffort === "medium" || thinkingEffort === "high") {
|
|
3606
|
+
return thinkingEffort;
|
|
3607
|
+
}
|
|
3608
|
+
if (thinkingEffort === "max") {
|
|
3609
|
+
return "xhigh";
|
|
3610
|
+
}
|
|
3611
|
+
if (thinkingType === "adaptive") {
|
|
3612
|
+
return "xhigh";
|
|
3613
|
+
}
|
|
3614
|
+
if (thinkingType === "enabled") {
|
|
3615
|
+
const budgetTokens = anthropicBody.thinking.budget_tokens;
|
|
3616
|
+
if (typeof budgetTokens === "number") {
|
|
3617
|
+
if (budgetTokens < 4000)
|
|
3618
|
+
return "low";
|
|
3619
|
+
if (budgetTokens < 16000)
|
|
3620
|
+
return "medium";
|
|
3621
|
+
}
|
|
3622
|
+
return "high";
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
return;
|
|
3626
|
+
}
|
|
3627
|
+
supportsReasoningEffort(model) {
|
|
3628
|
+
if (!model || typeof model !== "string") {
|
|
3629
|
+
return false;
|
|
3630
|
+
}
|
|
3631
|
+
const normalized = model.trim().toLowerCase();
|
|
3632
|
+
if (normalized.length > 1 && normalized[0] === "o" && /\d/.test(normalized[1])) {
|
|
3633
|
+
return true;
|
|
3634
|
+
}
|
|
3635
|
+
if (normalized.startsWith("gpt-")) {
|
|
3636
|
+
const match = normalized.match(/gpt-(\d+)/);
|
|
3637
|
+
if (match) {
|
|
3638
|
+
const version = parseInt(match[1], 10);
|
|
3639
|
+
return version >= 5;
|
|
3640
|
+
}
|
|
3641
|
+
}
|
|
3642
|
+
return false;
|
|
3563
3643
|
}
|
|
3564
3644
|
convertSystemMessages(system) {
|
|
3565
3645
|
if (typeof system === "string") {
|
|
3566
|
-
const content = system.trim();
|
|
3646
|
+
const content = this.sanitizeSystemText(system).trim();
|
|
3567
3647
|
return content ? [{ role: "system", content }] : [];
|
|
3568
3648
|
}
|
|
3569
3649
|
if (!Array.isArray(system)) {
|
|
@@ -3572,45 +3652,64 @@ class AnthropicToOpenAIConverter {
|
|
|
3572
3652
|
const results = [];
|
|
3573
3653
|
for (const block of system) {
|
|
3574
3654
|
if (typeof block === "string") {
|
|
3575
|
-
const content = block.trim();
|
|
3655
|
+
const content = this.sanitizeSystemText(block).trim();
|
|
3576
3656
|
if (content) {
|
|
3577
3657
|
results.push({ role: "system", content });
|
|
3578
3658
|
}
|
|
3579
3659
|
continue;
|
|
3580
3660
|
}
|
|
3581
3661
|
if (block && typeof block === "object" && block.type === "text") {
|
|
3582
|
-
const content = typeof block.text === "string" ? block.text.trim() : "";
|
|
3662
|
+
const content = typeof block.text === "string" ? this.sanitizeSystemText(block.text).trim() : "";
|
|
3583
3663
|
if (content) {
|
|
3584
|
-
|
|
3664
|
+
const message = { role: "system", content };
|
|
3665
|
+
if (block.cache_control !== undefined) {
|
|
3666
|
+
message.cache_control = block.cache_control;
|
|
3667
|
+
}
|
|
3668
|
+
results.push(message);
|
|
3585
3669
|
}
|
|
3586
3670
|
}
|
|
3587
3671
|
}
|
|
3588
3672
|
return results;
|
|
3589
3673
|
}
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3674
|
+
sanitizeSystemText(text) {
|
|
3675
|
+
return text.split(/(?<=\n)/).filter((segment) => !segment.replace(/\n$/, "").trimStart().startsWith("x-anthropic-billing-header:")).join("");
|
|
3676
|
+
}
|
|
3677
|
+
extractTextFromOpenAIContent(content) {
|
|
3678
|
+
if (typeof content === "string") {
|
|
3679
|
+
return content;
|
|
3680
|
+
}
|
|
3681
|
+
if (!Array.isArray(content)) {
|
|
3682
|
+
return "";
|
|
3683
|
+
}
|
|
3684
|
+
return content.map((part) => part?.type === "text" && typeof part.text === "string" ? part.text : "").filter((text) => text.length > 0).join(`
|
|
3685
|
+
`);
|
|
3686
|
+
}
|
|
3687
|
+
normalizeOpenAISystemMessages(messages) {
|
|
3688
|
+
const systemMessages = messages.filter((message) => message?.role === "system");
|
|
3689
|
+
if (systemMessages.length === 0) {
|
|
3690
|
+
return;
|
|
3691
|
+
}
|
|
3692
|
+
if (systemMessages.length === 1) {
|
|
3693
|
+
const index = messages.indexOf(systemMessages[0]);
|
|
3694
|
+
if (index > 0) {
|
|
3695
|
+
messages.splice(index, 1);
|
|
3696
|
+
messages.unshift(systemMessages[0]);
|
|
3607
3697
|
}
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3698
|
+
return;
|
|
3699
|
+
}
|
|
3700
|
+
const combinedContent = systemMessages.map((message) => this.extractTextFromOpenAIContent(message.content)).filter((text) => text.length > 0).join(`
|
|
3701
|
+
`);
|
|
3702
|
+
for (let index = messages.length - 1;index >= 0; index -= 1) {
|
|
3703
|
+
if (messages[index]?.role === "system") {
|
|
3704
|
+
messages.splice(index, 1);
|
|
3611
3705
|
}
|
|
3612
3706
|
}
|
|
3613
|
-
|
|
3707
|
+
if (combinedContent) {
|
|
3708
|
+
messages.unshift({
|
|
3709
|
+
role: "system",
|
|
3710
|
+
content: combinedContent
|
|
3711
|
+
});
|
|
3712
|
+
}
|
|
3614
3713
|
}
|
|
3615
3714
|
normalizeToolResultContent(content) {
|
|
3616
3715
|
if (content === undefined || content === null) {
|
|
@@ -3619,59 +3718,16 @@ class AnthropicToOpenAIConverter {
|
|
|
3619
3718
|
if (typeof content === "string") {
|
|
3620
3719
|
return content;
|
|
3621
3720
|
}
|
|
3622
|
-
if (
|
|
3623
|
-
const normalizedBlocks = content.map((part) => {
|
|
3624
|
-
if (typeof part === "string") {
|
|
3625
|
-
return { type: "text", text: part };
|
|
3626
|
-
}
|
|
3627
|
-
if (!part || typeof part !== "object") {
|
|
3628
|
-
return null;
|
|
3629
|
-
}
|
|
3630
|
-
if (part.type === "text" && typeof part.text === "string") {
|
|
3631
|
-
return { type: "text", text: part.text };
|
|
3632
|
-
}
|
|
3633
|
-
if (part.type === "image" && part.source) {
|
|
3634
|
-
if (part.source.type === "url" && typeof part.source.url === "string") {
|
|
3635
|
-
return {
|
|
3636
|
-
type: "image_url",
|
|
3637
|
-
image_url: {
|
|
3638
|
-
url: part.source.url
|
|
3639
|
-
}
|
|
3640
|
-
};
|
|
3641
|
-
}
|
|
3642
|
-
if (part.source.type === "base64" && part.source.media_type && part.source.data) {
|
|
3643
|
-
return {
|
|
3644
|
-
type: "image_url",
|
|
3645
|
-
image_url: {
|
|
3646
|
-
url: `data:${part.source.media_type};base64,${part.source.data}`
|
|
3647
|
-
}
|
|
3648
|
-
};
|
|
3649
|
-
}
|
|
3650
|
-
}
|
|
3651
|
-
return {
|
|
3652
|
-
type: "text",
|
|
3653
|
-
text: JSON.stringify(part)
|
|
3654
|
-
};
|
|
3655
|
-
}).filter((block) => block !== null);
|
|
3656
|
-
if (normalizedBlocks.length === 0) {
|
|
3657
|
-
return "";
|
|
3658
|
-
}
|
|
3659
|
-
const firstBlock = normalizedBlocks[0];
|
|
3660
|
-
if (normalizedBlocks.length === 1 && firstBlock && firstBlock.type === "text") {
|
|
3661
|
-
return typeof firstBlock.text === "string" ? firstBlock.text : "";
|
|
3662
|
-
}
|
|
3663
|
-
return JSON.stringify(normalizedBlocks);
|
|
3664
|
-
}
|
|
3665
|
-
if (typeof content === "object") {
|
|
3666
|
-
if ("content" in content) {
|
|
3667
|
-
return this.normalizeToolResultContent(content.content);
|
|
3668
|
-
}
|
|
3721
|
+
if (typeof content === "object")
|
|
3669
3722
|
return JSON.stringify(content);
|
|
3670
|
-
}
|
|
3671
3723
|
return String(content);
|
|
3672
3724
|
}
|
|
3673
3725
|
convertUserMessage(msg, messages) {
|
|
3674
3726
|
const content = msg.content;
|
|
3727
|
+
if (content === undefined) {
|
|
3728
|
+
messages.push({ role: "user", content: null });
|
|
3729
|
+
return;
|
|
3730
|
+
}
|
|
3675
3731
|
if (typeof content === "string") {
|
|
3676
3732
|
messages.push({
|
|
3677
3733
|
role: "user",
|
|
@@ -3683,8 +3739,8 @@ class AnthropicToOpenAIConverter {
|
|
|
3683
3739
|
const hasToolResult = content.some((block) => block?.type === "tool_result");
|
|
3684
3740
|
if (hasToolResult) {
|
|
3685
3741
|
for (const block of content) {
|
|
3686
|
-
const toolUseId = typeof block?.tool_use_id === "string" ? block.tool_use_id
|
|
3687
|
-
if (block?.type !== "tool_result"
|
|
3742
|
+
const toolUseId = typeof block?.tool_use_id === "string" ? block.tool_use_id : "";
|
|
3743
|
+
if (block?.type !== "tool_result") {
|
|
3688
3744
|
continue;
|
|
3689
3745
|
}
|
|
3690
3746
|
messages.push({
|
|
@@ -3698,39 +3754,43 @@ class AnthropicToOpenAIConverter {
|
|
|
3698
3754
|
if (userContentBlocks.length > 0) {
|
|
3699
3755
|
messages.push({
|
|
3700
3756
|
role: "user",
|
|
3701
|
-
content: userContentBlocks
|
|
3757
|
+
content: this.normalizeOpenAIMessageContent(userContentBlocks)
|
|
3702
3758
|
});
|
|
3703
3759
|
}
|
|
3760
|
+
return;
|
|
3704
3761
|
}
|
|
3762
|
+
messages.push({ role: "user", content });
|
|
3705
3763
|
}
|
|
3706
3764
|
convertUserContentBlock(block) {
|
|
3707
3765
|
if (!block || typeof block !== "object") {
|
|
3708
3766
|
return null;
|
|
3709
3767
|
}
|
|
3710
3768
|
if (block.type === "text") {
|
|
3711
|
-
|
|
3769
|
+
const textBlock = {
|
|
3712
3770
|
type: "text",
|
|
3713
3771
|
text: block.text || ""
|
|
3714
3772
|
};
|
|
3773
|
+
if (block.cache_control !== undefined) {
|
|
3774
|
+
textBlock.cache_control = block.cache_control;
|
|
3775
|
+
}
|
|
3776
|
+
return textBlock;
|
|
3715
3777
|
}
|
|
3716
3778
|
if (block.type === "image" && block.source) {
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
};
|
|
3722
|
-
}
|
|
3723
|
-
if (block.source.type === "base64" && block.source.media_type && block.source.data) {
|
|
3724
|
-
return {
|
|
3725
|
-
type: "image_url",
|
|
3726
|
-
image_url: { url: `data:${block.source.media_type};base64,${block.source.data}` }
|
|
3727
|
-
};
|
|
3728
|
-
}
|
|
3779
|
+
const mediaType = typeof block.source.media_type === "string" ? block.source.media_type : "image/png";
|
|
3780
|
+
const data = typeof block.source.data === "string" ? block.source.data : "";
|
|
3781
|
+
return {
|
|
3782
|
+
type: "image_url",
|
|
3783
|
+
image_url: { url: `data:${mediaType};base64,${data}` }
|
|
3784
|
+
};
|
|
3729
3785
|
}
|
|
3730
3786
|
return null;
|
|
3731
3787
|
}
|
|
3732
3788
|
convertAssistantMessage(msg, messages) {
|
|
3733
3789
|
const content = msg.content;
|
|
3790
|
+
if (content === undefined) {
|
|
3791
|
+
messages.push({ role: "assistant", content: null });
|
|
3792
|
+
return;
|
|
3793
|
+
}
|
|
3734
3794
|
if (typeof content === "string") {
|
|
3735
3795
|
messages.push({
|
|
3736
3796
|
role: "assistant",
|
|
@@ -3739,122 +3799,49 @@ class AnthropicToOpenAIConverter {
|
|
|
3739
3799
|
return;
|
|
3740
3800
|
}
|
|
3741
3801
|
if (Array.isArray(content)) {
|
|
3742
|
-
const
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
}
|
|
3752
|
-
}));
|
|
3753
|
-
let textContent = "";
|
|
3754
|
-
for (const block of content) {
|
|
3755
|
-
if (block.type === "text") {
|
|
3756
|
-
textContent += block.text || "";
|
|
3757
|
-
} else if (block.type === "thinking") {
|
|
3758
|
-
textContent += `<thinking>
|
|
3759
|
-
${block.thinking || ""}
|
|
3760
|
-
</thinking>
|
|
3761
|
-
|
|
3762
|
-
`;
|
|
3802
|
+
const contentParts = [];
|
|
3803
|
+
const toolCalls = [];
|
|
3804
|
+
for (const block of content) {
|
|
3805
|
+
if (!block || typeof block !== "object")
|
|
3806
|
+
continue;
|
|
3807
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
3808
|
+
const textBlock = { type: "text", text: block.text };
|
|
3809
|
+
if (block.cache_control !== undefined) {
|
|
3810
|
+
textBlock.cache_control = block.cache_control;
|
|
3763
3811
|
}
|
|
3812
|
+
contentParts.push(textBlock);
|
|
3813
|
+
continue;
|
|
3764
3814
|
}
|
|
3765
|
-
if (
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3815
|
+
if (block.type === "tool_use") {
|
|
3816
|
+
toolCalls.push({
|
|
3817
|
+
id: typeof block.id === "string" ? block.id : "",
|
|
3818
|
+
type: "function",
|
|
3819
|
+
function: {
|
|
3820
|
+
name: typeof block.name === "string" ? block.name : "",
|
|
3821
|
+
arguments: JSON.stringify(block.input ?? {})
|
|
3822
|
+
}
|
|
3770
3823
|
});
|
|
3771
3824
|
}
|
|
3772
|
-
}
|
|
3773
|
-
|
|
3774
|
-
for (const block of content) {
|
|
3775
|
-
if (block.type === "text") {
|
|
3776
|
-
textContent += block.text || "";
|
|
3777
|
-
} else if (block.type === "thinking") {
|
|
3778
|
-
textContent += `<thinking>
|
|
3779
|
-
${block.thinking || ""}
|
|
3780
|
-
</thinking>
|
|
3781
|
-
|
|
3782
|
-
`;
|
|
3783
|
-
}
|
|
3784
|
-
}
|
|
3825
|
+
}
|
|
3826
|
+
if (contentParts.length > 0 || toolCalls.length > 0) {
|
|
3785
3827
|
messages.push({
|
|
3786
3828
|
role: "assistant",
|
|
3787
|
-
content:
|
|
3829
|
+
content: contentParts.length > 0 ? this.normalizeOpenAIMessageContent(contentParts) : null,
|
|
3830
|
+
...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
|
|
3788
3831
|
});
|
|
3789
3832
|
}
|
|
3790
|
-
}
|
|
3791
|
-
}
|
|
3792
|
-
normalizeToolCallId(rawId, name, index) {
|
|
3793
|
-
if (typeof rawId === "string" && rawId.trim()) {
|
|
3794
|
-
return rawId.trim();
|
|
3795
|
-
}
|
|
3796
|
-
const normalizedName = typeof name === "string" && name.trim() ? name.trim() : "tool";
|
|
3797
|
-
return `call_${normalizedName}_${index}`;
|
|
3798
|
-
}
|
|
3799
|
-
mapAnthropicToolChoiceToOpenAI(toolChoice) {
|
|
3800
|
-
if (toolChoice === undefined || toolChoice === null) {
|
|
3801
|
-
return;
|
|
3802
|
-
}
|
|
3803
|
-
if (typeof toolChoice === "string") {
|
|
3804
|
-
const normalized = toolChoice.trim().toLowerCase();
|
|
3805
|
-
if (normalized === "auto" || normalized === "none" || normalized === "required") {
|
|
3806
|
-
return normalized;
|
|
3807
|
-
}
|
|
3808
|
-
if (normalized === "any") {
|
|
3809
|
-
return "required";
|
|
3810
|
-
}
|
|
3811
|
-
return;
|
|
3812
|
-
}
|
|
3813
|
-
if (typeof toolChoice !== "object" || Array.isArray(toolChoice)) {
|
|
3814
3833
|
return;
|
|
3815
3834
|
}
|
|
3816
|
-
|
|
3817
|
-
const type = typeof choice.type === "string" ? choice.type.trim().toLowerCase() : "";
|
|
3818
|
-
if (type === "tool") {
|
|
3819
|
-
const toolName = typeof choice.name === "string" ? choice.name.trim() : "";
|
|
3820
|
-
if (!toolName) {
|
|
3821
|
-
return;
|
|
3822
|
-
}
|
|
3823
|
-
return {
|
|
3824
|
-
type: "function",
|
|
3825
|
-
function: {
|
|
3826
|
-
name: toolName
|
|
3827
|
-
}
|
|
3828
|
-
};
|
|
3829
|
-
}
|
|
3830
|
-
if (type === "any") {
|
|
3831
|
-
return "required";
|
|
3832
|
-
}
|
|
3833
|
-
if (type === "auto" || type === "none") {
|
|
3834
|
-
return type;
|
|
3835
|
-
}
|
|
3836
|
-
return;
|
|
3835
|
+
messages.push({ role: "assistant", content });
|
|
3837
3836
|
}
|
|
3838
|
-
|
|
3839
|
-
if (
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
return null;
|
|
3845
|
-
return {
|
|
3846
|
-
type: "base64",
|
|
3847
|
-
media_type: parts[0].replace("data:", ""),
|
|
3848
|
-
data: parts[1]
|
|
3849
|
-
};
|
|
3850
|
-
}
|
|
3851
|
-
if (url.startsWith("http://") || url.startsWith("https://")) {
|
|
3852
|
-
return {
|
|
3853
|
-
type: "url",
|
|
3854
|
-
url
|
|
3855
|
-
};
|
|
3837
|
+
normalizeOpenAIMessageContent(contentParts) {
|
|
3838
|
+
if (contentParts.length === 1) {
|
|
3839
|
+
const first = contentParts[0];
|
|
3840
|
+
if (first?.type === "text" && first.cache_control === undefined) {
|
|
3841
|
+
return first.text;
|
|
3842
|
+
}
|
|
3856
3843
|
}
|
|
3857
|
-
return
|
|
3844
|
+
return contentParts;
|
|
3858
3845
|
}
|
|
3859
3846
|
normalizeToolParametersSchema(schema) {
|
|
3860
3847
|
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
|
|
@@ -3868,7 +3855,24 @@ ${block.thinking || ""}
|
|
|
3868
3855
|
normalized.properties = {};
|
|
3869
3856
|
}
|
|
3870
3857
|
}
|
|
3871
|
-
return normalized;
|
|
3858
|
+
return normalized;
|
|
3859
|
+
}
|
|
3860
|
+
cleanOpenAIToolSchema(schema) {
|
|
3861
|
+
return this.normalizeJsonSchema(schema);
|
|
3862
|
+
}
|
|
3863
|
+
resolvePromptCacheKey(anthropicBody) {
|
|
3864
|
+
const candidates = [
|
|
3865
|
+
anthropicBody?.prompt_cache_key,
|
|
3866
|
+
anthropicBody?.metadata?.prompt_cache_key,
|
|
3867
|
+
anthropicBody?.metadata?.provider_id,
|
|
3868
|
+
anthropicBody?.metadata?.providerId
|
|
3869
|
+
];
|
|
3870
|
+
for (const candidate of candidates) {
|
|
3871
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
3872
|
+
return candidate.trim();
|
|
3873
|
+
}
|
|
3874
|
+
}
|
|
3875
|
+
return;
|
|
3872
3876
|
}
|
|
3873
3877
|
normalizeJsonSchema(schema) {
|
|
3874
3878
|
if (!schema || typeof schema !== "object") {
|
|
@@ -3879,6 +3883,9 @@ ${block.thinking || ""}
|
|
|
3879
3883
|
}
|
|
3880
3884
|
const result = {};
|
|
3881
3885
|
for (const [key, value] of Object.entries(schema)) {
|
|
3886
|
+
if (key === "format" && value === "uri") {
|
|
3887
|
+
continue;
|
|
3888
|
+
}
|
|
3882
3889
|
if (key === "properties" && value && typeof value === "object" && !Array.isArray(value)) {
|
|
3883
3890
|
const normalizedProperties = {};
|
|
3884
3891
|
for (const [propName, propSchema] of Object.entries(value)) {
|
|
@@ -3944,6 +3951,14 @@ ${block.thinking || ""}
|
|
|
3944
3951
|
headers: ctx.response.headers
|
|
3945
3952
|
});
|
|
3946
3953
|
}
|
|
3954
|
+
if (this.isLikelyOpenAIErrorBody(openaiBody) && !this.isLikelyOpenAISuccessBody(openaiBody)) {
|
|
3955
|
+
const anthropicError = this.convertOpenAIErrorToAnthropic(openaiBody, ctx.response.status);
|
|
3956
|
+
return new Response(JSON.stringify(anthropicError), {
|
|
3957
|
+
status: ctx.response.status,
|
|
3958
|
+
statusText: ctx.response.statusText,
|
|
3959
|
+
headers: ctx.response.headers
|
|
3960
|
+
});
|
|
3961
|
+
}
|
|
3947
3962
|
if (!this.isLikelyOpenAISuccessBody(openaiBody)) {
|
|
3948
3963
|
return ctx.response;
|
|
3949
3964
|
}
|
|
@@ -3970,11 +3985,12 @@ ${block.thinking || ""}
|
|
|
3970
3985
|
return 0;
|
|
3971
3986
|
}
|
|
3972
3987
|
convertOpenAIErrorToAnthropic(openaiBody, status) {
|
|
3973
|
-
const message = typeof openaiBody?.error?.message === "string" && openaiBody.error.message || typeof openaiBody?.message === "string" && openaiBody.message ||
|
|
3988
|
+
const message = typeof openaiBody?.error?.message === "string" && openaiBody.error.message || typeof openaiBody?.message === "string" && openaiBody.message || "Upstream error";
|
|
3989
|
+
const type = typeof openaiBody?.error?.type === "string" && openaiBody.error.type.length > 0 ? openaiBody.error.type : "invalid_request_error";
|
|
3974
3990
|
return {
|
|
3975
3991
|
type: "error",
|
|
3976
3992
|
error: {
|
|
3977
|
-
type
|
|
3993
|
+
type,
|
|
3978
3994
|
message
|
|
3979
3995
|
}
|
|
3980
3996
|
};
|
|
@@ -4065,68 +4081,11 @@ ${block.thinking || ""}
|
|
|
4065
4081
|
}
|
|
4066
4082
|
return texts;
|
|
4067
4083
|
}
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
continue;
|
|
4072
|
-
const normalized = text.trim();
|
|
4073
|
-
if (!normalized)
|
|
4074
|
-
continue;
|
|
4075
|
-
if (seen.has(normalized))
|
|
4076
|
-
continue;
|
|
4077
|
-
content.push({
|
|
4078
|
-
type: "thinking",
|
|
4079
|
-
thinking: normalized
|
|
4080
|
-
});
|
|
4081
|
-
seen.add(normalized);
|
|
4082
|
-
}
|
|
4083
|
-
}
|
|
4084
|
-
appendParsedContent(content, parsedContent, seenThinking) {
|
|
4085
|
-
for (const block of parsedContent) {
|
|
4086
|
-
if (!block || typeof block !== "object")
|
|
4087
|
-
continue;
|
|
4088
|
-
if (block.type === "thinking") {
|
|
4089
|
-
const thinkingText = typeof block.thinking === "string" ? block.thinking.trim() : "";
|
|
4090
|
-
if (!thinkingText || seenThinking.has(thinkingText)) {
|
|
4091
|
-
continue;
|
|
4092
|
-
}
|
|
4093
|
-
content.push({
|
|
4094
|
-
type: "thinking",
|
|
4095
|
-
thinking: thinkingText
|
|
4096
|
-
});
|
|
4097
|
-
seenThinking.add(thinkingText);
|
|
4098
|
-
continue;
|
|
4099
|
-
}
|
|
4100
|
-
if (block.type === "text") {
|
|
4101
|
-
const text = typeof block.text === "string" ? block.text : "";
|
|
4102
|
-
if (!text)
|
|
4103
|
-
continue;
|
|
4104
|
-
content.push({
|
|
4105
|
-
type: "text",
|
|
4106
|
-
text
|
|
4107
|
-
});
|
|
4108
|
-
}
|
|
4109
|
-
}
|
|
4110
|
-
}
|
|
4111
|
-
extractChatCompletionReasoningTexts(message) {
|
|
4112
|
-
const texts = [];
|
|
4113
|
-
if (!message || typeof message !== "object") {
|
|
4114
|
-
return texts;
|
|
4115
|
-
}
|
|
4116
|
-
const directReasoning = this.extractReasoningSummaryTexts({
|
|
4117
|
-
summary: message.reasoning,
|
|
4118
|
-
text: message.reasoning_content
|
|
4119
|
-
});
|
|
4120
|
-
texts.push(...directReasoning);
|
|
4121
|
-
if (typeof message.reasoning === "string") {
|
|
4122
|
-
texts.push(message.reasoning);
|
|
4123
|
-
}
|
|
4124
|
-
if (Array.isArray(message.reasoning_details)) {
|
|
4125
|
-
for (const detail of message.reasoning_details) {
|
|
4126
|
-
texts.push(...this.extractReasoningSummaryTexts(detail));
|
|
4127
|
-
}
|
|
4084
|
+
extractStrictResponsesReasoningText(reasoningItem) {
|
|
4085
|
+
if (!reasoningItem || typeof reasoningItem !== "object" || !Array.isArray(reasoningItem.summary)) {
|
|
4086
|
+
return "";
|
|
4128
4087
|
}
|
|
4129
|
-
return
|
|
4088
|
+
return reasoningItem.summary.filter((item) => item?.type === "summary_text" && typeof item.text === "string").map((item) => item.text).join("");
|
|
4130
4089
|
}
|
|
4131
4090
|
extractReasoningTextFromDeltaContentPart(part) {
|
|
4132
4091
|
if (!part || typeof part !== "object") {
|
|
@@ -4230,6 +4189,19 @@ ${block.thinking || ""}
|
|
|
4230
4189
|
ctx.streamState.set("chat_thinking_index", index);
|
|
4231
4190
|
return index;
|
|
4232
4191
|
}
|
|
4192
|
+
closeChatNonToolBlock(events, ctx) {
|
|
4193
|
+
const textIndex = ctx.streamState.get("chat_text_index");
|
|
4194
|
+
if (typeof textIndex === "number" && Number.isFinite(textIndex)) {
|
|
4195
|
+
events.push({ type: "content_block_stop", index: textIndex });
|
|
4196
|
+
ctx.streamState.delete("chat_text_index");
|
|
4197
|
+
ctx.streamState.delete("text_block_started");
|
|
4198
|
+
}
|
|
4199
|
+
const thinkingIndex = ctx.streamState.get("chat_thinking_index");
|
|
4200
|
+
if (typeof thinkingIndex === "number" && Number.isFinite(thinkingIndex)) {
|
|
4201
|
+
events.push({ type: "content_block_stop", index: thinkingIndex });
|
|
4202
|
+
ctx.streamState.delete("chat_thinking_index");
|
|
4203
|
+
}
|
|
4204
|
+
}
|
|
4233
4205
|
resolveChatToolIndex(rawIndex, ctx) {
|
|
4234
4206
|
const normalizedRawIndex = Number.isFinite(rawIndex) ? Math.trunc(rawIndex) : 0;
|
|
4235
4207
|
const mapKey = `chat_tool_index_${normalizedRawIndex}`;
|
|
@@ -4255,6 +4227,59 @@ ${block.thinking || ""}
|
|
|
4255
4227
|
}
|
|
4256
4228
|
return 1;
|
|
4257
4229
|
}
|
|
4230
|
+
nextResponsesContentIndex(ctx) {
|
|
4231
|
+
const current = ctx.streamState.get("resp_next_content_index");
|
|
4232
|
+
const next = typeof current === "number" && Number.isFinite(current) ? current : 0;
|
|
4233
|
+
ctx.streamState.set("resp_next_content_index", next + 1);
|
|
4234
|
+
return next;
|
|
4235
|
+
}
|
|
4236
|
+
resolveResponsesContentIndex(chunk, ctx) {
|
|
4237
|
+
const itemId = typeof chunk?.item_id === "string" ? chunk.item_id : "";
|
|
4238
|
+
const contentIndex = typeof chunk?.content_index === "number" ? chunk.content_index : undefined;
|
|
4239
|
+
const outputIndex = typeof chunk?.output_index === "number" ? chunk.output_index : undefined;
|
|
4240
|
+
const key = itemId && contentIndex !== undefined ? `resp_part_${itemId}_${contentIndex}` : outputIndex !== undefined && contentIndex !== undefined ? `resp_part_out_${outputIndex}_${contentIndex}` : "";
|
|
4241
|
+
if (key) {
|
|
4242
|
+
const existing = ctx.streamState.get(key);
|
|
4243
|
+
if (typeof existing === "number" && Number.isFinite(existing))
|
|
4244
|
+
return existing;
|
|
4245
|
+
const assigned2 = this.nextResponsesContentIndex(ctx);
|
|
4246
|
+
ctx.streamState.set(key, assigned2);
|
|
4247
|
+
return assigned2;
|
|
4248
|
+
}
|
|
4249
|
+
const fallback = ctx.streamState.get("resp_fallback_text_index");
|
|
4250
|
+
if (typeof fallback === "number" && Number.isFinite(fallback))
|
|
4251
|
+
return fallback;
|
|
4252
|
+
const assigned = this.nextResponsesContentIndex(ctx);
|
|
4253
|
+
ctx.streamState.set("resp_fallback_text_index", assigned);
|
|
4254
|
+
return assigned;
|
|
4255
|
+
}
|
|
4256
|
+
ensureResponsesTextBlockStarted(events, ctx, index) {
|
|
4257
|
+
const key = `resp_text_block_${index}`;
|
|
4258
|
+
if (ctx.streamState.get(key) === true)
|
|
4259
|
+
return;
|
|
4260
|
+
events.push({
|
|
4261
|
+
type: "content_block_start",
|
|
4262
|
+
index,
|
|
4263
|
+
content_block: { type: "text", text: "" }
|
|
4264
|
+
});
|
|
4265
|
+
ctx.streamState.set(key, true);
|
|
4266
|
+
}
|
|
4267
|
+
resolveResponsesToolIndex(chunk, ctx) {
|
|
4268
|
+
const itemId = typeof chunk?.item_id === "string" ? chunk.item_id : "";
|
|
4269
|
+
if (itemId) {
|
|
4270
|
+
const byItem = ctx.streamState.get(`resp_tool_item_${itemId}`);
|
|
4271
|
+
if (typeof byItem === "number" && Number.isFinite(byItem))
|
|
4272
|
+
return byItem;
|
|
4273
|
+
}
|
|
4274
|
+
const outputIndex = typeof chunk?.output_index === "number" ? chunk.output_index : undefined;
|
|
4275
|
+
if (outputIndex !== undefined) {
|
|
4276
|
+
const byOutput = ctx.streamState.get(`resp_tool_out_${outputIndex}`);
|
|
4277
|
+
if (typeof byOutput === "number" && Number.isFinite(byOutput))
|
|
4278
|
+
return byOutput;
|
|
4279
|
+
}
|
|
4280
|
+
const last = ctx.streamState.get("resp_last_tool_index");
|
|
4281
|
+
return typeof last === "number" && Number.isFinite(last) ? last : undefined;
|
|
4282
|
+
}
|
|
4258
4283
|
ensureResponsesThinkingBlockStarted(events, ctx, index) {
|
|
4259
4284
|
const blockKey = `resp_reasoning_block_${index}`;
|
|
4260
4285
|
if (ctx.streamState.get(blockKey) === true) {
|
|
@@ -4305,15 +4330,16 @@ ${block.thinking || ""}
|
|
|
4305
4330
|
convertOpenAIResponsesToAnthropic(openaiBody) {
|
|
4306
4331
|
const content = [];
|
|
4307
4332
|
const output = Array.isArray(openaiBody.output) ? openaiBody.output : [];
|
|
4333
|
+
let hasToolUse = false;
|
|
4308
4334
|
for (const item of output) {
|
|
4309
4335
|
if (!item || typeof item !== "object")
|
|
4310
4336
|
continue;
|
|
4311
4337
|
if (item.type === "reasoning") {
|
|
4312
|
-
const
|
|
4313
|
-
|
|
4338
|
+
const thinkingText = this.extractStrictResponsesReasoningText(item);
|
|
4339
|
+
if (thinkingText) {
|
|
4314
4340
|
content.push({
|
|
4315
4341
|
type: "thinking",
|
|
4316
|
-
thinking:
|
|
4342
|
+
thinking: thinkingText
|
|
4317
4343
|
});
|
|
4318
4344
|
}
|
|
4319
4345
|
continue;
|
|
@@ -4337,6 +4363,7 @@ ${block.thinking || ""}
|
|
|
4337
4363
|
name: item.name || "",
|
|
4338
4364
|
input: parsedInput
|
|
4339
4365
|
});
|
|
4366
|
+
hasToolUse = true;
|
|
4340
4367
|
continue;
|
|
4341
4368
|
}
|
|
4342
4369
|
if (item.type === "message" && Array.isArray(item.content)) {
|
|
@@ -4344,28 +4371,64 @@ ${block.thinking || ""}
|
|
|
4344
4371
|
if (!part || typeof part !== "object")
|
|
4345
4372
|
continue;
|
|
4346
4373
|
if (part.type === "output_text" && typeof part.text === "string") {
|
|
4347
|
-
|
|
4374
|
+
if (part.text)
|
|
4375
|
+
content.push({ type: "text", text: part.text });
|
|
4348
4376
|
continue;
|
|
4349
4377
|
}
|
|
4350
|
-
if (part.type === "
|
|
4351
|
-
|
|
4378
|
+
if (part.type === "refusal" && typeof part.refusal === "string") {
|
|
4379
|
+
if (part.refusal)
|
|
4380
|
+
content.push({ type: "text", text: part.refusal });
|
|
4352
4381
|
}
|
|
4353
4382
|
}
|
|
4354
4383
|
}
|
|
4355
4384
|
}
|
|
4356
|
-
const stopReason =
|
|
4385
|
+
const stopReason = this.mapResponsesStopReason(openaiBody.status, hasToolUse, openaiBody.incomplete_details?.reason);
|
|
4386
|
+
const usage = this.buildAnthropicUsageFromResponses(openaiBody.usage);
|
|
4357
4387
|
return {
|
|
4358
|
-
id: openaiBody.id
|
|
4388
|
+
id: typeof openaiBody.id === "string" ? openaiBody.id : "",
|
|
4359
4389
|
type: "message",
|
|
4360
4390
|
role: "assistant",
|
|
4361
4391
|
content,
|
|
4362
|
-
model: openaiBody.model
|
|
4392
|
+
model: typeof openaiBody.model === "string" ? openaiBody.model : "",
|
|
4363
4393
|
stop_reason: stopReason,
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4394
|
+
stop_sequence: null,
|
|
4395
|
+
usage
|
|
4396
|
+
};
|
|
4397
|
+
}
|
|
4398
|
+
mapResponsesStopReason(status, hasToolUse, incompleteReason) {
|
|
4399
|
+
if (typeof status !== "string") {
|
|
4400
|
+
return null;
|
|
4401
|
+
}
|
|
4402
|
+
if (status === "completed") {
|
|
4403
|
+
return hasToolUse ? "tool_use" : "end_turn";
|
|
4404
|
+
}
|
|
4405
|
+
if (status === "incomplete") {
|
|
4406
|
+
return incompleteReason === undefined || incompleteReason === "max_output_tokens" || incompleteReason === "max_tokens" ? "max_tokens" : "end_turn";
|
|
4407
|
+
}
|
|
4408
|
+
return "end_turn";
|
|
4409
|
+
}
|
|
4410
|
+
buildAnthropicUsageFromResponses(usage) {
|
|
4411
|
+
if (!usage || typeof usage !== "object") {
|
|
4412
|
+
return { input_tokens: 0, output_tokens: 0 };
|
|
4413
|
+
}
|
|
4414
|
+
const result = {
|
|
4415
|
+
input_tokens: typeof usage.input_tokens === "number" ? usage.input_tokens : 0,
|
|
4416
|
+
output_tokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0
|
|
4368
4417
|
};
|
|
4418
|
+
const cachedFromInput = usage.input_tokens_details?.cached_tokens;
|
|
4419
|
+
const cachedFromPrompt = usage.prompt_tokens_details?.cached_tokens;
|
|
4420
|
+
if (typeof cachedFromInput === "number") {
|
|
4421
|
+
result.cache_read_input_tokens = cachedFromInput;
|
|
4422
|
+
} else if (typeof cachedFromPrompt === "number") {
|
|
4423
|
+
result.cache_read_input_tokens = cachedFromPrompt;
|
|
4424
|
+
}
|
|
4425
|
+
if (usage.cache_read_input_tokens !== undefined) {
|
|
4426
|
+
result.cache_read_input_tokens = usage.cache_read_input_tokens;
|
|
4427
|
+
}
|
|
4428
|
+
if (usage.cache_creation_input_tokens !== undefined) {
|
|
4429
|
+
result.cache_creation_input_tokens = usage.cache_creation_input_tokens;
|
|
4430
|
+
}
|
|
4431
|
+
return result;
|
|
4369
4432
|
}
|
|
4370
4433
|
convertOpenAIResponseToAnthropic(openaiBody) {
|
|
4371
4434
|
const choice = openaiBody.choices?.[0];
|
|
@@ -4385,77 +4448,90 @@ ${block.thinking || ""}
|
|
|
4385
4448
|
}
|
|
4386
4449
|
const message = choice.message;
|
|
4387
4450
|
const content = [];
|
|
4388
|
-
const
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
const parsedValue = JSON.parse(tc.function?.arguments || "{}");
|
|
4395
|
-
if (parsedValue && typeof parsedValue === "object" && !Array.isArray(parsedValue)) {
|
|
4396
|
-
parsedInput = parsedValue;
|
|
4397
|
-
}
|
|
4398
|
-
} catch {
|
|
4399
|
-
parsedInput = {};
|
|
4400
|
-
}
|
|
4401
|
-
content.push({
|
|
4402
|
-
type: "tool_use",
|
|
4403
|
-
id: tc.id || "",
|
|
4404
|
-
name: tc.function?.name || "",
|
|
4405
|
-
input: parsedInput
|
|
4406
|
-
});
|
|
4407
|
-
}
|
|
4451
|
+
const hasToolCalls2 = Array.isArray(message.tool_calls) && message.tool_calls.length > 0;
|
|
4452
|
+
if (message.refusal && typeof message.refusal === "string") {
|
|
4453
|
+
content.push({
|
|
4454
|
+
type: "text",
|
|
4455
|
+
text: message.refusal
|
|
4456
|
+
});
|
|
4408
4457
|
}
|
|
4409
4458
|
if (typeof message.content === "string" && message.content.length > 0) {
|
|
4410
|
-
|
|
4411
|
-
this.appendParsedContent(content, parsedContent, seenThinking);
|
|
4459
|
+
content.push({ type: "text", text: message.content });
|
|
4412
4460
|
} else if (Array.isArray(message.content)) {
|
|
4413
4461
|
for (const part of message.content) {
|
|
4414
|
-
if (part?.type === "text" && typeof part.text === "string") {
|
|
4415
|
-
|
|
4462
|
+
if ((part?.type === "text" || part?.type === "output_text") && typeof part.text === "string") {
|
|
4463
|
+
if (part.text)
|
|
4464
|
+
content.push({ type: "text", text: part.text });
|
|
4416
4465
|
continue;
|
|
4417
4466
|
}
|
|
4418
|
-
if (typeof part === "string") {
|
|
4419
|
-
|
|
4467
|
+
if (part?.type === "refusal" && typeof part.refusal === "string") {
|
|
4468
|
+
if (part.refusal)
|
|
4469
|
+
content.push({ type: "text", text: part.refusal });
|
|
4420
4470
|
continue;
|
|
4421
4471
|
}
|
|
4422
|
-
if (part && typeof part === "object") {
|
|
4423
|
-
const partType = typeof part.type === "string" ? part.type : "";
|
|
4424
|
-
if (partType === "reasoning" || partType === "thinking" || part.thought === true) {
|
|
4425
|
-
this.appendUniqueThinkingBlocks(content, this.extractReasoningSummaryTexts(part), seenThinking);
|
|
4426
|
-
continue;
|
|
4427
|
-
}
|
|
4428
|
-
if (part.type === "image_url" && typeof part.image_url?.url === "string") {
|
|
4429
|
-
const source = this.parseOpenAIImageUrlSource(part.image_url.url);
|
|
4430
|
-
if (source) {
|
|
4431
|
-
content.push({
|
|
4432
|
-
type: "image",
|
|
4433
|
-
source
|
|
4434
|
-
});
|
|
4435
|
-
continue;
|
|
4436
|
-
}
|
|
4437
|
-
}
|
|
4438
|
-
content.push({
|
|
4439
|
-
type: "text",
|
|
4440
|
-
text: JSON.stringify(part)
|
|
4441
|
-
});
|
|
4442
|
-
}
|
|
4443
4472
|
}
|
|
4444
4473
|
}
|
|
4445
|
-
|
|
4474
|
+
if (hasToolCalls2) {
|
|
4475
|
+
for (const tc of message.tool_calls) {
|
|
4476
|
+
content.push({
|
|
4477
|
+
type: "tool_use",
|
|
4478
|
+
id: tc.id || "",
|
|
4479
|
+
name: tc.function?.name || "",
|
|
4480
|
+
input: this.parseToolArguments(tc.function?.arguments)
|
|
4481
|
+
});
|
|
4482
|
+
}
|
|
4483
|
+
}
|
|
4484
|
+
if (!hasToolCalls2 && message.function_call) {
|
|
4485
|
+
const functionCall = message.function_call;
|
|
4486
|
+
const hasArguments = functionCall.arguments !== undefined;
|
|
4487
|
+
const name = typeof functionCall.name === "string" ? functionCall.name : "";
|
|
4488
|
+
if (name || hasArguments) {
|
|
4489
|
+
content.push({
|
|
4490
|
+
type: "tool_use",
|
|
4491
|
+
id: typeof functionCall.id === "string" ? functionCall.id : "",
|
|
4492
|
+
name,
|
|
4493
|
+
input: typeof functionCall.arguments === "string" ? this.parseToolArguments(functionCall.arguments) : functionCall.arguments && typeof functionCall.arguments === "object" ? functionCall.arguments : {}
|
|
4494
|
+
});
|
|
4495
|
+
}
|
|
4496
|
+
}
|
|
4497
|
+
let stopReason = mapOpenAIFinishReasonToAnthropic(choice.finish_reason || "stop");
|
|
4498
|
+
if (!choice.finish_reason && content.some((block) => block.type === "tool_use")) {
|
|
4499
|
+
stopReason = "tool_use";
|
|
4500
|
+
}
|
|
4501
|
+
const usage = {
|
|
4502
|
+
input_tokens: openaiBody.usage?.prompt_tokens ?? openaiBody.usage?.input_tokens ?? 0,
|
|
4503
|
+
output_tokens: openaiBody.usage?.completion_tokens ?? openaiBody.usage?.output_tokens ?? 0
|
|
4504
|
+
};
|
|
4505
|
+
const cacheReadTokens = openaiBody.usage?.prompt_tokens_details?.cached_tokens ?? openaiBody.usage?.cache_read_input_tokens;
|
|
4506
|
+
if (typeof cacheReadTokens === "number") {
|
|
4507
|
+
usage.cache_read_input_tokens = cacheReadTokens;
|
|
4508
|
+
}
|
|
4509
|
+
const cacheCreationTokens = openaiBody.usage?.cache_creation_input_tokens;
|
|
4510
|
+
if (typeof cacheCreationTokens === "number") {
|
|
4511
|
+
usage.cache_creation_input_tokens = cacheCreationTokens;
|
|
4512
|
+
}
|
|
4446
4513
|
return {
|
|
4447
|
-
id: openaiBody.id
|
|
4514
|
+
id: typeof openaiBody.id === "string" ? openaiBody.id : "",
|
|
4448
4515
|
type: "message",
|
|
4449
4516
|
role: "assistant",
|
|
4450
4517
|
content,
|
|
4451
|
-
model: openaiBody.model
|
|
4518
|
+
model: typeof openaiBody.model === "string" ? openaiBody.model : "",
|
|
4452
4519
|
stop_reason: stopReason,
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
output_tokens: openaiBody.usage?.completion_tokens || 0
|
|
4456
|
-
}
|
|
4520
|
+
stop_sequence: null,
|
|
4521
|
+
usage
|
|
4457
4522
|
};
|
|
4458
4523
|
}
|
|
4524
|
+
parseToolArguments(argumentsValue) {
|
|
4525
|
+
if (typeof argumentsValue !== "string") {
|
|
4526
|
+
return {};
|
|
4527
|
+
}
|
|
4528
|
+
try {
|
|
4529
|
+
const parsedValue = JSON.parse(argumentsValue || "{}");
|
|
4530
|
+
return parsedValue && typeof parsedValue === "object" && !Array.isArray(parsedValue) ? parsedValue : {};
|
|
4531
|
+
} catch {
|
|
4532
|
+
return {};
|
|
4533
|
+
}
|
|
4534
|
+
}
|
|
4459
4535
|
async processStreamChunk(chunk, ctx) {
|
|
4460
4536
|
if (this.isResponsesStreamChunk(chunk)) {
|
|
4461
4537
|
return this.processResponsesStreamChunk(chunk, ctx);
|
|
@@ -4489,7 +4565,7 @@ ${block.thinking || ""}
|
|
|
4489
4565
|
const deltaText = this.extractDeltaText(delta?.content);
|
|
4490
4566
|
const finishReason = choice.finish_reason;
|
|
4491
4567
|
if (!ctx.streamState.has("message_started")) {
|
|
4492
|
-
if (delta && (delta.content || delta.tool_calls || delta.role || thinkingDeltaText)) {
|
|
4568
|
+
if (delta && (delta.content || delta.tool_calls || delta.function_call || delta.role || thinkingDeltaText)) {
|
|
4493
4569
|
events.push({
|
|
4494
4570
|
type: "message_start",
|
|
4495
4571
|
message: {
|
|
@@ -4497,14 +4573,20 @@ ${block.thinking || ""}
|
|
|
4497
4573
|
type: "message",
|
|
4498
4574
|
role: "assistant",
|
|
4499
4575
|
content: [],
|
|
4500
|
-
model: chunk.model || "
|
|
4501
|
-
usage: { input_tokens: 0, output_tokens: 0 }
|
|
4576
|
+
model: chunk.model || "",
|
|
4577
|
+
usage: streamUsage || { input_tokens: 0, output_tokens: 0 }
|
|
4502
4578
|
}
|
|
4503
4579
|
});
|
|
4504
4580
|
ctx.streamState.set("message_started", true);
|
|
4505
4581
|
}
|
|
4506
4582
|
}
|
|
4507
4583
|
if (thinkingDeltaText) {
|
|
4584
|
+
const textIndex = ctx.streamState.get("chat_text_index");
|
|
4585
|
+
if (typeof textIndex === "number" && Number.isFinite(textIndex)) {
|
|
4586
|
+
events.push({ type: "content_block_stop", index: textIndex });
|
|
4587
|
+
ctx.streamState.delete("chat_text_index");
|
|
4588
|
+
ctx.streamState.delete("text_block_started");
|
|
4589
|
+
}
|
|
4508
4590
|
const thinkingIndex = this.ensureChatThinkingBlockStarted(events, ctx);
|
|
4509
4591
|
events.push({
|
|
4510
4592
|
type: "content_block_delta",
|
|
@@ -4516,6 +4598,11 @@ ${block.thinking || ""}
|
|
|
4516
4598
|
});
|
|
4517
4599
|
}
|
|
4518
4600
|
if (deltaText) {
|
|
4601
|
+
const thinkingIndex = ctx.streamState.get("chat_thinking_index");
|
|
4602
|
+
if (typeof thinkingIndex === "number" && Number.isFinite(thinkingIndex)) {
|
|
4603
|
+
events.push({ type: "content_block_stop", index: thinkingIndex });
|
|
4604
|
+
ctx.streamState.delete("chat_thinking_index");
|
|
4605
|
+
}
|
|
4519
4606
|
const textIndex = this.ensureChatTextBlockStarted(events, ctx);
|
|
4520
4607
|
events.push({
|
|
4521
4608
|
type: "content_block_delta",
|
|
@@ -4526,7 +4613,8 @@ ${block.thinking || ""}
|
|
|
4526
4613
|
}
|
|
4527
4614
|
});
|
|
4528
4615
|
}
|
|
4529
|
-
if (delta
|
|
4616
|
+
if (delta?.tool_calls) {
|
|
4617
|
+
this.closeChatNonToolBlock(events, ctx);
|
|
4530
4618
|
for (const tc of delta.tool_calls) {
|
|
4531
4619
|
const rawIndex = typeof tc.index === "number" ? tc.index : 0;
|
|
4532
4620
|
const index = this.resolveChatToolIndex(rawIndex, ctx);
|
|
@@ -4535,52 +4623,123 @@ ${block.thinking || ""}
|
|
|
4535
4623
|
ctx.streamState.set(toolKey, {
|
|
4536
4624
|
id: tc.id || "",
|
|
4537
4625
|
name: tc.function?.name || "",
|
|
4538
|
-
arguments: ""
|
|
4626
|
+
arguments: "",
|
|
4627
|
+
started: false
|
|
4539
4628
|
});
|
|
4629
|
+
}
|
|
4630
|
+
const toolState = ctx.streamState.get(toolKey);
|
|
4631
|
+
if (typeof tc.id === "string")
|
|
4632
|
+
toolState.id = tc.id;
|
|
4633
|
+
if (typeof tc.function?.name === "string")
|
|
4634
|
+
toolState.name = tc.function.name;
|
|
4635
|
+
const argsDelta = typeof tc.function?.arguments === "string" ? tc.function.arguments : "";
|
|
4636
|
+
if (!toolState.started && toolState.id && toolState.name) {
|
|
4637
|
+
toolState.started = true;
|
|
4540
4638
|
events.push({
|
|
4541
4639
|
type: "content_block_start",
|
|
4542
4640
|
index,
|
|
4543
4641
|
content_block: {
|
|
4544
4642
|
type: "tool_use",
|
|
4545
|
-
id:
|
|
4546
|
-
name:
|
|
4643
|
+
id: toolState.id,
|
|
4644
|
+
name: toolState.name,
|
|
4547
4645
|
input: {}
|
|
4548
4646
|
}
|
|
4549
4647
|
});
|
|
4648
|
+
if (toolState.arguments) {
|
|
4649
|
+
events.push({
|
|
4650
|
+
type: "content_block_delta",
|
|
4651
|
+
index,
|
|
4652
|
+
delta: {
|
|
4653
|
+
type: "input_json_delta",
|
|
4654
|
+
partial_json: toolState.arguments
|
|
4655
|
+
}
|
|
4656
|
+
});
|
|
4657
|
+
toolState.arguments = "";
|
|
4658
|
+
}
|
|
4550
4659
|
}
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4660
|
+
if (argsDelta) {
|
|
4661
|
+
if (toolState.started) {
|
|
4662
|
+
events.push({
|
|
4663
|
+
type: "content_block_delta",
|
|
4664
|
+
index,
|
|
4665
|
+
delta: {
|
|
4666
|
+
type: "input_json_delta",
|
|
4667
|
+
partial_json: argsDelta
|
|
4668
|
+
}
|
|
4669
|
+
});
|
|
4670
|
+
} else {
|
|
4671
|
+
toolState.arguments += argsDelta;
|
|
4672
|
+
}
|
|
4554
4673
|
ctx.streamState.set(toolKey, toolState);
|
|
4555
|
-
events.push({
|
|
4556
|
-
type: "content_block_delta",
|
|
4557
|
-
index,
|
|
4558
|
-
delta: {
|
|
4559
|
-
type: "input_json_delta",
|
|
4560
|
-
partial_json: tc.function.arguments
|
|
4561
|
-
}
|
|
4562
|
-
});
|
|
4563
4674
|
}
|
|
4564
4675
|
}
|
|
4565
4676
|
}
|
|
4566
|
-
if (
|
|
4567
|
-
|
|
4568
|
-
|
|
4677
|
+
if (delta?.function_call) {
|
|
4678
|
+
this.closeChatNonToolBlock(events, ctx);
|
|
4679
|
+
const functionCall = delta.function_call;
|
|
4680
|
+
if (typeof functionCall.name === "string") {
|
|
4681
|
+
ctx.streamState.set("legacy_function_name", functionCall.name);
|
|
4682
|
+
}
|
|
4683
|
+
if (!ctx.streamState.has("legacy_function_index")) {
|
|
4684
|
+
const index2 = this.allocateChatContentBlockIndex(ctx, 0, 0);
|
|
4685
|
+
ctx.streamState.set("legacy_function_index", index2);
|
|
4569
4686
|
events.push({
|
|
4570
|
-
type: "
|
|
4571
|
-
index:
|
|
4687
|
+
type: "content_block_start",
|
|
4688
|
+
index: index2,
|
|
4689
|
+
content_block: {
|
|
4690
|
+
type: "tool_use",
|
|
4691
|
+
id: "",
|
|
4692
|
+
name: ctx.streamState.get("legacy_function_name") || "",
|
|
4693
|
+
input: {}
|
|
4694
|
+
}
|
|
4572
4695
|
});
|
|
4573
4696
|
}
|
|
4574
|
-
const
|
|
4575
|
-
if (typeof
|
|
4697
|
+
const index = ctx.streamState.get("legacy_function_index");
|
|
4698
|
+
if (typeof functionCall.arguments === "string" && functionCall.arguments.length > 0) {
|
|
4576
4699
|
events.push({
|
|
4577
|
-
type: "
|
|
4578
|
-
index
|
|
4700
|
+
type: "content_block_delta",
|
|
4701
|
+
index,
|
|
4702
|
+
delta: {
|
|
4703
|
+
type: "input_json_delta",
|
|
4704
|
+
partial_json: functionCall.arguments
|
|
4705
|
+
}
|
|
4579
4706
|
});
|
|
4580
4707
|
}
|
|
4708
|
+
}
|
|
4709
|
+
if (finishReason) {
|
|
4710
|
+
this.closeChatNonToolBlock(events, ctx);
|
|
4711
|
+
const legacyFunctionIndex = ctx.streamState.get("legacy_function_index");
|
|
4712
|
+
if (typeof legacyFunctionIndex === "number" && Number.isFinite(legacyFunctionIndex)) {
|
|
4713
|
+
events.push({ type: "content_block_stop", index: legacyFunctionIndex });
|
|
4714
|
+
}
|
|
4581
4715
|
const toolKeys = Array.from(ctx.streamState.keys()).filter((k) => k.startsWith("tool_")).sort((a, b) => Number(a.slice(5)) - Number(b.slice(5)));
|
|
4582
4716
|
for (const toolKey of toolKeys) {
|
|
4583
4717
|
const toolIndex = Number(toolKey.slice(5));
|
|
4718
|
+
const toolState = ctx.streamState.get(toolKey);
|
|
4719
|
+
if (toolState && !toolState.started && (toolState.arguments || toolState.id || toolState.name)) {
|
|
4720
|
+
const fallbackId = toolState.id || `tool_call_${toolIndex}`;
|
|
4721
|
+
const fallbackName = toolState.name || "unknown_tool";
|
|
4722
|
+
events.push({
|
|
4723
|
+
type: "content_block_start",
|
|
4724
|
+
index: Number.isFinite(toolIndex) ? toolIndex : 0,
|
|
4725
|
+
content_block: {
|
|
4726
|
+
type: "tool_use",
|
|
4727
|
+
id: fallbackId,
|
|
4728
|
+
name: fallbackName,
|
|
4729
|
+
input: {}
|
|
4730
|
+
}
|
|
4731
|
+
});
|
|
4732
|
+
if (toolState.arguments) {
|
|
4733
|
+
events.push({
|
|
4734
|
+
type: "content_block_delta",
|
|
4735
|
+
index: Number.isFinite(toolIndex) ? toolIndex : 0,
|
|
4736
|
+
delta: {
|
|
4737
|
+
type: "input_json_delta",
|
|
4738
|
+
partial_json: toolState.arguments
|
|
4739
|
+
}
|
|
4740
|
+
});
|
|
4741
|
+
}
|
|
4742
|
+
}
|
|
4584
4743
|
events.push({
|
|
4585
4744
|
type: "content_block_stop",
|
|
4586
4745
|
index: Number.isFinite(toolIndex) ? toolIndex : 0
|
|
@@ -4659,8 +4818,8 @@ ${block.thinking || ""}
|
|
|
4659
4818
|
type: "message",
|
|
4660
4819
|
role: "assistant",
|
|
4661
4820
|
content: [],
|
|
4662
|
-
model: chunk.response?.model || "
|
|
4663
|
-
usage:
|
|
4821
|
+
model: chunk.response?.model || "",
|
|
4822
|
+
usage: this.buildAnthropicUsageFromResponses(chunk.response?.usage)
|
|
4664
4823
|
}
|
|
4665
4824
|
});
|
|
4666
4825
|
ctx.streamState.set("message_started", true);
|
|
@@ -4681,22 +4840,42 @@ ${block.thinking || ""}
|
|
|
4681
4840
|
ensureMessageStarted();
|
|
4682
4841
|
return this.finalizeResponsesStreamEvents(events, ctx);
|
|
4683
4842
|
}
|
|
4684
|
-
if (eventType === "response.
|
|
4843
|
+
if (eventType === "response.content_part.added") {
|
|
4685
4844
|
ensureMessageStarted();
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4845
|
+
const partType = chunk.part?.type;
|
|
4846
|
+
if (partType === "output_text" || partType === "refusal") {
|
|
4847
|
+
const index = this.resolveResponsesContentIndex(chunk, ctx);
|
|
4848
|
+
this.ensureResponsesTextBlockStarted(events, ctx, index);
|
|
4849
|
+
}
|
|
4850
|
+
return this.finalizeResponsesStreamEvents(events, ctx);
|
|
4851
|
+
}
|
|
4852
|
+
if (eventType === "response.content_part.done" || eventType === "response.refusal.done" || eventType === "response.reasoning.done") {
|
|
4853
|
+
const index = this.resolveResponsesContentIndex(chunk, ctx);
|
|
4854
|
+
if (ctx.streamState.get(`resp_text_block_${index}`) === true || ctx.streamState.get(`resp_reasoning_block_${index}`) === true) {
|
|
4855
|
+
events.push({ type: "content_block_stop", index });
|
|
4856
|
+
ctx.streamState.delete(`resp_text_block_${index}`);
|
|
4857
|
+
ctx.streamState.delete(`resp_reasoning_block_${index}`);
|
|
4858
|
+
}
|
|
4859
|
+
return this.finalizeResponsesStreamEvents(events, ctx);
|
|
4860
|
+
}
|
|
4861
|
+
if (eventType === "response.in_progress" || eventType === "keepalive") {
|
|
4689
4862
|
return this.finalizeResponsesStreamEvents(events, ctx);
|
|
4690
4863
|
}
|
|
4691
4864
|
if (eventType === "response.output_item.added") {
|
|
4692
4865
|
const item = chunk.item;
|
|
4693
4866
|
if (item?.type === "function_call") {
|
|
4694
|
-
const
|
|
4695
|
-
const index = Number.isFinite(rawIndex) ? rawIndex : 1;
|
|
4867
|
+
const index = this.nextResponsesContentIndex(ctx);
|
|
4696
4868
|
const metaKey = `resp_tool_meta_${index}`;
|
|
4697
4869
|
const callId = typeof item.call_id === "string" ? item.call_id : typeof item.id === "string" ? item.id : `tool_${index}`;
|
|
4698
4870
|
const name = typeof item.name === "string" ? item.name : "tool";
|
|
4699
4871
|
ctx.streamState.set(metaKey, { callId, name });
|
|
4872
|
+
if (typeof item.id === "string")
|
|
4873
|
+
ctx.streamState.set(`resp_tool_item_${item.id}`, index);
|
|
4874
|
+
if (typeof chunk.item_id === "string")
|
|
4875
|
+
ctx.streamState.set(`resp_tool_item_${chunk.item_id}`, index);
|
|
4876
|
+
if (typeof chunk.output_index === "number")
|
|
4877
|
+
ctx.streamState.set(`resp_tool_out_${chunk.output_index}`, index);
|
|
4878
|
+
ctx.streamState.set("resp_last_tool_index", index);
|
|
4700
4879
|
if (!ctx.streamState.has("message_started")) {
|
|
4701
4880
|
events.push({
|
|
4702
4881
|
type: "message_start",
|
|
@@ -4705,7 +4884,7 @@ ${block.thinking || ""}
|
|
|
4705
4884
|
type: "message",
|
|
4706
4885
|
role: "assistant",
|
|
4707
4886
|
content: [],
|
|
4708
|
-
model: chunk.response?.model || "
|
|
4887
|
+
model: chunk.response?.model || "",
|
|
4709
4888
|
usage: { input_tokens: 0, output_tokens: 0 }
|
|
4710
4889
|
}
|
|
4711
4890
|
});
|
|
@@ -4734,8 +4913,7 @@ ${block.thinking || ""}
|
|
|
4734
4913
|
if (eventType === "response.output_item.done") {
|
|
4735
4914
|
const item = chunk.item;
|
|
4736
4915
|
if (item?.type === "function_call") {
|
|
4737
|
-
const
|
|
4738
|
-
const index = Number.isFinite(rawIndex) ? rawIndex : 1;
|
|
4916
|
+
const index = this.resolveResponsesToolIndex({ ...chunk, item_id: chunk.item_id || item.id }, ctx) ?? this.nextResponsesContentIndex(ctx);
|
|
4739
4917
|
const toolKey = `resp_tool_${index}`;
|
|
4740
4918
|
const metaKey = `resp_tool_meta_${index}`;
|
|
4741
4919
|
const meta = ctx.streamState.get(metaKey);
|
|
@@ -4749,7 +4927,7 @@ ${block.thinking || ""}
|
|
|
4749
4927
|
type: "message",
|
|
4750
4928
|
role: "assistant",
|
|
4751
4929
|
content: [],
|
|
4752
|
-
model: chunk.response?.model || "
|
|
4930
|
+
model: chunk.response?.model || "",
|
|
4753
4931
|
usage: { input_tokens: 0, output_tokens: 0 }
|
|
4754
4932
|
}
|
|
4755
4933
|
});
|
|
@@ -4787,8 +4965,10 @@ ${block.thinking || ""}
|
|
|
4787
4965
|
return this.finalizeResponsesStreamEvents(events, ctx);
|
|
4788
4966
|
}
|
|
4789
4967
|
if (eventType === "response.function_call_arguments.done") {
|
|
4790
|
-
const
|
|
4791
|
-
|
|
4968
|
+
const index = this.resolveResponsesToolIndex(chunk, ctx);
|
|
4969
|
+
if (index === undefined) {
|
|
4970
|
+
return this.finalizeResponsesStreamEvents(events, ctx);
|
|
4971
|
+
}
|
|
4792
4972
|
const toolKey = `resp_tool_${index}`;
|
|
4793
4973
|
const metaKey = `resp_tool_meta_${index}`;
|
|
4794
4974
|
const meta = ctx.streamState.get(metaKey);
|
|
@@ -4802,7 +4982,7 @@ ${block.thinking || ""}
|
|
|
4802
4982
|
type: "message",
|
|
4803
4983
|
role: "assistant",
|
|
4804
4984
|
content: [],
|
|
4805
|
-
model: chunk.response?.model || "
|
|
4985
|
+
model: chunk.response?.model || "",
|
|
4806
4986
|
usage: { input_tokens: 0, output_tokens: 0 }
|
|
4807
4987
|
}
|
|
4808
4988
|
});
|
|
@@ -4821,36 +5001,24 @@ ${block.thinking || ""}
|
|
|
4821
5001
|
}
|
|
4822
5002
|
});
|
|
4823
5003
|
}
|
|
4824
|
-
const
|
|
4825
|
-
const hasDelta = ctx.streamState.get(
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
const argsDoneSeenKey = `resp_tool_args_done_seen_${index}`;
|
|
4829
|
-
const argsDoneSeen = ctx.streamState.get(argsDoneSeenKey) === true;
|
|
4830
|
-
const doneArgsKey = `resp_tool_done_arguments_${index}`;
|
|
4831
|
-
const previousDoneArgs = ctx.streamState.get(doneArgsKey);
|
|
4832
|
-
const fullArgs = typeof chunk.arguments === "string" ? chunk.arguments : "";
|
|
4833
|
-
if (argsDoneSeen && (fullArgsSent || hasDelta || fullArgs && previousDoneArgs === fullArgs)) {
|
|
4834
|
-
return this.finalizeResponsesStreamEvents(events, ctx);
|
|
4835
|
-
}
|
|
4836
|
-
ctx.streamState.set(argsDoneSeenKey, true);
|
|
4837
|
-
if (fullArgs) {
|
|
4838
|
-
ctx.streamState.set(doneArgsKey, fullArgs);
|
|
4839
|
-
}
|
|
4840
|
-
if (fullArgs && !hasDelta && !fullArgsSent) {
|
|
4841
|
-
ctx.streamState.set(fullArgsSentKey, true);
|
|
5004
|
+
const doneArguments = typeof chunk.arguments === "string" ? chunk.arguments : "";
|
|
5005
|
+
const hasDelta = ctx.streamState.get(`resp_tool_has_delta_${index}`) === true;
|
|
5006
|
+
if (doneArguments && !hasDelta) {
|
|
5007
|
+
ctx.streamState.set(`resp_tool_has_delta_${index}`, true);
|
|
4842
5008
|
events.push({
|
|
4843
5009
|
type: "content_block_delta",
|
|
4844
5010
|
index,
|
|
4845
5011
|
delta: {
|
|
4846
5012
|
type: "input_json_delta",
|
|
4847
|
-
partial_json:
|
|
5013
|
+
partial_json: doneArguments
|
|
4848
5014
|
}
|
|
4849
5015
|
});
|
|
4850
5016
|
}
|
|
5017
|
+
events.push({ type: "content_block_stop", index });
|
|
5018
|
+
ctx.streamState.set(`resp_tool_closed_${index}`, true);
|
|
4851
5019
|
return this.finalizeResponsesStreamEvents(events, ctx);
|
|
4852
5020
|
}
|
|
4853
|
-
if (!ctx.streamState.has("message_started") && eventType === "response.output_text.delta") {
|
|
5021
|
+
if (!ctx.streamState.has("message_started") && (eventType === "response.output_text.delta" || eventType === "response.refusal.delta")) {
|
|
4854
5022
|
events.push({
|
|
4855
5023
|
type: "message_start",
|
|
4856
5024
|
message: {
|
|
@@ -4858,29 +5026,20 @@ ${block.thinking || ""}
|
|
|
4858
5026
|
type: "message",
|
|
4859
5027
|
role: "assistant",
|
|
4860
5028
|
content: [],
|
|
4861
|
-
model: chunk.response?.model || "
|
|
5029
|
+
model: chunk.response?.model || "",
|
|
4862
5030
|
usage: { input_tokens: 0, output_tokens: 0 }
|
|
4863
5031
|
}
|
|
4864
5032
|
});
|
|
4865
5033
|
ctx.streamState.set("message_started", true);
|
|
4866
5034
|
}
|
|
4867
|
-
if (eventType === "response.output_text.delta") {
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
type: "content_block_start",
|
|
4871
|
-
index: 0,
|
|
4872
|
-
content_block: {
|
|
4873
|
-
type: "text",
|
|
4874
|
-
text: ""
|
|
4875
|
-
}
|
|
4876
|
-
});
|
|
4877
|
-
ctx.streamState.set("text_block_started", true);
|
|
4878
|
-
}
|
|
5035
|
+
if (eventType === "response.output_text.delta" || eventType === "response.refusal.delta") {
|
|
5036
|
+
const index = this.resolveResponsesContentIndex(chunk, ctx);
|
|
5037
|
+
this.ensureResponsesTextBlockStarted(events, ctx, index);
|
|
4879
5038
|
const deltaText = typeof chunk.delta === "string" ? chunk.delta : typeof chunk.text === "string" ? chunk.text : "";
|
|
4880
5039
|
if (deltaText) {
|
|
4881
5040
|
events.push({
|
|
4882
5041
|
type: "content_block_delta",
|
|
4883
|
-
index
|
|
5042
|
+
index,
|
|
4884
5043
|
delta: {
|
|
4885
5044
|
type: "text_delta",
|
|
4886
5045
|
text: deltaText
|
|
@@ -4915,8 +5074,7 @@ ${block.thinking || ""}
|
|
|
4915
5074
|
return this.finalizeResponsesStreamEvents(events, ctx);
|
|
4916
5075
|
}
|
|
4917
5076
|
if (eventType === "response.function_call_arguments.delta") {
|
|
4918
|
-
const
|
|
4919
|
-
const index = Number.isFinite(rawIndex) ? rawIndex : 1;
|
|
5077
|
+
const index = this.resolveResponsesToolIndex(chunk, ctx) ?? this.nextResponsesContentIndex(ctx);
|
|
4920
5078
|
const argsDoneSeen = ctx.streamState.get(`resp_tool_args_done_seen_${index}`) === true;
|
|
4921
5079
|
const fullArgsSent = ctx.streamState.get(`resp_tool_full_args_sent_${index}`) === true;
|
|
4922
5080
|
if (argsDoneSeen || fullArgsSent) {
|
|
@@ -4930,13 +5088,16 @@ ${block.thinking || ""}
|
|
|
4930
5088
|
type: "message",
|
|
4931
5089
|
role: "assistant",
|
|
4932
5090
|
content: [],
|
|
4933
|
-
model: chunk.response?.model || "
|
|
5091
|
+
model: chunk.response?.model || "",
|
|
4934
5092
|
usage: { input_tokens: 0, output_tokens: 0 }
|
|
4935
5093
|
}
|
|
4936
5094
|
});
|
|
4937
5095
|
ctx.streamState.set("message_started", true);
|
|
4938
5096
|
}
|
|
4939
5097
|
const toolKey = `resp_tool_${index}`;
|
|
5098
|
+
if (ctx.streamState.get(`resp_tool_closed_${index}`) === true) {
|
|
5099
|
+
return this.finalizeResponsesStreamEvents(events, ctx);
|
|
5100
|
+
}
|
|
4940
5101
|
const metaKey = `resp_tool_meta_${index}`;
|
|
4941
5102
|
const meta = ctx.streamState.get(metaKey);
|
|
4942
5103
|
const callId = typeof chunk.call_id === "string" ? chunk.call_id : typeof meta?.callId === "string" ? meta.callId : typeof chunk.item_id === "string" ? chunk.item_id : `tool_${index}`;
|
|
@@ -4978,17 +5139,16 @@ ${block.thinking || ""}
|
|
|
4978
5139
|
type: "message",
|
|
4979
5140
|
role: "assistant",
|
|
4980
5141
|
content: [],
|
|
4981
|
-
model: chunk.response?.model || "
|
|
5142
|
+
model: chunk.response?.model || "",
|
|
4982
5143
|
usage: { input_tokens: 0, output_tokens: 0 }
|
|
4983
5144
|
}
|
|
4984
5145
|
});
|
|
4985
5146
|
ctx.streamState.set("message_started", true);
|
|
4986
5147
|
}
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
});
|
|
5148
|
+
const textBlockKeys = Array.from(ctx.streamState.keys()).filter((k) => typeof k === "string" && /^resp_text_block_\d+$/.test(k)).sort((a, b) => Number(a.slice("resp_text_block_".length)) - Number(b.slice("resp_text_block_".length)));
|
|
5149
|
+
for (const blockKey of textBlockKeys) {
|
|
5150
|
+
const textIndex = Number(blockKey.slice("resp_text_block_".length));
|
|
5151
|
+
events.push({ type: "content_block_stop", index: Number.isFinite(textIndex) ? textIndex : 0 });
|
|
4992
5152
|
}
|
|
4993
5153
|
const reasoningBlockKeys = Array.from(ctx.streamState.keys()).filter((k) => typeof k === "string" && /^resp_reasoning_block_\d+$/.test(k)).sort((a, b) => Number(a.slice("resp_reasoning_block_".length)) - Number(b.slice("resp_reasoning_block_".length)));
|
|
4994
5154
|
for (const blockKey of reasoningBlockKeys) {
|
|
@@ -5001,20 +5161,22 @@ ${block.thinking || ""}
|
|
|
5001
5161
|
const toolKeys = Array.from(ctx.streamState.keys()).filter((k) => typeof k === "string" && /^resp_tool_\d+$/.test(k)).sort((a, b) => Number(a.slice(10)) - Number(b.slice(10)));
|
|
5002
5162
|
for (const toolKey of toolKeys) {
|
|
5003
5163
|
const toolIndex = Number(toolKey.slice(10));
|
|
5164
|
+
if (ctx.streamState.get(`resp_tool_closed_${toolIndex}`) === true) {
|
|
5165
|
+
continue;
|
|
5166
|
+
}
|
|
5004
5167
|
events.push({
|
|
5005
5168
|
type: "content_block_stop",
|
|
5006
5169
|
index: Number.isFinite(toolIndex) ? toolIndex : 1
|
|
5007
5170
|
});
|
|
5008
5171
|
}
|
|
5009
5172
|
const usage = chunk.response?.usage && typeof chunk.response.usage === "object" ? chunk.response.usage : chunk.usage;
|
|
5010
|
-
const normalizedUsage = usage ?
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
} : undefined;
|
|
5173
|
+
const normalizedUsage = usage ? this.buildAnthropicUsageFromResponses(usage) : undefined;
|
|
5174
|
+
const responseStatus = typeof chunk.response?.status === "string" ? chunk.response.status : eventType === "response.completed" ? "completed" : eventType === "response.incomplete" ? "incomplete" : "failed";
|
|
5175
|
+
const hasToolUse = toolKeys.length > 0;
|
|
5014
5176
|
events.push({
|
|
5015
5177
|
type: "message_delta",
|
|
5016
5178
|
delta: {
|
|
5017
|
-
stop_reason:
|
|
5179
|
+
stop_reason: this.mapResponsesStopReason(responseStatus, hasToolUse, chunk.response?.incomplete_details?.reason)
|
|
5018
5180
|
},
|
|
5019
5181
|
...normalizedUsage && { usage: normalizedUsage }
|
|
5020
5182
|
});
|
|
@@ -5197,10 +5359,14 @@ class GeminiToAnthropicConverter {
|
|
|
5197
5359
|
const text = part.text.trim();
|
|
5198
5360
|
if (text) {
|
|
5199
5361
|
if (part.thought) {
|
|
5200
|
-
|
|
5362
|
+
const thinkingBlock = {
|
|
5201
5363
|
type: "thinking",
|
|
5202
5364
|
thinking: text
|
|
5203
|
-
}
|
|
5365
|
+
};
|
|
5366
|
+
if (part.thoughtSignature) {
|
|
5367
|
+
thinkingBlock.thought_signature = part.thoughtSignature;
|
|
5368
|
+
}
|
|
5369
|
+
anthropicContent.push(thinkingBlock);
|
|
5204
5370
|
} else {
|
|
5205
5371
|
anthropicContent.push({
|
|
5206
5372
|
type: "text",
|
|
@@ -5223,12 +5389,16 @@ class GeminiToAnthropicConverter {
|
|
|
5223
5389
|
const queue = pendingToolUseIdsByName.get(fc.name) || [];
|
|
5224
5390
|
queue.push(toolUseId);
|
|
5225
5391
|
pendingToolUseIdsByName.set(fc.name, queue);
|
|
5226
|
-
|
|
5392
|
+
const toolUseBlock = {
|
|
5227
5393
|
type: "tool_use",
|
|
5228
5394
|
id: toolUseId,
|
|
5229
5395
|
name: fc.name,
|
|
5230
5396
|
input: fc.args || {}
|
|
5231
|
-
}
|
|
5397
|
+
};
|
|
5398
|
+
if (part.thoughtSignature) {
|
|
5399
|
+
toolUseBlock.thought_signature = part.thoughtSignature;
|
|
5400
|
+
}
|
|
5401
|
+
anthropicContent.push(toolUseBlock);
|
|
5232
5402
|
} else if (part.functionResponse) {
|
|
5233
5403
|
const fr = part.functionResponse;
|
|
5234
5404
|
const respContent = typeof fr.response === "object" ? fr.response.content || JSON.stringify(fr.response) : String(fr.response);
|
|
@@ -5421,15 +5591,23 @@ class GeminiToAnthropicConverter {
|
|
|
5421
5591
|
} else if (item.type === "thinking") {
|
|
5422
5592
|
const thinking = item.thinking || "";
|
|
5423
5593
|
if (thinking.trim()) {
|
|
5424
|
-
|
|
5594
|
+
const thinkingPart = { text: thinking, thought: true };
|
|
5595
|
+
if (item.thought_signature) {
|
|
5596
|
+
thinkingPart.thoughtSignature = item.thought_signature;
|
|
5597
|
+
}
|
|
5598
|
+
parts.push(thinkingPart);
|
|
5425
5599
|
}
|
|
5426
5600
|
} else if (item.type === "tool_use") {
|
|
5427
|
-
|
|
5601
|
+
const fcPart = {
|
|
5428
5602
|
functionCall: {
|
|
5429
5603
|
name: item.name || "",
|
|
5430
5604
|
args: item.input || {}
|
|
5431
5605
|
}
|
|
5432
|
-
}
|
|
5606
|
+
};
|
|
5607
|
+
if (item.thought_signature) {
|
|
5608
|
+
fcPart.thoughtSignature = item.thought_signature;
|
|
5609
|
+
}
|
|
5610
|
+
parts.push(fcPart);
|
|
5433
5611
|
}
|
|
5434
5612
|
}
|
|
5435
5613
|
}
|
|
@@ -5464,7 +5642,15 @@ class GeminiToAnthropicConverter {
|
|
|
5464
5642
|
}
|
|
5465
5643
|
async processStreamChunk(chunk, ctx) {
|
|
5466
5644
|
const eventType = chunk.type;
|
|
5467
|
-
if (eventType === "
|
|
5645
|
+
if (eventType === "content_block_start") {
|
|
5646
|
+
const block = chunk.content_block;
|
|
5647
|
+
if (block && block.thought_signature) {
|
|
5648
|
+
const idx = chunk.index ?? 0;
|
|
5649
|
+
ctx.streamState.set(`part_${idx}_sig`, block.thought_signature);
|
|
5650
|
+
}
|
|
5651
|
+
return [];
|
|
5652
|
+
}
|
|
5653
|
+
if (eventType === "message_start" || eventType === "content_block_stop" || eventType === "message_stop" || eventType === "ping") {
|
|
5468
5654
|
return [];
|
|
5469
5655
|
}
|
|
5470
5656
|
if (eventType === "content_block_delta") {
|
|
@@ -5490,7 +5676,12 @@ class GeminiToAnthropicConverter {
|
|
|
5490
5676
|
accumulatedThinking += delta.thinking || "";
|
|
5491
5677
|
ctx.streamState.set("accumulated_thinking", accumulatedThinking);
|
|
5492
5678
|
if (accumulatedThinking) {
|
|
5493
|
-
|
|
5679
|
+
const thinkingPart = { text: accumulatedThinking, thought: true };
|
|
5680
|
+
const sig = ctx.streamState.get(`part_${chunk.index ?? 0}_sig`);
|
|
5681
|
+
if (sig) {
|
|
5682
|
+
thinkingPart.thoughtSignature = sig;
|
|
5683
|
+
}
|
|
5684
|
+
parts.push(thinkingPart);
|
|
5494
5685
|
}
|
|
5495
5686
|
} else if (delta.type === "input_json_delta") {
|
|
5496
5687
|
return [];
|
|
@@ -5640,7 +5831,7 @@ class GeminiToOpenAIConverter {
|
|
|
5640
5831
|
const queue = pendingToolCallIdsByName.get(functionName) || [];
|
|
5641
5832
|
queue.push(toolCallId);
|
|
5642
5833
|
pendingToolCallIdsByName.set(functionName, queue);
|
|
5643
|
-
|
|
5834
|
+
const tc = {
|
|
5644
5835
|
id: toolCallId,
|
|
5645
5836
|
type: "function",
|
|
5646
5837
|
function: {
|
|
@@ -5648,6 +5839,10 @@ class GeminiToOpenAIConverter {
|
|
|
5648
5839
|
arguments: JSON.stringify(p.functionCall.args || {})
|
|
5649
5840
|
}
|
|
5650
5841
|
};
|
|
5842
|
+
if (p.thoughtSignature) {
|
|
5843
|
+
tc.extra_content = { google: { thought_signature: p.thoughtSignature } };
|
|
5844
|
+
}
|
|
5845
|
+
return tc;
|
|
5651
5846
|
})
|
|
5652
5847
|
});
|
|
5653
5848
|
} else {
|
|
@@ -5835,12 +6030,17 @@ class GeminiToOpenAIConverter {
|
|
|
5835
6030
|
const parts = [];
|
|
5836
6031
|
if (message.tool_calls) {
|
|
5837
6032
|
for (const tc of message.tool_calls) {
|
|
5838
|
-
|
|
6033
|
+
const fcPart = {
|
|
5839
6034
|
functionCall: {
|
|
5840
6035
|
name: tc.function.name,
|
|
5841
6036
|
args: this.parseToolArgumentsToObject(tc.function.arguments)
|
|
5842
6037
|
}
|
|
5843
|
-
}
|
|
6038
|
+
};
|
|
6039
|
+
const sig = tc.extra_content?.google?.thought_signature || tc.thought_signature;
|
|
6040
|
+
if (sig) {
|
|
6041
|
+
fcPart.thoughtSignature = sig;
|
|
6042
|
+
}
|
|
6043
|
+
parts.push(fcPart);
|
|
5844
6044
|
}
|
|
5845
6045
|
} else if (message.content) {
|
|
5846
6046
|
parts.push({ text: message.content });
|
|
@@ -6321,12 +6521,19 @@ class OpenAIToGeminiConverter {
|
|
|
6321
6521
|
role: "model",
|
|
6322
6522
|
parts: [
|
|
6323
6523
|
...textParts,
|
|
6324
|
-
...m.tool_calls.map((tc) =>
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6524
|
+
...m.tool_calls.map((tc) => {
|
|
6525
|
+
const fcPart = {
|
|
6526
|
+
functionCall: {
|
|
6527
|
+
name: tc.function.name,
|
|
6528
|
+
args: this.parseToolArgumentsToObject(tc.function.arguments)
|
|
6529
|
+
}
|
|
6530
|
+
};
|
|
6531
|
+
const sig = tc.extra_content?.google?.thought_signature || tc.thought_signature;
|
|
6532
|
+
if (sig) {
|
|
6533
|
+
fcPart.thoughtSignature = sig;
|
|
6328
6534
|
}
|
|
6329
|
-
|
|
6535
|
+
return fcPart;
|
|
6536
|
+
})
|
|
6330
6537
|
]
|
|
6331
6538
|
};
|
|
6332
6539
|
}
|
|
@@ -6528,14 +6735,18 @@ class OpenAIToGeminiConverter {
|
|
|
6528
6735
|
if (part.text) {
|
|
6529
6736
|
textContent += part.text;
|
|
6530
6737
|
} else if (part.functionCall) {
|
|
6531
|
-
|
|
6738
|
+
const tc = {
|
|
6532
6739
|
id: `call_${part.functionCall.name}_${index}`,
|
|
6533
6740
|
type: "function",
|
|
6534
6741
|
function: {
|
|
6535
6742
|
name: part.functionCall.name,
|
|
6536
6743
|
arguments: JSON.stringify(part.functionCall.args || {})
|
|
6537
6744
|
}
|
|
6538
|
-
}
|
|
6745
|
+
};
|
|
6746
|
+
if (part.thoughtSignature) {
|
|
6747
|
+
tc.extra_content = { google: { thought_signature: part.thoughtSignature } };
|
|
6748
|
+
}
|
|
6749
|
+
toolCalls.push(tc);
|
|
6539
6750
|
}
|
|
6540
6751
|
}
|
|
6541
6752
|
const message = { role: "assistant" };
|