@cognipeer/agent-sdk 0.10.6 → 0.12.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 +28 -3
- package/dist/index.cjs +1069 -235
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +289 -18
- package/dist/index.d.ts +289 -18
- package/dist/index.mjs +1068 -236
- package/dist/index.mjs.map +1 -1
- package/package.json +39 -1
package/dist/index.cjs
CHANGED
|
@@ -2441,6 +2441,15 @@ function estimatePayloadBytes(value) {
|
|
|
2441
2441
|
}
|
|
2442
2442
|
}
|
|
2443
2443
|
}
|
|
2444
|
+
function toMessageUsage(u) {
|
|
2445
|
+
return {
|
|
2446
|
+
prompt_tokens: u.inputTokens,
|
|
2447
|
+
completion_tokens: u.outputTokens,
|
|
2448
|
+
total_tokens: u.totalTokens,
|
|
2449
|
+
prompt_tokens_details: { cached_tokens: u.cachedInputTokens, cache_write_tokens: u.cachedWriteTokens },
|
|
2450
|
+
completion_tokens_details: { reasoning_tokens: u.reasoningTokens }
|
|
2451
|
+
};
|
|
2452
|
+
}
|
|
2444
2453
|
function fromNativeProvider(provider, options) {
|
|
2445
2454
|
let boundTools = options?.__tools;
|
|
2446
2455
|
const modelName = options?.model ?? provider.defaultModel ?? provider.providerName;
|
|
@@ -2450,8 +2459,9 @@ function fromNativeProvider(provider, options) {
|
|
|
2450
2459
|
invoke: async (messages, _invokeOptions) => {
|
|
2451
2460
|
const link = linkAbortSignal(_invokeOptions);
|
|
2452
2461
|
try {
|
|
2453
|
-
|
|
2462
|
+
let request = buildRequest(messages, boundTools, options, _invokeOptions);
|
|
2454
2463
|
if (link.signal) request.signal = link.signal;
|
|
2464
|
+
request = await applyTransform(request);
|
|
2455
2465
|
const response = await provider.complete(request);
|
|
2456
2466
|
return toBaseChatMessage(response);
|
|
2457
2467
|
} finally {
|
|
@@ -2461,33 +2471,46 @@ function fromNativeProvider(provider, options) {
|
|
|
2461
2471
|
stream: async function* (messages, _invokeOptions) {
|
|
2462
2472
|
const link = linkAbortSignal(_invokeOptions);
|
|
2463
2473
|
try {
|
|
2464
|
-
|
|
2474
|
+
let request = buildRequest(messages, boundTools, options, _invokeOptions);
|
|
2465
2475
|
request.stream = true;
|
|
2466
2476
|
if (link.signal) request.signal = link.signal;
|
|
2477
|
+
request = await applyTransform(request);
|
|
2467
2478
|
let fullContent = "";
|
|
2468
2479
|
let allToolCalls = [];
|
|
2469
2480
|
let lastUsage;
|
|
2470
2481
|
let reasoningBlocks;
|
|
2482
|
+
let reasoningText = "";
|
|
2483
|
+
let finishReason;
|
|
2484
|
+
let responseModel;
|
|
2471
2485
|
const toolCallBuffers = /* @__PURE__ */ new Map();
|
|
2472
2486
|
for await (const chunk of provider.completeStream(request)) {
|
|
2473
2487
|
if (chunk.delta.content) {
|
|
2474
2488
|
fullContent += chunk.delta.content;
|
|
2475
2489
|
yield chunk.delta.content;
|
|
2476
2490
|
}
|
|
2491
|
+
if (chunk.model) responseModel = chunk.model;
|
|
2492
|
+
if (chunk.finishReason) finishReason = chunk.finishReason;
|
|
2477
2493
|
if (chunk.delta.reasoning?.blocks?.length) {
|
|
2478
2494
|
reasoningBlocks = chunk.delta.reasoning.blocks;
|
|
2479
2495
|
}
|
|
2496
|
+
const reasoningDelta = chunk.delta.reasoning?.summary;
|
|
2497
|
+
if (reasoningDelta) {
|
|
2498
|
+
reasoningText += reasoningDelta;
|
|
2499
|
+
yield { type: "reasoning", reasoning_content: reasoningDelta };
|
|
2500
|
+
}
|
|
2480
2501
|
if (chunk.delta.toolCalls) {
|
|
2481
2502
|
for (const tc of chunk.delta.toolCalls) {
|
|
2482
2503
|
if (tc.id) {
|
|
2483
2504
|
const existing = toolCallBuffers.get(tc.id);
|
|
2484
2505
|
if (existing) {
|
|
2485
2506
|
if (tc.arguments) existing.arguments += tc.arguments;
|
|
2507
|
+
if (tc.thoughtSignature) existing.thoughtSignature = tc.thoughtSignature;
|
|
2486
2508
|
} else {
|
|
2487
2509
|
toolCallBuffers.set(tc.id, {
|
|
2488
2510
|
id: tc.id,
|
|
2489
2511
|
name: tc.name ?? "",
|
|
2490
|
-
arguments: tc.arguments ?? ""
|
|
2512
|
+
arguments: tc.arguments ?? "",
|
|
2513
|
+
...tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}
|
|
2491
2514
|
});
|
|
2492
2515
|
}
|
|
2493
2516
|
}
|
|
@@ -2498,8 +2521,18 @@ function fromNativeProvider(provider, options) {
|
|
|
2498
2521
|
if (link.signal?.aborted) return;
|
|
2499
2522
|
allToolCalls = [...toolCallBuffers.values()];
|
|
2500
2523
|
const finalMessage = assembleMessage(fullContent, allToolCalls, lastUsage);
|
|
2501
|
-
if (reasoningBlocks?.length) {
|
|
2502
|
-
finalMessage.reasoning = {
|
|
2524
|
+
if (reasoningBlocks?.length || reasoningText) {
|
|
2525
|
+
finalMessage.reasoning = {
|
|
2526
|
+
...reasoningBlocks?.length ? { blocks: reasoningBlocks } : {},
|
|
2527
|
+
...reasoningText ? { summary: reasoningText } : {}
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2530
|
+
if (finishReason || responseModel) {
|
|
2531
|
+
finalMessage.response_metadata = {
|
|
2532
|
+
...finalMessage.response_metadata ?? {},
|
|
2533
|
+
...finishReason ? { finish_reason: finishReason } : {},
|
|
2534
|
+
...responseModel ? { model_name: responseModel } : {}
|
|
2535
|
+
};
|
|
2503
2536
|
}
|
|
2504
2537
|
yield finalMessage;
|
|
2505
2538
|
} finally {
|
|
@@ -2513,6 +2546,11 @@ function fromNativeProvider(provider, options) {
|
|
|
2513
2546
|
}
|
|
2514
2547
|
};
|
|
2515
2548
|
return adapted;
|
|
2549
|
+
async function applyTransform(req) {
|
|
2550
|
+
if (!options?.transformRequest) return req;
|
|
2551
|
+
const out = await options.transformRequest(req);
|
|
2552
|
+
return out ?? req;
|
|
2553
|
+
}
|
|
2516
2554
|
function buildRequest(messages, tools, opts, invokeOptions) {
|
|
2517
2555
|
const req = {
|
|
2518
2556
|
model: opts?.model ?? modelName,
|
|
@@ -2520,6 +2558,10 @@ function fromNativeProvider(provider, options) {
|
|
|
2520
2558
|
};
|
|
2521
2559
|
if (opts?.temperature != null) req.temperature = opts.temperature;
|
|
2522
2560
|
if (opts?.maxTokens != null) req.maxTokens = opts.maxTokens;
|
|
2561
|
+
if (opts?.topP != null) req.topP = opts.topP;
|
|
2562
|
+
if (opts?.stop?.length) req.stop = opts.stop;
|
|
2563
|
+
if (opts?.presencePenalty != null) req.presencePenalty = opts.presencePenalty;
|
|
2564
|
+
if (opts?.frequencyPenalty != null) req.frequencyPenalty = opts.frequencyPenalty;
|
|
2523
2565
|
if (tools?.length) req.tools = tools;
|
|
2524
2566
|
if (opts?.extra) req.extra = opts.extra;
|
|
2525
2567
|
const reasoningOverride = invokeOptions?.reasoning;
|
|
@@ -2572,7 +2614,8 @@ function toUnifiedMessage(msg) {
|
|
|
2572
2614
|
unified.toolCalls = msg.tool_calls.map((tc) => ({
|
|
2573
2615
|
id: tc.id,
|
|
2574
2616
|
name: tc.function?.name ?? tc.name ?? "",
|
|
2575
|
-
arguments: tc.function?.arguments ?? tc.arguments ?? "{}"
|
|
2617
|
+
arguments: tc.function?.arguments ?? tc.arguments ?? "{}",
|
|
2618
|
+
...tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}
|
|
2576
2619
|
}));
|
|
2577
2620
|
}
|
|
2578
2621
|
const reasoning = msg.reasoning;
|
|
@@ -2846,29 +2889,16 @@ function toToolDefinition(tool, strict) {
|
|
|
2846
2889
|
}
|
|
2847
2890
|
return { name, description, parameters, ...useStrict ? { strict: true } : {} };
|
|
2848
2891
|
}
|
|
2892
|
+
function legacyTokenUsage(u) {
|
|
2893
|
+
return { ...toMessageUsage(u), cached_tokens: u.cachedInputTokens, cache_write_tokens: u.cachedWriteTokens };
|
|
2894
|
+
}
|
|
2849
2895
|
function toBaseChatMessage(response) {
|
|
2850
2896
|
const msg = {
|
|
2851
2897
|
role: "assistant",
|
|
2852
2898
|
content: response.content ?? "",
|
|
2853
|
-
usage:
|
|
2854
|
-
prompt_tokens: response.usage.inputTokens,
|
|
2855
|
-
completion_tokens: response.usage.outputTokens,
|
|
2856
|
-
total_tokens: response.usage.totalTokens,
|
|
2857
|
-
prompt_tokens_details: {
|
|
2858
|
-
cached_tokens: response.usage.cachedInputTokens
|
|
2859
|
-
},
|
|
2860
|
-
completion_tokens_details: {
|
|
2861
|
-
reasoning_tokens: response.usage.reasoningTokens
|
|
2862
|
-
}
|
|
2863
|
-
},
|
|
2899
|
+
usage: toMessageUsage(response.usage),
|
|
2864
2900
|
response_metadata: {
|
|
2865
|
-
token_usage:
|
|
2866
|
-
prompt_tokens: response.usage.inputTokens,
|
|
2867
|
-
completion_tokens: response.usage.outputTokens,
|
|
2868
|
-
total_tokens: response.usage.totalTokens,
|
|
2869
|
-
cached_tokens: response.usage.cachedInputTokens,
|
|
2870
|
-
cache_write_tokens: response.usage.cachedWriteTokens
|
|
2871
|
-
},
|
|
2901
|
+
token_usage: legacyTokenUsage(response.usage),
|
|
2872
2902
|
finish_reason: response.finishReason,
|
|
2873
2903
|
model_name: response.model
|
|
2874
2904
|
}
|
|
@@ -2877,7 +2907,8 @@ function toBaseChatMessage(response) {
|
|
|
2877
2907
|
msg.tool_calls = response.toolCalls.map((tc) => ({
|
|
2878
2908
|
id: tc.id,
|
|
2879
2909
|
type: "function",
|
|
2880
|
-
function: { name: tc.name, arguments: tc.arguments }
|
|
2910
|
+
function: { name: tc.name, arguments: tc.arguments },
|
|
2911
|
+
...tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}
|
|
2881
2912
|
}));
|
|
2882
2913
|
}
|
|
2883
2914
|
if (response.reasoning && (response.reasoning.blocks?.length || response.reasoning.summary)) {
|
|
@@ -2891,32 +2922,15 @@ function assembleMessage(content, toolCalls, usage) {
|
|
|
2891
2922
|
content
|
|
2892
2923
|
};
|
|
2893
2924
|
if (usage) {
|
|
2894
|
-
msg.usage =
|
|
2895
|
-
|
|
2896
|
-
completion_tokens: usage.outputTokens,
|
|
2897
|
-
total_tokens: usage.totalTokens,
|
|
2898
|
-
prompt_tokens_details: {
|
|
2899
|
-
cached_tokens: usage.cachedInputTokens
|
|
2900
|
-
},
|
|
2901
|
-
completion_tokens_details: {
|
|
2902
|
-
reasoning_tokens: usage.reasoningTokens
|
|
2903
|
-
}
|
|
2904
|
-
};
|
|
2905
|
-
msg.response_metadata = {
|
|
2906
|
-
token_usage: {
|
|
2907
|
-
prompt_tokens: usage.inputTokens,
|
|
2908
|
-
completion_tokens: usage.outputTokens,
|
|
2909
|
-
total_tokens: usage.totalTokens,
|
|
2910
|
-
cached_tokens: usage.cachedInputTokens,
|
|
2911
|
-
cache_write_tokens: usage.cachedWriteTokens
|
|
2912
|
-
}
|
|
2913
|
-
};
|
|
2925
|
+
msg.usage = toMessageUsage(usage);
|
|
2926
|
+
msg.response_metadata = { token_usage: legacyTokenUsage(usage) };
|
|
2914
2927
|
}
|
|
2915
2928
|
if (toolCalls.length > 0) {
|
|
2916
2929
|
msg.tool_calls = toolCalls.map((tc) => ({
|
|
2917
2930
|
id: tc.id,
|
|
2918
2931
|
type: "function",
|
|
2919
|
-
function: { name: tc.name, arguments: tc.arguments }
|
|
2932
|
+
function: { name: tc.name, arguments: tc.arguments },
|
|
2933
|
+
...tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}
|
|
2920
2934
|
}));
|
|
2921
2935
|
}
|
|
2922
2936
|
return msg;
|
|
@@ -2949,8 +2963,8 @@ function resolveCapabilities(provider) {
|
|
|
2949
2963
|
return {
|
|
2950
2964
|
structuredOutput: "tool_based",
|
|
2951
2965
|
strictToolCalling: false,
|
|
2952
|
-
streaming:
|
|
2953
|
-
//
|
|
2966
|
+
streaming: true,
|
|
2967
|
+
// ConverseStream (AWS event-stream frames)
|
|
2954
2968
|
provider: "bedrock"
|
|
2955
2969
|
};
|
|
2956
2970
|
case "vertex":
|
|
@@ -4656,6 +4670,10 @@ ${append}`;
|
|
|
4656
4670
|
if (chunk && typeof chunk === "object" && chunk.role) {
|
|
4657
4671
|
streamedMessage = chunk;
|
|
4658
4672
|
}
|
|
4673
|
+
if (chunk && typeof chunk === "object" && chunk.type === "reasoning" && typeof chunk.reasoning_content === "string") {
|
|
4674
|
+
onEvent?.({ type: "reasoning", text: chunk.reasoning_content });
|
|
4675
|
+
continue;
|
|
4676
|
+
}
|
|
4659
4677
|
const text = extractText2(chunk);
|
|
4660
4678
|
const isAssembledMessage = Boolean(chunk && typeof chunk === "object" && chunk.role) && text.length > 0 && text === streamedText;
|
|
4661
4679
|
if (text && !isAssembledMessage) {
|
|
@@ -11536,6 +11554,28 @@ async function runSmartAgentEvalHarness(params) {
|
|
|
11536
11554
|
return results;
|
|
11537
11555
|
}
|
|
11538
11556
|
|
|
11557
|
+
// src/providers/types.ts
|
|
11558
|
+
function emptyUsage() {
|
|
11559
|
+
return {
|
|
11560
|
+
inputTokens: 0,
|
|
11561
|
+
outputTokens: 0,
|
|
11562
|
+
totalTokens: 0,
|
|
11563
|
+
cachedInputTokens: 0,
|
|
11564
|
+
cachedWriteTokens: 0,
|
|
11565
|
+
cachedOutputTokens: 0,
|
|
11566
|
+
reasoningTokens: 0
|
|
11567
|
+
};
|
|
11568
|
+
}
|
|
11569
|
+
var ProviderError = class extends Error {
|
|
11570
|
+
constructor(message, provider, statusCode, responseBody) {
|
|
11571
|
+
super(message);
|
|
11572
|
+
this.provider = provider;
|
|
11573
|
+
this.statusCode = statusCode;
|
|
11574
|
+
this.responseBody = responseBody;
|
|
11575
|
+
this.name = "ProviderError";
|
|
11576
|
+
}
|
|
11577
|
+
};
|
|
11578
|
+
|
|
11539
11579
|
// src/providers/base.ts
|
|
11540
11580
|
var DEFAULT_RETRY = {
|
|
11541
11581
|
maxRetries: 3,
|
|
@@ -11546,6 +11586,55 @@ var BaseProvider = class {
|
|
|
11546
11586
|
constructor() {
|
|
11547
11587
|
this.retryPolicy = DEFAULT_RETRY;
|
|
11548
11588
|
}
|
|
11589
|
+
/** Applies the transport options shared by every provider config. */
|
|
11590
|
+
configureTransport(config) {
|
|
11591
|
+
this.fetchImpl = config.fetch;
|
|
11592
|
+
this.timeoutMs = typeof config.timeoutMs === "number" && config.timeoutMs > 0 ? config.timeoutMs : void 0;
|
|
11593
|
+
}
|
|
11594
|
+
/**
|
|
11595
|
+
* The single HTTP entry point for every provider call site. Uses the
|
|
11596
|
+
* configured `fetch` and applies `timeoutMs`: the timer covers the time until
|
|
11597
|
+
* the response HEADERS arrive and is cleared after that, so a long-running
|
|
11598
|
+
* stream is never cut by it (use the request `signal` to cancel a stream).
|
|
11599
|
+
* A timeout surfaces as a `ProviderError` with status 408. The caller's
|
|
11600
|
+
* `signal` still aborts the request as before.
|
|
11601
|
+
*/
|
|
11602
|
+
async httpFetch(url, init = {}) {
|
|
11603
|
+
const doFetch = this.fetchImpl ?? fetch;
|
|
11604
|
+
const timeoutMs = this.timeoutMs;
|
|
11605
|
+
if (!timeoutMs) return doFetch(url, init);
|
|
11606
|
+
const controller = new AbortController();
|
|
11607
|
+
const outer = init.signal ?? void 0;
|
|
11608
|
+
let timedOut = false;
|
|
11609
|
+
const onOuterAbort = () => controller.abort(outer?.reason);
|
|
11610
|
+
if (outer) {
|
|
11611
|
+
if (outer.aborted) controller.abort(outer.reason);
|
|
11612
|
+
else outer.addEventListener("abort", onOuterAbort, { once: true });
|
|
11613
|
+
}
|
|
11614
|
+
const timer = setTimeout(() => {
|
|
11615
|
+
timedOut = true;
|
|
11616
|
+
controller.abort(new Error(`timed out after ${timeoutMs}ms`));
|
|
11617
|
+
}, timeoutMs);
|
|
11618
|
+
try {
|
|
11619
|
+
const res = await doFetch(url, { ...init, signal: controller.signal });
|
|
11620
|
+
return res;
|
|
11621
|
+
} catch (err) {
|
|
11622
|
+
if (timedOut) {
|
|
11623
|
+
throw new ProviderError(`${this.providerName} request timed out after ${timeoutMs}ms`, this.providerName, 408);
|
|
11624
|
+
}
|
|
11625
|
+
throw err;
|
|
11626
|
+
} finally {
|
|
11627
|
+
clearTimeout(timer);
|
|
11628
|
+
}
|
|
11629
|
+
}
|
|
11630
|
+
/**
|
|
11631
|
+
* Embeds `request.input`. Implemented by the providers that have an
|
|
11632
|
+
* embeddings endpoint (OpenAI, Azure, openai-compatible, Bedrock, Vertex);
|
|
11633
|
+
* the others reject with a 400 ProviderError.
|
|
11634
|
+
*/
|
|
11635
|
+
async embed(_request) {
|
|
11636
|
+
throw new ProviderError(`${this.providerName} does not support embeddings`, this.providerName, 400);
|
|
11637
|
+
}
|
|
11549
11638
|
/**
|
|
11550
11639
|
* Helper concrete providers can wrap their fetch with. Retries 429/5xx
|
|
11551
11640
|
* with exponential backoff and respects Retry-After if the response
|
|
@@ -11587,34 +11676,13 @@ var BaseProvider = class {
|
|
|
11587
11676
|
}
|
|
11588
11677
|
};
|
|
11589
11678
|
|
|
11590
|
-
// src/providers/types.ts
|
|
11591
|
-
function emptyUsage() {
|
|
11592
|
-
return {
|
|
11593
|
-
inputTokens: 0,
|
|
11594
|
-
outputTokens: 0,
|
|
11595
|
-
totalTokens: 0,
|
|
11596
|
-
cachedInputTokens: 0,
|
|
11597
|
-
cachedWriteTokens: 0,
|
|
11598
|
-
cachedOutputTokens: 0,
|
|
11599
|
-
reasoningTokens: 0
|
|
11600
|
-
};
|
|
11601
|
-
}
|
|
11602
|
-
var ProviderError = class extends Error {
|
|
11603
|
-
constructor(message, provider, statusCode, responseBody) {
|
|
11604
|
-
super(message);
|
|
11605
|
-
this.provider = provider;
|
|
11606
|
-
this.statusCode = statusCode;
|
|
11607
|
-
this.responseBody = responseBody;
|
|
11608
|
-
this.name = "ProviderError";
|
|
11609
|
-
}
|
|
11610
|
-
};
|
|
11611
|
-
|
|
11612
11679
|
// src/providers/jev.ts
|
|
11613
11680
|
var JEV_DEFAULT_BASE_URL = "https://api.typesafe.ai";
|
|
11614
11681
|
var JevProvider = class extends BaseProvider {
|
|
11615
11682
|
constructor(config) {
|
|
11616
11683
|
super();
|
|
11617
11684
|
this.providerName = "jev";
|
|
11685
|
+
this.configureTransport(config);
|
|
11618
11686
|
this.apiKey = config.apiKey;
|
|
11619
11687
|
this.baseURL = (config.baseURL ?? JEV_DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
11620
11688
|
this.defaultModel = config.defaultModel;
|
|
@@ -11630,7 +11698,7 @@ var JevProvider = class extends BaseProvider {
|
|
|
11630
11698
|
questions: request.questions
|
|
11631
11699
|
};
|
|
11632
11700
|
const res = await this.doFetchWithRetry(
|
|
11633
|
-
() =>
|
|
11701
|
+
() => this.httpFetch(`${this.baseURL}/v1/systemone`, {
|
|
11634
11702
|
method: "POST",
|
|
11635
11703
|
headers: {
|
|
11636
11704
|
"Content-Type": "application/json",
|
|
@@ -13866,12 +13934,12 @@ function responseCache(config = {}) {
|
|
|
13866
13934
|
};
|
|
13867
13935
|
}
|
|
13868
13936
|
function signRequest(req) {
|
|
13869
|
-
const now = /* @__PURE__ */ new Date();
|
|
13937
|
+
const now = req.now ?? /* @__PURE__ */ new Date();
|
|
13870
13938
|
const dateStamp = toDateStamp(now);
|
|
13871
13939
|
const amzDate = toAmzDate(now);
|
|
13872
13940
|
const parsed = new URL(req.url);
|
|
13873
13941
|
const host = parsed.host;
|
|
13874
|
-
const canonicalUri = parsed.pathname || "/";
|
|
13942
|
+
const canonicalUri = (parsed.pathname || "/").split("/").map((seg) => encodeURIComponent(seg).replace(/[!'()*]/g, (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase())).join("/");
|
|
13875
13943
|
const canonicalQuerystring = sortQueryParams(parsed.searchParams);
|
|
13876
13944
|
const headers = {
|
|
13877
13945
|
...req.headers,
|
|
@@ -15188,73 +15256,6 @@ function languageGuard(config) {
|
|
|
15188
15256
|
// src/index.ts
|
|
15189
15257
|
init_content();
|
|
15190
15258
|
|
|
15191
|
-
// src/providers/utils/sse.ts
|
|
15192
|
-
async function* parseSSEStream(body, signal) {
|
|
15193
|
-
const reader = body.getReader();
|
|
15194
|
-
const decoder = new TextDecoder();
|
|
15195
|
-
let buffer = "";
|
|
15196
|
-
try {
|
|
15197
|
-
while (true) {
|
|
15198
|
-
if (signal?.aborted) throw abortReason(signal);
|
|
15199
|
-
const { done, value } = await reader.read();
|
|
15200
|
-
if (done) break;
|
|
15201
|
-
buffer += decoder.decode(value, { stream: true });
|
|
15202
|
-
const parts = buffer.split(/\r?\n\r?\n/);
|
|
15203
|
-
buffer = parts.pop() ?? "";
|
|
15204
|
-
for (const part of parts) {
|
|
15205
|
-
if (!part.trim()) continue;
|
|
15206
|
-
const event = parseSSEBlock(part);
|
|
15207
|
-
if (event) yield event;
|
|
15208
|
-
}
|
|
15209
|
-
}
|
|
15210
|
-
if (buffer.trim()) {
|
|
15211
|
-
const event = parseSSEBlock(buffer);
|
|
15212
|
-
if (event) yield event;
|
|
15213
|
-
}
|
|
15214
|
-
} finally {
|
|
15215
|
-
reader.cancel().catch(() => void 0);
|
|
15216
|
-
reader.releaseLock();
|
|
15217
|
-
}
|
|
15218
|
-
}
|
|
15219
|
-
function parseSSEBlock(block) {
|
|
15220
|
-
let event;
|
|
15221
|
-
let id;
|
|
15222
|
-
let retry;
|
|
15223
|
-
const dataLines = [];
|
|
15224
|
-
for (const line of block.split(/\r?\n/)) {
|
|
15225
|
-
if (line.startsWith(":")) continue;
|
|
15226
|
-
const colonIdx = line.indexOf(":");
|
|
15227
|
-
let field;
|
|
15228
|
-
let value;
|
|
15229
|
-
if (colonIdx === -1) {
|
|
15230
|
-
field = line;
|
|
15231
|
-
value = "";
|
|
15232
|
-
} else {
|
|
15233
|
-
field = line.slice(0, colonIdx);
|
|
15234
|
-
value = line.slice(colonIdx + 1);
|
|
15235
|
-
if (value.startsWith(" ")) value = value.slice(1);
|
|
15236
|
-
}
|
|
15237
|
-
switch (field) {
|
|
15238
|
-
case "event":
|
|
15239
|
-
event = value;
|
|
15240
|
-
break;
|
|
15241
|
-
case "data":
|
|
15242
|
-
dataLines.push(value);
|
|
15243
|
-
break;
|
|
15244
|
-
case "id":
|
|
15245
|
-
id = value;
|
|
15246
|
-
break;
|
|
15247
|
-
case "retry": {
|
|
15248
|
-
const n = parseInt(value, 10);
|
|
15249
|
-
if (!isNaN(n)) retry = n;
|
|
15250
|
-
break;
|
|
15251
|
-
}
|
|
15252
|
-
}
|
|
15253
|
-
}
|
|
15254
|
-
if (dataLines.length === 0) return null;
|
|
15255
|
-
return { event, data: dataLines.join("\n"), id, retry };
|
|
15256
|
-
}
|
|
15257
|
-
|
|
15258
15259
|
// src/providers/utils/reasoning.ts
|
|
15259
15260
|
var ANTHROPIC_EFFORT_BUDGET = {
|
|
15260
15261
|
// 0 = off. `applyAnthropicReasoning` bails on a falsy budget and emits no
|
|
@@ -15276,6 +15277,7 @@ var GEMINI_EFFORT_BUDGET = {
|
|
|
15276
15277
|
high: 12e3
|
|
15277
15278
|
};
|
|
15278
15279
|
var THINKING_OUTPUT_RESERVE = 1024;
|
|
15280
|
+
var MIN_THINKING_BUDGET = 1024;
|
|
15279
15281
|
var THINKING_MIN_MAX_TOKENS = 4096;
|
|
15280
15282
|
function applyOpenAIReasoning(body, reasoning, mode = "legacy_effort") {
|
|
15281
15283
|
if (!reasoning) return;
|
|
@@ -15291,8 +15293,48 @@ function applyOpenAIReasoning(body, reasoning, mode = "legacy_effort") {
|
|
|
15291
15293
|
if (reasoning.providerExtras) Object.assign(body, reasoning.providerExtras);
|
|
15292
15294
|
}
|
|
15293
15295
|
}
|
|
15294
|
-
function
|
|
15296
|
+
function supportsAdaptiveThinking(model) {
|
|
15297
|
+
if (typeof model !== "string") return false;
|
|
15298
|
+
const id = model.toLowerCase();
|
|
15299
|
+
const at = id.indexOf("claude-");
|
|
15300
|
+
if (at < 0) return false;
|
|
15301
|
+
for (const tok of id.slice(at + 7).split("-")) {
|
|
15302
|
+
if (tok === "fable") return true;
|
|
15303
|
+
const m = /^(\d+)(?:@|$)/.exec(tok);
|
|
15304
|
+
if (m) {
|
|
15305
|
+
if (m[1].length <= 2) return Number(m[1]) >= 5;
|
|
15306
|
+
return false;
|
|
15307
|
+
}
|
|
15308
|
+
}
|
|
15309
|
+
return false;
|
|
15310
|
+
}
|
|
15311
|
+
function resolveThinkingForm(model, mode) {
|
|
15312
|
+
if (mode === "adaptive" || mode === "budget") return mode;
|
|
15313
|
+
return supportsAdaptiveThinking(model) ? "adaptive" : "budget";
|
|
15314
|
+
}
|
|
15315
|
+
function applyAdaptiveThinking(_body, reasoning, setThinking, setOutputConfig) {
|
|
15316
|
+
const requested = normalizeEffort(reasoning.effort);
|
|
15317
|
+
if (requested === "none") return false;
|
|
15318
|
+
let effort;
|
|
15319
|
+
if (requested) effort = requested === "minimal" ? "low" : requested;
|
|
15320
|
+
else if (typeof reasoning.budgetTokens === "number" && reasoning.budgetTokens > 0) {
|
|
15321
|
+
effort = reasoning.budgetTokens <= ANTHROPIC_EFFORT_BUDGET.low ? "low" : reasoning.budgetTokens <= ANTHROPIC_EFFORT_BUDGET.medium ? "medium" : "high";
|
|
15322
|
+
}
|
|
15323
|
+
if (!effort) return false;
|
|
15324
|
+
const thinking = { type: "adaptive" };
|
|
15325
|
+
if (reasoning.includeThoughts !== false) thinking.display = "summarized";
|
|
15326
|
+
if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
|
|
15327
|
+
setThinking({ thinking });
|
|
15328
|
+
setOutputConfig({ effort });
|
|
15329
|
+
return true;
|
|
15330
|
+
}
|
|
15331
|
+
function applyAnthropicReasoning(body, reasoning, opts) {
|
|
15295
15332
|
if (!reasoning) return;
|
|
15333
|
+
if (resolveThinkingForm(opts?.model ?? body.model, opts?.mode) === "adaptive") {
|
|
15334
|
+
const on = applyAdaptiveThinking(body, reasoning, (extra) => body.thinking = extra.thinking, (o) => body.output_config = { ...body.output_config ?? {}, ...o });
|
|
15335
|
+
if (on) stripSamplingParams(body, ["temperature", "top_p", "top_k"]);
|
|
15336
|
+
return;
|
|
15337
|
+
}
|
|
15296
15338
|
let budget = resolveBudget(reasoning, ANTHROPIC_EFFORT_BUDGET);
|
|
15297
15339
|
if (!budget) return;
|
|
15298
15340
|
const currentMax = typeof body.max_tokens === "number" ? body.max_tokens : 0;
|
|
@@ -15300,14 +15342,20 @@ function applyAnthropicReasoning(body, reasoning) {
|
|
|
15300
15342
|
if (currentMax < requiredMax) {
|
|
15301
15343
|
body.max_tokens = requiredMax;
|
|
15302
15344
|
}
|
|
15303
|
-
budget = clampBudget(budget, body.max_tokens);
|
|
15345
|
+
budget = Math.max(MIN_THINKING_BUDGET, clampBudget(budget, body.max_tokens));
|
|
15304
15346
|
const thinking = { type: "enabled", budget_tokens: budget };
|
|
15305
15347
|
if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
|
|
15306
15348
|
body.thinking = thinking;
|
|
15307
15349
|
stripSamplingParams(body, ["temperature", "top_p", "top_k"]);
|
|
15308
15350
|
}
|
|
15309
|
-
function applyBedrockReasoning(body, reasoning) {
|
|
15351
|
+
function applyBedrockReasoning(body, reasoning, opts) {
|
|
15310
15352
|
if (!reasoning) return;
|
|
15353
|
+
if (resolveThinkingForm(opts?.model ?? body.modelId, opts?.mode) === "adaptive") {
|
|
15354
|
+
const fields = () => body.additionalModelRequestFields ?? (body.additionalModelRequestFields = {});
|
|
15355
|
+
const on = applyAdaptiveThinking(body, reasoning, (extra2) => fields().thinking = extra2.thinking, (o) => fields().output_config = { ...fields().output_config ?? {}, ...o });
|
|
15356
|
+
if (on && body.inferenceConfig) stripSamplingParams(body.inferenceConfig, ["temperature", "topP"]);
|
|
15357
|
+
return;
|
|
15358
|
+
}
|
|
15311
15359
|
let budget = resolveBudget(reasoning, ANTHROPIC_EFFORT_BUDGET);
|
|
15312
15360
|
if (!budget) return;
|
|
15313
15361
|
const inference = body.inferenceConfig ?? (body.inferenceConfig = {});
|
|
@@ -15316,7 +15364,7 @@ function applyBedrockReasoning(body, reasoning) {
|
|
|
15316
15364
|
if (currentMax < requiredMax) {
|
|
15317
15365
|
inference.maxTokens = requiredMax;
|
|
15318
15366
|
}
|
|
15319
|
-
budget = clampBudget(budget, inference.maxTokens);
|
|
15367
|
+
budget = Math.max(MIN_THINKING_BUDGET, clampBudget(budget, inference.maxTokens));
|
|
15320
15368
|
const extra = body.additionalModelRequestFields ?? (body.additionalModelRequestFields = {});
|
|
15321
15369
|
const thinking = { type: "enabled", budget_tokens: budget };
|
|
15322
15370
|
if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
|
|
@@ -15326,8 +15374,9 @@ function applyBedrockReasoning(body, reasoning) {
|
|
|
15326
15374
|
function applyGeminiReasoning(generationConfig, reasoning) {
|
|
15327
15375
|
if (!reasoning) return;
|
|
15328
15376
|
const budget = resolveBudget(reasoning, GEMINI_EFFORT_BUDGET);
|
|
15329
|
-
|
|
15330
|
-
|
|
15377
|
+
const level = typeof reasoning.thinkingLevel === "string" && reasoning.thinkingLevel ? reasoning.thinkingLevel : void 0;
|
|
15378
|
+
if (budget == null && !level) return;
|
|
15379
|
+
const thinkingConfig = level && budget !== 0 ? { thinkingLevel: level } : { thinkingBudget: budget ?? 0 };
|
|
15331
15380
|
if (reasoning.includeThoughts) thinkingConfig.includeThoughts = true;
|
|
15332
15381
|
if (reasoning.providerExtras) Object.assign(thinkingConfig, reasoning.providerExtras);
|
|
15333
15382
|
generationConfig.thinkingConfig = thinkingConfig;
|
|
@@ -15358,6 +15407,73 @@ function resolveBudget(reasoning, effortBudget) {
|
|
|
15358
15407
|
return void 0;
|
|
15359
15408
|
}
|
|
15360
15409
|
|
|
15410
|
+
// src/providers/utils/sse.ts
|
|
15411
|
+
async function* parseSSEStream(body, signal) {
|
|
15412
|
+
const reader = body.getReader();
|
|
15413
|
+
const decoder = new TextDecoder();
|
|
15414
|
+
let buffer = "";
|
|
15415
|
+
try {
|
|
15416
|
+
while (true) {
|
|
15417
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
15418
|
+
const { done, value } = await reader.read();
|
|
15419
|
+
if (done) break;
|
|
15420
|
+
buffer += decoder.decode(value, { stream: true });
|
|
15421
|
+
const parts = buffer.split(/\r?\n\r?\n/);
|
|
15422
|
+
buffer = parts.pop() ?? "";
|
|
15423
|
+
for (const part of parts) {
|
|
15424
|
+
if (!part.trim()) continue;
|
|
15425
|
+
const event = parseSSEBlock(part);
|
|
15426
|
+
if (event) yield event;
|
|
15427
|
+
}
|
|
15428
|
+
}
|
|
15429
|
+
if (buffer.trim()) {
|
|
15430
|
+
const event = parseSSEBlock(buffer);
|
|
15431
|
+
if (event) yield event;
|
|
15432
|
+
}
|
|
15433
|
+
} finally {
|
|
15434
|
+
reader.cancel().catch(() => void 0);
|
|
15435
|
+
reader.releaseLock();
|
|
15436
|
+
}
|
|
15437
|
+
}
|
|
15438
|
+
function parseSSEBlock(block) {
|
|
15439
|
+
let event;
|
|
15440
|
+
let id;
|
|
15441
|
+
let retry;
|
|
15442
|
+
const dataLines = [];
|
|
15443
|
+
for (const line of block.split(/\r?\n/)) {
|
|
15444
|
+
if (line.startsWith(":")) continue;
|
|
15445
|
+
const colonIdx = line.indexOf(":");
|
|
15446
|
+
let field;
|
|
15447
|
+
let value;
|
|
15448
|
+
if (colonIdx === -1) {
|
|
15449
|
+
field = line;
|
|
15450
|
+
value = "";
|
|
15451
|
+
} else {
|
|
15452
|
+
field = line.slice(0, colonIdx);
|
|
15453
|
+
value = line.slice(colonIdx + 1);
|
|
15454
|
+
if (value.startsWith(" ")) value = value.slice(1);
|
|
15455
|
+
}
|
|
15456
|
+
switch (field) {
|
|
15457
|
+
case "event":
|
|
15458
|
+
event = value;
|
|
15459
|
+
break;
|
|
15460
|
+
case "data":
|
|
15461
|
+
dataLines.push(value);
|
|
15462
|
+
break;
|
|
15463
|
+
case "id":
|
|
15464
|
+
id = value;
|
|
15465
|
+
break;
|
|
15466
|
+
case "retry": {
|
|
15467
|
+
const n = parseInt(value, 10);
|
|
15468
|
+
if (!isNaN(n)) retry = n;
|
|
15469
|
+
break;
|
|
15470
|
+
}
|
|
15471
|
+
}
|
|
15472
|
+
}
|
|
15473
|
+
if (dataLines.length === 0) return null;
|
|
15474
|
+
return { event, data: dataLines.join("\n"), id, retry };
|
|
15475
|
+
}
|
|
15476
|
+
|
|
15361
15477
|
// src/providers/utils/media.ts
|
|
15362
15478
|
var EXTENSION_MIME = {
|
|
15363
15479
|
jpg: "image/jpeg",
|
|
@@ -15549,11 +15665,64 @@ function isObject2(value) {
|
|
|
15549
15665
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15550
15666
|
}
|
|
15551
15667
|
|
|
15668
|
+
// src/providers/utils/embeddings.ts
|
|
15669
|
+
function chunkArray(items, size) {
|
|
15670
|
+
const n = Math.max(1, Math.floor(size));
|
|
15671
|
+
const out = [];
|
|
15672
|
+
for (let i = 0; i < items.length; i += n) out.push(items.slice(i, i + n));
|
|
15673
|
+
return out;
|
|
15674
|
+
}
|
|
15675
|
+
async function mapWithConcurrency(items, limit, fn, signal) {
|
|
15676
|
+
const results = new Array(items.length);
|
|
15677
|
+
let next = 0;
|
|
15678
|
+
let failure;
|
|
15679
|
+
let failed = false;
|
|
15680
|
+
const worker = async () => {
|
|
15681
|
+
while (!failed) {
|
|
15682
|
+
if (signal?.aborted) throw signal.reason ?? new Error("aborted");
|
|
15683
|
+
const i = next++;
|
|
15684
|
+
if (i >= items.length) return;
|
|
15685
|
+
try {
|
|
15686
|
+
results[i] = await fn(items[i], i);
|
|
15687
|
+
} catch (err) {
|
|
15688
|
+
failed = true;
|
|
15689
|
+
failure = err;
|
|
15690
|
+
return;
|
|
15691
|
+
}
|
|
15692
|
+
}
|
|
15693
|
+
};
|
|
15694
|
+
await Promise.all(Array.from({ length: Math.min(Math.max(1, limit), items.length) }, worker));
|
|
15695
|
+
if (failed) throw failure;
|
|
15696
|
+
return results;
|
|
15697
|
+
}
|
|
15698
|
+
function decodeBase64Vector(b64) {
|
|
15699
|
+
const buf = Buffer.from(b64, "base64");
|
|
15700
|
+
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
15701
|
+
const out = new Array(Math.floor(buf.byteLength / 4));
|
|
15702
|
+
for (let i = 0; i < out.length; i++) out[i] = view.getFloat32(i * 4, true);
|
|
15703
|
+
return out;
|
|
15704
|
+
}
|
|
15705
|
+
function assertVectorCount(provider, vectors, expected) {
|
|
15706
|
+
if (vectors.length !== expected) {
|
|
15707
|
+
throw new ProviderError(
|
|
15708
|
+
`${provider} embeddings returned ${vectors.length} vectors for ${expected} inputs`,
|
|
15709
|
+
provider,
|
|
15710
|
+
502
|
|
15711
|
+
);
|
|
15712
|
+
}
|
|
15713
|
+
}
|
|
15714
|
+
function requireEmbeddingModel(provider, model) {
|
|
15715
|
+
if (!model) throw new ProviderError(`${provider} embeddings require a model`, provider, 400);
|
|
15716
|
+
return model;
|
|
15717
|
+
}
|
|
15718
|
+
|
|
15552
15719
|
// src/providers/openai.ts
|
|
15553
|
-
var OpenAIProvider = class
|
|
15720
|
+
var OpenAIProvider = class extends BaseProvider {
|
|
15554
15721
|
constructor(config) {
|
|
15555
15722
|
super();
|
|
15556
15723
|
this.providerName = "openai";
|
|
15724
|
+
this.configureTransport(config);
|
|
15725
|
+
this.maxTokensParam = config.maxTokensParam ?? "auto";
|
|
15557
15726
|
this.apiKey = config.apiKey;
|
|
15558
15727
|
this.baseURL = (config.baseURL ?? "https://api.openai.com/v1").replace(/\/$/, "");
|
|
15559
15728
|
this.defaultModel = config.defaultModel ?? "gpt-4o";
|
|
@@ -15561,10 +15730,14 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15561
15730
|
...config.defaultHeaders,
|
|
15562
15731
|
...config.organization ? { "OpenAI-Organization": config.organization } : {}
|
|
15563
15732
|
};
|
|
15564
|
-
|
|
15565
|
-
const
|
|
15566
|
-
this.
|
|
15567
|
-
this.
|
|
15733
|
+
const legacy = config.responsesApi;
|
|
15734
|
+
const explicit = config.api === "chat" || config.api === "responses" ? config.api : void 0;
|
|
15735
|
+
this.responsesApi = explicit === "responses" ? "always" : explicit === "chat" ? "never" : legacy ?? (config._autoMeansChat ? "never" : "auto");
|
|
15736
|
+
this.apiExplicitChat = explicit === "chat" || explicit === void 0 && legacy === "never";
|
|
15737
|
+
this.apiExplicitResponses = this.responsesApi === "always";
|
|
15738
|
+
this.apiResponsesNative = explicit === "responses";
|
|
15739
|
+
this.responsesApiModels = [...config.responsesApiModels ?? []];
|
|
15740
|
+
this.responsesStreaming = config.responsesStreaming ?? this.apiResponsesNative;
|
|
15568
15741
|
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
15569
15742
|
}
|
|
15570
15743
|
/** True when the caller listed this request's model in `responsesApiModels`. */
|
|
@@ -15590,9 +15763,10 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15590
15763
|
* A model listed in `responsesApiModels` is the caller stating the answer
|
|
15591
15764
|
* for that model alone; `never` still overrides it. */
|
|
15592
15765
|
useResponsesApi(request) {
|
|
15593
|
-
if (this.
|
|
15594
|
-
if (this.
|
|
15766
|
+
if (this.apiExplicitChat) return false;
|
|
15767
|
+
if (this.apiExplicitResponses) return true;
|
|
15595
15768
|
if (this.isResponsesApiModel(request)) return true;
|
|
15769
|
+
if (this.responsesApi !== "auto") return false;
|
|
15596
15770
|
if (!request.reasoning) return false;
|
|
15597
15771
|
return isReasoningModel(request.model || this.defaultModel);
|
|
15598
15772
|
}
|
|
@@ -15607,7 +15781,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15607
15781
|
}
|
|
15608
15782
|
async *completeStream(request) {
|
|
15609
15783
|
if (this.useResponsesApi(request)) {
|
|
15610
|
-
if (this.responsesStreaming && this.isResponsesApiModel(request)) {
|
|
15784
|
+
if (this.responsesStreaming && (this.apiResponsesNative || this.isResponsesApiModel(request))) {
|
|
15611
15785
|
yield* this.streamResponses(request);
|
|
15612
15786
|
return;
|
|
15613
15787
|
}
|
|
@@ -15657,9 +15831,14 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15657
15831
|
delta: {},
|
|
15658
15832
|
finishReason: mapFinishReason(choice.finish_reason)
|
|
15659
15833
|
};
|
|
15834
|
+
if (typeof choice.finish_reason === "string") out.rawFinishReason = choice.finish_reason;
|
|
15835
|
+
const fields = pickProviderFields(chunk, delta, choice);
|
|
15836
|
+
if (fields) out.providerFields = fields;
|
|
15660
15837
|
if (delta.content) {
|
|
15661
15838
|
out.delta.content = delta.content;
|
|
15662
15839
|
}
|
|
15840
|
+
const reasoningDelta = extractReasoningText(delta);
|
|
15841
|
+
if (reasoningDelta) out.delta.reasoning = { summary: reasoningDelta };
|
|
15663
15842
|
if (delta.tool_calls) {
|
|
15664
15843
|
const partials = [];
|
|
15665
15844
|
for (const tc of delta.tool_calls) {
|
|
@@ -15671,7 +15850,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15671
15850
|
if (tc.id) buf.id = tc.id;
|
|
15672
15851
|
if (tc.function?.name) buf.name = tc.function.name;
|
|
15673
15852
|
if (tc.function?.arguments) buf.arguments += tc.function.arguments;
|
|
15674
|
-
partials.push({ id: buf.id, name: buf.name || void 0, arguments: tc.function?.arguments });
|
|
15853
|
+
partials.push({ id: buf.id, name: buf.name || void 0, arguments: tc.function?.arguments, index: idx });
|
|
15675
15854
|
}
|
|
15676
15855
|
out.delta.toolCalls = partials;
|
|
15677
15856
|
}
|
|
@@ -15679,6 +15858,51 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15679
15858
|
yield out;
|
|
15680
15859
|
}
|
|
15681
15860
|
}
|
|
15861
|
+
// ─── Embeddings ──────────────────────────────────────────────────────────
|
|
15862
|
+
/** POST /embeddings, batched (default 2048 inputs per request, the OpenAI cap). */
|
|
15863
|
+
async embed(request) {
|
|
15864
|
+
const model = requireEmbeddingModel(this.providerName, request.model);
|
|
15865
|
+
const batches = chunkArray(request.input, request.batchSize ?? 2048);
|
|
15866
|
+
const vectors = [];
|
|
15867
|
+
const raw = [];
|
|
15868
|
+
let inputTokens = 0;
|
|
15869
|
+
let totalTokens = 0;
|
|
15870
|
+
let responseModel = model;
|
|
15871
|
+
for (const batch of batches) {
|
|
15872
|
+
const body = { model, input: batch };
|
|
15873
|
+
if (request.dimensions != null) body.dimensions = request.dimensions;
|
|
15874
|
+
if (request.encodingFormat) body.encoding_format = request.encodingFormat;
|
|
15875
|
+
if (request.extra) Object.assign(body, request.extra);
|
|
15876
|
+
const res = await this.embeddingsFetch(body, request.signal);
|
|
15877
|
+
const json = await res.json();
|
|
15878
|
+
raw.push(json);
|
|
15879
|
+
const data = Array.isArray(json.data) ? [...json.data] : [];
|
|
15880
|
+
data.sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
15881
|
+
for (const d of data) {
|
|
15882
|
+
vectors.push(typeof d.embedding === "string" ? decodeBase64Vector(d.embedding) : d.embedding);
|
|
15883
|
+
}
|
|
15884
|
+
inputTokens += json.usage?.prompt_tokens ?? 0;
|
|
15885
|
+
totalTokens += json.usage?.total_tokens ?? json.usage?.prompt_tokens ?? 0;
|
|
15886
|
+
responseModel = json.model ?? responseModel;
|
|
15887
|
+
}
|
|
15888
|
+
assertVectorCount(this.providerName, vectors, request.input.length);
|
|
15889
|
+
return { model: responseModel, vectors, usage: { inputTokens, totalTokens }, raw };
|
|
15890
|
+
}
|
|
15891
|
+
async embeddingsFetch(body, signal) {
|
|
15892
|
+
const url = `${this.baseURL}/embeddings`;
|
|
15893
|
+
const headers = {
|
|
15894
|
+
"Content-Type": "application/json",
|
|
15895
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
15896
|
+
...this.defaultHeaders
|
|
15897
|
+
};
|
|
15898
|
+
const payload = JSON.stringify(body);
|
|
15899
|
+
const res = await this.doFetchWithRetry(() => this.httpFetch(url, { method: "POST", headers, body: payload, signal }), signal);
|
|
15900
|
+
if (!res.ok) {
|
|
15901
|
+
const text = await res.text().catch(() => "");
|
|
15902
|
+
throw new ProviderError(`OpenAI embeddings API error ${res.status}: ${text}`, this.providerName, res.status, text);
|
|
15903
|
+
}
|
|
15904
|
+
return res;
|
|
15905
|
+
}
|
|
15682
15906
|
// ─── Internals ───────────────────────────────────────────────────────────
|
|
15683
15907
|
buildRequestBody(request, stream) {
|
|
15684
15908
|
const body = {
|
|
@@ -15686,15 +15910,27 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15686
15910
|
messages: request.messages.map((m) => this.toOpenAIMessage(m)),
|
|
15687
15911
|
stream
|
|
15688
15912
|
};
|
|
15689
|
-
if (stream) {
|
|
15913
|
+
if (stream && request.streamUsage !== false) {
|
|
15690
15914
|
body.stream_options = { include_usage: true };
|
|
15691
15915
|
}
|
|
15692
|
-
|
|
15693
|
-
|
|
15694
|
-
if (request.
|
|
15916
|
+
const modelId = String(body.model);
|
|
15917
|
+
const reasoningModel = isReasoningModel(modelId) && !/chat/i.test(modelId);
|
|
15918
|
+
if (request.temperature != null && !reasoningModel) body.temperature = request.temperature;
|
|
15919
|
+
if (request.maxTokens != null) {
|
|
15920
|
+
const useCompletionTokens = this.maxTokensParam === "max_completion_tokens" || this.maxTokensParam === "auto" && isReasoningModel(modelId);
|
|
15921
|
+
body[useCompletionTokens ? "max_completion_tokens" : "max_tokens"] = request.maxTokens;
|
|
15922
|
+
}
|
|
15923
|
+
if (request.topP != null && !reasoningModel) body.top_p = request.topP;
|
|
15695
15924
|
if (request.stop) body.stop = request.stop;
|
|
15925
|
+
if (request.presencePenalty != null) body.presence_penalty = request.presencePenalty;
|
|
15926
|
+
if (request.frequencyPenalty != null) body.frequency_penalty = request.frequencyPenalty;
|
|
15927
|
+
if (request.seed != null) body.seed = request.seed;
|
|
15928
|
+
if (request.n != null) body.n = request.n;
|
|
15929
|
+
if (request.logprobs != null) body.logprobs = request.logprobs;
|
|
15930
|
+
if (request.topLogprobs != null) body.top_logprobs = request.topLogprobs;
|
|
15696
15931
|
if (request.tools?.length) {
|
|
15697
15932
|
body.tools = request.tools.map((t) => this.toOpenAITool(t));
|
|
15933
|
+
if (request.parallelToolCalls != null) body.parallel_tool_calls = request.parallelToolCalls;
|
|
15698
15934
|
}
|
|
15699
15935
|
if (request.toolChoice != null && request.tools?.length) {
|
|
15700
15936
|
if (typeof request.toolChoice === "string") {
|
|
@@ -15726,7 +15962,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15726
15962
|
// ─── Responses API (reasoning models) ──────────────────────────────────────
|
|
15727
15963
|
async completeResponses(request) {
|
|
15728
15964
|
const body = this.buildResponsesBody(request);
|
|
15729
|
-
if (this.isResponsesApiModel(request)) adaptChatExtrasForResponses(body, request.extra);
|
|
15965
|
+
if (this.apiResponsesNative || this.isResponsesApiModel(request)) adaptChatExtrasForResponses(body, request.extra);
|
|
15730
15966
|
const res = await this.responsesFetch(body, request.signal);
|
|
15731
15967
|
const json = await res.json();
|
|
15732
15968
|
return this.parseResponsesResponse(json, request.model || this.defaultModel);
|
|
@@ -15749,6 +15985,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15749
15985
|
let model = modelId;
|
|
15750
15986
|
const calls = /* @__PURE__ */ new Map();
|
|
15751
15987
|
let final;
|
|
15988
|
+
let streamedSummary = false;
|
|
15752
15989
|
for await (const event of parseSSEStream(res.body, request.signal)) {
|
|
15753
15990
|
if (event.data === "[DONE]") break;
|
|
15754
15991
|
let data;
|
|
@@ -15766,19 +16003,28 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15766
16003
|
case "response.output_text.delta":
|
|
15767
16004
|
if (data.delta) yield { id, model, delta: { content: data.delta } };
|
|
15768
16005
|
break;
|
|
16006
|
+
case "response.reasoning_summary_part.added":
|
|
16007
|
+
if (streamedSummary) yield { id, model, delta: { reasoning: { summary: "\n\n" } } };
|
|
16008
|
+
break;
|
|
16009
|
+
case "response.reasoning_summary_text.delta":
|
|
16010
|
+
if (typeof data.delta === "string" && data.delta) {
|
|
16011
|
+
streamedSummary = true;
|
|
16012
|
+
yield { id, model, delta: { reasoning: { summary: data.delta } } };
|
|
16013
|
+
}
|
|
16014
|
+
break;
|
|
15769
16015
|
case "response.output_item.added": {
|
|
15770
16016
|
const item = data.item;
|
|
15771
16017
|
if (item?.type !== "function_call") break;
|
|
15772
|
-
const call = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: item.arguments ?? "" };
|
|
16018
|
+
const call = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: item.arguments ?? "", index: calls.size };
|
|
15773
16019
|
calls.set(item.id ?? call.callId, call);
|
|
15774
|
-
yield { id, model, delta: { toolCalls: [{ id: call.callId, name: call.name, arguments: call.arguments }] } };
|
|
16020
|
+
yield { id, model, delta: { toolCalls: [{ id: call.callId, name: call.name, arguments: call.arguments, index: call.index }] } };
|
|
15775
16021
|
break;
|
|
15776
16022
|
}
|
|
15777
16023
|
case "response.function_call_arguments.delta": {
|
|
15778
16024
|
const call = calls.get(data.item_id);
|
|
15779
16025
|
if (!call || !data.delta) break;
|
|
15780
16026
|
call.arguments += data.delta;
|
|
15781
|
-
yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: data.delta }] } };
|
|
16027
|
+
yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: data.delta, index: call.index }] } };
|
|
15782
16028
|
break;
|
|
15783
16029
|
}
|
|
15784
16030
|
case "response.output_item.done": {
|
|
@@ -15787,13 +16033,13 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15787
16033
|
const full = typeof item.arguments === "string" ? item.arguments : "";
|
|
15788
16034
|
const call = calls.get(item.id ?? item.call_id);
|
|
15789
16035
|
if (!call) {
|
|
15790
|
-
const added = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: full };
|
|
16036
|
+
const added = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: full, index: calls.size };
|
|
15791
16037
|
calls.set(item.id ?? added.callId, added);
|
|
15792
|
-
yield { id, model, delta: { toolCalls: [{ id: added.callId, name: added.name, arguments: full }] } };
|
|
16038
|
+
yield { id, model, delta: { toolCalls: [{ id: added.callId, name: added.name, arguments: full, index: added.index }] } };
|
|
15793
16039
|
} else if (full.length > call.arguments.length && full.startsWith(call.arguments)) {
|
|
15794
16040
|
const rest = full.slice(call.arguments.length);
|
|
15795
16041
|
call.arguments = full;
|
|
15796
|
-
yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: rest }] } };
|
|
16042
|
+
yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: rest, index: call.index }] } };
|
|
15797
16043
|
}
|
|
15798
16044
|
break;
|
|
15799
16045
|
}
|
|
@@ -15816,7 +16062,8 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15816
16062
|
yield {
|
|
15817
16063
|
id: final?.id || id,
|
|
15818
16064
|
model: final?.model || model,
|
|
15819
|
-
|
|
16065
|
+
// Summary text already went out as deltas; do not repeat it.
|
|
16066
|
+
delta: final?.reasoning && !streamedSummary ? { reasoning: final.reasoning } : {},
|
|
15820
16067
|
usage: final?.usage,
|
|
15821
16068
|
finishReason: final?.finishReason ?? (calls.size > 0 ? "tool_calls" : "stop")
|
|
15822
16069
|
};
|
|
@@ -15926,7 +16173,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15926
16173
|
...this.defaultHeaders
|
|
15927
16174
|
};
|
|
15928
16175
|
const payload = JSON.stringify(body);
|
|
15929
|
-
const res = await this.doFetchWithRetry(() =>
|
|
16176
|
+
const res = await this.doFetchWithRetry(() => this.httpFetch(url, { method: "POST", headers, body: payload, signal }), signal);
|
|
15930
16177
|
if (!res.ok) {
|
|
15931
16178
|
const text = await res.text().catch(() => "");
|
|
15932
16179
|
throw new ProviderError(
|
|
@@ -16055,7 +16302,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
16055
16302
|
...this.defaultHeaders
|
|
16056
16303
|
};
|
|
16057
16304
|
const payload = JSON.stringify(body);
|
|
16058
|
-
const res = await this.doFetchWithRetry(() =>
|
|
16305
|
+
const res = await this.doFetchWithRetry(() => this.httpFetch(url, { method: "POST", headers, body: payload, signal }), signal);
|
|
16059
16306
|
if (!res.ok) {
|
|
16060
16307
|
const text = await res.text().catch(() => "");
|
|
16061
16308
|
throw new ProviderError(
|
|
@@ -16075,6 +16322,8 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
16075
16322
|
name: tc.function?.name ?? "",
|
|
16076
16323
|
arguments: tc.function?.arguments ?? "{}"
|
|
16077
16324
|
}));
|
|
16325
|
+
const reasoningText = extractReasoningText(message);
|
|
16326
|
+
const providerFields = pickProviderFields(json, message, choice);
|
|
16078
16327
|
return {
|
|
16079
16328
|
id: json.id ?? "",
|
|
16080
16329
|
model: json.model ?? "",
|
|
@@ -16082,6 +16331,9 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
16082
16331
|
toolCalls,
|
|
16083
16332
|
usage: this.extractUsage(json.usage),
|
|
16084
16333
|
finishReason: mapFinishReason(choice?.finish_reason),
|
|
16334
|
+
...typeof choice?.finish_reason === "string" ? { rawFinishReason: choice.finish_reason } : {},
|
|
16335
|
+
...providerFields ? { providerFields } : {},
|
|
16336
|
+
...reasoningText ? { reasoning: { summary: reasoningText } } : {},
|
|
16085
16337
|
raw: json
|
|
16086
16338
|
};
|
|
16087
16339
|
}
|
|
@@ -16100,6 +16352,23 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
16100
16352
|
};
|
|
16101
16353
|
}
|
|
16102
16354
|
};
|
|
16355
|
+
function extractReasoningText(src) {
|
|
16356
|
+
if (!src || typeof src !== "object") return void 0;
|
|
16357
|
+
for (const key of ["reasoning_content", "reasoning"]) {
|
|
16358
|
+
const v = src[key];
|
|
16359
|
+
if (typeof v === "string" && v.length > 0) return v;
|
|
16360
|
+
}
|
|
16361
|
+
return void 0;
|
|
16362
|
+
}
|
|
16363
|
+
function pickProviderFields(top, message, choice) {
|
|
16364
|
+
const out = {};
|
|
16365
|
+
if (top?.system_fingerprint != null) out.system_fingerprint = top.system_fingerprint;
|
|
16366
|
+
if (top?.service_tier != null) out.service_tier = top.service_tier;
|
|
16367
|
+
if (message?.annotations != null) out.annotations = message.annotations;
|
|
16368
|
+
if (message?.refusal != null) out.refusal = message.refusal;
|
|
16369
|
+
if (choice?.logprobs != null) out.logprobs = choice.logprobs;
|
|
16370
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
16371
|
+
}
|
|
16103
16372
|
function isReasoningModel(model) {
|
|
16104
16373
|
if (!model) return false;
|
|
16105
16374
|
const m = model.toLowerCase();
|
|
@@ -16176,36 +16445,86 @@ function mapFinishReason(reason) {
|
|
|
16176
16445
|
}
|
|
16177
16446
|
}
|
|
16178
16447
|
|
|
16448
|
+
// src/providers/utils/toolChoice.ts
|
|
16449
|
+
function supportsForcedToolChoice(model) {
|
|
16450
|
+
if (typeof model !== "string") return true;
|
|
16451
|
+
const id = model.toLowerCase();
|
|
16452
|
+
const at = id.indexOf("claude-");
|
|
16453
|
+
if (at < 0) return true;
|
|
16454
|
+
const toks = id.slice(at + 7).split("-");
|
|
16455
|
+
let family = false;
|
|
16456
|
+
for (const tok of toks) {
|
|
16457
|
+
if (tok === "sonnet" || tok === "opus") {
|
|
16458
|
+
family = true;
|
|
16459
|
+
continue;
|
|
16460
|
+
}
|
|
16461
|
+
const m = /^(\d+)(?:@|:|$)/.exec(tok);
|
|
16462
|
+
if (m) {
|
|
16463
|
+
if (m[1].length > 2) continue;
|
|
16464
|
+
if (Number(m[1]) < 5) return true;
|
|
16465
|
+
if (family) return false;
|
|
16466
|
+
return !toks.some((t) => t === "sonnet" || t === "opus");
|
|
16467
|
+
}
|
|
16468
|
+
}
|
|
16469
|
+
return true;
|
|
16470
|
+
}
|
|
16471
|
+
function forcedToolChoiceRejected(text) {
|
|
16472
|
+
const t = text.toLowerCase();
|
|
16473
|
+
return t.indexOf("tool_choice") >= 0 && t.indexOf("not supported for this model") >= 0;
|
|
16474
|
+
}
|
|
16475
|
+
|
|
16179
16476
|
// src/providers/anthropic.ts
|
|
16477
|
+
var DEFAULT_MAX_TOKENS = 4096;
|
|
16478
|
+
function normalizeExplicitThinking(body) {
|
|
16479
|
+
const t = body.thinking;
|
|
16480
|
+
if (t == null) {
|
|
16481
|
+
delete body.thinking;
|
|
16482
|
+
return;
|
|
16483
|
+
}
|
|
16484
|
+
if (t.type !== "enabled") return;
|
|
16485
|
+
const budget = typeof t.budget_tokens === "number" ? t.budget_tokens : 0;
|
|
16486
|
+
if (budget > 0 && (typeof body.max_tokens !== "number" || body.max_tokens <= budget)) {
|
|
16487
|
+
body.max_tokens = budget + 1024;
|
|
16488
|
+
}
|
|
16489
|
+
for (const k of ["temperature", "top_p", "top_k"]) delete body[k];
|
|
16490
|
+
}
|
|
16491
|
+
function thinkingFormRejected(text) {
|
|
16492
|
+
const t = text.toLowerCase();
|
|
16493
|
+
if (t.indexOf("thinking.type.enabled") >= 0 && t.indexOf("not supported") >= 0) return "enabled";
|
|
16494
|
+
if (t.indexOf("thinking.type.adaptive") >= 0 && t.indexOf("not supported") >= 0 && t.indexOf("thinking.type.enabled") < 0) return "adaptive";
|
|
16495
|
+
return void 0;
|
|
16496
|
+
}
|
|
16180
16497
|
var AnthropicProvider = class extends BaseProvider {
|
|
16181
16498
|
constructor(config) {
|
|
16182
16499
|
super();
|
|
16183
16500
|
this.providerName = "anthropic";
|
|
16501
|
+
this.configureTransport(config);
|
|
16184
16502
|
this.apiKey = config.apiKey;
|
|
16185
16503
|
this.baseURL = (config.baseURL ?? "https://api.anthropic.com").replace(/\/$/, "");
|
|
16186
16504
|
this.defaultModel = config.defaultModel ?? "claude-sonnet-4-20250514";
|
|
16187
16505
|
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
16188
16506
|
this.anthropicVersion = config.anthropicVersion ?? "2023-06-01";
|
|
16507
|
+
this.thinkingMode = config.thinkingMode ?? "auto";
|
|
16189
16508
|
this.promptCachingEnabled = config.prompt_caching?.enabled === true;
|
|
16190
16509
|
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
16191
16510
|
}
|
|
16192
16511
|
async complete(request) {
|
|
16193
|
-
const
|
|
16194
|
-
const res = await this.doFetch(body, request.signal);
|
|
16512
|
+
const res = await this.send(request, false);
|
|
16195
16513
|
const json = await res.json();
|
|
16196
16514
|
return this.parseResponse(json);
|
|
16197
16515
|
}
|
|
16198
16516
|
async *completeStream(request) {
|
|
16199
|
-
const
|
|
16200
|
-
const res = await this.doFetch(body, request.signal);
|
|
16517
|
+
const res = await this.send(request, true);
|
|
16201
16518
|
if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
|
|
16202
16519
|
let id = "";
|
|
16203
16520
|
let model = "";
|
|
16204
16521
|
let inputTokens = 0;
|
|
16205
16522
|
let outputTokens = 0;
|
|
16523
|
+
let thinkingTokens = 0;
|
|
16206
16524
|
let cacheReadInputTokens = 0;
|
|
16207
16525
|
let cacheCreationInputTokens = 0;
|
|
16208
16526
|
const currentToolCalls = /* @__PURE__ */ new Map();
|
|
16527
|
+
let toolOrdinal = 0;
|
|
16209
16528
|
const reasoningBlocks = /* @__PURE__ */ new Map();
|
|
16210
16529
|
for await (const event of parseSSEStream(res.body, request.signal)) {
|
|
16211
16530
|
let data;
|
|
@@ -16234,8 +16553,11 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
16234
16553
|
currentToolCalls.set(idx, {
|
|
16235
16554
|
id: block.id ?? "",
|
|
16236
16555
|
name: block.name ?? "",
|
|
16237
|
-
arguments: ""
|
|
16556
|
+
arguments: "",
|
|
16557
|
+
ordinal: toolOrdinal++
|
|
16238
16558
|
});
|
|
16559
|
+
const opened = currentToolCalls.get(idx);
|
|
16560
|
+
yield { id, model, delta: { toolCalls: [{ id: opened.id, name: opened.name, arguments: "", index: opened.ordinal }] } };
|
|
16239
16561
|
} else if (block?.type === "thinking") {
|
|
16240
16562
|
reasoningBlocks.set(idx, { type: "thinking", text: block.thinking ?? "", signature: block.signature });
|
|
16241
16563
|
} else if (block?.type === "redacted_thinking") {
|
|
@@ -16273,7 +16595,7 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
16273
16595
|
id,
|
|
16274
16596
|
model,
|
|
16275
16597
|
delta: {
|
|
16276
|
-
toolCalls: [{ id: tc.id, name: tc.name, arguments: delta.partial_json }]
|
|
16598
|
+
toolCalls: [{ id: tc.id, name: tc.name, arguments: delta.partial_json, index: tc.ordinal }]
|
|
16277
16599
|
}
|
|
16278
16600
|
};
|
|
16279
16601
|
}
|
|
@@ -16286,8 +16608,10 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
16286
16608
|
const delta = data.delta;
|
|
16287
16609
|
if (data.usage) {
|
|
16288
16610
|
outputTokens = data.usage.output_tokens ?? outputTokens;
|
|
16611
|
+
thinkingTokens = data.usage.output_tokens_details?.thinking_tokens ?? thinkingTokens;
|
|
16289
16612
|
}
|
|
16290
16613
|
const finishReason = mapAnthropicStopReason(delta?.stop_reason);
|
|
16614
|
+
const rawFinishReason = typeof delta?.stop_reason === "string" ? delta.stop_reason : void 0;
|
|
16291
16615
|
const usage = {
|
|
16292
16616
|
inputTokens,
|
|
16293
16617
|
outputTokens,
|
|
@@ -16295,7 +16619,7 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
16295
16619
|
cachedInputTokens: cacheReadInputTokens,
|
|
16296
16620
|
cachedWriteTokens: cacheCreationInputTokens,
|
|
16297
16621
|
cachedOutputTokens: 0,
|
|
16298
|
-
reasoningTokens:
|
|
16622
|
+
reasoningTokens: thinkingTokens
|
|
16299
16623
|
};
|
|
16300
16624
|
const blocks = [...reasoningBlocks.values()].filter(
|
|
16301
16625
|
(b) => b.type === "thinking" && b.signature || b.type === "redacted_thinking" && b.data
|
|
@@ -16305,6 +16629,7 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
16305
16629
|
model,
|
|
16306
16630
|
delta: blocks.length > 0 ? { reasoning: { blocks } } : {},
|
|
16307
16631
|
finishReason,
|
|
16632
|
+
...rawFinishReason ? { rawFinishReason } : {},
|
|
16308
16633
|
usage
|
|
16309
16634
|
};
|
|
16310
16635
|
break;
|
|
@@ -16319,12 +16644,12 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
16319
16644
|
}
|
|
16320
16645
|
}
|
|
16321
16646
|
// ─── Internals ───────────────────────────────────────────────────────────
|
|
16322
|
-
buildRequestBody(request, stream) {
|
|
16647
|
+
buildRequestBody(request, stream, modeOverride, downgradeToolChoice = false) {
|
|
16323
16648
|
const { system, messages } = this.splitSystemMessages(request.messages);
|
|
16324
16649
|
const body = {
|
|
16325
16650
|
model: request.model || this.defaultModel,
|
|
16326
16651
|
messages: this.toAnthropicMessages(messages),
|
|
16327
|
-
max_tokens: request.maxTokens ??
|
|
16652
|
+
max_tokens: request.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
16328
16653
|
stream
|
|
16329
16654
|
};
|
|
16330
16655
|
if (system) {
|
|
@@ -16345,18 +16670,27 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
16345
16670
|
body.tools = tools;
|
|
16346
16671
|
}
|
|
16347
16672
|
if (request.toolChoice != null) {
|
|
16673
|
+
const forceOk = !downgradeToolChoice && supportsForcedToolChoice(body.model);
|
|
16348
16674
|
if (request.toolChoice === "auto") {
|
|
16349
16675
|
body.tool_choice = { type: "auto" };
|
|
16350
16676
|
} else if (request.toolChoice === "required") {
|
|
16351
|
-
body.tool_choice = { type: "any" };
|
|
16677
|
+
body.tool_choice = forceOk ? { type: "any" } : { type: "auto" };
|
|
16352
16678
|
} else if (request.toolChoice === "none") ; else {
|
|
16353
|
-
body.tool_choice = { type: "tool", name: request.toolChoice.name };
|
|
16679
|
+
body.tool_choice = forceOk ? { type: "tool", name: request.toolChoice.name } : { type: "auto" };
|
|
16354
16680
|
}
|
|
16355
16681
|
}
|
|
16356
16682
|
if (request.extra) {
|
|
16357
16683
|
Object.assign(body, request.extra);
|
|
16358
16684
|
}
|
|
16359
|
-
|
|
16685
|
+
const explicitThinking = request.extra && typeof request.extra === "object" && "thinking" in request.extra;
|
|
16686
|
+
if (explicitThinking) {
|
|
16687
|
+
normalizeExplicitThinking(body);
|
|
16688
|
+
} else {
|
|
16689
|
+
applyAnthropicReasoning(body, request.reasoning, { model: body.model, mode: modeOverride ?? this.thinkingMode });
|
|
16690
|
+
}
|
|
16691
|
+
if (typeof body.max_tokens !== "number" || !(body.max_tokens > 0)) {
|
|
16692
|
+
body.max_tokens = DEFAULT_MAX_TOKENS;
|
|
16693
|
+
}
|
|
16360
16694
|
return body;
|
|
16361
16695
|
}
|
|
16362
16696
|
splitSystemMessages(messages) {
|
|
@@ -16487,6 +16821,34 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
16487
16821
|
input_schema: t.parameters
|
|
16488
16822
|
};
|
|
16489
16823
|
}
|
|
16824
|
+
/** Builds and sends the request; retries once with the other thinking form
|
|
16825
|
+
* when the upstream rejects the one we picked. */
|
|
16826
|
+
async send(request, stream) {
|
|
16827
|
+
let mode;
|
|
16828
|
+
let downgrade = false;
|
|
16829
|
+
let flippedThinking = false;
|
|
16830
|
+
for (; ; ) {
|
|
16831
|
+
const body = this.buildRequestBody(request, stream, mode, downgrade);
|
|
16832
|
+
try {
|
|
16833
|
+
return await this.doFetch(body, request.signal);
|
|
16834
|
+
} catch (err) {
|
|
16835
|
+
if (!(err instanceof ProviderError) || err.statusCode !== 400) throw err;
|
|
16836
|
+
const text = String(err.responseBody ?? err.message);
|
|
16837
|
+
if (!downgrade && body.tool_choice && body.tool_choice.type !== "auto" && forcedToolChoiceRejected(text)) {
|
|
16838
|
+
downgrade = true;
|
|
16839
|
+
continue;
|
|
16840
|
+
}
|
|
16841
|
+
const explicit = request.extra && typeof request.extra === "object" && "thinking" in request.extra;
|
|
16842
|
+
if (flippedThinking || explicit || !request.reasoning) throw err;
|
|
16843
|
+
const wrong = thinkingFormRejected(text);
|
|
16844
|
+
if (!wrong) throw err;
|
|
16845
|
+
const used = resolveThinkingForm(request.model ?? this.defaultModel, this.thinkingMode);
|
|
16846
|
+
if (wrong === "enabled" && used !== "budget" || wrong === "adaptive" && used !== "adaptive") throw err;
|
|
16847
|
+
mode = wrong === "enabled" ? "adaptive" : "budget";
|
|
16848
|
+
flippedThinking = true;
|
|
16849
|
+
}
|
|
16850
|
+
}
|
|
16851
|
+
}
|
|
16490
16852
|
async doFetch(body, signal) {
|
|
16491
16853
|
const url = `${this.baseURL}/v1/messages`;
|
|
16492
16854
|
const headers = {
|
|
@@ -16496,7 +16858,7 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
16496
16858
|
...this.defaultHeaders
|
|
16497
16859
|
};
|
|
16498
16860
|
const payload = JSON.stringify(body);
|
|
16499
|
-
const res = await this.doFetchWithRetry(() =>
|
|
16861
|
+
const res = await this.doFetchWithRetry(() => this.httpFetch(url, { method: "POST", headers, body: payload, signal }), signal);
|
|
16500
16862
|
if (!res.ok) {
|
|
16501
16863
|
const text = await res.text().catch(() => "");
|
|
16502
16864
|
throw new ProviderError(
|
|
@@ -16541,10 +16903,9 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
16541
16903
|
cachedInputTokens: usage.cache_read_input_tokens ?? 0,
|
|
16542
16904
|
cachedWriteTokens: usage.cache_creation_input_tokens ?? 0,
|
|
16543
16905
|
cachedOutputTokens: 0,
|
|
16544
|
-
//
|
|
16545
|
-
//
|
|
16546
|
-
|
|
16547
|
-
reasoningTokens: 0
|
|
16906
|
+
// Thinking tokens are billed inside `output_tokens`; newer models also
|
|
16907
|
+
// report the split as `output_tokens_details.thinking_tokens`.
|
|
16908
|
+
reasoningTokens: usage.output_tokens_details?.thinking_tokens ?? 0
|
|
16548
16909
|
};
|
|
16549
16910
|
return {
|
|
16550
16911
|
id: json.id ?? "",
|
|
@@ -16553,6 +16914,7 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
16553
16914
|
toolCalls,
|
|
16554
16915
|
usage: tokenUsage,
|
|
16555
16916
|
finishReason: mapAnthropicStopReason(json.stop_reason),
|
|
16917
|
+
...typeof json.stop_reason === "string" ? { rawFinishReason: json.stop_reason } : {},
|
|
16556
16918
|
reasoning: reasoningBlocks.length > 0 ? { blocks: reasoningBlocks, summary: summaryParts.join("\n").trim() || void 0 } : void 0,
|
|
16557
16919
|
raw: json
|
|
16558
16920
|
};
|
|
@@ -16598,7 +16960,15 @@ var AzureProvider = class extends OpenAIProvider {
|
|
|
16598
16960
|
provider: "openai",
|
|
16599
16961
|
apiKey: config.apiKey,
|
|
16600
16962
|
defaultModel: config.defaultModel ?? config.deploymentName ?? "gpt-4o",
|
|
16601
|
-
defaultHeaders: config.defaultHeaders
|
|
16963
|
+
defaultHeaders: config.defaultHeaders,
|
|
16964
|
+
retry: config.retry,
|
|
16965
|
+
fetch: config.fetch,
|
|
16966
|
+
timeoutMs: config.timeoutMs,
|
|
16967
|
+
maxTokensParam: config.maxTokensParam,
|
|
16968
|
+
api: config.api,
|
|
16969
|
+
responsesApi: config.responsesApi,
|
|
16970
|
+
responsesApiModels: config.responsesApiModels,
|
|
16971
|
+
responsesStreaming: config.responsesStreaming
|
|
16602
16972
|
});
|
|
16603
16973
|
this.providerName = "azure";
|
|
16604
16974
|
this.endpoint = config.endpoint.replace(/\/$/, "");
|
|
@@ -16613,16 +16983,17 @@ var AzureProvider = class extends OpenAIProvider {
|
|
|
16613
16983
|
}
|
|
16614
16984
|
const url = `${this.endpoint}/openai/deployments/${deployment}/chat/completions?api-version=${this.apiVersion}`;
|
|
16615
16985
|
const { model: _model, ...bodyWithoutModel } = body;
|
|
16616
|
-
const
|
|
16986
|
+
const payload = JSON.stringify(bodyWithoutModel);
|
|
16987
|
+
const res = await this.doFetchWithRetry(() => this.httpFetch(url, {
|
|
16617
16988
|
method: "POST",
|
|
16618
16989
|
headers: {
|
|
16619
16990
|
"Content-Type": "application/json",
|
|
16620
16991
|
"api-key": this.azureApiKey,
|
|
16621
16992
|
...this.defaultHeaders
|
|
16622
16993
|
},
|
|
16623
|
-
body:
|
|
16994
|
+
body: payload,
|
|
16624
16995
|
signal
|
|
16625
|
-
});
|
|
16996
|
+
}), signal);
|
|
16626
16997
|
if (!res.ok) {
|
|
16627
16998
|
const text = await res.text().catch(() => "");
|
|
16628
16999
|
throw new ProviderError(
|
|
@@ -16634,6 +17005,32 @@ var AzureProvider = class extends OpenAIProvider {
|
|
|
16634
17005
|
}
|
|
16635
17006
|
return res;
|
|
16636
17007
|
}
|
|
17008
|
+
/** Azure embeddings: the deployment named by the request's model (falling back
|
|
17009
|
+
* to the configured deployment) in the path, api-key auth. */
|
|
17010
|
+
async embeddingsFetch(body, signal) {
|
|
17011
|
+
const deployment = body.model || this.deploymentName;
|
|
17012
|
+
if (!deployment) {
|
|
17013
|
+
throw new ProviderError("Azure embeddings require a deployment name or model", this.providerName, 400);
|
|
17014
|
+
}
|
|
17015
|
+
const url = `${this.endpoint}/openai/deployments/${deployment}/embeddings?api-version=${this.apiVersion}`;
|
|
17016
|
+
const { model: _model, ...bodyWithoutModel } = body;
|
|
17017
|
+
const payload = JSON.stringify(bodyWithoutModel);
|
|
17018
|
+
const res = await this.doFetchWithRetry(() => this.httpFetch(url, {
|
|
17019
|
+
method: "POST",
|
|
17020
|
+
headers: {
|
|
17021
|
+
"Content-Type": "application/json",
|
|
17022
|
+
"api-key": this.azureApiKey,
|
|
17023
|
+
...this.defaultHeaders
|
|
17024
|
+
},
|
|
17025
|
+
body: payload,
|
|
17026
|
+
signal
|
|
17027
|
+
}), signal);
|
|
17028
|
+
if (!res.ok) {
|
|
17029
|
+
const text = await res.text().catch(() => "");
|
|
17030
|
+
throw new ProviderError(`Azure OpenAI embeddings API error ${res.status}: ${text}`, this.providerName, res.status, text);
|
|
17031
|
+
}
|
|
17032
|
+
return res;
|
|
17033
|
+
}
|
|
16637
17034
|
/** Azure exposes the Responses API at /openai/responses with api-key auth.
|
|
16638
17035
|
* Reasoning summaries require a preview api-version; callers can override via
|
|
16639
17036
|
* the `apiVersion` config. */
|
|
@@ -16645,7 +17042,7 @@ var AzureProvider = class extends OpenAIProvider {
|
|
|
16645
17042
|
}
|
|
16646
17043
|
const url = `${this.endpoint}/openai/responses?api-version=${this.apiVersion}`;
|
|
16647
17044
|
const payload = JSON.stringify({ ...body, model: deployment });
|
|
16648
|
-
const res = await
|
|
17045
|
+
const res = await this.doFetchWithRetry(() => this.httpFetch(url, {
|
|
16649
17046
|
method: "POST",
|
|
16650
17047
|
headers: {
|
|
16651
17048
|
"Content-Type": "application/json",
|
|
@@ -16654,7 +17051,7 @@ var AzureProvider = class extends OpenAIProvider {
|
|
|
16654
17051
|
},
|
|
16655
17052
|
body: payload,
|
|
16656
17053
|
signal
|
|
16657
|
-
});
|
|
17054
|
+
}), signal);
|
|
16658
17055
|
if (!res.ok) {
|
|
16659
17056
|
const text = await res.text().catch(() => "");
|
|
16660
17057
|
throw new ProviderError(
|
|
@@ -16690,20 +17087,155 @@ var OpenAICompatibleProvider = class extends OpenAIProvider {
|
|
|
16690
17087
|
apiKey: config.apiKey,
|
|
16691
17088
|
baseURL: config.baseURL,
|
|
16692
17089
|
defaultModel: config.defaultModel,
|
|
16693
|
-
defaultHeaders: config.defaultHeaders
|
|
17090
|
+
defaultHeaders: config.defaultHeaders,
|
|
17091
|
+
organization: config.organization,
|
|
17092
|
+
retry: config.retry,
|
|
17093
|
+
fetch: config.fetch,
|
|
17094
|
+
timeoutMs: config.timeoutMs,
|
|
17095
|
+
maxTokensParam: config.maxTokensParam,
|
|
17096
|
+
api: config.api,
|
|
17097
|
+
// Unlike OpenAI / Azure, "auto" on a compatible server means Chat
|
|
17098
|
+
// Completions (the 0.11 default): the model name is the operator's.
|
|
17099
|
+
// `responsesApiModels` still applies; `responsesApi: "auto"` opts in.
|
|
17100
|
+
_autoMeansChat: true,
|
|
17101
|
+
responsesApi: config.responsesApi,
|
|
17102
|
+
responsesApiModels: config.responsesApiModels,
|
|
17103
|
+
responsesStreaming: config.responsesStreaming
|
|
16694
17104
|
});
|
|
16695
17105
|
this.providerName = "openai-compatible";
|
|
16696
17106
|
}
|
|
16697
17107
|
};
|
|
16698
17108
|
|
|
17109
|
+
// src/providers/utils/eventStream.ts
|
|
17110
|
+
var EventStreamError = class extends Error {
|
|
17111
|
+
constructor(message) {
|
|
17112
|
+
super(message);
|
|
17113
|
+
this.name = "EventStreamError";
|
|
17114
|
+
}
|
|
17115
|
+
};
|
|
17116
|
+
var CRC_TABLE = (() => {
|
|
17117
|
+
const table = new Uint32Array(256);
|
|
17118
|
+
for (let n = 0; n < 256; n++) {
|
|
17119
|
+
let c = n;
|
|
17120
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
17121
|
+
table[n] = c >>> 0;
|
|
17122
|
+
}
|
|
17123
|
+
return table;
|
|
17124
|
+
})();
|
|
17125
|
+
function crc32(bytes, start = 0, end = bytes.length) {
|
|
17126
|
+
let c = 4294967295;
|
|
17127
|
+
for (let i = start; i < end; i++) c = CRC_TABLE[(c ^ bytes[i]) & 255] ^ c >>> 8;
|
|
17128
|
+
return (c ^ 4294967295) >>> 0;
|
|
17129
|
+
}
|
|
17130
|
+
function decodeFrame(frame) {
|
|
17131
|
+
const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
|
|
17132
|
+
const total = view.getUint32(0);
|
|
17133
|
+
const headersLen = view.getUint32(4);
|
|
17134
|
+
if (total !== frame.byteLength) throw new EventStreamError("event-stream frame length mismatch");
|
|
17135
|
+
if (crc32(frame, 0, 8) !== view.getUint32(8)) throw new EventStreamError("event-stream prelude CRC mismatch");
|
|
17136
|
+
if (crc32(frame, 0, total - 4) !== view.getUint32(total - 4)) throw new EventStreamError("event-stream message CRC mismatch");
|
|
17137
|
+
if (12 + headersLen > total - 4) throw new EventStreamError("event-stream headers overflow frame");
|
|
17138
|
+
const headers = {};
|
|
17139
|
+
const td = new TextDecoder();
|
|
17140
|
+
let o = 12;
|
|
17141
|
+
const headersEnd = 12 + headersLen;
|
|
17142
|
+
while (o < headersEnd) {
|
|
17143
|
+
const nameLen = frame[o++];
|
|
17144
|
+
const name = td.decode(frame.subarray(o, o + nameLen));
|
|
17145
|
+
o += nameLen;
|
|
17146
|
+
const type = frame[o++];
|
|
17147
|
+
switch (type) {
|
|
17148
|
+
case 0:
|
|
17149
|
+
headers[name] = true;
|
|
17150
|
+
break;
|
|
17151
|
+
case 1:
|
|
17152
|
+
headers[name] = false;
|
|
17153
|
+
break;
|
|
17154
|
+
case 2:
|
|
17155
|
+
headers[name] = view.getInt8(o);
|
|
17156
|
+
o += 1;
|
|
17157
|
+
break;
|
|
17158
|
+
case 3:
|
|
17159
|
+
headers[name] = view.getInt16(o);
|
|
17160
|
+
o += 2;
|
|
17161
|
+
break;
|
|
17162
|
+
case 4:
|
|
17163
|
+
headers[name] = view.getInt32(o);
|
|
17164
|
+
o += 4;
|
|
17165
|
+
break;
|
|
17166
|
+
case 5:
|
|
17167
|
+
headers[name] = view.getBigInt64(o);
|
|
17168
|
+
o += 8;
|
|
17169
|
+
break;
|
|
17170
|
+
case 6: {
|
|
17171
|
+
const len = view.getUint16(o);
|
|
17172
|
+
o += 2;
|
|
17173
|
+
headers[name] = frame.slice(o, o + len);
|
|
17174
|
+
o += len;
|
|
17175
|
+
break;
|
|
17176
|
+
}
|
|
17177
|
+
case 7: {
|
|
17178
|
+
const len = view.getUint16(o);
|
|
17179
|
+
o += 2;
|
|
17180
|
+
headers[name] = td.decode(frame.subarray(o, o + len));
|
|
17181
|
+
o += len;
|
|
17182
|
+
break;
|
|
17183
|
+
}
|
|
17184
|
+
case 8:
|
|
17185
|
+
headers[name] = view.getBigInt64(o);
|
|
17186
|
+
o += 8;
|
|
17187
|
+
break;
|
|
17188
|
+
case 9:
|
|
17189
|
+
headers[name] = frame.slice(o, o + 16);
|
|
17190
|
+
o += 16;
|
|
17191
|
+
break;
|
|
17192
|
+
default:
|
|
17193
|
+
throw new EventStreamError(`unknown event-stream header type ${type}`);
|
|
17194
|
+
}
|
|
17195
|
+
}
|
|
17196
|
+
return { headers, payload: frame.slice(headersEnd, total - 4) };
|
|
17197
|
+
}
|
|
17198
|
+
async function* parseEventStream(body, signal) {
|
|
17199
|
+
const reader = body.getReader();
|
|
17200
|
+
let buf = new Uint8Array(0);
|
|
17201
|
+
try {
|
|
17202
|
+
while (true) {
|
|
17203
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
17204
|
+
const { done, value } = await reader.read();
|
|
17205
|
+
if (done) break;
|
|
17206
|
+
if (!value?.length) continue;
|
|
17207
|
+
const merged = new Uint8Array(buf.length + value.length);
|
|
17208
|
+
merged.set(buf, 0);
|
|
17209
|
+
merged.set(value, buf.length);
|
|
17210
|
+
buf = merged;
|
|
17211
|
+
while (buf.length >= 12) {
|
|
17212
|
+
const total = new DataView(buf.buffer, buf.byteOffset, buf.byteLength).getUint32(0);
|
|
17213
|
+
if (total < 16) throw new EventStreamError("invalid event-stream frame length");
|
|
17214
|
+
if (buf.length < total) break;
|
|
17215
|
+
yield decodeFrame(buf.subarray(0, total));
|
|
17216
|
+
buf = buf.subarray(total);
|
|
17217
|
+
}
|
|
17218
|
+
}
|
|
17219
|
+
if (buf.length > 0) throw new EventStreamError("event-stream ended inside a frame");
|
|
17220
|
+
} finally {
|
|
17221
|
+
reader.cancel().catch(() => void 0);
|
|
17222
|
+
try {
|
|
17223
|
+
reader.releaseLock();
|
|
17224
|
+
} catch {
|
|
17225
|
+
}
|
|
17226
|
+
}
|
|
17227
|
+
}
|
|
17228
|
+
|
|
16699
17229
|
// src/providers/bedrock.ts
|
|
16700
17230
|
var BedrockProvider = class extends BaseProvider {
|
|
16701
17231
|
constructor(config) {
|
|
16702
17232
|
super();
|
|
16703
17233
|
this.providerName = "bedrock";
|
|
17234
|
+
this.configureTransport(config);
|
|
16704
17235
|
this.region = config.region;
|
|
16705
17236
|
this.defaultModel = config.defaultModel ?? "anthropic.claude-sonnet-4-20250514-v1:0";
|
|
16706
17237
|
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
17238
|
+
this.thinkingMode = config.thinkingMode ?? "auto";
|
|
16707
17239
|
this.promptCachingEnabled = config.prompt_caching?.enabled === true;
|
|
16708
17240
|
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
16709
17241
|
this.credentials = {
|
|
@@ -16720,25 +17252,202 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16720
17252
|
}
|
|
16721
17253
|
async complete(request) {
|
|
16722
17254
|
const modelId = request.model || this.defaultModel;
|
|
16723
|
-
const
|
|
16724
|
-
|
|
16725
|
-
return this.parseResponse(json, modelId);
|
|
17255
|
+
const res = await this.converseFetch(modelId, request, false);
|
|
17256
|
+
return this.parseResponse(await res.json(), modelId);
|
|
16726
17257
|
}
|
|
17258
|
+
/**
|
|
17259
|
+
* Real streaming over ConverseStream. The response is an AWS event-stream
|
|
17260
|
+
* (binary frames); each frame carries one JSON event: messageStart,
|
|
17261
|
+
* contentBlockStart/Delta/Stop (text, toolUse, reasoningContent),
|
|
17262
|
+
* messageStop and, last, metadata (usage). Text, tool-call argument and
|
|
17263
|
+
* reasoning deltas are yielded as they arrive; one closing chunk carries the
|
|
17264
|
+
* finish reason, usage and the signed reasoning blocks.
|
|
17265
|
+
*/
|
|
16727
17266
|
async *completeStream(request) {
|
|
16728
|
-
const
|
|
17267
|
+
const modelId = request.model || this.defaultModel;
|
|
17268
|
+
const res = await this.converseFetch(modelId, request, true);
|
|
17269
|
+
if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
|
|
17270
|
+
const id = res.headers.get("x-amzn-requestid") ?? "";
|
|
17271
|
+
const toolsByIndex = /* @__PURE__ */ new Map();
|
|
17272
|
+
const reasoningByIndex = /* @__PURE__ */ new Map();
|
|
17273
|
+
let finishReason;
|
|
17274
|
+
let rawFinishReason;
|
|
17275
|
+
let toolOrdinal = 0;
|
|
17276
|
+
let usage;
|
|
17277
|
+
for await (const msg of parseEventStream(res.body, request.signal)) {
|
|
17278
|
+
const messageType = String(msg.headers[":message-type"] ?? "event");
|
|
17279
|
+
const eventType = String(msg.headers[":event-type"] ?? msg.headers[":exception-type"] ?? "");
|
|
17280
|
+
let data = {};
|
|
17281
|
+
const text = new TextDecoder().decode(msg.payload);
|
|
17282
|
+
if (text) {
|
|
17283
|
+
try {
|
|
17284
|
+
data = JSON.parse(text);
|
|
17285
|
+
} catch {
|
|
17286
|
+
data = { message: text };
|
|
17287
|
+
}
|
|
17288
|
+
}
|
|
17289
|
+
if (messageType === "exception" || messageType === "error") {
|
|
17290
|
+
const code = String(msg.headers[":exception-type"] ?? msg.headers[":error-code"] ?? eventType);
|
|
17291
|
+
throw new ProviderError(
|
|
17292
|
+
`Bedrock stream error ${code}: ${data.message ?? text}`,
|
|
17293
|
+
this.providerName,
|
|
17294
|
+
bedrockExceptionStatus(code),
|
|
17295
|
+
data
|
|
17296
|
+
);
|
|
17297
|
+
}
|
|
17298
|
+
switch (eventType) {
|
|
17299
|
+
case "contentBlockStart": {
|
|
17300
|
+
const idx = data.contentBlockIndex ?? 0;
|
|
17301
|
+
const tu = data.start?.toolUse;
|
|
17302
|
+
if (tu) {
|
|
17303
|
+
const ordinal = toolOrdinal++;
|
|
17304
|
+
toolsByIndex.set(idx, { id: tu.toolUseId ?? "", name: tu.name ?? "", ordinal });
|
|
17305
|
+
yield { id, model: modelId, delta: { toolCalls: [{ id: tu.toolUseId, name: tu.name, arguments: "", index: ordinal }] } };
|
|
17306
|
+
}
|
|
17307
|
+
break;
|
|
17308
|
+
}
|
|
17309
|
+
case "contentBlockDelta": {
|
|
17310
|
+
const idx = data.contentBlockIndex ?? 0;
|
|
17311
|
+
const d = data.delta ?? {};
|
|
17312
|
+
if (typeof d.text === "string" && d.text) {
|
|
17313
|
+
yield { id, model: modelId, delta: { content: d.text } };
|
|
17314
|
+
} else if (d.toolUse) {
|
|
17315
|
+
const tc = toolsByIndex.get(idx);
|
|
17316
|
+
if (tc && typeof d.toolUse.input === "string" && d.toolUse.input) {
|
|
17317
|
+
yield { id, model: modelId, delta: { toolCalls: [{ id: tc.id, name: tc.name, arguments: d.toolUse.input, index: tc.ordinal }] } };
|
|
17318
|
+
}
|
|
17319
|
+
} else if (d.reasoningContent) {
|
|
17320
|
+
const rc = d.reasoningContent;
|
|
17321
|
+
const blk = reasoningByIndex.get(idx) ?? { type: "thinking", text: "" };
|
|
17322
|
+
if (typeof rc.text === "string" && rc.text) {
|
|
17323
|
+
blk.text = (blk.text ?? "") + rc.text;
|
|
17324
|
+
reasoningByIndex.set(idx, blk);
|
|
17325
|
+
yield { id, model: modelId, delta: { reasoning: { summary: rc.text } } };
|
|
17326
|
+
}
|
|
17327
|
+
if (typeof rc.signature === "string" && rc.signature) {
|
|
17328
|
+
blk.signature = (blk.signature ?? "") + rc.signature;
|
|
17329
|
+
reasoningByIndex.set(idx, blk);
|
|
17330
|
+
}
|
|
17331
|
+
if (rc.redactedContent) {
|
|
17332
|
+
reasoningByIndex.set(idx, { type: "redacted_thinking", data: rc.redactedContent });
|
|
17333
|
+
}
|
|
17334
|
+
}
|
|
17335
|
+
break;
|
|
17336
|
+
}
|
|
17337
|
+
case "messageStop":
|
|
17338
|
+
finishReason = mapBedrockStopReason(data.stopReason);
|
|
17339
|
+
if (typeof data.stopReason === "string") rawFinishReason = data.stopReason;
|
|
17340
|
+
break;
|
|
17341
|
+
case "metadata": {
|
|
17342
|
+
const u = data.usage ?? {};
|
|
17343
|
+
usage = {
|
|
17344
|
+
inputTokens: u.inputTokens ?? 0,
|
|
17345
|
+
outputTokens: u.outputTokens ?? 0,
|
|
17346
|
+
totalTokens: u.totalTokens ?? (u.inputTokens ?? 0) + (u.outputTokens ?? 0),
|
|
17347
|
+
cachedInputTokens: u.cacheReadInputTokens ?? u.cacheReadInputTokenCount ?? 0,
|
|
17348
|
+
cachedWriteTokens: u.cacheWriteInputTokens ?? u.cacheWriteInputTokenCount ?? 0,
|
|
17349
|
+
cachedOutputTokens: 0,
|
|
17350
|
+
reasoningTokens: 0
|
|
17351
|
+
};
|
|
17352
|
+
break;
|
|
17353
|
+
}
|
|
17354
|
+
}
|
|
17355
|
+
}
|
|
17356
|
+
const blocks = [...reasoningByIndex.values()].filter(
|
|
17357
|
+
(b) => b.type === "thinking" && b.signature || b.type === "redacted_thinking" && b.data
|
|
17358
|
+
);
|
|
16729
17359
|
yield {
|
|
16730
|
-
id
|
|
16731
|
-
model:
|
|
16732
|
-
delta: {
|
|
16733
|
-
|
|
16734
|
-
|
|
16735
|
-
|
|
16736
|
-
usage: response.usage,
|
|
16737
|
-
finishReason: response.finishReason
|
|
17360
|
+
id,
|
|
17361
|
+
model: modelId,
|
|
17362
|
+
delta: blocks.length > 0 ? { reasoning: { blocks } } : {},
|
|
17363
|
+
finishReason: finishReason ?? "stop",
|
|
17364
|
+
...rawFinishReason ? { rawFinishReason } : {},
|
|
17365
|
+
usage
|
|
16738
17366
|
};
|
|
16739
17367
|
}
|
|
17368
|
+
// ─── Embeddings ──────────────────────────────────────────────────────────
|
|
17369
|
+
/**
|
|
17370
|
+
* InvokeModel embeddings. Supported families (by model id, inference-profile
|
|
17371
|
+
* prefixes such as `us.` allowed):
|
|
17372
|
+
* - Amazon Titan Text Embeddings (v1, v2): one text per call, `dimensions`
|
|
17373
|
+
* (v2: 256/512/1024) and `normalize` via `extra`.
|
|
17374
|
+
* - Cohere Embed (v3, v4): a batch of texts per call (default 96), `inputType`
|
|
17375
|
+
* maps to `input_type`, `dimensions` to `output_dimension` (v4).
|
|
17376
|
+
*/
|
|
17377
|
+
async embed(request) {
|
|
17378
|
+
const model = requireEmbeddingModel(this.providerName, request.model);
|
|
17379
|
+
const id = model.toLowerCase();
|
|
17380
|
+
const raw = [];
|
|
17381
|
+
let inputTokens = 0;
|
|
17382
|
+
let vectors = [];
|
|
17383
|
+
if (id.includes("titan-embed")) {
|
|
17384
|
+
const results = await mapWithConcurrency(request.input, 4, async (text) => {
|
|
17385
|
+
const body = { inputText: text };
|
|
17386
|
+
if (request.dimensions != null && !id.includes("titan-embed-text-v1")) body.dimensions = request.dimensions;
|
|
17387
|
+
if (request.extra) Object.assign(body, request.extra);
|
|
17388
|
+
const res = await this.invokeModel(model, body, request.signal);
|
|
17389
|
+
const json = await res.json();
|
|
17390
|
+
return { json, tokens: json.inputTextTokenCount ?? Number(res.headers.get("x-amzn-bedrock-input-token-count") ?? 0) };
|
|
17391
|
+
}, request.signal);
|
|
17392
|
+
for (const r of results) {
|
|
17393
|
+
raw.push(r.json);
|
|
17394
|
+
inputTokens += r.tokens || 0;
|
|
17395
|
+
vectors.push(r.json.embedding ?? r.json.embeddingsByType?.float);
|
|
17396
|
+
}
|
|
17397
|
+
} else if (id.includes("cohere.embed")) {
|
|
17398
|
+
for (const batch of chunkArray(request.input, request.batchSize ?? 96)) {
|
|
17399
|
+
const body = {
|
|
17400
|
+
texts: batch,
|
|
17401
|
+
input_type: request.inputType ?? "search_document",
|
|
17402
|
+
embedding_types: ["float"]
|
|
17403
|
+
};
|
|
17404
|
+
if (request.dimensions != null) body.output_dimension = request.dimensions;
|
|
17405
|
+
if (request.extra) Object.assign(body, request.extra);
|
|
17406
|
+
const res = await this.invokeModel(model, body, request.signal);
|
|
17407
|
+
const json = await res.json();
|
|
17408
|
+
raw.push(json);
|
|
17409
|
+
const emb = Array.isArray(json.embeddings) ? json.embeddings : json.embeddings?.float;
|
|
17410
|
+
vectors.push(...emb ?? []);
|
|
17411
|
+
inputTokens += Number(res.headers.get("x-amzn-bedrock-input-token-count") ?? 0) || 0;
|
|
17412
|
+
}
|
|
17413
|
+
} else {
|
|
17414
|
+
throw new ProviderError(
|
|
17415
|
+
`Bedrock embeddings are supported for Amazon Titan and Cohere Embed models, not "${model}"`,
|
|
17416
|
+
this.providerName,
|
|
17417
|
+
400
|
|
17418
|
+
);
|
|
17419
|
+
}
|
|
17420
|
+
assertVectorCount(this.providerName, vectors, request.input.length);
|
|
17421
|
+
return { model, vectors, usage: { inputTokens, totalTokens: inputTokens }, raw };
|
|
17422
|
+
}
|
|
17423
|
+
async invokeModel(modelId, body, signal) {
|
|
17424
|
+
const host = `bedrock-runtime.${this.region}.amazonaws.com`;
|
|
17425
|
+
const url = `https://${host}/model/${encodeURIComponent(modelId)}/invoke`;
|
|
17426
|
+
const bodyStr = JSON.stringify(body);
|
|
17427
|
+
const res = await this.doFetchWithRetry(() => {
|
|
17428
|
+
const headers = signRequest({
|
|
17429
|
+
method: "POST",
|
|
17430
|
+
url,
|
|
17431
|
+
headers: {
|
|
17432
|
+
"Content-Type": "application/json",
|
|
17433
|
+
Accept: "application/json",
|
|
17434
|
+
...this.defaultHeaders
|
|
17435
|
+
},
|
|
17436
|
+
body: bodyStr,
|
|
17437
|
+
region: this.region,
|
|
17438
|
+
service: "bedrock",
|
|
17439
|
+
credentials: this.credentials
|
|
17440
|
+
});
|
|
17441
|
+
return this.httpFetch(url, { method: "POST", headers, body: bodyStr, signal });
|
|
17442
|
+
}, signal);
|
|
17443
|
+
if (!res.ok) {
|
|
17444
|
+
const text = await res.text().catch(() => "");
|
|
17445
|
+
throw new ProviderError(`Bedrock API error ${res.status}: ${text}`, this.providerName, res.status, text);
|
|
17446
|
+
}
|
|
17447
|
+
return res;
|
|
17448
|
+
}
|
|
16740
17449
|
// ─── Internals ───────────────────────────────────────────────────────────
|
|
16741
|
-
buildConverseBody(request) {
|
|
17450
|
+
buildConverseBody(request, downgradeToolChoice = false) {
|
|
16742
17451
|
const { system, messages } = this.splitSystemMessages(request.messages);
|
|
16743
17452
|
const body = {
|
|
16744
17453
|
messages: this.toBedrockMessages(messages)
|
|
@@ -16762,8 +17471,9 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16762
17471
|
body.toolConfig = {
|
|
16763
17472
|
tools
|
|
16764
17473
|
};
|
|
17474
|
+
const forceOk = !downgradeToolChoice && supportsForcedToolChoice(request.model || this.defaultModel);
|
|
16765
17475
|
if (request.toolChoice != null) {
|
|
16766
|
-
if (request.toolChoice === "auto") {
|
|
17476
|
+
if (request.toolChoice === "auto" || !forceOk && request.toolChoice !== "none") {
|
|
16767
17477
|
body.toolConfig.toolChoice = { auto: {} };
|
|
16768
17478
|
} else if (request.toolChoice === "required") {
|
|
16769
17479
|
body.toolConfig.toolChoice = { any: {} };
|
|
@@ -16773,9 +17483,17 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16773
17483
|
}
|
|
16774
17484
|
}
|
|
16775
17485
|
if (request.extra) {
|
|
16776
|
-
|
|
17486
|
+
const fields = { ...request.extra.additionalModelRequestFields ?? {} };
|
|
17487
|
+
const topLevel = { ...request.extra.__top ?? {} };
|
|
17488
|
+
for (const [key, value] of Object.entries(request.extra)) {
|
|
17489
|
+
if (key === "additionalModelRequestFields" || key === "__top") continue;
|
|
17490
|
+
if (CONVERSE_TOP_LEVEL_FIELDS.has(key)) body[key] = value;
|
|
17491
|
+
else fields[key] = value;
|
|
17492
|
+
}
|
|
17493
|
+
Object.assign(body, topLevel);
|
|
17494
|
+
if (Object.keys(fields).length > 0) body.additionalModelRequestFields = fields;
|
|
16777
17495
|
}
|
|
16778
|
-
applyBedrockReasoning(body, request.reasoning);
|
|
17496
|
+
applyBedrockReasoning(body, request.reasoning, { model: request.model || this.defaultModel, mode: this.thinkingMode });
|
|
16779
17497
|
return body;
|
|
16780
17498
|
}
|
|
16781
17499
|
splitSystemMessages(messages) {
|
|
@@ -16898,9 +17616,22 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16898
17616
|
}
|
|
16899
17617
|
};
|
|
16900
17618
|
}
|
|
16901
|
-
|
|
17619
|
+
/** Sends the Converse call; retries once with toolChoice auto when the
|
|
17620
|
+
* model rejects forced tool use. */
|
|
17621
|
+
async converseFetch(modelId, request, stream) {
|
|
17622
|
+
const body = this.buildConverseBody(request);
|
|
17623
|
+
try {
|
|
17624
|
+
return await this.doFetch(modelId, body, request.signal, stream);
|
|
17625
|
+
} catch (err) {
|
|
17626
|
+
const tc = body.toolConfig?.toolChoice;
|
|
17627
|
+
if (!(err instanceof ProviderError) || err.statusCode !== 400 || !tc || tc.auto) throw err;
|
|
17628
|
+
if (!forcedToolChoiceRejected(String(err.responseBody ?? err.message))) throw err;
|
|
17629
|
+
return this.doFetch(modelId, this.buildConverseBody(request, true), request.signal, stream);
|
|
17630
|
+
}
|
|
17631
|
+
}
|
|
17632
|
+
async doFetch(modelId, body, signal, stream) {
|
|
16902
17633
|
const host = `bedrock-runtime.${this.region}.amazonaws.com`;
|
|
16903
|
-
const url = `https://${host}/model/${encodeURIComponent(modelId)}
|
|
17634
|
+
const url = `https://${host}/model/${encodeURIComponent(modelId)}/${stream ? "converse-stream" : "converse"}`;
|
|
16904
17635
|
const bodyStr = JSON.stringify(body);
|
|
16905
17636
|
const res = await this.doFetchWithRetry(() => {
|
|
16906
17637
|
const headers = signRequest({
|
|
@@ -16908,7 +17639,7 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16908
17639
|
url,
|
|
16909
17640
|
headers: {
|
|
16910
17641
|
"Content-Type": "application/json",
|
|
16911
|
-
Accept: "application/json",
|
|
17642
|
+
Accept: stream ? "application/vnd.amazon.eventstream" : "application/json",
|
|
16912
17643
|
...this.defaultHeaders
|
|
16913
17644
|
},
|
|
16914
17645
|
body: bodyStr,
|
|
@@ -16916,7 +17647,7 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16916
17647
|
service: "bedrock",
|
|
16917
17648
|
credentials: this.credentials
|
|
16918
17649
|
});
|
|
16919
|
-
return
|
|
17650
|
+
return this.httpFetch(url, { method: "POST", headers, body: bodyStr, signal });
|
|
16920
17651
|
}, signal);
|
|
16921
17652
|
if (!res.ok) {
|
|
16922
17653
|
const text = await res.text().catch(() => "");
|
|
@@ -16927,7 +17658,7 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16927
17658
|
text
|
|
16928
17659
|
);
|
|
16929
17660
|
}
|
|
16930
|
-
return res
|
|
17661
|
+
return res;
|
|
16931
17662
|
}
|
|
16932
17663
|
parseResponse(json, modelId) {
|
|
16933
17664
|
const output = json.output?.message;
|
|
@@ -16963,8 +17694,8 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16963
17694
|
inputTokens: usage.inputTokens ?? 0,
|
|
16964
17695
|
outputTokens: usage.outputTokens ?? 0,
|
|
16965
17696
|
totalTokens: usage.totalTokens ?? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0),
|
|
16966
|
-
cachedInputTokens: usage.cacheReadInputTokenCount ?? 0,
|
|
16967
|
-
cachedWriteTokens: usage.cacheWriteInputTokenCount ?? 0,
|
|
17697
|
+
cachedInputTokens: usage.cacheReadInputTokens ?? usage.cacheReadInputTokenCount ?? 0,
|
|
17698
|
+
cachedWriteTokens: usage.cacheWriteInputTokens ?? usage.cacheWriteInputTokenCount ?? 0,
|
|
16968
17699
|
cachedOutputTokens: 0,
|
|
16969
17700
|
// Bedrock bills thinking tokens inside outputTokens; no separate count.
|
|
16970
17701
|
reasoningTokens: 0
|
|
@@ -16976,11 +17707,26 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16976
17707
|
toolCalls,
|
|
16977
17708
|
usage: tokenUsage,
|
|
16978
17709
|
finishReason: mapBedrockStopReason(json.stopReason),
|
|
17710
|
+
...typeof json.stopReason === "string" ? { rawFinishReason: json.stopReason } : {},
|
|
16979
17711
|
reasoning: reasoningBlocks.length > 0 ? { blocks: reasoningBlocks, summary: summaryParts.join("\n").trim() || void 0 } : void 0,
|
|
16980
17712
|
raw: json
|
|
16981
17713
|
};
|
|
16982
17714
|
}
|
|
16983
17715
|
};
|
|
17716
|
+
var CONVERSE_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Set([
|
|
17717
|
+
"messages",
|
|
17718
|
+
"system",
|
|
17719
|
+
"inferenceConfig",
|
|
17720
|
+
"toolConfig",
|
|
17721
|
+
"guardrailConfig",
|
|
17722
|
+
"additionalModelRequestFields",
|
|
17723
|
+
"additionalModelResponseFieldPaths",
|
|
17724
|
+
"promptVariables",
|
|
17725
|
+
"requestMetadata",
|
|
17726
|
+
"performanceConfig",
|
|
17727
|
+
"serviceTier",
|
|
17728
|
+
"outputConfig"
|
|
17729
|
+
]);
|
|
16984
17730
|
function appendBedrockReasoning(content, blocks) {
|
|
16985
17731
|
if (!blocks?.length) return;
|
|
16986
17732
|
for (const b of blocks) {
|
|
@@ -16995,6 +17741,23 @@ function appendBedrockReasoning(content, blocks) {
|
|
|
16995
17741
|
}
|
|
16996
17742
|
}
|
|
16997
17743
|
}
|
|
17744
|
+
function bedrockExceptionStatus(code) {
|
|
17745
|
+
switch (code) {
|
|
17746
|
+
case "throttlingException":
|
|
17747
|
+
return 429;
|
|
17748
|
+
case "validationException":
|
|
17749
|
+
return 400;
|
|
17750
|
+
case "modelTimeoutException":
|
|
17751
|
+
return 408;
|
|
17752
|
+
case "serviceUnavailableException":
|
|
17753
|
+
return 503;
|
|
17754
|
+
case "internalServerException":
|
|
17755
|
+
case "modelStreamErrorException":
|
|
17756
|
+
return 500;
|
|
17757
|
+
default:
|
|
17758
|
+
return void 0;
|
|
17759
|
+
}
|
|
17760
|
+
}
|
|
16998
17761
|
function mapBedrockStopReason(reason) {
|
|
16999
17762
|
switch (reason) {
|
|
17000
17763
|
case "end_turn":
|
|
@@ -17003,8 +17766,10 @@ function mapBedrockStopReason(reason) {
|
|
|
17003
17766
|
case "tool_use":
|
|
17004
17767
|
return "tool_calls";
|
|
17005
17768
|
case "max_tokens":
|
|
17769
|
+
case "model_context_window_exceeded":
|
|
17006
17770
|
return "length";
|
|
17007
17771
|
case "content_filtered":
|
|
17772
|
+
case "guardrail_intervened":
|
|
17008
17773
|
case "guardrail":
|
|
17009
17774
|
return "content_filter";
|
|
17010
17775
|
default:
|
|
@@ -17034,10 +17799,12 @@ var VertexProvider = class extends BaseProvider {
|
|
|
17034
17799
|
super();
|
|
17035
17800
|
this.providerName = "vertex";
|
|
17036
17801
|
this.tokenExpiry = 0;
|
|
17802
|
+
this.configureTransport(config);
|
|
17037
17803
|
this.projectId = config.projectId;
|
|
17038
17804
|
this.location = config.location ?? "us-central1";
|
|
17039
17805
|
this.defaultModel = config.defaultModel ?? "gemini-2.0-flash";
|
|
17040
17806
|
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
17807
|
+
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
17041
17808
|
if (config.accessToken) {
|
|
17042
17809
|
this.accessToken = config.accessToken;
|
|
17043
17810
|
this.tokenExpiry = Date.now() + 36e5;
|
|
@@ -17060,6 +17827,7 @@ var VertexProvider = class extends BaseProvider {
|
|
|
17060
17827
|
const url = this.buildUrl(modelId, true);
|
|
17061
17828
|
const res = await this.doFetch(url, body, request.signal);
|
|
17062
17829
|
if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
|
|
17830
|
+
const counter = { next: 0 };
|
|
17063
17831
|
const contentType = res.headers.get("content-type") ?? "";
|
|
17064
17832
|
if (contentType.includes("text/event-stream")) {
|
|
17065
17833
|
for await (const event of parseSSEStream(res.body, request.signal)) {
|
|
@@ -17070,7 +17838,7 @@ var VertexProvider = class extends BaseProvider {
|
|
|
17070
17838
|
} catch {
|
|
17071
17839
|
continue;
|
|
17072
17840
|
}
|
|
17073
|
-
yield this.parseStreamChunk(chunk, modelId);
|
|
17841
|
+
yield this.parseStreamChunk(chunk, modelId, counter);
|
|
17074
17842
|
}
|
|
17075
17843
|
} else {
|
|
17076
17844
|
const reader = res.body.getReader();
|
|
@@ -17088,7 +17856,7 @@ var VertexProvider = class extends BaseProvider {
|
|
|
17088
17856
|
if (!trimmed || trimmed === "[" || trimmed === "]") continue;
|
|
17089
17857
|
try {
|
|
17090
17858
|
const chunk = JSON.parse(trimmed);
|
|
17091
|
-
yield this.parseStreamChunk(chunk, modelId);
|
|
17859
|
+
yield this.parseStreamChunk(chunk, modelId, counter);
|
|
17092
17860
|
} catch {
|
|
17093
17861
|
buffer = line + "\n" + buffer;
|
|
17094
17862
|
break;
|
|
@@ -17099,7 +17867,7 @@ var VertexProvider = class extends BaseProvider {
|
|
|
17099
17867
|
if (remaining && remaining !== "[" && remaining !== "]") {
|
|
17100
17868
|
try {
|
|
17101
17869
|
const chunk = JSON.parse(remaining);
|
|
17102
|
-
yield this.parseStreamChunk(chunk, modelId);
|
|
17870
|
+
yield this.parseStreamChunk(chunk, modelId, counter);
|
|
17103
17871
|
} catch {
|
|
17104
17872
|
}
|
|
17105
17873
|
}
|
|
@@ -17110,6 +17878,48 @@ var VertexProvider = class extends BaseProvider {
|
|
|
17110
17878
|
}
|
|
17111
17879
|
}
|
|
17112
17880
|
// ─── Internals ───────────────────────────────────────────────────────────
|
|
17881
|
+
/**
|
|
17882
|
+
* Vertex `:predict` embeddings (text-embedding-00x, text-multilingual-embedding,
|
|
17883
|
+
* gemini-embedding-001). `inputType` maps to the task type; `dimensions` to
|
|
17884
|
+
* `outputDimensionality`. gemini-embedding takes one instance per request, the
|
|
17885
|
+
* text-embedding models up to `batchSize` (default 100).
|
|
17886
|
+
*/
|
|
17887
|
+
async embed(request) {
|
|
17888
|
+
const model = requireEmbeddingModel(this.providerName, request.model);
|
|
17889
|
+
const perRequest = request.batchSize ?? (/gemini-embedding/i.test(model) ? 1 : 100);
|
|
17890
|
+
const url = `https://${this.location}-aiplatform.googleapis.com/v1/projects/${this.projectId}/locations/${this.location}/publishers/google/models/${model}:predict`;
|
|
17891
|
+
const taskType = request.inputType ? Object.prototype.hasOwnProperty.call(GEMINI_TASK_TYPES, request.inputType) ? GEMINI_TASK_TYPES[request.inputType] : request.inputType.toUpperCase() : void 0;
|
|
17892
|
+
const vectors = [];
|
|
17893
|
+
const raw = [];
|
|
17894
|
+
let inputTokens = 0;
|
|
17895
|
+
for (const batch of chunkArray(request.input, perRequest)) {
|
|
17896
|
+
const body = {
|
|
17897
|
+
instances: batch.map((content) => ({ content, ...taskType ? { task_type: taskType } : {} }))
|
|
17898
|
+
};
|
|
17899
|
+
const { parameters: extraParameters, ...extraRest } = request.extra ?? {};
|
|
17900
|
+
const parameters = {
|
|
17901
|
+
...request.dimensions != null ? { outputDimensionality: request.dimensions } : {},
|
|
17902
|
+
// Caller-supplied parameters are merged in, not replacing the above (an
|
|
17903
|
+
// explicit extra.parameters.outputDimensionality still wins).
|
|
17904
|
+
...extraParameters && typeof extraParameters === "object" ? extraParameters : {}
|
|
17905
|
+
};
|
|
17906
|
+
if (Object.keys(parameters).length > 0) body.parameters = parameters;
|
|
17907
|
+
Object.assign(body, extraRest);
|
|
17908
|
+
const res = await this.doFetch(url, body, request.signal);
|
|
17909
|
+
const json = await res.json();
|
|
17910
|
+
raw.push(json);
|
|
17911
|
+
for (const p of json.predictions ?? []) {
|
|
17912
|
+
const values = p.embeddings?.values;
|
|
17913
|
+
if (!Array.isArray(values)) {
|
|
17914
|
+
throw new ProviderError("Vertex embeddings response has a prediction without an embeddings.values array", this.providerName, void 0, json);
|
|
17915
|
+
}
|
|
17916
|
+
vectors.push(values);
|
|
17917
|
+
inputTokens += p.embeddings?.statistics?.token_count ?? 0;
|
|
17918
|
+
}
|
|
17919
|
+
}
|
|
17920
|
+
assertVectorCount(this.providerName, vectors, request.input.length);
|
|
17921
|
+
return { model, vectors, usage: { inputTokens, totalTokens: inputTokens }, raw };
|
|
17922
|
+
}
|
|
17113
17923
|
buildUrl(modelId, stream) {
|
|
17114
17924
|
const base = `https://${this.location}-aiplatform.googleapis.com/v1`;
|
|
17115
17925
|
const path2 = `projects/${this.projectId}/locations/${this.location}/publishers/google/models/${modelId}`;
|
|
@@ -17207,7 +18017,9 @@ var VertexProvider = class extends BaseProvider {
|
|
|
17207
18017
|
functionCall: {
|
|
17208
18018
|
name: tc.name,
|
|
17209
18019
|
args: safeJsonParse3(tc.arguments)
|
|
17210
|
-
}
|
|
18020
|
+
},
|
|
18021
|
+
// Gemini 3 requires the signature of each functionCall echoed back.
|
|
18022
|
+
...tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}
|
|
17211
18023
|
});
|
|
17212
18024
|
}
|
|
17213
18025
|
return { role: "model", parts: parts2 };
|
|
@@ -17304,7 +18116,7 @@ var VertexProvider = class extends BaseProvider {
|
|
|
17304
18116
|
sign.update(signingInput);
|
|
17305
18117
|
const signature = sign.sign(sa.private_key, "base64url");
|
|
17306
18118
|
const jwt = `${signingInput}.${signature}`;
|
|
17307
|
-
const tokenRes = await
|
|
18119
|
+
const tokenRes = await this.httpFetch("https://oauth2.googleapis.com/token", {
|
|
17308
18120
|
method: "POST",
|
|
17309
18121
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
17310
18122
|
body: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}`
|
|
@@ -17318,16 +18130,20 @@ var VertexProvider = class extends BaseProvider {
|
|
|
17318
18130
|
}
|
|
17319
18131
|
async doFetch(url, body, signal) {
|
|
17320
18132
|
const token = await this.getAccessToken();
|
|
17321
|
-
const
|
|
17322
|
-
|
|
17323
|
-
|
|
17324
|
-
|
|
17325
|
-
|
|
17326
|
-
|
|
17327
|
-
|
|
17328
|
-
|
|
18133
|
+
const bodyStr = JSON.stringify(body);
|
|
18134
|
+
const res = await this.doFetchWithRetry(
|
|
18135
|
+
() => this.httpFetch(url, {
|
|
18136
|
+
method: "POST",
|
|
18137
|
+
headers: {
|
|
18138
|
+
"Content-Type": "application/json",
|
|
18139
|
+
Authorization: `Bearer ${token}`,
|
|
18140
|
+
...this.defaultHeaders
|
|
18141
|
+
},
|
|
18142
|
+
body: bodyStr,
|
|
18143
|
+
signal
|
|
18144
|
+
}),
|
|
17329
18145
|
signal
|
|
17330
|
-
|
|
18146
|
+
);
|
|
17331
18147
|
if (!res.ok) {
|
|
17332
18148
|
const text = await res.text().catch(() => "");
|
|
17333
18149
|
throw new ProviderError(
|
|
@@ -17357,7 +18173,8 @@ var VertexProvider = class extends BaseProvider {
|
|
|
17357
18173
|
toolCalls.push({
|
|
17358
18174
|
id: `call_${toolCallIdx++}`,
|
|
17359
18175
|
name: part.functionCall.name,
|
|
17360
|
-
arguments: JSON.stringify(part.functionCall.args ?? {})
|
|
18176
|
+
arguments: JSON.stringify(part.functionCall.args ?? {}),
|
|
18177
|
+
...part.thoughtSignature ? { thoughtSignature: part.thoughtSignature } : {}
|
|
17361
18178
|
});
|
|
17362
18179
|
}
|
|
17363
18180
|
}
|
|
@@ -17378,11 +18195,12 @@ var VertexProvider = class extends BaseProvider {
|
|
|
17378
18195
|
toolCalls,
|
|
17379
18196
|
usage: tokenUsage,
|
|
17380
18197
|
finishReason: mapGeminiFinishReason(candidate?.finishReason),
|
|
18198
|
+
...typeof candidate?.finishReason === "string" ? { rawFinishReason: candidate.finishReason } : {},
|
|
17381
18199
|
reasoning: thoughtParts.length > 0 ? { summary: thoughtParts.join("\n").trim() || void 0 } : void 0,
|
|
17382
18200
|
raw: json
|
|
17383
18201
|
};
|
|
17384
18202
|
}
|
|
17385
|
-
parseStreamChunk(chunk, modelId) {
|
|
18203
|
+
parseStreamChunk(chunk, modelId, counter) {
|
|
17386
18204
|
const candidate = chunk.candidates?.[0];
|
|
17387
18205
|
const parts = candidate?.content?.parts ?? [];
|
|
17388
18206
|
const delta = {};
|
|
@@ -17396,9 +18214,12 @@ var VertexProvider = class extends BaseProvider {
|
|
|
17396
18214
|
} else if (part.functionCall) {
|
|
17397
18215
|
if (!delta.toolCalls) delta.toolCalls = [];
|
|
17398
18216
|
delta.toolCalls.push({
|
|
17399
|
-
|
|
18217
|
+
// Unique across the whole stream: the adapter merges chunks by id.
|
|
18218
|
+
id: `call_${counter.next}`,
|
|
18219
|
+
index: counter.next++,
|
|
17400
18220
|
name: part.functionCall.name,
|
|
17401
|
-
arguments: JSON.stringify(part.functionCall.args ?? {})
|
|
18221
|
+
arguments: JSON.stringify(part.functionCall.args ?? {}),
|
|
18222
|
+
...part.thoughtSignature ? { thoughtSignature: part.thoughtSignature } : {}
|
|
17402
18223
|
});
|
|
17403
18224
|
}
|
|
17404
18225
|
}
|
|
@@ -17420,10 +18241,21 @@ var VertexProvider = class extends BaseProvider {
|
|
|
17420
18241
|
model: modelId,
|
|
17421
18242
|
delta,
|
|
17422
18243
|
usage,
|
|
17423
|
-
finishReason: mapGeminiFinishReason(candidate?.finishReason)
|
|
18244
|
+
finishReason: mapGeminiFinishReason(candidate?.finishReason),
|
|
18245
|
+
...typeof candidate?.finishReason === "string" ? { rawFinishReason: candidate.finishReason } : {}
|
|
17424
18246
|
};
|
|
17425
18247
|
}
|
|
17426
18248
|
};
|
|
18249
|
+
var GEMINI_TASK_TYPES = {
|
|
18250
|
+
search_document: "RETRIEVAL_DOCUMENT",
|
|
18251
|
+
search_query: "RETRIEVAL_QUERY",
|
|
18252
|
+
classification: "CLASSIFICATION",
|
|
18253
|
+
clustering: "CLUSTERING",
|
|
18254
|
+
semantic_similarity: "SEMANTIC_SIMILARITY",
|
|
18255
|
+
question_answering: "QUESTION_ANSWERING",
|
|
18256
|
+
fact_verification: "FACT_VERIFICATION",
|
|
18257
|
+
code_retrieval_query: "CODE_RETRIEVAL_QUERY"
|
|
18258
|
+
};
|
|
17427
18259
|
function mapGeminiFinishReason(reason) {
|
|
17428
18260
|
switch (reason) {
|
|
17429
18261
|
case "STOP":
|
|
@@ -17667,7 +18499,9 @@ exports.sessionMetrics = sessionMetrics;
|
|
|
17667
18499
|
exports.setTokenCounter = setTokenCounter;
|
|
17668
18500
|
exports.startStreamingSession = startStreamingSession;
|
|
17669
18501
|
exports.summarizeObject = summarizeObject;
|
|
18502
|
+
exports.supportsAdaptiveThinking = supportsAdaptiveThinking;
|
|
17670
18503
|
exports.textFromContent = textFromContent;
|
|
18504
|
+
exports.toMessageUsage = toMessageUsage;
|
|
17671
18505
|
exports.toStrictCompatible = toStrictCompatible;
|
|
17672
18506
|
exports.toStrictToolSchema = toStrictToolSchema;
|
|
17673
18507
|
exports.tokenize = tokenize;
|