@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/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
- const request = buildRequest(messages, boundTools, options, _invokeOptions);
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
- const request = buildRequest(messages, boundTools, options, _invokeOptions);
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 = { blocks: reasoningBlocks };
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
- prompt_tokens: usage.inputTokens,
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: false,
2953
- // streaming uses binary protocol
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
- () => fetch(`${this.baseURL}/v1/systemone`, {
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 applyAnthropicReasoning(body, reasoning) {
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
- if (budget == null) return;
15330
- const thinkingConfig = { thinkingBudget: budget };
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 _OpenAIProvider extends BaseProvider {
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
- this.responsesApi = config.responsesApi ?? "auto";
15565
- const ownInstance = new.target === _OpenAIProvider;
15566
- this.responsesApiModels = ownInstance ? [...config.responsesApiModels ?? []] : [];
15567
- this.responsesStreaming = ownInstance && config.responsesStreaming === true;
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.responsesApi === "never") return false;
15594
- if (this.responsesApi === "always") return true;
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
- if (request.temperature != null) body.temperature = request.temperature;
15693
- if (request.maxTokens != null) body.max_tokens = request.maxTokens;
15694
- if (request.topP != null) body.top_p = request.topP;
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
- delta: final?.reasoning ? { reasoning: final.reasoning } : {},
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(() => fetch(url, { method: "POST", headers, body: payload, signal }), signal);
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(() => fetch(url, { method: "POST", headers, body: payload, signal }), signal);
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 body = this.buildRequestBody(request, false);
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 body = this.buildRequestBody(request, true);
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: 0
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 ?? 4096,
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
- applyAnthropicReasoning(body, request.reasoning);
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(() => fetch(url, { method: "POST", headers, body: payload, signal }), signal);
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
- // Anthropic bills thinking tokens inside `output_tokens` and does not
16545
- // expose a separate count, so reasoningTokens stays 0 here (cost is still
16546
- // captured via outputTokens). See docs for the limitation.
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 res = await fetch(url, {
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: JSON.stringify(bodyWithoutModel),
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 fetch(url, {
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 body = this.buildConverseBody(request);
16724
- const json = await this.doFetch(modelId, body, request.signal);
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 response = await this.complete(request);
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: response.id,
16731
- model: response.model,
16732
- delta: {
16733
- content: response.content ?? void 0,
16734
- toolCalls: response.toolCalls.length > 0 ? response.toolCalls : void 0
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
- Object.assign(body, request.extra);
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
- async doFetch(modelId, body, signal) {
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)}/converse`;
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 fetch(url, { method: "POST", headers, body: bodyStr, signal });
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.json();
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 fetch("https://oauth2.googleapis.com/token", {
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 res = await fetch(url, {
17322
- method: "POST",
17323
- headers: {
17324
- "Content-Type": "application/json",
17325
- Authorization: `Bearer ${token}`,
17326
- ...this.defaultHeaders
17327
- },
17328
- body: JSON.stringify(body),
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
- id: `call_${delta.toolCalls.length}`,
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;