@cognipeer/agent-sdk 0.10.6 → 0.13.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) {
@@ -5087,10 +5105,10 @@ function createToolsNode(initialTools, opts) {
5087
5105
  const toolCallId = tc.id || `${tc.name}_${toolCount + 1}`;
5088
5106
  const validatedArgs = validateToolArgs(tool, args);
5089
5107
  if (!validatedArgs.ok && resolved.toolResponses.schemaValidation === "strict") {
5090
- const errorMessage = `Tool argument validation failed for ${tc.name}: ${validatedArgs.message}`;
5091
- push({ role: "tool", content: errorMessage, tool_call_id: toolCallId, name: tc.name });
5092
- markToolFailure(errorMessage, tc.name, toolCallId);
5093
- onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args, error: { message: errorMessage } });
5108
+ const errorMessage2 = `Tool argument validation failed for ${tc.name}: ${validatedArgs.message}`;
5109
+ push({ role: "tool", content: errorMessage2, tool_call_id: toolCallId, name: tc.name });
5110
+ markToolFailure(errorMessage2, tc.name, toolCallId);
5111
+ onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args, error: { message: errorMessage2 } });
5094
5112
  toolCount += 1;
5095
5113
  return { status: "error" };
5096
5114
  }
@@ -6101,7 +6119,7 @@ var LEVEL_DEFAULTS = {
6101
6119
  };
6102
6120
  var VALID_LEVELS = ["minimal", "low", "medium", "high"];
6103
6121
  var VALID_CADENCES = ["off", "every_turn", "after_tool", "on_branch", "initial_then_after_tool"];
6104
- var VALID_EFFORTS = ["none", "minimal", "low", "medium", "high"];
6122
+ var VALID_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
6105
6123
  function validateReasoningConfig(input) {
6106
6124
  if (!input) return;
6107
6125
  if (input.level !== void 0 && !VALID_LEVELS.includes(input.level)) {
@@ -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,56 @@ 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.onModelRuleLearned = config.onModelRuleLearned;
11593
+ this.timeoutMs = typeof config.timeoutMs === "number" && config.timeoutMs > 0 ? config.timeoutMs : void 0;
11594
+ }
11595
+ /**
11596
+ * The single HTTP entry point for every provider call site. Uses the
11597
+ * configured `fetch` and applies `timeoutMs`: the timer covers the time until
11598
+ * the response HEADERS arrive and is cleared after that, so a long-running
11599
+ * stream is never cut by it (use the request `signal` to cancel a stream).
11600
+ * A timeout surfaces as a `ProviderError` with status 408. The caller's
11601
+ * `signal` still aborts the request as before.
11602
+ */
11603
+ async httpFetch(url, init = {}) {
11604
+ const doFetch = this.fetchImpl ?? fetch;
11605
+ const timeoutMs = this.timeoutMs;
11606
+ if (!timeoutMs) return doFetch(url, init);
11607
+ const controller = new AbortController();
11608
+ const outer = init.signal ?? void 0;
11609
+ let timedOut = false;
11610
+ const onOuterAbort = () => controller.abort(outer?.reason);
11611
+ if (outer) {
11612
+ if (outer.aborted) controller.abort(outer.reason);
11613
+ else outer.addEventListener("abort", onOuterAbort, { once: true });
11614
+ }
11615
+ const timer = setTimeout(() => {
11616
+ timedOut = true;
11617
+ controller.abort(new Error(`timed out after ${timeoutMs}ms`));
11618
+ }, timeoutMs);
11619
+ try {
11620
+ const res = await doFetch(url, { ...init, signal: controller.signal });
11621
+ return res;
11622
+ } catch (err) {
11623
+ if (timedOut) {
11624
+ throw new ProviderError(`${this.providerName} request timed out after ${timeoutMs}ms`, this.providerName, 408);
11625
+ }
11626
+ throw err;
11627
+ } finally {
11628
+ clearTimeout(timer);
11629
+ }
11630
+ }
11631
+ /**
11632
+ * Embeds `request.input`. Implemented by the providers that have an
11633
+ * embeddings endpoint (OpenAI, Azure, openai-compatible, Bedrock, Vertex);
11634
+ * the others reject with a 400 ProviderError.
11635
+ */
11636
+ async embed(_request) {
11637
+ throw new ProviderError(`${this.providerName} does not support embeddings`, this.providerName, 400);
11638
+ }
11549
11639
  /**
11550
11640
  * Helper concrete providers can wrap their fetch with. Retries 429/5xx
11551
11641
  * with exponential backoff and respects Retry-After if the response
@@ -11587,34 +11677,13 @@ var BaseProvider = class {
11587
11677
  }
11588
11678
  };
11589
11679
 
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
11680
  // src/providers/jev.ts
11613
11681
  var JEV_DEFAULT_BASE_URL = "https://api.typesafe.ai";
11614
11682
  var JevProvider = class extends BaseProvider {
11615
11683
  constructor(config) {
11616
11684
  super();
11617
11685
  this.providerName = "jev";
11686
+ this.configureTransport(config);
11618
11687
  this.apiKey = config.apiKey;
11619
11688
  this.baseURL = (config.baseURL ?? JEV_DEFAULT_BASE_URL).replace(/\/$/, "");
11620
11689
  this.defaultModel = config.defaultModel;
@@ -11630,7 +11699,7 @@ var JevProvider = class extends BaseProvider {
11630
11699
  questions: request.questions
11631
11700
  };
11632
11701
  const res = await this.doFetchWithRetry(
11633
- () => fetch(`${this.baseURL}/v1/systemone`, {
11702
+ () => this.httpFetch(`${this.baseURL}/v1/systemone`, {
11634
11703
  method: "POST",
11635
11704
  headers: {
11636
11705
  "Content-Type": "application/json",
@@ -13866,12 +13935,12 @@ function responseCache(config = {}) {
13866
13935
  };
13867
13936
  }
13868
13937
  function signRequest(req) {
13869
- const now = /* @__PURE__ */ new Date();
13938
+ const now = req.now ?? /* @__PURE__ */ new Date();
13870
13939
  const dateStamp = toDateStamp(now);
13871
13940
  const amzDate = toAmzDate(now);
13872
13941
  const parsed = new URL(req.url);
13873
13942
  const host = parsed.host;
13874
- const canonicalUri = parsed.pathname || "/";
13943
+ const canonicalUri = (parsed.pathname || "/").split("/").map((seg) => encodeURIComponent(seg).replace(/[!'()*]/g, (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase())).join("/");
13875
13944
  const canonicalQuerystring = sortQueryParams(parsed.searchParams);
13876
13945
  const headers = {
13877
13946
  ...req.headers,
@@ -15188,98 +15257,805 @@ function languageGuard(config) {
15188
15257
  // src/index.ts
15189
15258
  init_content();
15190
15259
 
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
- }
15260
+ // src/models/model-rules.json
15261
+ var model_rules_default = {
15262
+ version: 1,
15263
+ effortLadder: [
15264
+ "none",
15265
+ "minimal",
15266
+ "low",
15267
+ "medium",
15268
+ "high",
15269
+ "xhigh",
15270
+ "max"
15271
+ ],
15272
+ budgets: {
15273
+ minimal: 1024,
15274
+ low: 2048,
15275
+ medium: 8192,
15276
+ high: 16384,
15277
+ xhigh: 24576,
15278
+ max: 32768
15279
+ },
15280
+ geminiBudgets: {
15281
+ minimal: 512,
15282
+ low: 1024,
15283
+ medium: 4096,
15284
+ high: 12e3,
15285
+ xhigh: 24576,
15286
+ max: 24576
15287
+ },
15288
+ rules: [
15289
+ {
15290
+ id: "openai-chat-models",
15291
+ family: "openai",
15292
+ match: "^(?:chatgpt|gpt-[\\d.]+[a-z0-9.-]*?-chat)",
15293
+ reasoning: {
15294
+ style: "none",
15295
+ efforts: [],
15296
+ off: null
15297
+ },
15298
+ sampling: {
15299
+ temperature: "any",
15300
+ topP: true,
15301
+ topK: true,
15302
+ penalties: true,
15303
+ logprobs: true
15304
+ },
15305
+ maxTokensParam: "max_completion_tokens",
15306
+ chatTools: {
15307
+ reasoning: "any"
15308
+ },
15309
+ source: "docs",
15310
+ note: "Chat-tuned *-chat variants are not reasoning models: ordinary sampling, no reasoning field."
15311
+ },
15312
+ {
15313
+ id: "openai-gpt-6-plus",
15314
+ family: "openai",
15315
+ match: "^gpt-?(?:[6-9]|(?!35(?!\\d))[1-9]\\d+)(?:[.-]|$)",
15316
+ reasoning: {
15317
+ style: "effort",
15318
+ efforts: [
15319
+ "none",
15320
+ "low",
15321
+ "medium",
15322
+ "high",
15323
+ "xhigh"
15324
+ ],
15325
+ off: "none",
15326
+ responsesEfforts: [
15327
+ "none",
15328
+ "low",
15329
+ "medium",
15330
+ "high",
15331
+ "xhigh",
15332
+ "max"
15333
+ ]
15334
+ },
15335
+ sampling: {
15336
+ temperature: "default-only",
15337
+ topP: false,
15338
+ topK: false,
15339
+ penalties: false,
15340
+ logprobs: false
15341
+ },
15342
+ maxTokensParam: "max_completion_tokens",
15343
+ chatTools: {
15344
+ reasoning: "force-none"
15345
+ },
15346
+ source: "verified-2026-10-14",
15347
+ note: "gpt-N, N>=6. Chat Completions takes none/low/medium/high/xhigh (minimal and max are 400s); Responses also takes max. Function tools with an effort other than none are a 400 on Chat Completions and the default effort is not none, so none is sent explicitly. Rejects max_tokens, temperature != 1, top_p, penalties, logprobs."
15348
+ },
15349
+ {
15350
+ id: "openai-gpt-5x",
15351
+ family: "openai",
15352
+ match: "^gpt-?5\\.[1-9]",
15353
+ reasoning: {
15354
+ style: "effort",
15355
+ efforts: [
15356
+ "none",
15357
+ "low",
15358
+ "medium",
15359
+ "high",
15360
+ "xhigh"
15361
+ ],
15362
+ off: "none",
15363
+ responsesEfforts: [
15364
+ "none",
15365
+ "low",
15366
+ "medium",
15367
+ "high",
15368
+ "xhigh",
15369
+ "max"
15370
+ ]
15371
+ },
15372
+ sampling: {
15373
+ temperature: "default-only",
15374
+ topP: false,
15375
+ topK: false,
15376
+ penalties: false,
15377
+ logprobs: false
15378
+ },
15379
+ maxTokensParam: "max_completion_tokens",
15380
+ chatTools: {
15381
+ reasoning: "force-none"
15382
+ },
15383
+ source: "verified-2026-10-14",
15384
+ note: "gpt-5.1 and newer 5.x. Same wire rules as gpt-6: no minimal/max on Chat Completions (Responses takes max), function tools need reasoning_effort none on Chat Completions."
15385
+ },
15386
+ {
15387
+ id: "openai-gpt-5-base",
15388
+ family: "openai",
15389
+ match: "^gpt-?5(?![.\\d])",
15390
+ reasoning: {
15391
+ style: "effort",
15392
+ efforts: [
15393
+ "minimal",
15394
+ "low",
15395
+ "medium",
15396
+ "high"
15397
+ ],
15398
+ off: null
15399
+ },
15400
+ sampling: {
15401
+ temperature: "default-only",
15402
+ topP: false,
15403
+ topK: false,
15404
+ penalties: false,
15405
+ logprobs: false
15406
+ },
15407
+ maxTokensParam: "max_completion_tokens",
15408
+ chatTools: {
15409
+ reasoning: "any"
15410
+ },
15411
+ source: "docs",
15412
+ note: "gpt-5, -mini, -nano, -codex: minimal/low/medium/high, reasoning cannot be turned off (none maps to minimal)."
15413
+ },
15414
+ {
15415
+ id: "openai-o-series",
15416
+ family: "openai",
15417
+ match: "^o[1-9]\\d?(?![a-z0-9])",
15418
+ reasoning: {
15419
+ style: "effort",
15420
+ efforts: [
15421
+ "low",
15422
+ "medium",
15423
+ "high"
15424
+ ],
15425
+ off: null
15426
+ },
15427
+ sampling: {
15428
+ temperature: "default-only",
15429
+ topP: false,
15430
+ topK: false,
15431
+ penalties: false,
15432
+ logprobs: false
15433
+ },
15434
+ maxTokensParam: "max_completion_tokens",
15435
+ chatTools: {
15436
+ reasoning: "any"
15437
+ },
15438
+ source: "docs",
15439
+ note: "o1/o3/o4 family: low/medium/high, no sampling overrides."
15440
+ },
15441
+ {
15442
+ id: "openai-gpt-oss",
15443
+ family: "openai",
15444
+ match: "^gpt-?oss",
15445
+ reasoning: {
15446
+ style: "effort",
15447
+ efforts: [
15448
+ "low",
15449
+ "medium",
15450
+ "high"
15451
+ ],
15452
+ off: null
15453
+ },
15454
+ sampling: {
15455
+ temperature: "any",
15456
+ topP: true,
15457
+ topK: true,
15458
+ penalties: true,
15459
+ logprobs: true
15460
+ },
15461
+ maxTokensParam: "max_tokens",
15462
+ chatTools: {
15463
+ reasoning: "any"
15464
+ },
15465
+ source: "docs",
15466
+ note: "Open-weight gpt-oss: low/medium/high; sampling and max_tokens behave like an ordinary chat model."
15467
+ },
15468
+ {
15469
+ id: "openai-legacy-gpt",
15470
+ family: "openai",
15471
+ match: "^(?:gpt-?(?:[0-4]|35)(?![0-9])|text-|davinci|babbage)",
15472
+ reasoning: {
15473
+ style: "none",
15474
+ efforts: [],
15475
+ off: null
15476
+ },
15477
+ sampling: {
15478
+ temperature: "any",
15479
+ topP: true,
15480
+ topK: true,
15481
+ penalties: true,
15482
+ logprobs: true
15483
+ },
15484
+ maxTokensParam: "max_tokens",
15485
+ chatTools: {
15486
+ reasoning: "any"
15487
+ },
15488
+ source: "docs",
15489
+ note: "gpt-4o, gpt-4.1, gpt-3.5 and older: no reasoning, ordinary sampling."
15490
+ },
15491
+ {
15492
+ id: "openai-compatible-toggle",
15493
+ family: "openai",
15494
+ match: "^(?:deepseek-(?:r\\d|reasoner)|qwq|qwen-?3)",
15495
+ reasoning: {
15496
+ style: "toggle",
15497
+ efforts: [
15498
+ "none",
15499
+ "minimal",
15500
+ "low",
15501
+ "medium",
15502
+ "high",
15503
+ "xhigh",
15504
+ "max"
15505
+ ],
15506
+ off: "none"
15507
+ },
15508
+ sampling: {
15509
+ temperature: "any",
15510
+ topP: true,
15511
+ topK: true,
15512
+ penalties: true,
15513
+ logprobs: true
15514
+ },
15515
+ maxTokensParam: "max_tokens",
15516
+ chatTools: {
15517
+ reasoning: "any"
15518
+ },
15519
+ source: "docs",
15520
+ note: "DeepSeek R/reasoner, QwQ, Qwen3: on/off style hosts. The effort is forwarded as-is (including none, which switches thinking off on thinking-by-default models)."
15521
+ },
15522
+ {
15523
+ id: "openai-default",
15524
+ family: "openai",
15525
+ match: ".*",
15526
+ reasoning: {
15527
+ style: "effort",
15528
+ efforts: [
15529
+ "none",
15530
+ "minimal",
15531
+ "low",
15532
+ "medium",
15533
+ "high",
15534
+ "xhigh",
15535
+ "max"
15536
+ ],
15537
+ off: "none"
15538
+ },
15539
+ sampling: {
15540
+ temperature: "any",
15541
+ topP: true,
15542
+ topK: true,
15543
+ penalties: true,
15544
+ logprobs: true
15545
+ },
15546
+ maxTokensParam: "max_tokens",
15547
+ chatTools: {
15548
+ reasoning: "any"
15549
+ },
15550
+ source: "docs",
15551
+ note: "Permissive default for unknown OpenAI-wire models: pass what the caller asked; the provider learns from 400s."
15552
+ },
15553
+ {
15554
+ id: "claude-haiku-5",
15555
+ family: "anthropic",
15556
+ match: "^claude-(?:haiku-5|5(?:-\\d+)?-haiku)(?!\\d)",
15557
+ reasoning: {
15558
+ style: "adaptive",
15559
+ efforts: [
15560
+ "low",
15561
+ "medium",
15562
+ "high",
15563
+ "xhigh",
15564
+ "max"
15565
+ ],
15566
+ off: "disabled"
15567
+ },
15568
+ sampling: {
15569
+ temperature: "unsupported",
15570
+ topP: false,
15571
+ topK: false,
15572
+ penalties: false,
15573
+ logprobs: false
15574
+ },
15575
+ maxTokensParam: "max_tokens",
15576
+ chatTools: {
15577
+ reasoning: "any"
15578
+ },
15579
+ source: "verified-2026-10-14",
15580
+ note: "Claude Haiku 5.x: thinking:{type:adaptive} + output_config.effort; thinking:{type:enabled,budget_tokens} is rejected. Off = thinking:{type:disabled}. Sampling params are rejected."
15581
+ },
15582
+ {
15583
+ id: "claude-sonnet-5",
15584
+ family: "anthropic",
15585
+ match: "^claude-(?:sonnet-5|5(?:-\\d+)?-sonnet)(?!\\d)",
15586
+ reasoning: {
15587
+ style: "adaptive",
15588
+ efforts: [
15589
+ "low",
15590
+ "medium",
15591
+ "high",
15592
+ "xhigh",
15593
+ "max"
15594
+ ],
15595
+ off: "between_tools"
15596
+ },
15597
+ sampling: {
15598
+ temperature: "unsupported",
15599
+ topP: false,
15600
+ topK: false,
15601
+ penalties: false,
15602
+ logprobs: false
15603
+ },
15604
+ maxTokensParam: "max_tokens",
15605
+ chatTools: {
15606
+ reasoning: "any"
15607
+ },
15608
+ source: "verified-2026-10-14",
15609
+ note: "Claude Sonnet 5.x: adaptive + output_config.effort. thinking:{type:disabled} is rejected; off = thinking:{type:between_tools}. Sampling params are rejected."
15610
+ },
15611
+ {
15612
+ id: "claude-opus-5",
15613
+ family: "anthropic",
15614
+ match: "^claude-(?:opus-5|5(?:-\\d+)?-opus)(?!\\d)",
15615
+ reasoning: {
15616
+ style: "adaptive",
15617
+ efforts: [
15618
+ "low",
15619
+ "medium",
15620
+ "high",
15621
+ "xhigh",
15622
+ "max"
15623
+ ],
15624
+ off: null
15625
+ },
15626
+ sampling: {
15627
+ temperature: "unsupported",
15628
+ topP: false,
15629
+ topK: false,
15630
+ penalties: false,
15631
+ logprobs: false
15632
+ },
15633
+ maxTokensParam: "max_tokens",
15634
+ chatTools: {
15635
+ reasoning: "any"
15636
+ },
15637
+ source: "verified-2026-10-14",
15638
+ note: "Claude Opus 5.x: adaptive + output_config.effort; thinking cannot be turned off (none maps to the lowest effort). Sampling params are rejected."
15639
+ },
15640
+ {
15641
+ id: "claude-fable",
15642
+ family: "anthropic",
15643
+ match: "^claude-fable",
15644
+ reasoning: {
15645
+ style: "adaptive",
15646
+ efforts: [
15647
+ "low",
15648
+ "medium",
15649
+ "high",
15650
+ "xhigh",
15651
+ "max"
15652
+ ],
15653
+ off: null
15654
+ },
15655
+ sampling: {
15656
+ temperature: "unsupported",
15657
+ topP: false,
15658
+ topK: false,
15659
+ penalties: false,
15660
+ logprobs: false
15661
+ },
15662
+ maxTokensParam: "max_tokens",
15663
+ chatTools: {
15664
+ reasoning: "any"
15665
+ },
15666
+ source: "docs",
15667
+ note: "Claude Fable line: treated like the 5.x adaptive models."
15668
+ },
15669
+ {
15670
+ id: "claude-4-7",
15671
+ family: "anthropic",
15672
+ match: "^claude-(?:(?:opus|sonnet|haiku)-4[-.]7|4[-.]7-(?:opus|sonnet|haiku))(?!\\d)",
15673
+ reasoning: {
15674
+ style: "adaptive",
15675
+ efforts: [
15676
+ "low",
15677
+ "medium",
15678
+ "high",
15679
+ "xhigh",
15680
+ "max"
15681
+ ],
15682
+ off: "disabled"
15683
+ },
15684
+ sampling: {
15685
+ temperature: "thinking-requires-1",
15686
+ topP: true,
15687
+ topK: true,
15688
+ penalties: true,
15689
+ logprobs: true
15690
+ },
15691
+ maxTokensParam: "max_tokens",
15692
+ chatTools: {
15693
+ reasoning: "any"
15694
+ },
15695
+ source: "docs",
15696
+ note: "Claude 4.7: adaptive thinking, low/medium/high/xhigh/max."
15697
+ },
15698
+ {
15699
+ id: "claude-4-6",
15700
+ family: "anthropic",
15701
+ match: "^claude-(?:(?:opus|sonnet|haiku)-4[-.]6|4[-.]6-(?:opus|sonnet|haiku))(?!\\d)",
15702
+ reasoning: {
15703
+ style: "adaptive",
15704
+ efforts: [
15705
+ "low",
15706
+ "medium",
15707
+ "high",
15708
+ "max"
15709
+ ],
15710
+ off: "disabled"
15711
+ },
15712
+ sampling: {
15713
+ temperature: "thinking-requires-1",
15714
+ topP: true,
15715
+ topK: true,
15716
+ penalties: true,
15717
+ logprobs: true
15718
+ },
15719
+ maxTokensParam: "max_tokens",
15720
+ chatTools: {
15721
+ reasoning: "any"
15722
+ },
15723
+ source: "docs",
15724
+ note: "Claude 4.6: adaptive thinking, low/medium/high/max (no xhigh)."
15725
+ },
15726
+ {
15727
+ id: "claude-legacy-budget",
15728
+ family: "anthropic",
15729
+ match: "^claude-(?:3|4-|(?:opus|sonnet|haiku)-4)",
15730
+ reasoning: {
15731
+ style: "budget",
15732
+ efforts: [
15733
+ "none",
15734
+ "minimal",
15735
+ "low",
15736
+ "medium",
15737
+ "high",
15738
+ "xhigh",
15739
+ "max"
15740
+ ],
15741
+ off: null
15742
+ },
15743
+ sampling: {
15744
+ temperature: "thinking-requires-1",
15745
+ topP: true,
15746
+ topK: true,
15747
+ penalties: true,
15748
+ logprobs: true
15749
+ },
15750
+ maxTokensParam: "max_tokens",
15751
+ chatTools: {
15752
+ reasoning: "any"
15753
+ },
15754
+ source: "docs",
15755
+ note: "Claude 3.7 and 4.0-4.5: thinking:{type:enabled,budget_tokens}; every effort maps to a budget; sampling overrides are not allowed while thinking."
15756
+ },
15757
+ {
15758
+ id: "anthropic-default",
15759
+ family: "anthropic",
15760
+ match: ".*",
15761
+ reasoning: {
15762
+ style: "budget",
15763
+ efforts: [
15764
+ "none",
15765
+ "minimal",
15766
+ "low",
15767
+ "medium",
15768
+ "high",
15769
+ "xhigh",
15770
+ "max"
15771
+ ],
15772
+ off: null
15773
+ },
15774
+ sampling: {
15775
+ temperature: "thinking-requires-1",
15776
+ topP: true,
15777
+ topK: true,
15778
+ penalties: true,
15779
+ logprobs: true
15780
+ },
15781
+ maxTokensParam: "max_tokens",
15782
+ chatTools: {
15783
+ reasoning: "any"
15784
+ },
15785
+ source: "docs",
15786
+ note: "Permissive default for unknown Anthropic-wire models: budget style (the long-standing form); the provider flips the thinking form when the API says so."
15787
+ },
15788
+ {
15789
+ id: "gemini-budget",
15790
+ family: "gemini",
15791
+ match: "^gemini",
15792
+ reasoning: {
15793
+ style: "budget",
15794
+ efforts: [
15795
+ "none",
15796
+ "minimal",
15797
+ "low",
15798
+ "medium",
15799
+ "high",
15800
+ "xhigh",
15801
+ "max"
15802
+ ],
15803
+ off: "none"
15804
+ },
15805
+ sampling: {
15806
+ temperature: "any",
15807
+ topP: true,
15808
+ topK: true,
15809
+ penalties: true,
15810
+ logprobs: true
15811
+ },
15812
+ maxTokensParam: "max_tokens",
15813
+ chatTools: {
15814
+ reasoning: "any"
15815
+ },
15816
+ source: "docs",
15817
+ note: "Gemini 2.5-style thinkingBudget; effort maps to a token budget, none = budget 0."
15818
+ },
15819
+ {
15820
+ id: "gemini-default",
15821
+ family: "gemini",
15822
+ match: ".*",
15823
+ reasoning: {
15824
+ style: "budget",
15825
+ efforts: [
15826
+ "none",
15827
+ "minimal",
15828
+ "low",
15829
+ "medium",
15830
+ "high",
15831
+ "xhigh",
15832
+ "max"
15833
+ ],
15834
+ off: "none"
15835
+ },
15836
+ sampling: {
15837
+ temperature: "any",
15838
+ topP: true,
15839
+ topK: true,
15840
+ penalties: true,
15841
+ logprobs: true
15842
+ },
15843
+ maxTokensParam: "max_tokens",
15844
+ chatTools: {
15845
+ reasoning: "any"
15846
+ },
15847
+ source: "docs",
15848
+ note: "Permissive default for unknown Gemini-wire models."
15849
+ },
15850
+ {
15851
+ id: "other-default",
15852
+ family: "other",
15853
+ match: ".*",
15854
+ reasoning: {
15855
+ style: "effort",
15856
+ efforts: [
15857
+ "none",
15858
+ "minimal",
15859
+ "low",
15860
+ "medium",
15861
+ "high",
15862
+ "xhigh",
15863
+ "max"
15864
+ ],
15865
+ off: "none"
15866
+ },
15867
+ sampling: {
15868
+ temperature: "any",
15869
+ topP: true,
15870
+ topK: true,
15871
+ penalties: true,
15872
+ logprobs: true
15873
+ },
15874
+ maxTokensParam: "max_tokens",
15875
+ chatTools: {
15876
+ reasoning: "any"
15877
+ },
15878
+ source: "docs",
15879
+ note: "Permissive default for model families the SDK has no rules for."
15209
15880
  }
15210
- if (buffer.trim()) {
15211
- const event = parseSSEBlock(buffer);
15212
- if (event) yield event;
15881
+ ]
15882
+ };
15883
+
15884
+ // src/models/rules.ts
15885
+ var MODEL_RULES = model_rules_default;
15886
+ var LADDER = MODEL_RULES.effortLadder;
15887
+ var compiled = /* @__PURE__ */ new Map();
15888
+ function regexFor(rule) {
15889
+ let re = compiled.get(rule.id);
15890
+ if (!re) {
15891
+ re = new RegExp(rule.match, "i");
15892
+ compiled.set(rule.id, re);
15893
+ }
15894
+ return re;
15895
+ }
15896
+ var GATEWAY_PREFIX = /^(?:(?:global|us|eu|apac|jp|au|ca|us-gov)\.)?(?:(?:openai|anthropic|google|meta|amazon|deepseek|qwen|mistral|cohere|ai21)\.)?/;
15897
+ function modelIdCandidates(model) {
15898
+ if (!model) return [];
15899
+ const lower = String(model).trim().toLowerCase();
15900
+ const last = lower.split("/").pop() ?? lower;
15901
+ const out = [];
15902
+ for (const id of [lower, last]) {
15903
+ for (const c of [id, id.replace(GATEWAY_PREFIX, "")]) {
15904
+ if (c && !out.includes(c)) out.push(c);
15213
15905
  }
15214
- } finally {
15215
- reader.cancel().catch(() => void 0);
15216
- reader.releaseLock();
15217
15906
  }
15907
+ return out;
15218
15908
  }
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
- }
15909
+ var OPENAI_WIRE = /* @__PURE__ */ new Set(["openai", "azure", "openai-compatible", "together", "openrouter", "groq", "fireworks", "ollama", "vllm", "litellm", "mistral", "deepseek", "xai", "jev"]);
15910
+ function familiesFor(provider) {
15911
+ const p = (provider ?? "").toLowerCase();
15912
+ if (OPENAI_WIRE.has(p)) return ["openai"];
15913
+ if (p === "anthropic") return ["anthropic"];
15914
+ if (p === "bedrock") return ["anthropic", "openai", "other"];
15915
+ if (p === "vertex" || p === "gemini" || p === "google") return ["gemini", "anthropic"];
15916
+ return ["openai", "anthropic", "gemini", "other"];
15917
+ }
15918
+ function matchRule(family, ids, allowDefault) {
15919
+ for (const rule of MODEL_RULES.rules) {
15920
+ if (rule.family !== family) continue;
15921
+ if (rule.match === ".*") {
15922
+ if (allowDefault) return rule;
15923
+ continue;
15252
15924
  }
15925
+ const re = regexFor(rule);
15926
+ if (ids.some((id) => re.test(id))) return rule;
15253
15927
  }
15254
- if (dataLines.length === 0) return null;
15255
- return { event, data: dataLines.join("\n"), id, retry };
15928
+ return void 0;
15256
15929
  }
15257
-
15258
- // src/providers/utils/reasoning.ts
15259
- var ANTHROPIC_EFFORT_BUDGET = {
15260
- // 0 = off. `applyAnthropicReasoning` bails on a falsy budget and emits no
15261
- // thinking block, which on Anthropic IS off — thinking there is opt-in.
15930
+ function mergeRulePatch(base, patch) {
15931
+ if (!patch) return base;
15932
+ const out = { ...base };
15933
+ if (patch.reasoning) out.reasoning = { ...base.reasoning ?? {}, ...patch.reasoning };
15934
+ if (patch.sampling) out.sampling = { ...base.sampling ?? {}, ...patch.sampling };
15935
+ if (patch.chatTools) out.chatTools = { ...base.chatTools ?? {}, ...patch.chatTools };
15936
+ if (patch.maxTokensParam) out.maxTokensParam = patch.maxTokensParam;
15937
+ if (patch.dropParams) out.dropParams = [.../* @__PURE__ */ new Set([...base.dropParams ?? [], ...patch.dropParams])];
15938
+ return out;
15939
+ }
15940
+ var LEARNED_MAX = 500;
15941
+ var learned = /* @__PURE__ */ new Map();
15942
+ function learnedKey(provider, model, scope) {
15943
+ return `${(provider ?? "").toLowerCase()}@${scope ?? ""}::${(model ?? "").trim().toLowerCase()}`;
15944
+ }
15945
+ function rememberModelRulePatch(provider, model, patch, scope) {
15946
+ const key = learnedKey(provider, model, scope);
15947
+ const merged = mergeRulePatch(learned.get(key)?.patch ?? {}, patch);
15948
+ learned.delete(key);
15949
+ learned.set(key, { provider: provider.toLowerCase(), model: model.trim().toLowerCase(), scope, patch: merged });
15950
+ if (learned.size > LEARNED_MAX) learned.delete(learned.keys().next().value);
15951
+ }
15952
+ function getLearnedModelRulePatch(provider, model, scope) {
15953
+ return learned.get(learnedKey(provider, model, scope))?.patch;
15954
+ }
15955
+ function listLearnedModelRules() {
15956
+ return [...learned.values()].map((e) => ({ ...e }));
15957
+ }
15958
+ function clearLearnedModelRules() {
15959
+ learned.clear();
15960
+ }
15961
+ function resolveModelRule(opts) {
15962
+ const families = familiesFor(opts.provider);
15963
+ const ids = modelIdCandidates(opts.model);
15964
+ let rule;
15965
+ for (const family of families) {
15966
+ rule = matchRule(family, ids, false);
15967
+ if (rule) break;
15968
+ }
15969
+ let isDefault = false;
15970
+ if (!rule) {
15971
+ isDefault = true;
15972
+ rule = matchRule(families[0], ids, true) ?? MODEL_RULES.rules[MODEL_RULES.rules.length - 1];
15973
+ }
15974
+ let effective = rule;
15975
+ const known = opts.model ? learned.get(learnedKey(opts.provider, opts.model, opts.scope))?.patch : void 0;
15976
+ if (known) effective = mergeRulePatch(effective, known);
15977
+ if (opts.patch) effective = mergeRulePatch(effective, opts.patch);
15978
+ return { ...effective, matchedRuleId: rule.id, isDefault, learned: !!known };
15979
+ }
15980
+ function mapReasoningEffort(rule, effort, endpoint = "chat") {
15981
+ const efforts = endpoint === "responses" && rule.reasoning.responsesEfforts || rule.reasoning.efforts;
15982
+ if (rule.reasoning.style === "none" || efforts.length === 0) return { mapped: true };
15983
+ const supported = LADDER.filter((e) => efforts.includes(e));
15984
+ if (supported.includes(effort)) return { effort, mapped: false };
15985
+ if (effort === "none") {
15986
+ const off = rule.reasoning.off;
15987
+ if (off && off !== "none") return { off, mapped: true };
15988
+ const lowest = supported.find((e) => e !== "none");
15989
+ return lowest ? { effort: lowest, mapped: true } : { mapped: true };
15990
+ }
15991
+ const at = LADDER.indexOf(effort);
15992
+ if (at < 0) return { mapped: true };
15993
+ for (let i = at - 1; i >= 1; i--) if (supported.includes(LADDER[i])) return { effort: LADDER[i], mapped: true };
15994
+ for (let i = at + 1; i < LADDER.length; i++) if (supported.includes(LADDER[i])) return { effort: LADDER[i], mapped: true };
15995
+ return supported.includes("none") ? { effort: "none", mapped: true } : { mapped: true };
15996
+ }
15997
+ function allowsSampling(rule, field, value, thinkingOn = false) {
15998
+ const s = rule.sampling;
15999
+ switch (field) {
16000
+ case "temperature":
16001
+ switch (s.temperature) {
16002
+ case "any":
16003
+ return true;
16004
+ case "unsupported":
16005
+ return false;
16006
+ // The default is what the server applies anyway, so it is never sent.
16007
+ case "default-only":
16008
+ return false;
16009
+ case "thinking-requires-1":
16010
+ return !thinkingOn || value === 1;
16011
+ }
16012
+ return true;
16013
+ // Anthropic's thinking restriction covers top_p / top_k as well as temperature.
16014
+ case "topP":
16015
+ return s.topP && !(thinkingOn && s.temperature === "thinking-requires-1");
16016
+ case "topK":
16017
+ return s.topK && !(thinkingOn && s.temperature === "thinking-requires-1");
16018
+ case "presencePenalty":
16019
+ case "frequencyPenalty":
16020
+ return s.penalties;
16021
+ case "logprobs":
16022
+ return s.logprobs;
16023
+ }
16024
+ }
16025
+ function applyDropParams(body, rule) {
16026
+ for (const p of rule.dropParams ?? []) {
16027
+ const dot = p.indexOf(".");
16028
+ if (dot < 0) delete body[p];
16029
+ else if (body[p.slice(0, dot)] && typeof body[p.slice(0, dot)] === "object") delete body[p.slice(0, dot)][p.slice(dot + 1)];
16030
+ }
16031
+ }
16032
+ function effortBudget(effort, table = "anthropic") {
16033
+ if (effort === "none") return 0;
16034
+ return (table === "gemini" ? MODEL_RULES.geminiBudgets : MODEL_RULES.budgets)[effort] ?? 0;
16035
+ }
16036
+
16037
+ // src/providers/utils/reasoning.ts
16038
+ var ANTHROPIC_EFFORT_BUDGET = {
16039
+ // 0 = off. `applyAnthropicReasoning` bails on a falsy budget and emits no
16040
+ // thinking block, which on Anthropic IS off — thinking there is opt-in.
15262
16041
  none: 0,
15263
- minimal: 1024,
15264
- low: 2048,
15265
- medium: 8192,
15266
- high: 16384
16042
+ ...MODEL_RULES.budgets
15267
16043
  };
15268
16044
  var GEMINI_EFFORT_BUDGET = {
15269
16045
  // 0 is Gemini's own documented spelling for "do not think", and unlike
15270
16046
  // Anthropic it has to be SENT — hence `applyGeminiReasoning` testing for null
15271
16047
  // rather than falsiness.
15272
16048
  none: 0,
15273
- minimal: 512,
15274
- low: 1024,
15275
- medium: 4096,
15276
- high: 12e3
16049
+ ...MODEL_RULES.geminiBudgets
15277
16050
  };
15278
16051
  var THINKING_OUTPUT_RESERVE = 1024;
16052
+ var MIN_THINKING_BUDGET = 1024;
15279
16053
  var THINKING_MIN_MAX_TOKENS = 4096;
15280
- function applyOpenAIReasoning(body, reasoning, mode = "legacy_effort") {
16054
+ function applyOpenAIReasoning(body, reasoning, mode = "legacy_effort", model, rule) {
15281
16055
  if (!reasoning) return;
15282
- const effort = normalizeEffort(reasoning.effort);
16056
+ const requested = normalizeEffort(reasoning.effort);
16057
+ const r = rule ?? resolveModelRule({ provider: "openai", model: model ?? body.model });
16058
+ const effort = requested && mapReasoningEffort(r, requested, mode === "responses" ? "responses" : "chat").effort;
15283
16059
  if (mode === "responses") {
15284
16060
  const obj = {};
15285
16061
  if (effort) obj.effort = effort;
@@ -15291,52 +16067,107 @@ function applyOpenAIReasoning(body, reasoning, mode = "legacy_effort") {
15291
16067
  if (reasoning.providerExtras) Object.assign(body, reasoning.providerExtras);
15292
16068
  }
15293
16069
  }
15294
- function applyAnthropicReasoning(body, reasoning) {
15295
- if (!reasoning) return;
15296
- let budget = resolveBudget(reasoning, ANTHROPIC_EFFORT_BUDGET);
15297
- if (!budget) return;
16070
+ function supportsAdaptiveThinking(model) {
16071
+ if (typeof model !== "string") return false;
16072
+ return resolveModelRule({ provider: "anthropic", model }).reasoning.style === "adaptive";
16073
+ }
16074
+ function resolveThinkingForm(model, mode, rule) {
16075
+ if (mode === "adaptive" || mode === "budget") return mode;
16076
+ if (rule) return rule.reasoning.style === "adaptive" ? "adaptive" : "budget";
16077
+ return supportsAdaptiveThinking(model) ? "adaptive" : "budget";
16078
+ }
16079
+ function enforceAnthropicSampling(target, rule, thinkingOn, bedrock = false) {
16080
+ const fields = bedrock ? [["temperature", "temperature"], ["topP", "topP"]] : [["temperature", "temperature"], ["top_p", "topP"], ["top_k", "topK"]];
16081
+ for (const [key, field] of fields) {
16082
+ if (key in target && !allowsSampling(rule, field, target[key], thinkingOn)) delete target[key];
16083
+ }
16084
+ }
16085
+ function applyAdaptiveThinking(reasoning, rule, setThinking, setOutputConfig) {
16086
+ let requested = normalizeEffort(reasoning.effort);
16087
+ if (!requested && typeof reasoning.budgetTokens === "number" && reasoning.budgetTokens > 0) {
16088
+ requested = reasoning.budgetTokens <= ANTHROPIC_EFFORT_BUDGET.low ? "low" : reasoning.budgetTokens <= ANTHROPIC_EFFORT_BUDGET.medium ? "medium" : "high";
16089
+ }
16090
+ if (!requested) return false;
16091
+ const mapped = mapReasoningEffort(rule, requested);
16092
+ if (mapped.off) {
16093
+ setThinking({ type: mapped.off });
16094
+ return false;
16095
+ }
16096
+ if (!mapped.effort || mapped.effort === "none") return false;
16097
+ const thinking = { type: "adaptive" };
16098
+ if (reasoning.includeThoughts !== false) thinking.display = "summarized";
16099
+ if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
16100
+ setThinking(thinking);
16101
+ setOutputConfig({ effort: mapped.effort });
16102
+ return true;
16103
+ }
16104
+ function applyAnthropicReasoning(body, reasoning, opts) {
16105
+ const rule = opts?.rule ?? resolveModelRule({ provider: "anthropic", model: opts?.model ?? body.model });
16106
+ if (!reasoning) {
16107
+ enforceAnthropicSampling(body, rule, false);
16108
+ return;
16109
+ }
16110
+ if (resolveThinkingForm(opts?.model ?? body.model, opts?.mode, rule) === "adaptive") {
16111
+ const on = applyAdaptiveThinking(reasoning, rule, (t) => body.thinking = t, (o) => body.output_config = { ...body.output_config ?? {}, ...o });
16112
+ enforceAnthropicSampling(body, rule, on);
16113
+ return;
16114
+ }
16115
+ let budget = resolveBudget(reasoning, ANTHROPIC_EFFORT_BUDGET, rule);
16116
+ if (!budget) {
16117
+ enforceAnthropicSampling(body, rule, false);
16118
+ return;
16119
+ }
15298
16120
  const currentMax = typeof body.max_tokens === "number" ? body.max_tokens : 0;
15299
16121
  const requiredMax = Math.max(THINKING_MIN_MAX_TOKENS, budget + THINKING_OUTPUT_RESERVE);
15300
16122
  if (currentMax < requiredMax) {
15301
16123
  body.max_tokens = requiredMax;
15302
16124
  }
15303
- budget = clampBudget(budget, body.max_tokens);
16125
+ budget = Math.max(MIN_THINKING_BUDGET, clampBudget(budget, body.max_tokens));
15304
16126
  const thinking = { type: "enabled", budget_tokens: budget };
15305
16127
  if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
15306
16128
  body.thinking = thinking;
15307
- stripSamplingParams(body, ["temperature", "top_p", "top_k"]);
16129
+ enforceAnthropicSampling(body, rule, true);
15308
16130
  }
15309
- function applyBedrockReasoning(body, reasoning) {
15310
- if (!reasoning) return;
15311
- let budget = resolveBudget(reasoning, ANTHROPIC_EFFORT_BUDGET);
15312
- if (!budget) return;
16131
+ function applyBedrockReasoning(body, reasoning, opts) {
16132
+ const rule = opts?.rule ?? resolveModelRule({ provider: "bedrock", model: opts?.model ?? body.modelId });
16133
+ if (!reasoning) {
16134
+ if (body.inferenceConfig) enforceAnthropicSampling(body.inferenceConfig, rule, false, true);
16135
+ return;
16136
+ }
16137
+ if (resolveThinkingForm(opts?.model ?? body.modelId, opts?.mode, rule) === "adaptive") {
16138
+ const fields = () => body.additionalModelRequestFields ?? (body.additionalModelRequestFields = {});
16139
+ const on = applyAdaptiveThinking(reasoning, rule, (t) => fields().thinking = t, (o) => fields().output_config = { ...fields().output_config ?? {}, ...o });
16140
+ if (body.inferenceConfig) enforceAnthropicSampling(body.inferenceConfig, rule, on, true);
16141
+ return;
16142
+ }
16143
+ let budget = resolveBudget(reasoning, ANTHROPIC_EFFORT_BUDGET, rule);
16144
+ if (!budget) {
16145
+ if (body.inferenceConfig) enforceAnthropicSampling(body.inferenceConfig, rule, false, true);
16146
+ return;
16147
+ }
15313
16148
  const inference = body.inferenceConfig ?? (body.inferenceConfig = {});
15314
16149
  const currentMax = typeof inference.maxTokens === "number" ? inference.maxTokens : 0;
15315
16150
  const requiredMax = Math.max(THINKING_MIN_MAX_TOKENS, budget + THINKING_OUTPUT_RESERVE);
15316
16151
  if (currentMax < requiredMax) {
15317
16152
  inference.maxTokens = requiredMax;
15318
16153
  }
15319
- budget = clampBudget(budget, inference.maxTokens);
16154
+ budget = Math.max(MIN_THINKING_BUDGET, clampBudget(budget, inference.maxTokens));
15320
16155
  const extra = body.additionalModelRequestFields ?? (body.additionalModelRequestFields = {});
15321
16156
  const thinking = { type: "enabled", budget_tokens: budget };
15322
16157
  if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
15323
16158
  extra.thinking = thinking;
15324
- stripSamplingParams(inference, ["temperature", "topP"]);
16159
+ enforceAnthropicSampling(inference, rule, true, true);
15325
16160
  }
15326
16161
  function applyGeminiReasoning(generationConfig, reasoning) {
15327
16162
  if (!reasoning) return;
15328
16163
  const budget = resolveBudget(reasoning, GEMINI_EFFORT_BUDGET);
15329
- if (budget == null) return;
15330
- const thinkingConfig = { thinkingBudget: budget };
16164
+ const level = typeof reasoning.thinkingLevel === "string" && reasoning.thinkingLevel ? reasoning.thinkingLevel : void 0;
16165
+ if (budget == null && !level) return;
16166
+ const thinkingConfig = level && budget !== 0 ? { thinkingLevel: level } : { thinkingBudget: budget ?? 0 };
15331
16167
  if (reasoning.includeThoughts) thinkingConfig.includeThoughts = true;
15332
16168
  if (reasoning.providerExtras) Object.assign(thinkingConfig, reasoning.providerExtras);
15333
16169
  generationConfig.thinkingConfig = thinkingConfig;
15334
16170
  }
15335
- function stripSamplingParams(target, keys) {
15336
- for (const k of keys) {
15337
- if (k in target) delete target[k];
15338
- }
15339
- }
15340
16171
  function clampBudget(budget, maxTokens) {
15341
16172
  if (typeof maxTokens !== "number" || maxTokens <= 0) return budget;
15342
16173
  const ceiling = maxTokens - THINKING_OUTPUT_RESERVE;
@@ -15345,19 +16176,292 @@ function clampBudget(budget, maxTokens) {
15345
16176
  }
15346
16177
  function normalizeEffort(effort) {
15347
16178
  if (!effort) return void 0;
15348
- const allowed = ["none", "minimal", "low", "medium", "high"];
16179
+ const allowed = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
15349
16180
  return allowed.includes(effort) ? effort : void 0;
15350
16181
  }
15351
- function resolveBudget(reasoning, effortBudget) {
16182
+ function resolveBudget(reasoning, effortBudget2, rule) {
15352
16183
  const effort = normalizeEffort(reasoning.effort);
15353
16184
  if (effort === "none") return 0;
15354
16185
  if (typeof reasoning.budgetTokens === "number" && reasoning.budgetTokens > 0) {
15355
16186
  return Math.floor(reasoning.budgetTokens);
15356
16187
  }
15357
- if (effort && effortBudget[effort]) return effortBudget[effort];
16188
+ const mapped = effort && rule ? mapReasoningEffort(rule, effort).effort : effort;
16189
+ if (mapped && effortBudget2[mapped]) return effortBudget2[mapped];
15358
16190
  return void 0;
15359
16191
  }
15360
16192
 
16193
+ // src/providers/utils/sse.ts
16194
+ async function* parseSSEStream(body, signal) {
16195
+ const reader = body.getReader();
16196
+ const decoder = new TextDecoder();
16197
+ let buffer = "";
16198
+ try {
16199
+ while (true) {
16200
+ if (signal?.aborted) throw abortReason(signal);
16201
+ const { done, value } = await reader.read();
16202
+ if (done) break;
16203
+ buffer += decoder.decode(value, { stream: true });
16204
+ const parts = buffer.split(/\r?\n\r?\n/);
16205
+ buffer = parts.pop() ?? "";
16206
+ for (const part of parts) {
16207
+ if (!part.trim()) continue;
16208
+ const event = parseSSEBlock(part);
16209
+ if (event) yield event;
16210
+ }
16211
+ }
16212
+ if (buffer.trim()) {
16213
+ const event = parseSSEBlock(buffer);
16214
+ if (event) yield event;
16215
+ }
16216
+ } finally {
16217
+ reader.cancel().catch(() => void 0);
16218
+ reader.releaseLock();
16219
+ }
16220
+ }
16221
+ function parseSSEBlock(block) {
16222
+ let event;
16223
+ let id;
16224
+ let retry;
16225
+ const dataLines = [];
16226
+ for (const line of block.split(/\r?\n/)) {
16227
+ if (line.startsWith(":")) continue;
16228
+ const colonIdx = line.indexOf(":");
16229
+ let field;
16230
+ let value;
16231
+ if (colonIdx === -1) {
16232
+ field = line;
16233
+ value = "";
16234
+ } else {
16235
+ field = line.slice(0, colonIdx);
16236
+ value = line.slice(colonIdx + 1);
16237
+ if (value.startsWith(" ")) value = value.slice(1);
16238
+ }
16239
+ switch (field) {
16240
+ case "event":
16241
+ event = value;
16242
+ break;
16243
+ case "data":
16244
+ dataLines.push(value);
16245
+ break;
16246
+ case "id":
16247
+ id = value;
16248
+ break;
16249
+ case "retry": {
16250
+ const n = parseInt(value, 10);
16251
+ if (!isNaN(n)) retry = n;
16252
+ break;
16253
+ }
16254
+ }
16255
+ }
16256
+ if (dataLines.length === 0) return null;
16257
+ return { event, data: dataLines.join("\n"), id, retry };
16258
+ }
16259
+
16260
+ // src/models/errors.ts
16261
+ var ModelRequestError = class extends ProviderError {
16262
+ constructor(init) {
16263
+ super(init.message, init.provider, init.status ?? 400, init.responseBody);
16264
+ this.status = init.status ?? 400;
16265
+ this.code = init.code;
16266
+ this.param = init.param;
16267
+ this.value = init.value;
16268
+ this.supportedValues = init.supportedValues;
16269
+ this.model = init.model;
16270
+ }
16271
+ };
16272
+
16273
+ // src/models/adjust.ts
16274
+ var MAX_ADJUSTMENTS = 3;
16275
+ var Q = "['\"`]";
16276
+ function errorMessage(text) {
16277
+ try {
16278
+ const j = JSON.parse(text);
16279
+ const e = j?.error ?? j;
16280
+ const message = typeof e?.message === "string" ? e.message : typeof j?.message === "string" ? j.message : text;
16281
+ return { message, param: typeof e?.param === "string" ? e.param : void 0, code: typeof e?.code === "string" ? e.code : void 0 };
16282
+ } catch {
16283
+ return { message: text };
16284
+ }
16285
+ }
16286
+ function quotedList(s) {
16287
+ const quoted = [...s.matchAll(new RegExp(`${Q}([^'"\`]+)${Q}`, "g"))].map((m) => m[1]);
16288
+ if (quoted.length > 0) return quoted;
16289
+ return s.split(/[,\s]+/).map((x) => x.replace(/[.\s]+$/, "")).filter(Boolean);
16290
+ }
16291
+ function parseModelError(text) {
16292
+ const { message, param: errParam } = errorMessage(String(text ?? ""));
16293
+ const m = message.toLowerCase();
16294
+ if (m.includes("function tools with reasoning_effort") && m.includes("not supported")) {
16295
+ return { kind: "chat_tools_effort", message };
16296
+ }
16297
+ if (m.includes("between_tools") && m.includes("thinking")) return { kind: "thinking_off_between_tools", message };
16298
+ if (m.includes("thinking.type.enabled") && m.includes("not supported")) return { kind: "thinking_enabled_unsupported", message };
16299
+ if (m.includes("thinking.type.adaptive") && m.includes("not supported")) return { kind: "thinking_adaptive_unsupported", message };
16300
+ if (m.includes("thinking.type.disabled") && m.includes("not supported")) return { kind: "thinking_disabled_unsupported", message };
16301
+ const tt = new RegExp(`${Q}?(temperature|top_p|top_k)${Q}?\\s+may only be set to 1 when thinking is enabled`, "i").exec(message);
16302
+ if (tt) return { kind: "temperature_thinking", param: tt[1].toLowerCase(), message };
16303
+ const dep = new RegExp(`${Q}([\\w.]+)${Q}\\s+is deprecated for this model`, "i").exec(message);
16304
+ if (dep) return { kind: "unsupported_parameter", param: dep[1], message };
16305
+ const rename = new RegExp(`use ${Q}(max_completion_tokens|max_tokens)${Q} instead`, "i").exec(message);
16306
+ if (rename) return { kind: "max_tokens_rename", renameTo: rename[1].toLowerCase(), param: rename[1].toLowerCase() === "max_tokens" ? "max_completion_tokens" : "max_tokens", message };
16307
+ const val = new RegExp(`unsupported value:?\\s*${Q}?([\\w.]+)${Q}?\\s+does not support\\s+(${Q}[^'"\`]*${Q}|[^\\s]+)`, "i").exec(message);
16308
+ if (val) {
16309
+ const param = val[1];
16310
+ const value = val[2].replace(/^['"`]|['"`]$/g, "");
16311
+ if (m.includes("only the default") && m.includes("value is supported")) return { kind: "default_only", param, value, message };
16312
+ const sup = /supported values? (?:are|is):?\s*([^\n]*)/i.exec(message);
16313
+ return { kind: "unsupported_value", param, value, supportedValues: sup ? quotedList(sup[1]) : void 0, message };
16314
+ }
16315
+ if (m.includes("only the default") && m.includes("value is supported")) {
16316
+ return { kind: "default_only", param: errParam ?? "temperature", message };
16317
+ }
16318
+ const prm = new RegExp(`unsupported parameter:?\\s*${Q}?([\\w.]+)${Q}?`, "i").exec(message);
16319
+ if (prm) return { kind: "unsupported_parameter", param: prm[1], message };
16320
+ return void 0;
16321
+ }
16322
+ var normParam = (p) => p.toLowerCase().replace(/[-\s]/g, "_");
16323
+ var LADDER2 = new Set(MODEL_RULES.effortLadder);
16324
+ function patchFromParsedError(parsed, endpoint = "chat") {
16325
+ const param = parsed.param ? normParam(parsed.param) : "";
16326
+ switch (parsed.kind) {
16327
+ case "chat_tools_effort":
16328
+ return { patch: { chatTools: { reasoning: "force-none" } }, adjustment: "chat-tools:force-effort-none" };
16329
+ case "max_tokens_rename":
16330
+ return { patch: { maxTokensParam: parsed.renameTo }, adjustment: `${parsed.param}->${parsed.renameTo}` };
16331
+ case "thinking_enabled_unsupported":
16332
+ return { patch: { reasoning: { style: "adaptive" } }, adjustment: "thinking:adaptive" };
16333
+ case "thinking_adaptive_unsupported":
16334
+ return { patch: { reasoning: { style: "budget" } }, adjustment: "thinking:budget" };
16335
+ case "thinking_off_between_tools":
16336
+ return { patch: { reasoning: { off: "between_tools" } }, adjustment: "thinking-off:between_tools" };
16337
+ case "thinking_disabled_unsupported":
16338
+ return { patch: { reasoning: { off: null } }, adjustment: "thinking-off:none" };
16339
+ case "temperature_thinking":
16340
+ return { patch: { sampling: { temperature: "unsupported" } }, adjustment: "drop:temperature" };
16341
+ case "default_only":
16342
+ if (param === "temperature") return { patch: { sampling: { temperature: "default-only" } }, adjustment: "temperature:default-only" };
16343
+ return { patch: { dropParams: [parsed.param ?? param] }, adjustment: `drop:${parsed.param ?? param}` };
16344
+ case "unsupported_value": {
16345
+ if (/^(reasoning_effort|reasoning\.effort|effort|output_config\.effort)$/.test(param)) {
16346
+ const list = (parsed.supportedValues ?? []).map((v) => v.toLowerCase()).filter((v) => LADDER2.has(v));
16347
+ if (list.length === 0) return void 0;
16348
+ const reasoning = endpoint === "responses" ? { responsesEfforts: list } : { efforts: list };
16349
+ return { patch: { reasoning }, adjustment: `efforts:${list.join("|")}` };
16350
+ }
16351
+ return paramDropPatch(param, parsed);
16352
+ }
16353
+ case "unsupported_parameter":
16354
+ return paramDropPatch(param, parsed);
16355
+ }
16356
+ }
16357
+ function paramDropPatch(param, parsed) {
16358
+ if (!param) return void 0;
16359
+ const label = `drop:${parsed.param ?? param}`;
16360
+ switch (param) {
16361
+ case "temperature":
16362
+ return { patch: { sampling: { temperature: "unsupported" } }, adjustment: label };
16363
+ case "top_p":
16364
+ return { patch: { sampling: { topP: false } }, adjustment: label };
16365
+ case "top_k":
16366
+ return { patch: { sampling: { topK: false } }, adjustment: label };
16367
+ case "presence_penalty":
16368
+ case "frequency_penalty":
16369
+ return { patch: { sampling: { penalties: false } }, adjustment: label };
16370
+ case "logprobs":
16371
+ case "top_logprobs":
16372
+ return { patch: { sampling: { logprobs: false } }, adjustment: label };
16373
+ case "max_tokens":
16374
+ return { patch: { maxTokensParam: "max_completion_tokens" }, adjustment: "max_tokens->max_completion_tokens" };
16375
+ case "max_completion_tokens":
16376
+ return { patch: { maxTokensParam: "max_tokens" }, adjustment: "max_completion_tokens->max_tokens" };
16377
+ case "reasoning_effort":
16378
+ case "reasoning.effort":
16379
+ case "reasoning":
16380
+ return { patch: { reasoning: { style: "none", efforts: [], responsesEfforts: [] }, dropParams: [param] }, adjustment: label };
16381
+ default:
16382
+ return { patch: { dropParams: [parsed.param ?? param] }, adjustment: label };
16383
+ }
16384
+ }
16385
+ function codeFor(parsed) {
16386
+ if (!parsed) return "model_request_invalid";
16387
+ if (parsed.kind === "unsupported_value" || parsed.kind === "default_only") return "unsupported_value";
16388
+ if (parsed.kind === "unsupported_parameter" || parsed.kind === "max_tokens_rename" || parsed.kind === "temperature_thinking") return "unsupported_parameter";
16389
+ return "model_request_invalid";
16390
+ }
16391
+ function toModelRequestError(err, parsed, provider, model) {
16392
+ return new ModelRequestError({
16393
+ status: err.statusCode ?? 400,
16394
+ code: codeFor(parsed),
16395
+ param: parsed?.param,
16396
+ value: parsed?.value,
16397
+ supportedValues: parsed?.supportedValues,
16398
+ provider: err.provider || provider,
16399
+ model,
16400
+ message: err.message,
16401
+ responseBody: err.responseBody
16402
+ });
16403
+ }
16404
+ var SameRequest = class extends Error {
16405
+ };
16406
+ function withoutCallerParams(step, callerParams) {
16407
+ const drops = step.patch.dropParams;
16408
+ if (!callerParams?.length || !drops) return step;
16409
+ const mine = (p) => callerParams.includes(p.split(".")[0]);
16410
+ if (!drops.some(mine)) return step;
16411
+ const { dropParams: _ignored, ...rest } = step.patch;
16412
+ const kept = drops.filter((p) => !mine(p));
16413
+ const patch = kept.length ? { ...rest, dropParams: kept } : rest;
16414
+ return Object.keys(patch).length ? { ...step, patch } : void 0;
16415
+ }
16416
+ async function runWithModelAdjustments(opts) {
16417
+ let overlay = {};
16418
+ const steps = [];
16419
+ let previous;
16420
+ let lastError;
16421
+ let lastParsed;
16422
+ for (; ; ) {
16423
+ const guard = (body) => {
16424
+ let json;
16425
+ try {
16426
+ json = JSON.stringify(body);
16427
+ } catch {
16428
+ return;
16429
+ }
16430
+ if (previous !== void 0 && json === previous) throw new SameRequest();
16431
+ previous = json;
16432
+ };
16433
+ try {
16434
+ const result = await opts.run(overlay, guard);
16435
+ for (const step of steps) {
16436
+ const s = withoutCallerParams(step, opts.callerParams);
16437
+ if (!s) continue;
16438
+ rememberModelRulePatch(opts.provider, opts.model, s.patch, opts.scope);
16439
+ if (opts.onLearned) {
16440
+ try {
16441
+ opts.onLearned({ provider: opts.provider, model: opts.model, adjustment: s.adjustment, patch: s.patch, reason: s.reason });
16442
+ } catch {
16443
+ }
16444
+ }
16445
+ }
16446
+ return result;
16447
+ } catch (err) {
16448
+ if (err instanceof SameRequest) throw toModelRequestError(lastError, lastParsed, opts.provider, opts.model);
16449
+ if (!(err instanceof ProviderError) || err instanceof ModelRequestError || err.statusCode !== 400) throw err;
16450
+ const parsed = parseModelError(String(err.responseBody ?? err.message));
16451
+ const fix = parsed ? patchFromParsedError(parsed, opts.endpoint) : void 0;
16452
+ lastError = err;
16453
+ lastParsed = parsed;
16454
+ if (!fix || steps.length >= MAX_ADJUSTMENTS) throw toModelRequestError(err, parsed, opts.provider, opts.model);
16455
+ overlay = mergeRulePatch(overlay, fix.patch);
16456
+ steps.push({ ...fix, reason: parsed.message });
16457
+ }
16458
+ }
16459
+ }
16460
+ function asModelRequestError(err, provider, model) {
16461
+ if (!(err instanceof ProviderError) || err instanceof ModelRequestError || err.statusCode !== 400) return err;
16462
+ return toModelRequestError(err, parseModelError(String(err.responseBody ?? err.message)), provider, model);
16463
+ }
16464
+
15361
16465
  // src/providers/utils/media.ts
15362
16466
  var EXTENSION_MIME = {
15363
16467
  jpg: "image/jpeg",
@@ -15549,11 +16653,64 @@ function isObject2(value) {
15549
16653
  return typeof value === "object" && value !== null && !Array.isArray(value);
15550
16654
  }
15551
16655
 
16656
+ // src/providers/utils/embeddings.ts
16657
+ function chunkArray(items, size) {
16658
+ const n = Math.max(1, Math.floor(size));
16659
+ const out = [];
16660
+ for (let i = 0; i < items.length; i += n) out.push(items.slice(i, i + n));
16661
+ return out;
16662
+ }
16663
+ async function mapWithConcurrency(items, limit, fn, signal) {
16664
+ const results = new Array(items.length);
16665
+ let next = 0;
16666
+ let failure;
16667
+ let failed = false;
16668
+ const worker = async () => {
16669
+ while (!failed) {
16670
+ if (signal?.aborted) throw signal.reason ?? new Error("aborted");
16671
+ const i = next++;
16672
+ if (i >= items.length) return;
16673
+ try {
16674
+ results[i] = await fn(items[i], i);
16675
+ } catch (err) {
16676
+ failed = true;
16677
+ failure = err;
16678
+ return;
16679
+ }
16680
+ }
16681
+ };
16682
+ await Promise.all(Array.from({ length: Math.min(Math.max(1, limit), items.length) }, worker));
16683
+ if (failed) throw failure;
16684
+ return results;
16685
+ }
16686
+ function decodeBase64Vector(b64) {
16687
+ const buf = Buffer.from(b64, "base64");
16688
+ const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
16689
+ const out = new Array(Math.floor(buf.byteLength / 4));
16690
+ for (let i = 0; i < out.length; i++) out[i] = view.getFloat32(i * 4, true);
16691
+ return out;
16692
+ }
16693
+ function assertVectorCount(provider, vectors, expected) {
16694
+ if (vectors.length !== expected) {
16695
+ throw new ProviderError(
16696
+ `${provider} embeddings returned ${vectors.length} vectors for ${expected} inputs`,
16697
+ provider,
16698
+ 502
16699
+ );
16700
+ }
16701
+ }
16702
+ function requireEmbeddingModel(provider, model) {
16703
+ if (!model) throw new ProviderError(`${provider} embeddings require a model`, provider, 400);
16704
+ return model;
16705
+ }
16706
+
15552
16707
  // src/providers/openai.ts
15553
- var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
16708
+ var OpenAIProvider = class extends BaseProvider {
15554
16709
  constructor(config) {
15555
16710
  super();
15556
16711
  this.providerName = "openai";
16712
+ this.configureTransport(config);
16713
+ this.maxTokensParam = config.maxTokensParam ?? "auto";
15557
16714
  this.apiKey = config.apiKey;
15558
16715
  this.baseURL = (config.baseURL ?? "https://api.openai.com/v1").replace(/\/$/, "");
15559
16716
  this.defaultModel = config.defaultModel ?? "gpt-4o";
@@ -15561,10 +16718,14 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15561
16718
  ...config.defaultHeaders,
15562
16719
  ...config.organization ? { "OpenAI-Organization": config.organization } : {}
15563
16720
  };
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;
16721
+ const legacy = config.responsesApi;
16722
+ const explicit = config.api === "chat" || config.api === "responses" ? config.api : void 0;
16723
+ this.responsesApi = explicit === "responses" ? "always" : explicit === "chat" ? "never" : legacy ?? (config._autoMeansChat ? "never" : "auto");
16724
+ this.apiExplicitChat = explicit === "chat" || explicit === void 0 && legacy === "never";
16725
+ this.apiExplicitResponses = this.responsesApi === "always";
16726
+ this.apiResponsesNative = explicit === "responses";
16727
+ this.responsesApiModels = [...config.responsesApiModels ?? []];
16728
+ this.responsesStreaming = config.responsesStreaming ?? this.apiResponsesNative;
15568
16729
  if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
15569
16730
  }
15570
16731
  /** True when the caller listed this request's model in `responsesApiModels`. */
@@ -15590,9 +16751,10 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15590
16751
  * A model listed in `responsesApiModels` is the caller stating the answer
15591
16752
  * for that model alone; `never` still overrides it. */
15592
16753
  useResponsesApi(request) {
15593
- if (this.responsesApi === "never") return false;
15594
- if (this.responsesApi === "always") return true;
16754
+ if (this.apiExplicitChat) return false;
16755
+ if (this.apiExplicitResponses) return true;
15595
16756
  if (this.isResponsesApiModel(request)) return true;
16757
+ if (this.responsesApi !== "auto") return false;
15596
16758
  if (!request.reasoning) return false;
15597
16759
  return isReasoningModel(request.model || this.defaultModel);
15598
16760
  }
@@ -15600,14 +16762,13 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15600
16762
  if (this.useResponsesApi(request)) {
15601
16763
  return this.completeResponses(request);
15602
16764
  }
15603
- const body = this.buildRequestBody(request, false);
15604
- const res = await this.doFetch(body, request.signal);
16765
+ const res = await this.fetchChat(request, false);
15605
16766
  const json = await res.json();
15606
16767
  return this.parseResponse(json);
15607
16768
  }
15608
16769
  async *completeStream(request) {
15609
16770
  if (this.useResponsesApi(request)) {
15610
- if (this.responsesStreaming && this.isResponsesApiModel(request)) {
16771
+ if (this.responsesStreaming && (this.apiResponsesNative || this.isResponsesApiModel(request))) {
15611
16772
  yield* this.streamResponses(request);
15612
16773
  return;
15613
16774
  }
@@ -15625,8 +16786,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15625
16786
  };
15626
16787
  return;
15627
16788
  }
15628
- const body = this.buildRequestBody(request, true);
15629
- const res = await this.doFetch(body, request.signal);
16789
+ const res = await this.fetchChat(request, true);
15630
16790
  if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
15631
16791
  let id = "";
15632
16792
  let model = "";
@@ -15657,9 +16817,14 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15657
16817
  delta: {},
15658
16818
  finishReason: mapFinishReason(choice.finish_reason)
15659
16819
  };
16820
+ if (typeof choice.finish_reason === "string") out.rawFinishReason = choice.finish_reason;
16821
+ const fields = pickProviderFields(chunk, delta, choice);
16822
+ if (fields) out.providerFields = fields;
15660
16823
  if (delta.content) {
15661
16824
  out.delta.content = delta.content;
15662
16825
  }
16826
+ const reasoningDelta = extractReasoningText(delta);
16827
+ if (reasoningDelta) out.delta.reasoning = { summary: reasoningDelta };
15663
16828
  if (delta.tool_calls) {
15664
16829
  const partials = [];
15665
16830
  for (const tc of delta.tool_calls) {
@@ -15671,7 +16836,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15671
16836
  if (tc.id) buf.id = tc.id;
15672
16837
  if (tc.function?.name) buf.name = tc.function.name;
15673
16838
  if (tc.function?.arguments) buf.arguments += tc.function.arguments;
15674
- partials.push({ id: buf.id, name: buf.name || void 0, arguments: tc.function?.arguments });
16839
+ partials.push({ id: buf.id, name: buf.name || void 0, arguments: tc.function?.arguments, index: idx });
15675
16840
  }
15676
16841
  out.delta.toolCalls = partials;
15677
16842
  }
@@ -15679,22 +16844,115 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15679
16844
  yield out;
15680
16845
  }
15681
16846
  }
16847
+ // ─── Embeddings ──────────────────────────────────────────────────────────
16848
+ /** POST /embeddings, batched (default 2048 inputs per request, the OpenAI cap). */
16849
+ async embed(request) {
16850
+ const model = requireEmbeddingModel(this.providerName, request.model);
16851
+ const batches = chunkArray(request.input, request.batchSize ?? 2048);
16852
+ const vectors = [];
16853
+ const raw = [];
16854
+ let inputTokens = 0;
16855
+ let totalTokens = 0;
16856
+ let responseModel = model;
16857
+ for (const batch of batches) {
16858
+ const body = { model, input: batch };
16859
+ if (request.dimensions != null) body.dimensions = request.dimensions;
16860
+ if (request.encodingFormat) body.encoding_format = request.encodingFormat;
16861
+ if (request.extra) Object.assign(body, request.extra);
16862
+ const res = await this.embeddingsFetch(body, request.signal);
16863
+ const json = await res.json();
16864
+ raw.push(json);
16865
+ const data = Array.isArray(json.data) ? [...json.data] : [];
16866
+ data.sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
16867
+ for (const d of data) {
16868
+ vectors.push(typeof d.embedding === "string" ? decodeBase64Vector(d.embedding) : d.embedding);
16869
+ }
16870
+ inputTokens += json.usage?.prompt_tokens ?? 0;
16871
+ totalTokens += json.usage?.total_tokens ?? json.usage?.prompt_tokens ?? 0;
16872
+ responseModel = json.model ?? responseModel;
16873
+ }
16874
+ assertVectorCount(this.providerName, vectors, request.input.length);
16875
+ return { model: responseModel, vectors, usage: { inputTokens, totalTokens }, raw };
16876
+ }
16877
+ async embeddingsFetch(body, signal) {
16878
+ const url = `${this.baseURL}/embeddings`;
16879
+ const headers = {
16880
+ "Content-Type": "application/json",
16881
+ Authorization: `Bearer ${this.apiKey}`,
16882
+ ...this.defaultHeaders
16883
+ };
16884
+ const payload = JSON.stringify(body);
16885
+ const res = await this.doFetchWithRetry(() => this.httpFetch(url, { method: "POST", headers, body: payload, signal }), signal);
16886
+ if (!res.ok) {
16887
+ const text = await res.text().catch(() => "");
16888
+ throw new ProviderError(`OpenAI embeddings API error ${res.status}: ${text}`, this.providerName, res.status, text);
16889
+ }
16890
+ return res;
16891
+ }
15682
16892
  // ─── Internals ───────────────────────────────────────────────────────────
15683
- buildRequestBody(request, stream) {
16893
+ /** Chat Completions call that corrects itself from the provider's own 400s (see models/adjust.ts). */
16894
+ fetchChat(request, stream) {
16895
+ const model = request.model || this.defaultModel;
16896
+ return runWithModelAdjustments({
16897
+ provider: this.providerName,
16898
+ model,
16899
+ endpoint: "chat",
16900
+ scope: this.baseURL,
16901
+ callerParams: request.extra ? Object.keys(request.extra) : void 0,
16902
+ onLearned: this.onModelRuleLearned,
16903
+ run: (overlay, guard) => {
16904
+ const body = this.buildRequestBody(request, stream, overlay);
16905
+ guard(body);
16906
+ return this.doFetch(body, request.signal);
16907
+ }
16908
+ });
16909
+ }
16910
+ /** Same for the Responses API. */
16911
+ fetchResponses(request, adaptExtras, stream = false) {
16912
+ const model = request.model || this.defaultModel;
16913
+ return runWithModelAdjustments({
16914
+ provider: this.providerName,
16915
+ model,
16916
+ endpoint: "responses",
16917
+ scope: this.baseURL,
16918
+ callerParams: request.extra ? Object.keys(request.extra) : void 0,
16919
+ onLearned: this.onModelRuleLearned,
16920
+ run: (overlay, guard) => {
16921
+ const body = this.buildResponsesBody(request, overlay);
16922
+ if (adaptExtras) adaptChatExtrasForResponses(body, request.extra);
16923
+ if (stream) body.stream = true;
16924
+ guard(body);
16925
+ return this.responsesFetch(body, request.signal);
16926
+ }
16927
+ });
16928
+ }
16929
+ buildRequestBody(request, stream, overlay) {
15684
16930
  const body = {
15685
16931
  model: request.model || this.defaultModel,
15686
16932
  messages: request.messages.map((m) => this.toOpenAIMessage(m)),
15687
16933
  stream
15688
16934
  };
15689
- if (stream) {
16935
+ if (stream && request.streamUsage !== false) {
15690
16936
  body.stream_options = { include_usage: true };
15691
16937
  }
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;
16938
+ const modelId = String(body.model);
16939
+ const rule = resolveModelRule({ provider: this.providerName, model: modelId, patch: overlay, scope: this.baseURL });
16940
+ if (request.temperature != null && allowsSampling(rule, "temperature", request.temperature)) body.temperature = request.temperature;
16941
+ if (request.maxTokens != null) {
16942
+ const param = this.maxTokensParam === "auto" ? rule.maxTokensParam : this.maxTokensParam;
16943
+ body[param] = request.maxTokens;
16944
+ }
16945
+ if (request.topP != null && allowsSampling(rule, "topP")) body.top_p = request.topP;
15695
16946
  if (request.stop) body.stop = request.stop;
16947
+ if (request.presencePenalty != null && allowsSampling(rule, "presencePenalty")) body.presence_penalty = request.presencePenalty;
16948
+ if (request.frequencyPenalty != null && allowsSampling(rule, "frequencyPenalty")) body.frequency_penalty = request.frequencyPenalty;
16949
+ if (request.seed != null) body.seed = request.seed;
16950
+ if (request.n != null) body.n = request.n;
16951
+ if (request.logprobs != null && allowsSampling(rule, "logprobs")) body.logprobs = request.logprobs;
16952
+ if (request.topLogprobs != null && allowsSampling(rule, "logprobs")) body.top_logprobs = request.topLogprobs;
15696
16953
  if (request.tools?.length) {
15697
16954
  body.tools = request.tools.map((t) => this.toOpenAITool(t));
16955
+ if (request.parallelToolCalls != null) body.parallel_tool_calls = request.parallelToolCalls;
15698
16956
  }
15699
16957
  if (request.toolChoice != null && request.tools?.length) {
15700
16958
  if (typeof request.toolChoice === "string") {
@@ -15720,14 +16978,17 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15720
16978
  if (request.extra) {
15721
16979
  Object.assign(body, request.extra);
15722
16980
  }
15723
- applyOpenAIReasoning(body, request.reasoning, "legacy_effort");
16981
+ applyOpenAIReasoning(body, request.reasoning, "legacy_effort", modelId, rule);
16982
+ if (body.tools?.length && rule.chatTools.reasoning === "force-none" && rule.reasoning.style !== "none") {
16983
+ body.reasoning_effort = "none";
16984
+ }
16985
+ applyDropParams(body, rule);
15724
16986
  return body;
15725
16987
  }
15726
16988
  // ─── Responses API (reasoning models) ──────────────────────────────────────
15727
16989
  async completeResponses(request) {
15728
- const body = this.buildResponsesBody(request);
15729
- if (this.isResponsesApiModel(request)) adaptChatExtrasForResponses(body, request.extra);
15730
- const res = await this.responsesFetch(body, request.signal);
16990
+ const adapt = this.apiResponsesNative || this.isResponsesApiModel(request);
16991
+ const res = await this.fetchResponses(request, adapt);
15731
16992
  const json = await res.json();
15732
16993
  return this.parseResponsesResponse(json, request.model || this.defaultModel);
15733
16994
  }
@@ -15740,15 +17001,13 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15740
17001
  * reads it). */
15741
17002
  async *streamResponses(request) {
15742
17003
  const modelId = request.model || this.defaultModel;
15743
- const body = this.buildResponsesBody(request);
15744
- adaptChatExtrasForResponses(body, request.extra);
15745
- body.stream = true;
15746
- const res = await this.responsesFetch(body, request.signal);
17004
+ const res = await this.fetchResponses(request, true, true);
15747
17005
  if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
15748
17006
  let id = "";
15749
17007
  let model = modelId;
15750
17008
  const calls = /* @__PURE__ */ new Map();
15751
17009
  let final;
17010
+ let streamedSummary = false;
15752
17011
  for await (const event of parseSSEStream(res.body, request.signal)) {
15753
17012
  if (event.data === "[DONE]") break;
15754
17013
  let data;
@@ -15766,19 +17025,28 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15766
17025
  case "response.output_text.delta":
15767
17026
  if (data.delta) yield { id, model, delta: { content: data.delta } };
15768
17027
  break;
17028
+ case "response.reasoning_summary_part.added":
17029
+ if (streamedSummary) yield { id, model, delta: { reasoning: { summary: "\n\n" } } };
17030
+ break;
17031
+ case "response.reasoning_summary_text.delta":
17032
+ if (typeof data.delta === "string" && data.delta) {
17033
+ streamedSummary = true;
17034
+ yield { id, model, delta: { reasoning: { summary: data.delta } } };
17035
+ }
17036
+ break;
15769
17037
  case "response.output_item.added": {
15770
17038
  const item = data.item;
15771
17039
  if (item?.type !== "function_call") break;
15772
- const call = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: item.arguments ?? "" };
17040
+ const call = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: item.arguments ?? "", index: calls.size };
15773
17041
  calls.set(item.id ?? call.callId, call);
15774
- yield { id, model, delta: { toolCalls: [{ id: call.callId, name: call.name, arguments: call.arguments }] } };
17042
+ yield { id, model, delta: { toolCalls: [{ id: call.callId, name: call.name, arguments: call.arguments, index: call.index }] } };
15775
17043
  break;
15776
17044
  }
15777
17045
  case "response.function_call_arguments.delta": {
15778
17046
  const call = calls.get(data.item_id);
15779
17047
  if (!call || !data.delta) break;
15780
17048
  call.arguments += data.delta;
15781
- yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: data.delta }] } };
17049
+ yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: data.delta, index: call.index }] } };
15782
17050
  break;
15783
17051
  }
15784
17052
  case "response.output_item.done": {
@@ -15787,13 +17055,13 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15787
17055
  const full = typeof item.arguments === "string" ? item.arguments : "";
15788
17056
  const call = calls.get(item.id ?? item.call_id);
15789
17057
  if (!call) {
15790
- const added = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: full };
17058
+ const added = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: full, index: calls.size };
15791
17059
  calls.set(item.id ?? added.callId, added);
15792
- yield { id, model, delta: { toolCalls: [{ id: added.callId, name: added.name, arguments: full }] } };
17060
+ yield { id, model, delta: { toolCalls: [{ id: added.callId, name: added.name, arguments: full, index: added.index }] } };
15793
17061
  } else if (full.length > call.arguments.length && full.startsWith(call.arguments)) {
15794
17062
  const rest = full.slice(call.arguments.length);
15795
17063
  call.arguments = full;
15796
- yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: rest }] } };
17064
+ yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: rest, index: call.index }] } };
15797
17065
  }
15798
17066
  break;
15799
17067
  }
@@ -15816,18 +17084,20 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15816
17084
  yield {
15817
17085
  id: final?.id || id,
15818
17086
  model: final?.model || model,
15819
- delta: final?.reasoning ? { reasoning: final.reasoning } : {},
17087
+ // Summary text already went out as deltas; do not repeat it.
17088
+ delta: final?.reasoning && !streamedSummary ? { reasoning: final.reasoning } : {},
15820
17089
  usage: final?.usage,
15821
17090
  finishReason: final?.finishReason ?? (calls.size > 0 ? "tool_calls" : "stop")
15822
17091
  };
15823
17092
  }
15824
- buildResponsesBody(request) {
17093
+ buildResponsesBody(request, overlay) {
15825
17094
  const body = {
15826
17095
  model: request.model || this.defaultModel,
15827
17096
  input: request.messages.map((m) => this.toResponsesInput(m))
15828
17097
  };
15829
17098
  if (request.maxTokens != null) body.max_output_tokens = request.maxTokens;
15830
- if (request.topP != null) body.top_p = request.topP;
17099
+ const rule = resolveModelRule({ provider: this.providerName, model: String(body.model), patch: overlay, scope: this.baseURL });
17100
+ if (request.topP != null && allowsSampling(rule, "topP")) body.top_p = request.topP;
15831
17101
  if (request.tools?.length) {
15832
17102
  body.tools = request.tools.map((t) => ({
15833
17103
  type: "function",
@@ -15859,7 +17129,8 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15859
17129
  }
15860
17130
  }
15861
17131
  if (request.extra) Object.assign(body, request.extra);
15862
- applyOpenAIReasoning(body, request.reasoning, "responses");
17132
+ applyOpenAIReasoning(body, request.reasoning, "responses", String(body.model), rule);
17133
+ applyDropParams(body, rule);
15863
17134
  return body;
15864
17135
  }
15865
17136
  /** Maps a unified message to a Responses API `input` item. */
@@ -15926,7 +17197,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15926
17197
  ...this.defaultHeaders
15927
17198
  };
15928
17199
  const payload = JSON.stringify(body);
15929
- const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload, signal }), signal);
17200
+ const res = await this.doFetchWithRetry(() => this.httpFetch(url, { method: "POST", headers, body: payload, signal }), signal);
15930
17201
  if (!res.ok) {
15931
17202
  const text = await res.text().catch(() => "");
15932
17203
  throw new ProviderError(
@@ -16055,7 +17326,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
16055
17326
  ...this.defaultHeaders
16056
17327
  };
16057
17328
  const payload = JSON.stringify(body);
16058
- const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload, signal }), signal);
17329
+ const res = await this.doFetchWithRetry(() => this.httpFetch(url, { method: "POST", headers, body: payload, signal }), signal);
16059
17330
  if (!res.ok) {
16060
17331
  const text = await res.text().catch(() => "");
16061
17332
  throw new ProviderError(
@@ -16075,6 +17346,8 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
16075
17346
  name: tc.function?.name ?? "",
16076
17347
  arguments: tc.function?.arguments ?? "{}"
16077
17348
  }));
17349
+ const reasoningText = extractReasoningText(message);
17350
+ const providerFields = pickProviderFields(json, message, choice);
16078
17351
  return {
16079
17352
  id: json.id ?? "",
16080
17353
  model: json.model ?? "",
@@ -16082,6 +17355,9 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
16082
17355
  toolCalls,
16083
17356
  usage: this.extractUsage(json.usage),
16084
17357
  finishReason: mapFinishReason(choice?.finish_reason),
17358
+ ...typeof choice?.finish_reason === "string" ? { rawFinishReason: choice.finish_reason } : {},
17359
+ ...providerFields ? { providerFields } : {},
17360
+ ...reasoningText ? { reasoning: { summary: reasoningText } } : {},
16085
17361
  raw: json
16086
17362
  };
16087
17363
  }
@@ -16100,10 +17376,27 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
16100
17376
  };
16101
17377
  }
16102
17378
  };
17379
+ function extractReasoningText(src) {
17380
+ if (!src || typeof src !== "object") return void 0;
17381
+ for (const key of ["reasoning_content", "reasoning"]) {
17382
+ const v = src[key];
17383
+ if (typeof v === "string" && v.length > 0) return v;
17384
+ }
17385
+ return void 0;
17386
+ }
17387
+ function pickProviderFields(top, message, choice) {
17388
+ const out = {};
17389
+ if (top?.system_fingerprint != null) out.system_fingerprint = top.system_fingerprint;
17390
+ if (top?.service_tier != null) out.service_tier = top.service_tier;
17391
+ if (message?.annotations != null) out.annotations = message.annotations;
17392
+ if (message?.refusal != null) out.refusal = message.refusal;
17393
+ if (choice?.logprobs != null) out.logprobs = choice.logprobs;
17394
+ return Object.keys(out).length > 0 ? out : void 0;
17395
+ }
16103
17396
  function isReasoningModel(model) {
16104
17397
  if (!model) return false;
16105
- const m = model.toLowerCase();
16106
- return /(^|[/_-])o[0-9]/.test(m) || m.startsWith("o1") || m.startsWith("o3") || m.startsWith("o4") || m.includes("gpt-5") || m.includes("gpt5");
17398
+ const rule = resolveModelRule({ provider: "openai", model });
17399
+ return !rule.isDefault && rule.reasoning.style === "effort" && rule.sampling.temperature !== "any";
16107
17400
  }
16108
17401
  function adaptChatExtrasForResponses(body, extra) {
16109
17402
  const native = extra ?? {};
@@ -16176,36 +17469,80 @@ function mapFinishReason(reason) {
16176
17469
  }
16177
17470
  }
16178
17471
 
17472
+ // src/providers/utils/toolChoice.ts
17473
+ function supportsForcedToolChoice(model) {
17474
+ if (typeof model !== "string") return true;
17475
+ const id = model.toLowerCase();
17476
+ const at = id.indexOf("claude-");
17477
+ if (at < 0) return true;
17478
+ const toks = id.slice(at + 7).split("-");
17479
+ let family = false;
17480
+ for (const tok of toks) {
17481
+ if (tok === "sonnet" || tok === "opus") {
17482
+ family = true;
17483
+ continue;
17484
+ }
17485
+ const m = /^(\d+)(?:@|:|$)/.exec(tok);
17486
+ if (m) {
17487
+ if (m[1].length > 2) continue;
17488
+ if (Number(m[1]) < 5) return true;
17489
+ if (family) return false;
17490
+ return !toks.some((t) => t === "sonnet" || t === "opus");
17491
+ }
17492
+ }
17493
+ return true;
17494
+ }
17495
+ function forcedToolChoiceRejected(text) {
17496
+ const t = text.toLowerCase();
17497
+ return t.indexOf("tool_choice") >= 0 && t.indexOf("not supported for this model") >= 0;
17498
+ }
17499
+
16179
17500
  // src/providers/anthropic.ts
17501
+ var DEFAULT_MAX_TOKENS = 4096;
17502
+ function normalizeExplicitThinking(body) {
17503
+ const t = body.thinking;
17504
+ if (t == null) {
17505
+ delete body.thinking;
17506
+ return;
17507
+ }
17508
+ if (t.type !== "enabled") return;
17509
+ const budget = typeof t.budget_tokens === "number" ? t.budget_tokens : 0;
17510
+ if (budget > 0 && (typeof body.max_tokens !== "number" || body.max_tokens <= budget)) {
17511
+ body.max_tokens = budget + 1024;
17512
+ }
17513
+ for (const k of ["temperature", "top_p", "top_k"]) delete body[k];
17514
+ }
16180
17515
  var AnthropicProvider = class extends BaseProvider {
16181
17516
  constructor(config) {
16182
17517
  super();
16183
17518
  this.providerName = "anthropic";
17519
+ this.configureTransport(config);
16184
17520
  this.apiKey = config.apiKey;
16185
17521
  this.baseURL = (config.baseURL ?? "https://api.anthropic.com").replace(/\/$/, "");
16186
17522
  this.defaultModel = config.defaultModel ?? "claude-sonnet-4-20250514";
16187
17523
  this.defaultHeaders = config.defaultHeaders ?? {};
16188
17524
  this.anthropicVersion = config.anthropicVersion ?? "2023-06-01";
17525
+ this.thinkingMode = config.thinkingMode ?? "auto";
16189
17526
  this.promptCachingEnabled = config.prompt_caching?.enabled === true;
16190
17527
  if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
16191
17528
  }
16192
17529
  async complete(request) {
16193
- const body = this.buildRequestBody(request, false);
16194
- const res = await this.doFetch(body, request.signal);
17530
+ const res = await this.send(request, false);
16195
17531
  const json = await res.json();
16196
17532
  return this.parseResponse(json);
16197
17533
  }
16198
17534
  async *completeStream(request) {
16199
- const body = this.buildRequestBody(request, true);
16200
- const res = await this.doFetch(body, request.signal);
17535
+ const res = await this.send(request, true);
16201
17536
  if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
16202
17537
  let id = "";
16203
17538
  let model = "";
16204
17539
  let inputTokens = 0;
16205
17540
  let outputTokens = 0;
17541
+ let thinkingTokens = 0;
16206
17542
  let cacheReadInputTokens = 0;
16207
17543
  let cacheCreationInputTokens = 0;
16208
17544
  const currentToolCalls = /* @__PURE__ */ new Map();
17545
+ let toolOrdinal = 0;
16209
17546
  const reasoningBlocks = /* @__PURE__ */ new Map();
16210
17547
  for await (const event of parseSSEStream(res.body, request.signal)) {
16211
17548
  let data;
@@ -16234,8 +17571,11 @@ var AnthropicProvider = class extends BaseProvider {
16234
17571
  currentToolCalls.set(idx, {
16235
17572
  id: block.id ?? "",
16236
17573
  name: block.name ?? "",
16237
- arguments: ""
17574
+ arguments: "",
17575
+ ordinal: toolOrdinal++
16238
17576
  });
17577
+ const opened = currentToolCalls.get(idx);
17578
+ yield { id, model, delta: { toolCalls: [{ id: opened.id, name: opened.name, arguments: "", index: opened.ordinal }] } };
16239
17579
  } else if (block?.type === "thinking") {
16240
17580
  reasoningBlocks.set(idx, { type: "thinking", text: block.thinking ?? "", signature: block.signature });
16241
17581
  } else if (block?.type === "redacted_thinking") {
@@ -16273,7 +17613,7 @@ var AnthropicProvider = class extends BaseProvider {
16273
17613
  id,
16274
17614
  model,
16275
17615
  delta: {
16276
- toolCalls: [{ id: tc.id, name: tc.name, arguments: delta.partial_json }]
17616
+ toolCalls: [{ id: tc.id, name: tc.name, arguments: delta.partial_json, index: tc.ordinal }]
16277
17617
  }
16278
17618
  };
16279
17619
  }
@@ -16286,8 +17626,10 @@ var AnthropicProvider = class extends BaseProvider {
16286
17626
  const delta = data.delta;
16287
17627
  if (data.usage) {
16288
17628
  outputTokens = data.usage.output_tokens ?? outputTokens;
17629
+ thinkingTokens = data.usage.output_tokens_details?.thinking_tokens ?? thinkingTokens;
16289
17630
  }
16290
17631
  const finishReason = mapAnthropicStopReason(delta?.stop_reason);
17632
+ const rawFinishReason = typeof delta?.stop_reason === "string" ? delta.stop_reason : void 0;
16291
17633
  const usage = {
16292
17634
  inputTokens,
16293
17635
  outputTokens,
@@ -16295,7 +17637,7 @@ var AnthropicProvider = class extends BaseProvider {
16295
17637
  cachedInputTokens: cacheReadInputTokens,
16296
17638
  cachedWriteTokens: cacheCreationInputTokens,
16297
17639
  cachedOutputTokens: 0,
16298
- reasoningTokens: 0
17640
+ reasoningTokens: thinkingTokens
16299
17641
  };
16300
17642
  const blocks = [...reasoningBlocks.values()].filter(
16301
17643
  (b) => b.type === "thinking" && b.signature || b.type === "redacted_thinking" && b.data
@@ -16305,6 +17647,7 @@ var AnthropicProvider = class extends BaseProvider {
16305
17647
  model,
16306
17648
  delta: blocks.length > 0 ? { reasoning: { blocks } } : {},
16307
17649
  finishReason,
17650
+ ...rawFinishReason ? { rawFinishReason } : {},
16308
17651
  usage
16309
17652
  };
16310
17653
  break;
@@ -16319,12 +17662,12 @@ var AnthropicProvider = class extends BaseProvider {
16319
17662
  }
16320
17663
  }
16321
17664
  // ─── Internals ───────────────────────────────────────────────────────────
16322
- buildRequestBody(request, stream) {
17665
+ buildRequestBody(request, stream, overlay, downgradeToolChoice = false) {
16323
17666
  const { system, messages } = this.splitSystemMessages(request.messages);
16324
17667
  const body = {
16325
17668
  model: request.model || this.defaultModel,
16326
17669
  messages: this.toAnthropicMessages(messages),
16327
- max_tokens: request.maxTokens ?? 4096,
17670
+ max_tokens: request.maxTokens ?? DEFAULT_MAX_TOKENS,
16328
17671
  stream
16329
17672
  };
16330
17673
  if (system) {
@@ -16334,6 +17677,7 @@ var AnthropicProvider = class extends BaseProvider {
16334
17677
  body.system = system;
16335
17678
  }
16336
17679
  }
17680
+ const rule = resolveModelRule({ provider: this.providerName, model: String(body.model), patch: overlay });
16337
17681
  if (request.temperature != null) body.temperature = request.temperature;
16338
17682
  if (request.topP != null) body.top_p = request.topP;
16339
17683
  if (request.stop) body.stop_sequences = request.stop;
@@ -16345,18 +17689,29 @@ var AnthropicProvider = class extends BaseProvider {
16345
17689
  body.tools = tools;
16346
17690
  }
16347
17691
  if (request.toolChoice != null) {
17692
+ const forceOk = !downgradeToolChoice && supportsForcedToolChoice(body.model);
16348
17693
  if (request.toolChoice === "auto") {
16349
17694
  body.tool_choice = { type: "auto" };
16350
17695
  } else if (request.toolChoice === "required") {
16351
- body.tool_choice = { type: "any" };
17696
+ body.tool_choice = forceOk ? { type: "any" } : { type: "auto" };
16352
17697
  } else if (request.toolChoice === "none") ; else {
16353
- body.tool_choice = { type: "tool", name: request.toolChoice.name };
17698
+ body.tool_choice = forceOk ? { type: "tool", name: request.toolChoice.name } : { type: "auto" };
16354
17699
  }
16355
17700
  }
16356
17701
  if (request.extra) {
16357
17702
  Object.assign(body, request.extra);
16358
17703
  }
16359
- applyAnthropicReasoning(body, request.reasoning);
17704
+ const explicitThinking = request.extra && typeof request.extra === "object" && "thinking" in request.extra;
17705
+ if (explicitThinking) {
17706
+ normalizeExplicitThinking(body);
17707
+ enforceAnthropicSampling(body, rule, body.thinking?.type === "enabled" || body.thinking?.type === "adaptive");
17708
+ } else {
17709
+ applyAnthropicReasoning(body, request.reasoning, { model: body.model, mode: this.thinkingMode, rule });
17710
+ }
17711
+ applyDropParams(body, rule);
17712
+ if (typeof body.max_tokens !== "number" || !(body.max_tokens > 0)) {
17713
+ body.max_tokens = DEFAULT_MAX_TOKENS;
17714
+ }
16360
17715
  return body;
16361
17716
  }
16362
17717
  splitSystemMessages(messages) {
@@ -16487,6 +17842,31 @@ var AnthropicProvider = class extends BaseProvider {
16487
17842
  input_schema: t.parameters
16488
17843
  };
16489
17844
  }
17845
+ /** Builds and sends the request; retries once with the other thinking form
17846
+ * when the upstream rejects the one we picked. */
17847
+ send(request, stream) {
17848
+ let downgrade = false;
17849
+ return runWithModelAdjustments({
17850
+ provider: this.providerName,
17851
+ model: request.model || this.defaultModel,
17852
+ endpoint: "messages",
17853
+ callerParams: request.extra ? Object.keys(request.extra) : void 0,
17854
+ onLearned: this.onModelRuleLearned,
17855
+ run: async (overlay, guard) => {
17856
+ for (; ; ) {
17857
+ const body = this.buildRequestBody(request, stream, overlay, downgrade);
17858
+ guard(body);
17859
+ try {
17860
+ return await this.doFetch(body, request.signal);
17861
+ } catch (err) {
17862
+ const rejected = err instanceof ProviderError && err.statusCode === 400 && !downgrade && body.tool_choice && body.tool_choice.type !== "auto" && forcedToolChoiceRejected(String(err.responseBody ?? err.message));
17863
+ if (!rejected) throw err;
17864
+ downgrade = true;
17865
+ }
17866
+ }
17867
+ }
17868
+ });
17869
+ }
16490
17870
  async doFetch(body, signal) {
16491
17871
  const url = `${this.baseURL}/v1/messages`;
16492
17872
  const headers = {
@@ -16496,7 +17876,7 @@ var AnthropicProvider = class extends BaseProvider {
16496
17876
  ...this.defaultHeaders
16497
17877
  };
16498
17878
  const payload = JSON.stringify(body);
16499
- const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload, signal }), signal);
17879
+ const res = await this.doFetchWithRetry(() => this.httpFetch(url, { method: "POST", headers, body: payload, signal }), signal);
16500
17880
  if (!res.ok) {
16501
17881
  const text = await res.text().catch(() => "");
16502
17882
  throw new ProviderError(
@@ -16541,10 +17921,9 @@ var AnthropicProvider = class extends BaseProvider {
16541
17921
  cachedInputTokens: usage.cache_read_input_tokens ?? 0,
16542
17922
  cachedWriteTokens: usage.cache_creation_input_tokens ?? 0,
16543
17923
  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
17924
+ // Thinking tokens are billed inside `output_tokens`; newer models also
17925
+ // report the split as `output_tokens_details.thinking_tokens`.
17926
+ reasoningTokens: usage.output_tokens_details?.thinking_tokens ?? 0
16548
17927
  };
16549
17928
  return {
16550
17929
  id: json.id ?? "",
@@ -16553,6 +17932,7 @@ var AnthropicProvider = class extends BaseProvider {
16553
17932
  toolCalls,
16554
17933
  usage: tokenUsage,
16555
17934
  finishReason: mapAnthropicStopReason(json.stop_reason),
17935
+ ...typeof json.stop_reason === "string" ? { rawFinishReason: json.stop_reason } : {},
16556
17936
  reasoning: reasoningBlocks.length > 0 ? { blocks: reasoningBlocks, summary: summaryParts.join("\n").trim() || void 0 } : void 0,
16557
17937
  raw: json
16558
17938
  };
@@ -16598,7 +17978,16 @@ var AzureProvider = class extends OpenAIProvider {
16598
17978
  provider: "openai",
16599
17979
  apiKey: config.apiKey,
16600
17980
  defaultModel: config.defaultModel ?? config.deploymentName ?? "gpt-4o",
16601
- defaultHeaders: config.defaultHeaders
17981
+ defaultHeaders: config.defaultHeaders,
17982
+ retry: config.retry,
17983
+ fetch: config.fetch,
17984
+ timeoutMs: config.timeoutMs,
17985
+ onModelRuleLearned: config.onModelRuleLearned,
17986
+ maxTokensParam: config.maxTokensParam,
17987
+ api: config.api,
17988
+ responsesApi: config.responsesApi,
17989
+ responsesApiModels: config.responsesApiModels,
17990
+ responsesStreaming: config.responsesStreaming
16602
17991
  });
16603
17992
  this.providerName = "azure";
16604
17993
  this.endpoint = config.endpoint.replace(/\/$/, "");
@@ -16613,16 +18002,17 @@ var AzureProvider = class extends OpenAIProvider {
16613
18002
  }
16614
18003
  const url = `${this.endpoint}/openai/deployments/${deployment}/chat/completions?api-version=${this.apiVersion}`;
16615
18004
  const { model: _model, ...bodyWithoutModel } = body;
16616
- const res = await fetch(url, {
18005
+ const payload = JSON.stringify(bodyWithoutModel);
18006
+ const res = await this.doFetchWithRetry(() => this.httpFetch(url, {
16617
18007
  method: "POST",
16618
18008
  headers: {
16619
18009
  "Content-Type": "application/json",
16620
18010
  "api-key": this.azureApiKey,
16621
18011
  ...this.defaultHeaders
16622
18012
  },
16623
- body: JSON.stringify(bodyWithoutModel),
18013
+ body: payload,
16624
18014
  signal
16625
- });
18015
+ }), signal);
16626
18016
  if (!res.ok) {
16627
18017
  const text = await res.text().catch(() => "");
16628
18018
  throw new ProviderError(
@@ -16634,6 +18024,32 @@ var AzureProvider = class extends OpenAIProvider {
16634
18024
  }
16635
18025
  return res;
16636
18026
  }
18027
+ /** Azure embeddings: the deployment named by the request's model (falling back
18028
+ * to the configured deployment) in the path, api-key auth. */
18029
+ async embeddingsFetch(body, signal) {
18030
+ const deployment = body.model || this.deploymentName;
18031
+ if (!deployment) {
18032
+ throw new ProviderError("Azure embeddings require a deployment name or model", this.providerName, 400);
18033
+ }
18034
+ const url = `${this.endpoint}/openai/deployments/${deployment}/embeddings?api-version=${this.apiVersion}`;
18035
+ const { model: _model, ...bodyWithoutModel } = body;
18036
+ const payload = JSON.stringify(bodyWithoutModel);
18037
+ const res = await this.doFetchWithRetry(() => this.httpFetch(url, {
18038
+ method: "POST",
18039
+ headers: {
18040
+ "Content-Type": "application/json",
18041
+ "api-key": this.azureApiKey,
18042
+ ...this.defaultHeaders
18043
+ },
18044
+ body: payload,
18045
+ signal
18046
+ }), signal);
18047
+ if (!res.ok) {
18048
+ const text = await res.text().catch(() => "");
18049
+ throw new ProviderError(`Azure OpenAI embeddings API error ${res.status}: ${text}`, this.providerName, res.status, text);
18050
+ }
18051
+ return res;
18052
+ }
16637
18053
  /** Azure exposes the Responses API at /openai/responses with api-key auth.
16638
18054
  * Reasoning summaries require a preview api-version; callers can override via
16639
18055
  * the `apiVersion` config. */
@@ -16645,7 +18061,7 @@ var AzureProvider = class extends OpenAIProvider {
16645
18061
  }
16646
18062
  const url = `${this.endpoint}/openai/responses?api-version=${this.apiVersion}`;
16647
18063
  const payload = JSON.stringify({ ...body, model: deployment });
16648
- const res = await fetch(url, {
18064
+ const res = await this.doFetchWithRetry(() => this.httpFetch(url, {
16649
18065
  method: "POST",
16650
18066
  headers: {
16651
18067
  "Content-Type": "application/json",
@@ -16654,7 +18070,7 @@ var AzureProvider = class extends OpenAIProvider {
16654
18070
  },
16655
18071
  body: payload,
16656
18072
  signal
16657
- });
18073
+ }), signal);
16658
18074
  if (!res.ok) {
16659
18075
  const text = await res.text().catch(() => "");
16660
18076
  throw new ProviderError(
@@ -16690,20 +18106,156 @@ var OpenAICompatibleProvider = class extends OpenAIProvider {
16690
18106
  apiKey: config.apiKey,
16691
18107
  baseURL: config.baseURL,
16692
18108
  defaultModel: config.defaultModel,
16693
- defaultHeaders: config.defaultHeaders
18109
+ defaultHeaders: config.defaultHeaders,
18110
+ organization: config.organization,
18111
+ retry: config.retry,
18112
+ fetch: config.fetch,
18113
+ timeoutMs: config.timeoutMs,
18114
+ onModelRuleLearned: config.onModelRuleLearned,
18115
+ maxTokensParam: config.maxTokensParam,
18116
+ api: config.api,
18117
+ // Unlike OpenAI / Azure, "auto" on a compatible server means Chat
18118
+ // Completions (the 0.11 default): the model name is the operator's.
18119
+ // `responsesApiModels` still applies; `responsesApi: "auto"` opts in.
18120
+ _autoMeansChat: true,
18121
+ responsesApi: config.responsesApi,
18122
+ responsesApiModels: config.responsesApiModels,
18123
+ responsesStreaming: config.responsesStreaming
16694
18124
  });
16695
18125
  this.providerName = "openai-compatible";
16696
18126
  }
16697
18127
  };
16698
18128
 
18129
+ // src/providers/utils/eventStream.ts
18130
+ var EventStreamError = class extends Error {
18131
+ constructor(message) {
18132
+ super(message);
18133
+ this.name = "EventStreamError";
18134
+ }
18135
+ };
18136
+ var CRC_TABLE = (() => {
18137
+ const table = new Uint32Array(256);
18138
+ for (let n = 0; n < 256; n++) {
18139
+ let c = n;
18140
+ for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
18141
+ table[n] = c >>> 0;
18142
+ }
18143
+ return table;
18144
+ })();
18145
+ function crc32(bytes, start = 0, end = bytes.length) {
18146
+ let c = 4294967295;
18147
+ for (let i = start; i < end; i++) c = CRC_TABLE[(c ^ bytes[i]) & 255] ^ c >>> 8;
18148
+ return (c ^ 4294967295) >>> 0;
18149
+ }
18150
+ function decodeFrame(frame) {
18151
+ const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
18152
+ const total = view.getUint32(0);
18153
+ const headersLen = view.getUint32(4);
18154
+ if (total !== frame.byteLength) throw new EventStreamError("event-stream frame length mismatch");
18155
+ if (crc32(frame, 0, 8) !== view.getUint32(8)) throw new EventStreamError("event-stream prelude CRC mismatch");
18156
+ if (crc32(frame, 0, total - 4) !== view.getUint32(total - 4)) throw new EventStreamError("event-stream message CRC mismatch");
18157
+ if (12 + headersLen > total - 4) throw new EventStreamError("event-stream headers overflow frame");
18158
+ const headers = {};
18159
+ const td = new TextDecoder();
18160
+ let o = 12;
18161
+ const headersEnd = 12 + headersLen;
18162
+ while (o < headersEnd) {
18163
+ const nameLen = frame[o++];
18164
+ const name = td.decode(frame.subarray(o, o + nameLen));
18165
+ o += nameLen;
18166
+ const type = frame[o++];
18167
+ switch (type) {
18168
+ case 0:
18169
+ headers[name] = true;
18170
+ break;
18171
+ case 1:
18172
+ headers[name] = false;
18173
+ break;
18174
+ case 2:
18175
+ headers[name] = view.getInt8(o);
18176
+ o += 1;
18177
+ break;
18178
+ case 3:
18179
+ headers[name] = view.getInt16(o);
18180
+ o += 2;
18181
+ break;
18182
+ case 4:
18183
+ headers[name] = view.getInt32(o);
18184
+ o += 4;
18185
+ break;
18186
+ case 5:
18187
+ headers[name] = view.getBigInt64(o);
18188
+ o += 8;
18189
+ break;
18190
+ case 6: {
18191
+ const len = view.getUint16(o);
18192
+ o += 2;
18193
+ headers[name] = frame.slice(o, o + len);
18194
+ o += len;
18195
+ break;
18196
+ }
18197
+ case 7: {
18198
+ const len = view.getUint16(o);
18199
+ o += 2;
18200
+ headers[name] = td.decode(frame.subarray(o, o + len));
18201
+ o += len;
18202
+ break;
18203
+ }
18204
+ case 8:
18205
+ headers[name] = view.getBigInt64(o);
18206
+ o += 8;
18207
+ break;
18208
+ case 9:
18209
+ headers[name] = frame.slice(o, o + 16);
18210
+ o += 16;
18211
+ break;
18212
+ default:
18213
+ throw new EventStreamError(`unknown event-stream header type ${type}`);
18214
+ }
18215
+ }
18216
+ return { headers, payload: frame.slice(headersEnd, total - 4) };
18217
+ }
18218
+ async function* parseEventStream(body, signal) {
18219
+ const reader = body.getReader();
18220
+ let buf = new Uint8Array(0);
18221
+ try {
18222
+ while (true) {
18223
+ if (signal?.aborted) throw abortReason(signal);
18224
+ const { done, value } = await reader.read();
18225
+ if (done) break;
18226
+ if (!value?.length) continue;
18227
+ const merged = new Uint8Array(buf.length + value.length);
18228
+ merged.set(buf, 0);
18229
+ merged.set(value, buf.length);
18230
+ buf = merged;
18231
+ while (buf.length >= 12) {
18232
+ const total = new DataView(buf.buffer, buf.byteOffset, buf.byteLength).getUint32(0);
18233
+ if (total < 16) throw new EventStreamError("invalid event-stream frame length");
18234
+ if (buf.length < total) break;
18235
+ yield decodeFrame(buf.subarray(0, total));
18236
+ buf = buf.subarray(total);
18237
+ }
18238
+ }
18239
+ if (buf.length > 0) throw new EventStreamError("event-stream ended inside a frame");
18240
+ } finally {
18241
+ reader.cancel().catch(() => void 0);
18242
+ try {
18243
+ reader.releaseLock();
18244
+ } catch {
18245
+ }
18246
+ }
18247
+ }
18248
+
16699
18249
  // src/providers/bedrock.ts
16700
18250
  var BedrockProvider = class extends BaseProvider {
16701
18251
  constructor(config) {
16702
18252
  super();
16703
18253
  this.providerName = "bedrock";
18254
+ this.configureTransport(config);
16704
18255
  this.region = config.region;
16705
18256
  this.defaultModel = config.defaultModel ?? "anthropic.claude-sonnet-4-20250514-v1:0";
16706
18257
  this.defaultHeaders = config.defaultHeaders ?? {};
18258
+ this.thinkingMode = config.thinkingMode ?? "auto";
16707
18259
  this.promptCachingEnabled = config.prompt_caching?.enabled === true;
16708
18260
  if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
16709
18261
  this.credentials = {
@@ -16720,25 +18272,202 @@ var BedrockProvider = class extends BaseProvider {
16720
18272
  }
16721
18273
  async complete(request) {
16722
18274
  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);
18275
+ const res = await this.converseFetch(modelId, request, false);
18276
+ return this.parseResponse(await res.json(), modelId);
16726
18277
  }
18278
+ /**
18279
+ * Real streaming over ConverseStream. The response is an AWS event-stream
18280
+ * (binary frames); each frame carries one JSON event: messageStart,
18281
+ * contentBlockStart/Delta/Stop (text, toolUse, reasoningContent),
18282
+ * messageStop and, last, metadata (usage). Text, tool-call argument and
18283
+ * reasoning deltas are yielded as they arrive; one closing chunk carries the
18284
+ * finish reason, usage and the signed reasoning blocks.
18285
+ */
16727
18286
  async *completeStream(request) {
16728
- const response = await this.complete(request);
18287
+ const modelId = request.model || this.defaultModel;
18288
+ const res = await this.converseFetch(modelId, request, true);
18289
+ if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
18290
+ const id = res.headers.get("x-amzn-requestid") ?? "";
18291
+ const toolsByIndex = /* @__PURE__ */ new Map();
18292
+ const reasoningByIndex = /* @__PURE__ */ new Map();
18293
+ let finishReason;
18294
+ let rawFinishReason;
18295
+ let toolOrdinal = 0;
18296
+ let usage;
18297
+ for await (const msg of parseEventStream(res.body, request.signal)) {
18298
+ const messageType = String(msg.headers[":message-type"] ?? "event");
18299
+ const eventType = String(msg.headers[":event-type"] ?? msg.headers[":exception-type"] ?? "");
18300
+ let data = {};
18301
+ const text = new TextDecoder().decode(msg.payload);
18302
+ if (text) {
18303
+ try {
18304
+ data = JSON.parse(text);
18305
+ } catch {
18306
+ data = { message: text };
18307
+ }
18308
+ }
18309
+ if (messageType === "exception" || messageType === "error") {
18310
+ const code = String(msg.headers[":exception-type"] ?? msg.headers[":error-code"] ?? eventType);
18311
+ throw new ProviderError(
18312
+ `Bedrock stream error ${code}: ${data.message ?? text}`,
18313
+ this.providerName,
18314
+ bedrockExceptionStatus(code),
18315
+ data
18316
+ );
18317
+ }
18318
+ switch (eventType) {
18319
+ case "contentBlockStart": {
18320
+ const idx = data.contentBlockIndex ?? 0;
18321
+ const tu = data.start?.toolUse;
18322
+ if (tu) {
18323
+ const ordinal = toolOrdinal++;
18324
+ toolsByIndex.set(idx, { id: tu.toolUseId ?? "", name: tu.name ?? "", ordinal });
18325
+ yield { id, model: modelId, delta: { toolCalls: [{ id: tu.toolUseId, name: tu.name, arguments: "", index: ordinal }] } };
18326
+ }
18327
+ break;
18328
+ }
18329
+ case "contentBlockDelta": {
18330
+ const idx = data.contentBlockIndex ?? 0;
18331
+ const d = data.delta ?? {};
18332
+ if (typeof d.text === "string" && d.text) {
18333
+ yield { id, model: modelId, delta: { content: d.text } };
18334
+ } else if (d.toolUse) {
18335
+ const tc = toolsByIndex.get(idx);
18336
+ if (tc && typeof d.toolUse.input === "string" && d.toolUse.input) {
18337
+ yield { id, model: modelId, delta: { toolCalls: [{ id: tc.id, name: tc.name, arguments: d.toolUse.input, index: tc.ordinal }] } };
18338
+ }
18339
+ } else if (d.reasoningContent) {
18340
+ const rc = d.reasoningContent;
18341
+ const blk = reasoningByIndex.get(idx) ?? { type: "thinking", text: "" };
18342
+ if (typeof rc.text === "string" && rc.text) {
18343
+ blk.text = (blk.text ?? "") + rc.text;
18344
+ reasoningByIndex.set(idx, blk);
18345
+ yield { id, model: modelId, delta: { reasoning: { summary: rc.text } } };
18346
+ }
18347
+ if (typeof rc.signature === "string" && rc.signature) {
18348
+ blk.signature = (blk.signature ?? "") + rc.signature;
18349
+ reasoningByIndex.set(idx, blk);
18350
+ }
18351
+ if (rc.redactedContent) {
18352
+ reasoningByIndex.set(idx, { type: "redacted_thinking", data: rc.redactedContent });
18353
+ }
18354
+ }
18355
+ break;
18356
+ }
18357
+ case "messageStop":
18358
+ finishReason = mapBedrockStopReason(data.stopReason);
18359
+ if (typeof data.stopReason === "string") rawFinishReason = data.stopReason;
18360
+ break;
18361
+ case "metadata": {
18362
+ const u = data.usage ?? {};
18363
+ usage = {
18364
+ inputTokens: u.inputTokens ?? 0,
18365
+ outputTokens: u.outputTokens ?? 0,
18366
+ totalTokens: u.totalTokens ?? (u.inputTokens ?? 0) + (u.outputTokens ?? 0),
18367
+ cachedInputTokens: u.cacheReadInputTokens ?? u.cacheReadInputTokenCount ?? 0,
18368
+ cachedWriteTokens: u.cacheWriteInputTokens ?? u.cacheWriteInputTokenCount ?? 0,
18369
+ cachedOutputTokens: 0,
18370
+ reasoningTokens: 0
18371
+ };
18372
+ break;
18373
+ }
18374
+ }
18375
+ }
18376
+ const blocks = [...reasoningByIndex.values()].filter(
18377
+ (b) => b.type === "thinking" && b.signature || b.type === "redacted_thinking" && b.data
18378
+ );
16729
18379
  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
18380
+ id,
18381
+ model: modelId,
18382
+ delta: blocks.length > 0 ? { reasoning: { blocks } } : {},
18383
+ finishReason: finishReason ?? "stop",
18384
+ ...rawFinishReason ? { rawFinishReason } : {},
18385
+ usage
16738
18386
  };
16739
18387
  }
18388
+ // ─── Embeddings ──────────────────────────────────────────────────────────
18389
+ /**
18390
+ * InvokeModel embeddings. Supported families (by model id, inference-profile
18391
+ * prefixes such as `us.` allowed):
18392
+ * - Amazon Titan Text Embeddings (v1, v2): one text per call, `dimensions`
18393
+ * (v2: 256/512/1024) and `normalize` via `extra`.
18394
+ * - Cohere Embed (v3, v4): a batch of texts per call (default 96), `inputType`
18395
+ * maps to `input_type`, `dimensions` to `output_dimension` (v4).
18396
+ */
18397
+ async embed(request) {
18398
+ const model = requireEmbeddingModel(this.providerName, request.model);
18399
+ const id = model.toLowerCase();
18400
+ const raw = [];
18401
+ let inputTokens = 0;
18402
+ let vectors = [];
18403
+ if (id.includes("titan-embed")) {
18404
+ const results = await mapWithConcurrency(request.input, 4, async (text) => {
18405
+ const body = { inputText: text };
18406
+ if (request.dimensions != null && !id.includes("titan-embed-text-v1")) body.dimensions = request.dimensions;
18407
+ if (request.extra) Object.assign(body, request.extra);
18408
+ const res = await this.invokeModel(model, body, request.signal);
18409
+ const json = await res.json();
18410
+ return { json, tokens: json.inputTextTokenCount ?? Number(res.headers.get("x-amzn-bedrock-input-token-count") ?? 0) };
18411
+ }, request.signal);
18412
+ for (const r of results) {
18413
+ raw.push(r.json);
18414
+ inputTokens += r.tokens || 0;
18415
+ vectors.push(r.json.embedding ?? r.json.embeddingsByType?.float);
18416
+ }
18417
+ } else if (id.includes("cohere.embed")) {
18418
+ for (const batch of chunkArray(request.input, request.batchSize ?? 96)) {
18419
+ const body = {
18420
+ texts: batch,
18421
+ input_type: request.inputType ?? "search_document",
18422
+ embedding_types: ["float"]
18423
+ };
18424
+ if (request.dimensions != null) body.output_dimension = request.dimensions;
18425
+ if (request.extra) Object.assign(body, request.extra);
18426
+ const res = await this.invokeModel(model, body, request.signal);
18427
+ const json = await res.json();
18428
+ raw.push(json);
18429
+ const emb = Array.isArray(json.embeddings) ? json.embeddings : json.embeddings?.float;
18430
+ vectors.push(...emb ?? []);
18431
+ inputTokens += Number(res.headers.get("x-amzn-bedrock-input-token-count") ?? 0) || 0;
18432
+ }
18433
+ } else {
18434
+ throw new ProviderError(
18435
+ `Bedrock embeddings are supported for Amazon Titan and Cohere Embed models, not "${model}"`,
18436
+ this.providerName,
18437
+ 400
18438
+ );
18439
+ }
18440
+ assertVectorCount(this.providerName, vectors, request.input.length);
18441
+ return { model, vectors, usage: { inputTokens, totalTokens: inputTokens }, raw };
18442
+ }
18443
+ async invokeModel(modelId, body, signal) {
18444
+ const host = `bedrock-runtime.${this.region}.amazonaws.com`;
18445
+ const url = `https://${host}/model/${encodeURIComponent(modelId)}/invoke`;
18446
+ const bodyStr = JSON.stringify(body);
18447
+ const res = await this.doFetchWithRetry(() => {
18448
+ const headers = signRequest({
18449
+ method: "POST",
18450
+ url,
18451
+ headers: {
18452
+ "Content-Type": "application/json",
18453
+ Accept: "application/json",
18454
+ ...this.defaultHeaders
18455
+ },
18456
+ body: bodyStr,
18457
+ region: this.region,
18458
+ service: "bedrock",
18459
+ credentials: this.credentials
18460
+ });
18461
+ return this.httpFetch(url, { method: "POST", headers, body: bodyStr, signal });
18462
+ }, signal);
18463
+ if (!res.ok) {
18464
+ const text = await res.text().catch(() => "");
18465
+ throw new ProviderError(`Bedrock API error ${res.status}: ${text}`, this.providerName, res.status, text);
18466
+ }
18467
+ return res;
18468
+ }
16740
18469
  // ─── Internals ───────────────────────────────────────────────────────────
16741
- buildConverseBody(request) {
18470
+ buildConverseBody(request, downgradeToolChoice = false, overlay) {
16742
18471
  const { system, messages } = this.splitSystemMessages(request.messages);
16743
18472
  const body = {
16744
18473
  messages: this.toBedrockMessages(messages)
@@ -16762,8 +18491,9 @@ var BedrockProvider = class extends BaseProvider {
16762
18491
  body.toolConfig = {
16763
18492
  tools
16764
18493
  };
18494
+ const forceOk = !downgradeToolChoice && supportsForcedToolChoice(request.model || this.defaultModel);
16765
18495
  if (request.toolChoice != null) {
16766
- if (request.toolChoice === "auto") {
18496
+ if (request.toolChoice === "auto" || !forceOk && request.toolChoice !== "none") {
16767
18497
  body.toolConfig.toolChoice = { auto: {} };
16768
18498
  } else if (request.toolChoice === "required") {
16769
18499
  body.toolConfig.toolChoice = { any: {} };
@@ -16773,9 +18503,22 @@ var BedrockProvider = class extends BaseProvider {
16773
18503
  }
16774
18504
  }
16775
18505
  if (request.extra) {
16776
- Object.assign(body, request.extra);
16777
- }
16778
- applyBedrockReasoning(body, request.reasoning);
18506
+ const fields = { ...request.extra.additionalModelRequestFields ?? {} };
18507
+ const topLevel = { ...request.extra.__top ?? {} };
18508
+ for (const [key, value] of Object.entries(request.extra)) {
18509
+ if (key === "additionalModelRequestFields" || key === "__top") continue;
18510
+ if (CONVERSE_TOP_LEVEL_FIELDS.has(key)) body[key] = value;
18511
+ else fields[key] = value;
18512
+ }
18513
+ Object.assign(body, topLevel);
18514
+ if (Object.keys(fields).length > 0) body.additionalModelRequestFields = fields;
18515
+ }
18516
+ const rule = resolveModelRule({ provider: this.providerName, model: request.model || this.defaultModel, patch: overlay });
18517
+ applyBedrockReasoning(body, request.reasoning, { model: request.model || this.defaultModel, mode: this.thinkingMode, rule });
18518
+ if (body.inferenceConfig && rule.dropParams?.length) {
18519
+ for (const p of rule.dropParams) delete body.inferenceConfig[p === "top_p" ? "topP" : p === "max_tokens" ? "maxTokens" : p];
18520
+ }
18521
+ applyDropParams(body, rule);
16779
18522
  return body;
16780
18523
  }
16781
18524
  splitSystemMessages(messages) {
@@ -16898,9 +18641,35 @@ var BedrockProvider = class extends BaseProvider {
16898
18641
  }
16899
18642
  };
16900
18643
  }
16901
- async doFetch(modelId, body, signal) {
18644
+ /** Sends the Converse call; retries once with toolChoice auto when the
18645
+ * model rejects forced tool use. */
18646
+ converseFetch(modelId, request, stream) {
18647
+ let downgrade = false;
18648
+ return runWithModelAdjustments({
18649
+ provider: this.providerName,
18650
+ model: modelId,
18651
+ endpoint: "converse",
18652
+ callerParams: request.extra ? Object.keys(request.extra) : void 0,
18653
+ onLearned: this.onModelRuleLearned,
18654
+ run: async (overlay, guard) => {
18655
+ for (; ; ) {
18656
+ const body = this.buildConverseBody(request, downgrade, overlay);
18657
+ guard(body);
18658
+ try {
18659
+ return await this.doFetch(modelId, body, request.signal, stream);
18660
+ } catch (err) {
18661
+ const tc = body.toolConfig?.toolChoice;
18662
+ const rejected = err instanceof ProviderError && err.statusCode === 400 && !downgrade && !!tc && !tc.auto && forcedToolChoiceRejected(String(err.responseBody ?? err.message));
18663
+ if (!rejected) throw err;
18664
+ downgrade = true;
18665
+ }
18666
+ }
18667
+ }
18668
+ });
18669
+ }
18670
+ async doFetch(modelId, body, signal, stream) {
16902
18671
  const host = `bedrock-runtime.${this.region}.amazonaws.com`;
16903
- const url = `https://${host}/model/${encodeURIComponent(modelId)}/converse`;
18672
+ const url = `https://${host}/model/${encodeURIComponent(modelId)}/${stream ? "converse-stream" : "converse"}`;
16904
18673
  const bodyStr = JSON.stringify(body);
16905
18674
  const res = await this.doFetchWithRetry(() => {
16906
18675
  const headers = signRequest({
@@ -16908,7 +18677,7 @@ var BedrockProvider = class extends BaseProvider {
16908
18677
  url,
16909
18678
  headers: {
16910
18679
  "Content-Type": "application/json",
16911
- Accept: "application/json",
18680
+ Accept: stream ? "application/vnd.amazon.eventstream" : "application/json",
16912
18681
  ...this.defaultHeaders
16913
18682
  },
16914
18683
  body: bodyStr,
@@ -16916,7 +18685,7 @@ var BedrockProvider = class extends BaseProvider {
16916
18685
  service: "bedrock",
16917
18686
  credentials: this.credentials
16918
18687
  });
16919
- return fetch(url, { method: "POST", headers, body: bodyStr, signal });
18688
+ return this.httpFetch(url, { method: "POST", headers, body: bodyStr, signal });
16920
18689
  }, signal);
16921
18690
  if (!res.ok) {
16922
18691
  const text = await res.text().catch(() => "");
@@ -16927,7 +18696,7 @@ var BedrockProvider = class extends BaseProvider {
16927
18696
  text
16928
18697
  );
16929
18698
  }
16930
- return res.json();
18699
+ return res;
16931
18700
  }
16932
18701
  parseResponse(json, modelId) {
16933
18702
  const output = json.output?.message;
@@ -16963,8 +18732,8 @@ var BedrockProvider = class extends BaseProvider {
16963
18732
  inputTokens: usage.inputTokens ?? 0,
16964
18733
  outputTokens: usage.outputTokens ?? 0,
16965
18734
  totalTokens: usage.totalTokens ?? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0),
16966
- cachedInputTokens: usage.cacheReadInputTokenCount ?? 0,
16967
- cachedWriteTokens: usage.cacheWriteInputTokenCount ?? 0,
18735
+ cachedInputTokens: usage.cacheReadInputTokens ?? usage.cacheReadInputTokenCount ?? 0,
18736
+ cachedWriteTokens: usage.cacheWriteInputTokens ?? usage.cacheWriteInputTokenCount ?? 0,
16968
18737
  cachedOutputTokens: 0,
16969
18738
  // Bedrock bills thinking tokens inside outputTokens; no separate count.
16970
18739
  reasoningTokens: 0
@@ -16976,11 +18745,26 @@ var BedrockProvider = class extends BaseProvider {
16976
18745
  toolCalls,
16977
18746
  usage: tokenUsage,
16978
18747
  finishReason: mapBedrockStopReason(json.stopReason),
18748
+ ...typeof json.stopReason === "string" ? { rawFinishReason: json.stopReason } : {},
16979
18749
  reasoning: reasoningBlocks.length > 0 ? { blocks: reasoningBlocks, summary: summaryParts.join("\n").trim() || void 0 } : void 0,
16980
18750
  raw: json
16981
18751
  };
16982
18752
  }
16983
18753
  };
18754
+ var CONVERSE_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Set([
18755
+ "messages",
18756
+ "system",
18757
+ "inferenceConfig",
18758
+ "toolConfig",
18759
+ "guardrailConfig",
18760
+ "additionalModelRequestFields",
18761
+ "additionalModelResponseFieldPaths",
18762
+ "promptVariables",
18763
+ "requestMetadata",
18764
+ "performanceConfig",
18765
+ "serviceTier",
18766
+ "outputConfig"
18767
+ ]);
16984
18768
  function appendBedrockReasoning(content, blocks) {
16985
18769
  if (!blocks?.length) return;
16986
18770
  for (const b of blocks) {
@@ -16995,6 +18779,23 @@ function appendBedrockReasoning(content, blocks) {
16995
18779
  }
16996
18780
  }
16997
18781
  }
18782
+ function bedrockExceptionStatus(code) {
18783
+ switch (code) {
18784
+ case "throttlingException":
18785
+ return 429;
18786
+ case "validationException":
18787
+ return 400;
18788
+ case "modelTimeoutException":
18789
+ return 408;
18790
+ case "serviceUnavailableException":
18791
+ return 503;
18792
+ case "internalServerException":
18793
+ case "modelStreamErrorException":
18794
+ return 500;
18795
+ default:
18796
+ return void 0;
18797
+ }
18798
+ }
16998
18799
  function mapBedrockStopReason(reason) {
16999
18800
  switch (reason) {
17000
18801
  case "end_turn":
@@ -17003,8 +18804,10 @@ function mapBedrockStopReason(reason) {
17003
18804
  case "tool_use":
17004
18805
  return "tool_calls";
17005
18806
  case "max_tokens":
18807
+ case "model_context_window_exceeded":
17006
18808
  return "length";
17007
18809
  case "content_filtered":
18810
+ case "guardrail_intervened":
17008
18811
  case "guardrail":
17009
18812
  return "content_filter";
17010
18813
  default:
@@ -17034,10 +18837,12 @@ var VertexProvider = class extends BaseProvider {
17034
18837
  super();
17035
18838
  this.providerName = "vertex";
17036
18839
  this.tokenExpiry = 0;
18840
+ this.configureTransport(config);
17037
18841
  this.projectId = config.projectId;
17038
18842
  this.location = config.location ?? "us-central1";
17039
18843
  this.defaultModel = config.defaultModel ?? "gemini-2.0-flash";
17040
18844
  this.defaultHeaders = config.defaultHeaders ?? {};
18845
+ if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
17041
18846
  if (config.accessToken) {
17042
18847
  this.accessToken = config.accessToken;
17043
18848
  this.tokenExpiry = Date.now() + 36e5;
@@ -17050,7 +18855,7 @@ var VertexProvider = class extends BaseProvider {
17050
18855
  const modelId = request.model || this.defaultModel;
17051
18856
  const body = this.buildGeminiBody(request);
17052
18857
  const url = this.buildUrl(modelId, false);
17053
- const res = await this.doFetch(url, body, request.signal);
18858
+ const res = await this.doFetch(url, body, request.signal, modelId);
17054
18859
  const json = await res.json();
17055
18860
  return this.parseResponse(json, modelId);
17056
18861
  }
@@ -17058,8 +18863,9 @@ var VertexProvider = class extends BaseProvider {
17058
18863
  const modelId = request.model || this.defaultModel;
17059
18864
  const body = this.buildGeminiBody(request);
17060
18865
  const url = this.buildUrl(modelId, true);
17061
- const res = await this.doFetch(url, body, request.signal);
18866
+ const res = await this.doFetch(url, body, request.signal, modelId);
17062
18867
  if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
18868
+ const counter = { next: 0 };
17063
18869
  const contentType = res.headers.get("content-type") ?? "";
17064
18870
  if (contentType.includes("text/event-stream")) {
17065
18871
  for await (const event of parseSSEStream(res.body, request.signal)) {
@@ -17070,7 +18876,7 @@ var VertexProvider = class extends BaseProvider {
17070
18876
  } catch {
17071
18877
  continue;
17072
18878
  }
17073
- yield this.parseStreamChunk(chunk, modelId);
18879
+ yield this.parseStreamChunk(chunk, modelId, counter);
17074
18880
  }
17075
18881
  } else {
17076
18882
  const reader = res.body.getReader();
@@ -17088,7 +18894,7 @@ var VertexProvider = class extends BaseProvider {
17088
18894
  if (!trimmed || trimmed === "[" || trimmed === "]") continue;
17089
18895
  try {
17090
18896
  const chunk = JSON.parse(trimmed);
17091
- yield this.parseStreamChunk(chunk, modelId);
18897
+ yield this.parseStreamChunk(chunk, modelId, counter);
17092
18898
  } catch {
17093
18899
  buffer = line + "\n" + buffer;
17094
18900
  break;
@@ -17099,7 +18905,7 @@ var VertexProvider = class extends BaseProvider {
17099
18905
  if (remaining && remaining !== "[" && remaining !== "]") {
17100
18906
  try {
17101
18907
  const chunk = JSON.parse(remaining);
17102
- yield this.parseStreamChunk(chunk, modelId);
18908
+ yield this.parseStreamChunk(chunk, modelId, counter);
17103
18909
  } catch {
17104
18910
  }
17105
18911
  }
@@ -17110,6 +18916,48 @@ var VertexProvider = class extends BaseProvider {
17110
18916
  }
17111
18917
  }
17112
18918
  // ─── Internals ───────────────────────────────────────────────────────────
18919
+ /**
18920
+ * Vertex `:predict` embeddings (text-embedding-00x, text-multilingual-embedding,
18921
+ * gemini-embedding-001). `inputType` maps to the task type; `dimensions` to
18922
+ * `outputDimensionality`. gemini-embedding takes one instance per request, the
18923
+ * text-embedding models up to `batchSize` (default 100).
18924
+ */
18925
+ async embed(request) {
18926
+ const model = requireEmbeddingModel(this.providerName, request.model);
18927
+ const perRequest = request.batchSize ?? (/gemini-embedding/i.test(model) ? 1 : 100);
18928
+ const url = `https://${this.location}-aiplatform.googleapis.com/v1/projects/${this.projectId}/locations/${this.location}/publishers/google/models/${model}:predict`;
18929
+ const taskType = request.inputType ? Object.prototype.hasOwnProperty.call(GEMINI_TASK_TYPES, request.inputType) ? GEMINI_TASK_TYPES[request.inputType] : request.inputType.toUpperCase() : void 0;
18930
+ const vectors = [];
18931
+ const raw = [];
18932
+ let inputTokens = 0;
18933
+ for (const batch of chunkArray(request.input, perRequest)) {
18934
+ const body = {
18935
+ instances: batch.map((content) => ({ content, ...taskType ? { task_type: taskType } : {} }))
18936
+ };
18937
+ const { parameters: extraParameters, ...extraRest } = request.extra ?? {};
18938
+ const parameters = {
18939
+ ...request.dimensions != null ? { outputDimensionality: request.dimensions } : {},
18940
+ // Caller-supplied parameters are merged in, not replacing the above (an
18941
+ // explicit extra.parameters.outputDimensionality still wins).
18942
+ ...extraParameters && typeof extraParameters === "object" ? extraParameters : {}
18943
+ };
18944
+ if (Object.keys(parameters).length > 0) body.parameters = parameters;
18945
+ Object.assign(body, extraRest);
18946
+ const res = await this.doFetch(url, body, request.signal, model);
18947
+ const json = await res.json();
18948
+ raw.push(json);
18949
+ for (const p of json.predictions ?? []) {
18950
+ const values = p.embeddings?.values;
18951
+ if (!Array.isArray(values)) {
18952
+ throw new ProviderError("Vertex embeddings response has a prediction without an embeddings.values array", this.providerName, void 0, json);
18953
+ }
18954
+ vectors.push(values);
18955
+ inputTokens += p.embeddings?.statistics?.token_count ?? 0;
18956
+ }
18957
+ }
18958
+ assertVectorCount(this.providerName, vectors, request.input.length);
18959
+ return { model, vectors, usage: { inputTokens, totalTokens: inputTokens }, raw };
18960
+ }
17113
18961
  buildUrl(modelId, stream) {
17114
18962
  const base = `https://${this.location}-aiplatform.googleapis.com/v1`;
17115
18963
  const path2 = `projects/${this.projectId}/locations/${this.location}/publishers/google/models/${modelId}`;
@@ -17207,7 +19055,9 @@ var VertexProvider = class extends BaseProvider {
17207
19055
  functionCall: {
17208
19056
  name: tc.name,
17209
19057
  args: safeJsonParse3(tc.arguments)
17210
- }
19058
+ },
19059
+ // Gemini 3 requires the signature of each functionCall echoed back.
19060
+ ...tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}
17211
19061
  });
17212
19062
  }
17213
19063
  return { role: "model", parts: parts2 };
@@ -17304,7 +19154,7 @@ var VertexProvider = class extends BaseProvider {
17304
19154
  sign.update(signingInput);
17305
19155
  const signature = sign.sign(sa.private_key, "base64url");
17306
19156
  const jwt = `${signingInput}.${signature}`;
17307
- const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
19157
+ const tokenRes = await this.httpFetch("https://oauth2.googleapis.com/token", {
17308
19158
  method: "POST",
17309
19159
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
17310
19160
  body: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}`
@@ -17316,20 +19166,38 @@ var VertexProvider = class extends BaseProvider {
17316
19166
  const tokenJson = await tokenRes.json();
17317
19167
  return tokenJson.access_token;
17318
19168
  }
17319
- async doFetch(url, body, signal) {
19169
+ async doFetch(url, body, signal, model = "") {
17320
19170
  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),
19171
+ const bodyStr = JSON.stringify(body);
19172
+ const res = await this.doFetchWithRetry(
19173
+ () => this.httpFetch(url, {
19174
+ method: "POST",
19175
+ headers: {
19176
+ "Content-Type": "application/json",
19177
+ Authorization: `Bearer ${token}`,
19178
+ ...this.defaultHeaders
19179
+ },
19180
+ body: bodyStr,
19181
+ signal
19182
+ }),
17329
19183
  signal
17330
- });
19184
+ );
17331
19185
  if (!res.ok) {
17332
19186
  const text = await res.text().catch(() => "");
19187
+ if (res.status === 400) {
19188
+ const parsed = parseModelError(text);
19189
+ throw new ModelRequestError({
19190
+ status: 400,
19191
+ code: parsed?.kind === "unsupported_value" ? "unsupported_value" : parsed?.kind === "unsupported_parameter" ? "unsupported_parameter" : "model_request_invalid",
19192
+ param: parsed?.param,
19193
+ value: parsed?.value,
19194
+ supportedValues: parsed?.supportedValues,
19195
+ provider: this.providerName,
19196
+ model,
19197
+ message: `Vertex AI API error ${res.status}: ${text}`,
19198
+ responseBody: text
19199
+ });
19200
+ }
17333
19201
  throw new ProviderError(
17334
19202
  `Vertex AI API error ${res.status}: ${text}`,
17335
19203
  this.providerName,
@@ -17357,7 +19225,8 @@ var VertexProvider = class extends BaseProvider {
17357
19225
  toolCalls.push({
17358
19226
  id: `call_${toolCallIdx++}`,
17359
19227
  name: part.functionCall.name,
17360
- arguments: JSON.stringify(part.functionCall.args ?? {})
19228
+ arguments: JSON.stringify(part.functionCall.args ?? {}),
19229
+ ...part.thoughtSignature ? { thoughtSignature: part.thoughtSignature } : {}
17361
19230
  });
17362
19231
  }
17363
19232
  }
@@ -17378,11 +19247,12 @@ var VertexProvider = class extends BaseProvider {
17378
19247
  toolCalls,
17379
19248
  usage: tokenUsage,
17380
19249
  finishReason: mapGeminiFinishReason(candidate?.finishReason),
19250
+ ...typeof candidate?.finishReason === "string" ? { rawFinishReason: candidate.finishReason } : {},
17381
19251
  reasoning: thoughtParts.length > 0 ? { summary: thoughtParts.join("\n").trim() || void 0 } : void 0,
17382
19252
  raw: json
17383
19253
  };
17384
19254
  }
17385
- parseStreamChunk(chunk, modelId) {
19255
+ parseStreamChunk(chunk, modelId, counter) {
17386
19256
  const candidate = chunk.candidates?.[0];
17387
19257
  const parts = candidate?.content?.parts ?? [];
17388
19258
  const delta = {};
@@ -17396,9 +19266,12 @@ var VertexProvider = class extends BaseProvider {
17396
19266
  } else if (part.functionCall) {
17397
19267
  if (!delta.toolCalls) delta.toolCalls = [];
17398
19268
  delta.toolCalls.push({
17399
- id: `call_${delta.toolCalls.length}`,
19269
+ // Unique across the whole stream: the adapter merges chunks by id.
19270
+ id: `call_${counter.next}`,
19271
+ index: counter.next++,
17400
19272
  name: part.functionCall.name,
17401
- arguments: JSON.stringify(part.functionCall.args ?? {})
19273
+ arguments: JSON.stringify(part.functionCall.args ?? {}),
19274
+ ...part.thoughtSignature ? { thoughtSignature: part.thoughtSignature } : {}
17402
19275
  });
17403
19276
  }
17404
19277
  }
@@ -17420,10 +19293,21 @@ var VertexProvider = class extends BaseProvider {
17420
19293
  model: modelId,
17421
19294
  delta,
17422
19295
  usage,
17423
- finishReason: mapGeminiFinishReason(candidate?.finishReason)
19296
+ finishReason: mapGeminiFinishReason(candidate?.finishReason),
19297
+ ...typeof candidate?.finishReason === "string" ? { rawFinishReason: candidate.finishReason } : {}
17424
19298
  };
17425
19299
  }
17426
19300
  };
19301
+ var GEMINI_TASK_TYPES = {
19302
+ search_document: "RETRIEVAL_DOCUMENT",
19303
+ search_query: "RETRIEVAL_QUERY",
19304
+ classification: "CLASSIFICATION",
19305
+ clustering: "CLUSTERING",
19306
+ semantic_similarity: "SEMANTIC_SIMILARITY",
19307
+ question_answering: "QUESTION_ANSWERING",
19308
+ fact_verification: "FACT_VERIFICATION",
19309
+ code_retrieval_query: "CODE_RETRIEVAL_QUERY"
19310
+ };
17427
19311
  function mapGeminiFinishReason(reason) {
17428
19312
  switch (reason) {
17429
19313
  case "STOP":
@@ -17495,6 +19379,9 @@ exports.HOOK_NAMES = HOOK_NAMES;
17495
19379
  exports.HookTimeoutError = HookTimeoutError;
17496
19380
  exports.InMemoryMemoryStore = InMemoryMemoryStore;
17497
19381
  exports.JevProvider = JevProvider;
19382
+ exports.MAX_ADJUSTMENTS = MAX_ADJUSTMENTS;
19383
+ exports.MODEL_RULES = MODEL_RULES;
19384
+ exports.ModelRequestError = ModelRequestError;
17498
19385
  exports.NativeJsonSchemaStrategy = NativeJsonSchemaStrategy;
17499
19386
  exports.OpenAICompatibleProvider = OpenAICompatibleProvider;
17500
19387
  exports.OpenAIProvider = OpenAIProvider;
@@ -17506,7 +19393,10 @@ exports.TOOL_INPUT_DIGEST_KEY = TOOL_INPUT_DIGEST_KEY;
17506
19393
  exports.ToolBasedStrategy = ToolBasedStrategy;
17507
19394
  exports.VertexProvider = VertexProvider;
17508
19395
  exports.agentVerdictRule = agentVerdictRule;
19396
+ exports.allowsSampling = allowsSampling;
17509
19397
  exports.appendBoundTools = appendBoundTools;
19398
+ exports.applyDropParams = applyDropParams;
19399
+ exports.asModelRequestError = asModelRequestError;
17510
19400
  exports.auditLog = auditLog;
17511
19401
  exports.azureContentSafety = azureContentSafety;
17512
19402
  exports.bedrockGuardrail = bedrockGuardrail;
@@ -17521,6 +19411,7 @@ exports.canOpenSkill = canOpenSkill;
17521
19411
  exports.canSpawn = canSpawn;
17522
19412
  exports.captureSnapshot = captureSnapshot;
17523
19413
  exports.checkpointing = checkpointing;
19414
+ exports.clearLearnedModelRules = clearLearnedModelRules;
17524
19415
  exports.codePresenceRule = codePresenceRule;
17525
19416
  exports.coerceToolArgs = coerceToolArgs;
17526
19417
  exports.cognipeerGuardrail = cognipeerGuardrail;
@@ -17583,6 +19474,7 @@ exports.detectTextFormat = detectTextFormat;
17583
19474
  exports.detectVolatileContent = detectVolatileContent;
17584
19475
  exports.digestToolInputArguments = digestToolInputArguments;
17585
19476
  exports.digestToolInputValue = digestToolInputValue;
19477
+ exports.effortBudget = effortBudget;
17586
19478
  exports.emptyUsage = emptyUsage;
17587
19479
  exports.evaluateGuardrails = evaluateGuardrails;
17588
19480
  exports.extractLatestUserQuery = extractLatestUserQuery;
@@ -17596,6 +19488,7 @@ exports.fromLangchainTools = fromLangchainTools;
17596
19488
  exports.fromNativeProvider = fromNativeProvider;
17597
19489
  exports.generateSpanId = generateSpanId;
17598
19490
  exports.generateTraceId = generateTraceId;
19491
+ exports.getLearnedModelRulePatch = getLearnedModelRulePatch;
17599
19492
  exports.getModelCapabilities = getModelCapabilities;
17600
19493
  exports.getNestedValue = getNestedValue;
17601
19494
  exports.getResolvedSmartConfig = getResolvedSmartConfig;
@@ -17616,12 +19509,16 @@ exports.jsonSchemaRule = jsonSchemaRule;
17616
19509
  exports.langfuseDefaultPayload = defaultBuildPayload;
17617
19510
  exports.langfuseTracing = langfuseTracing;
17618
19511
  exports.languageGuard = languageGuard;
19512
+ exports.listLearnedModelRules = listLearnedModelRules;
17619
19513
  exports.loadSkillsFromDisk = loadSkillsFromDisk;
17620
19514
  exports.mapConsoleHookVerdict = mapConsoleHookVerdict;
19515
+ exports.mapReasoningEffort = mapReasoningEffort;
17621
19516
  exports.mapTextParts = mapTextParts;
17622
19517
  exports.mcp = mcp;
17623
19518
  exports.mediaKindOf = mediaKindOf;
17624
19519
  exports.mediaPolicy = mediaPolicy;
19520
+ exports.mergeRulePatch = mergeRulePatch;
19521
+ exports.modelIdCandidates = modelIdCandidates;
17625
19522
  exports.normalizeSmartAgentOptions = normalizeSmartAgentOptions;
17626
19523
  exports.normalizeVerdicts = normalizeVerdicts;
17627
19524
  exports.openAIModeration = openAIModeration;
@@ -17629,6 +19526,7 @@ exports.otelTracing = otelTracing;
17629
19526
  exports.otlpSink = otlpSink;
17630
19527
  exports.outputGuard = outputGuard;
17631
19528
  exports.parseApprovalDecision = parseApprovalDecision;
19529
+ exports.parseModelError = parseModelError;
17632
19530
  exports.parseSkillFrontmatter = parseSkillFrontmatter;
17633
19531
  exports.pathSandbox = pathSandbox;
17634
19532
  exports.piiRedaction = piiRedaction;
@@ -17644,11 +19542,13 @@ exports.readMemoryFacts = readMemoryFacts;
17644
19542
  exports.redactText = redactText;
17645
19543
  exports.regexGuardrail = regexGuardrail;
17646
19544
  exports.regexRule = regexRule;
19545
+ exports.rememberModelRulePatch = rememberModelRulePatch;
17647
19546
  exports.replaceTextContent = replaceTextContent;
17648
19547
  exports.resolveAvailableSkills = resolveAvailableSkills;
17649
19548
  exports.resolveAvailableSubagents = resolveAvailableSubagents;
17650
19549
  exports.resolveInputRetention = resolveInputRetention;
17651
19550
  exports.resolveMemoryStore = resolveMemoryStore;
19551
+ exports.resolveModelRule = resolveModelRule;
17652
19552
  exports.resolveReasoning = resolveReasoning;
17653
19553
  exports.resolveStrategy = resolveStrategy;
17654
19554
  exports.resolveSubagentTools = resolveSubagentTools;
@@ -17659,6 +19559,7 @@ exports.responseCache = responseCache;
17659
19559
  exports.restoreSnapshot = restoreSnapshot;
17660
19560
  exports.restoreToolCalls = restoreToolCalls;
17661
19561
  exports.runSmartAgentEvalHarness = runSmartAgentEvalHarness;
19562
+ exports.runWithModelAdjustments = runWithModelAdjustments;
17662
19563
  exports.scoreItemsByRelevance = scoreItemsByRelevance;
17663
19564
  exports.searchSkills = searchSkills;
17664
19565
  exports.seedChildMessages = seedChildMessages;
@@ -17667,7 +19568,9 @@ exports.sessionMetrics = sessionMetrics;
17667
19568
  exports.setTokenCounter = setTokenCounter;
17668
19569
  exports.startStreamingSession = startStreamingSession;
17669
19570
  exports.summarizeObject = summarizeObject;
19571
+ exports.supportsAdaptiveThinking = supportsAdaptiveThinking;
17670
19572
  exports.textFromContent = textFromContent;
19573
+ exports.toMessageUsage = toMessageUsage;
17671
19574
  exports.toStrictCompatible = toStrictCompatible;
17672
19575
  exports.toStrictToolSchema = toStrictToolSchema;
17673
19576
  exports.tokenize = tokenize;