@averyyy/pi-ai 1.2.0-piclient.1 → 1.3.0-piclient.1

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.
Files changed (94) hide show
  1. package/README.md +5 -34
  2. package/dist/api/anthropic-messages.d.ts.map +1 -1
  3. package/dist/api/anthropic-messages.js +59 -54
  4. package/dist/api/anthropic-messages.js.map +1 -1
  5. package/dist/api/bedrock-converse-stream.js +16 -18
  6. package/dist/api/bedrock-converse-stream.js.map +1 -1
  7. package/dist/api/google-antigravity.d.ts +13 -0
  8. package/dist/api/google-antigravity.d.ts.map +1 -0
  9. package/dist/api/google-antigravity.js +291 -0
  10. package/dist/api/google-antigravity.js.map +1 -0
  11. package/dist/api/google-shared.d.ts +1 -1
  12. package/dist/api/google-shared.d.ts.map +1 -1
  13. package/dist/api/google-shared.js +2 -0
  14. package/dist/api/google-shared.js.map +1 -1
  15. package/dist/api/mistral-conversations.js +2 -2
  16. package/dist/api/mistral-conversations.js.map +1 -1
  17. package/dist/api/openai-completions.d.ts.map +1 -1
  18. package/dist/api/openai-completions.js +29 -27
  19. package/dist/api/openai-completions.js.map +1 -1
  20. package/dist/api/openai-responses-shared.d.ts +10 -0
  21. package/dist/api/openai-responses-shared.d.ts.map +1 -1
  22. package/dist/api/openai-responses-shared.js +15 -2
  23. package/dist/api/openai-responses-shared.js.map +1 -1
  24. package/dist/api/openai-responses.d.ts.map +1 -1
  25. package/dist/api/openai-responses.js +21 -4
  26. package/dist/api/openai-responses.js.map +1 -1
  27. package/dist/api/transform-messages.d.ts +16 -1
  28. package/dist/api/transform-messages.d.ts.map +1 -1
  29. package/dist/api/transform-messages.js +28 -15
  30. package/dist/api/transform-messages.js.map +1 -1
  31. package/dist/auth/oauth/google-antigravity.d.ts +5 -0
  32. package/dist/auth/oauth/google-antigravity.d.ts.map +1 -0
  33. package/dist/auth/oauth/google-antigravity.js +259 -0
  34. package/dist/auth/oauth/google-antigravity.js.map +1 -0
  35. package/dist/auth/oauth/load.d.ts +8 -1
  36. package/dist/auth/oauth/load.d.ts.map +1 -1
  37. package/dist/auth/oauth/load.js +11 -0
  38. package/dist/auth/oauth/load.js.map +1 -1
  39. package/dist/bun-oauth.d.ts.map +1 -1
  40. package/dist/bun-oauth.js +3 -0
  41. package/dist/bun-oauth.js.map +1 -1
  42. package/dist/compat.d.ts.map +1 -1
  43. package/dist/compat.js +2 -0
  44. package/dist/compat.js.map +1 -1
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js.map +1 -1
  48. package/dist/providers/all.d.ts.map +1 -1
  49. package/dist/providers/all.js +2 -0
  50. package/dist/providers/all.js.map +1 -1
  51. package/dist/providers/compat-schema.d.ts +3 -0
  52. package/dist/providers/compat-schema.d.ts.map +1 -1
  53. package/dist/providers/compat-schema.js +8 -0
  54. package/dist/providers/compat-schema.js.map +1 -1
  55. package/dist/providers/data/.manifest.json +1 -1
  56. package/dist/providers/data/anthropic.json +1 -1
  57. package/dist/providers/data/fireworks.json +1 -1
  58. package/dist/providers/data/github-copilot.json +1 -1
  59. package/dist/providers/data/kimi-coding.json +1 -1
  60. package/dist/providers/data/meta.json +1 -1
  61. package/dist/providers/data/nvidia.json +1 -1
  62. package/dist/providers/data/openai-codex.json +1 -1
  63. package/dist/providers/data/openai.json +1 -1
  64. package/dist/providers/data/opencode-go.json +1 -1
  65. package/dist/providers/data/opencode.json +1 -1
  66. package/dist/providers/data/openrouter.json +1 -1
  67. package/dist/providers/data/radius.json +1 -1
  68. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  69. package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
  70. package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
  71. package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
  72. package/dist/providers/data/xiaomi.json +1 -1
  73. package/dist/providers/google-antigravity.d.ts +3 -0
  74. package/dist/providers/google-antigravity.d.ts.map +1 -0
  75. package/dist/providers/google-antigravity.js +27 -0
  76. package/dist/providers/google-antigravity.js.map +1 -0
  77. package/dist/providers/kimi-coding.d.ts +1 -1
  78. package/dist/providers/kimi-coding.js +3 -3
  79. package/dist/providers/kimi-coding.js.map +1 -1
  80. package/dist/providers/opencode-go.d.ts +3 -1
  81. package/dist/providers/opencode-go.d.ts.map +1 -1
  82. package/dist/providers/opencode-go.js +2 -0
  83. package/dist/providers/opencode-go.js.map +1 -1
  84. package/dist/types.d.ts +9 -2
  85. package/dist/types.d.ts.map +1 -1
  86. package/dist/types.js.map +1 -1
  87. package/dist/utils/json-parse.d.ts +10 -0
  88. package/dist/utils/json-parse.d.ts.map +1 -1
  89. package/dist/utils/json-parse.js +26 -1
  90. package/dist/utils/json-parse.js.map +1 -1
  91. package/dist/utils/validation.d.ts.map +1 -1
  92. package/dist/utils/validation.js +3 -0
  93. package/dist/utils/validation.js.map +1 -1
  94. package/package.json +2 -2
package/README.md CHANGED
@@ -26,7 +26,6 @@ Unified LLM API with provider collections, automatic auth resolution, token and
26
26
  - [Streaming Tool Calls with Partial JSON](#streaming-tool-calls-with-partial-json)
27
27
  - [Validating Tool Arguments](#validating-tool-arguments)
28
28
  - [Complete Event Reference](#complete-event-reference)
29
- - [Compact Assistant Message Frames](#compact-assistant-message-frames)
30
29
  - [Image Input](#image-input)
31
30
  - [Image Generation](#image-generation)
32
31
  - [Classification](#classification)
@@ -723,34 +722,6 @@ All streaming events emitted during assistant message generation:
723
722
 
724
723
  Streaming events for different content blocks are not guaranteed to be contiguous. Providers may emit deltas for text, thinking, and tool calls in the same upstream chunk, and pi may surface corresponding events interleaved, for example `text_start`, `text_delta`, `toolcall_start`, `text_delta`, `toolcall_delta`. Consumers must use `contentIndex` to associate each delta/end event with its block and must not assume that a block's `*_start`/`*_delta`/`*_end` sequence is uninterrupted by events for other blocks.
725
724
 
726
- ### Compact Assistant Message Frames
727
-
728
- `AssistantMessageFrameEncoder` converts one stream into compact, persistable `AssistantMessageFrame` values. Create one encoder per stream and feed it every event in order. The encoder understands that `partial` is live: a block-start event consumed after the provider has already queued later deltas snapshots the current block once, and covered queued text/thinking deltas produce no duplicate frame. It retains only per-open-block counters plus, temporarily, the raw prefix needed to synchronize an already-advanced tool call. It never clones the growing full partial per token.
729
-
730
- The start frame contains message metadata with empty content. Text and thinking frames store each generated character at most once before the authoritative end frame. Tool calls that were already advanced when their start event was consumed use one compact JSON checkpoint before ordinary deltas resume. Terminal `done` and `error` events produce no frame because final message settlement is separate. A pre-generation `error` therefore produces no frames.
731
-
732
- `reduceAssistantMessageFrames()` is the canonical pure reducer. It reconstructs text, thinking, and tool-call arguments, including interleaved blocks identified by `contentIndex`, and rejects malformed sequences. It performs a single pass over the iterable and returns `undefined` when there is no start frame. End frames replace blocks with the provider's authoritative completed content and metadata. The reducer does not validate tool arguments against a TypeBox schema; call `validateToolCall` before execution.
733
-
734
- ```typescript
735
- import {
736
- AssistantMessageFrameEncoder,
737
- reduceAssistantMessageFrames,
738
- type AssistantMessageFrame,
739
- } from '@earendil-works/pi-ai';
740
-
741
- const encoder = new AssistantMessageFrameEncoder();
742
- const frames: AssistantMessageFrame[] = [];
743
- for await (const event of s) {
744
- const frame = encoder.encode(event);
745
- if (frame) frames.push(frame);
746
- }
747
-
748
- const reconstructedPartial = reduceAssistantMessageFrames(frames);
749
- const finalMessage = await s.result(); // Persist terminal settlement separately.
750
- ```
751
-
752
- An encoder rejects duplicate starts, updates before start, `done` before start, events after a terminal event, duplicate block starts, and block-kind mismatches. An `error` before start is valid and returns no frame.
753
-
754
725
  ## Image Input
755
726
 
756
727
  Models with vision capabilities can process images. You can check if a model supports images via the `input` property. If you pass images to a non-vision model, they are silently ignored.
@@ -1527,13 +1498,13 @@ Notes:
1527
1498
 
1528
1499
  ## Cross-Provider Handoffs
1529
1500
 
1530
- The library supports seamless handoffs between different LLM providers within the same conversation. This allows you to switch models mid-conversation while preserving context, including thinking blocks, tool calls, and tool results.
1501
+ The library supports seamless handoffs between different LLM providers within the same conversation. This allows you to switch models mid-conversation while preserving context, including text, tool calls, and tool results.
1531
1502
 
1532
1503
  When messages from one provider are sent to a different provider, the library automatically transforms them for compatibility:
1533
1504
 
1534
1505
  - **User and tool result messages** are passed through unchanged
1535
- - **Assistant messages from the same provider/API** are preserved as-is
1536
- - **Assistant messages from different providers** have their thinking blocks converted to text with `<thinking>` tags
1506
+ - **Assistant messages from the same provider, API, and model** are preserved as-is, including thinking blocks
1507
+ - **Assistant messages from a different model** have their thinking blocks dropped. Signatures and encrypted reasoning only work with the model that produced them, and readable thinking sent as plain text makes the new model treat it as part of the reply
1537
1508
  - **Tool calls and regular text** are preserved unchanged
1538
1509
 
1539
1510
  ```typescript
@@ -1554,7 +1525,7 @@ const claude = models.getModel('anthropic', 'claude-sonnet-4-5')!;
1554
1525
  context.messages.push({ role: 'user', content: 'What is 25 * 18?', timestamp: Date.now() });
1555
1526
  context.messages.push(await models.completeSimple(claude, context, { reasoning: 'medium' }));
1556
1527
 
1557
- // Switch to GPT-5 - it will see Claude's thinking as <thinking> tagged text
1528
+ // Switch to GPT-5 - it sees Claude's answer, but not Claude's thinking
1558
1529
  const gpt5 = models.getModel('openai', 'gpt-5-mini')!;
1559
1530
  context.messages.push({ role: 'user', content: 'Is that calculation correct?', timestamp: Date.now() });
1560
1531
  context.messages.push(await models.complete(gpt5, context));
@@ -1565,7 +1536,7 @@ context.messages.push({ role: 'user', content: 'What was the original question?'
1565
1536
  const geminiResponse = await models.complete(gemini, context);
1566
1537
  ```
1567
1538
 
1568
- All providers can handle messages from other providers — text, tool calls and results (including images), thinking blocks (transformed to tagged text), and aborted messages with partial content. This enables flexible workflows: start with a fast model, switch to a more capable one for complex reasoning, or maintain continuity across provider outages.
1539
+ All providers can handle messages from other providers — text, tool calls and results (including images), thinking blocks (dropped when the model changes), and aborted messages with partial content. This enables flexible workflows: start with a fast model, switch to a more capable one for complex reasoning, or maintain continuity across provider outages.
1569
1540
 
1570
1541
  ## System Messages
1571
1542
 
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAiC,MAAM,mBAAmB,CAAC;AAoBlE,OAAO,KAAK,EASX,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAMb,MAAM,aAAa,CAAC;AAmJrB,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AA2ChE,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB;AAwRD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CA2WzE,CAAC;AA2BF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CAgDlF,CAAC"}
1
+ {"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAiC,MAAM,mBAAmB,CAAC;AAoBlE,OAAO,KAAK,EASX,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAMb,MAAM,aAAa,CAAC;AAuJrB,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AA2ChE,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB;AAwRD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CA0WzE,CAAC;AA2BF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CAgDlF,CAAC"}
@@ -4,7 +4,7 @@ import { calculateCost } from "../models.js";
4
4
  import { appendAssistantMessageDiagnostic } from "../utils/diagnostics.js";
5
5
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
6
6
  import { headersToRecord } from "../utils/headers.js";
7
- import { parseJsonWithRepair, parseStreamingJson } from "../utils/json-parse.js";
7
+ import { parseJsonWithRepair, parseStreamingJson, parseToolCallArguments } from "../utils/json-parse.js";
8
8
  import { getPiUserAgent } from "../utils/pi-user-agent.js";
9
9
  import { getProviderEnvValue } from "../utils/provider-env.js";
10
10
  import { retryProviderRequest } from "../utils/provider-retry.js";
@@ -14,7 +14,7 @@ import { getCurrentTools, getInitialSystemMessage, resolveTranscript, } from "..
14
14
  import { getJsonSchemaToolParameters, resolveJsonSchemaStrictSampling, } from "./constrained-sampling.js";
15
15
  import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "./github-copilot-headers.js";
16
16
  import { adjustMaxTokensForThinking, buildBaseOptions, clampMaxTokensToContext } from "./simple-options.js";
17
- import { transformMessages } from "./transform-messages.js";
17
+ import { splitErrorToolResultImages, transformMessages } from "./transform-messages.js";
18
18
  /**
19
19
  * Resolve cache retention preference.
20
20
  * Defaults to "short" and uses PI_CACHE_RETENTION for backward compatibility.
@@ -75,6 +75,16 @@ const fromClaudeCodeName = (name, tools) => {
75
75
  }
76
76
  return name;
77
77
  };
78
+ function convertImageBlock(image) {
79
+ return {
80
+ type: "image",
81
+ source: {
82
+ type: "base64",
83
+ media_type: image.mimeType,
84
+ data: image.data,
85
+ },
86
+ };
87
+ }
78
88
  /**
79
89
  * Convert content blocks to Anthropic API format
80
90
  */
@@ -92,14 +102,7 @@ function convertContentBlocks(content) {
92
102
  text: sanitizeSurrogates(block.text),
93
103
  };
94
104
  }
95
- return {
96
- type: "image",
97
- source: {
98
- type: "base64",
99
- media_type: block.mimeType,
100
- data: block.data,
101
- },
102
- };
105
+ return convertImageBlock(block);
103
106
  });
104
107
  // If only images (no text), add placeholder text block
105
108
  const hasText = blocks.some((b) => b.type === "text");
@@ -378,14 +381,12 @@ export const stream = (model, context, options) => {
378
381
  const normalizedContext = resolveTranscript(context, getAnthropicCompat(model).supportsMidConvoSystemMessages);
379
382
  const currentTools = getCurrentTools(normalizedContext.messages);
380
383
  (async () => {
381
- const providerThinkingLevel = model.compat?.supportsMidConvoEffort ? (options?.effort ?? "high") : undefined;
382
384
  const output = {
383
385
  role: "assistant",
384
386
  content: [],
385
387
  api: model.api,
386
388
  provider: model.provider,
387
389
  model: model.id,
388
- ...(providerThinkingLevel === undefined ? {} : { providerThinkingLevel }),
389
390
  usage: {
390
391
  input: 0,
391
392
  output: 0,
@@ -435,6 +436,8 @@ export const stream = (model, context, options) => {
435
436
  for (;;) {
436
437
  options?.signal?.throwIfAborted();
437
438
  const built = buildParams(model, normalizedContext, isOAuth, options, disablePreferredStrictTools);
439
+ if (built.providerThinkingLevel)
440
+ output.providerThinkingLevel = built.providerThinkingLevel;
438
441
  let params = built.params;
439
442
  const nextParams = await options?.onPayload?.(params, model);
440
443
  if (nextParams !== undefined) {
@@ -625,7 +628,7 @@ export const stream = (model, context, options) => {
625
628
  });
626
629
  }
627
630
  else if (block.type === "toolCall") {
628
- block.arguments = parseStreamingJson(block.partialJson);
631
+ Object.assign(block, parseToolCallArguments(block.partialJson));
629
632
  // Finalize in-place and strip the scratch buffer so replay only
630
633
  // carries parsed arguments.
631
634
  delete block.partialJson;
@@ -911,14 +914,15 @@ function buildParams(model, context, isOAuthToken, options, disablePreferredStri
911
914
  };
912
915
  const initialTools = initialSystemMessage?.toolsAdded ?? [];
913
916
  const nativeToolChanges = compat.supportsMidConvoSystemMessages && compat.supportsMidConvoToolChanges && initialTools.length > 0;
914
- const converted = convertMessages(conversationMessages, isOAuthToken, cacheControl, compat.allowEmptySignature, model.compat?.supportsMidConvoEffort === true ? model.provider : undefined, nativeToolChanges ? convertToolDefinitions : undefined);
917
+ const thinkingDisabled = model.reasoning && options?.thinkingEnabled === false && model.thinkingLevelMap?.off !== null;
918
+ // Effort cannot change between messages while thinking is disabled, so "off" sends no effort markers.
919
+ const effortMessages = model.compat?.supportsMidConvoEffort === true && !thinkingDisabled;
920
+ const converted = convertMessages(conversationMessages, isOAuthToken, cacheControl, compat.allowEmptySignature, effortMessages ? model.provider : undefined, nativeToolChanges ? convertToolDefinitions : undefined);
915
921
  const activeEffort = options?.effort ?? "high";
916
922
  const betaFeatures = getBetaFeatures(model, context, isOAuthToken, nativeToolChanges, options);
917
923
  const params = {
918
924
  model: model.id,
919
- messages: model.compat?.supportsMidConvoEffort === true
920
- ? insertThinkingLevelMessages(converted, activeEffort)
921
- : converted.messages,
925
+ messages: effortMessages ? insertThinkingLevelMessages(converted, activeEffort) : converted.messages,
922
926
  max_tokens: options?.maxTokens ?? model.maxTokens,
923
927
  stream: true,
924
928
  ...(betaFeatures.length > 0 ? { betas: betaFeatures } : {}),
@@ -971,9 +975,14 @@ function buildParams(model, context, isOAuthToken, options, disablePreferredStri
971
975
  params.tools = convertToolDefinitions(tools, toolCacheControl);
972
976
  }
973
977
  }
974
- // Managed effort models always use adaptive thinking so prefix mismatches can
975
- // be dropped instead of surfacing as persistent 400 responses.
976
- if (model.compat?.supportsMidConvoEffort === true) {
978
+ if (thinkingDisabled) {
979
+ // No effort is sent, so the model default applies. Anthropic rejects `block_binding`
980
+ // with disabled thinking.
981
+ params.thinking = { type: "disabled" };
982
+ }
983
+ else if (effortMessages) {
984
+ // Managed effort models use adaptive thinking so prefix mismatches can
985
+ // be dropped instead of surfacing as persistent 400 responses.
977
986
  params.thinking = {
978
987
  type: "adaptive",
979
988
  display: options?.thinkingDisplay ?? "summarized",
@@ -981,29 +990,24 @@ function buildParams(model, context, isOAuthToken, options, disablePreferredStri
981
990
  };
982
991
  params.output_config = { effort: "high" };
983
992
  }
984
- else if (model.reasoning) {
985
- if (options?.thinkingEnabled) {
986
- // Default to "summarized" so Opus 4.7 and Mythos Preview behave like
987
- // older Claude 4 models (whose API default is also "summarized").
988
- const display = options.thinkingDisplay ?? "summarized";
989
- if (model.compat?.forceAdaptiveThinking === true) {
990
- // Adaptive thinking: Claude decides when and how much to think.
991
- params.thinking = { type: "adaptive", display };
992
- if (options.effort) {
993
- params.output_config = { effort: options.effort };
994
- }
995
- }
996
- else {
997
- // Budget-based thinking for older models
998
- params.thinking = {
999
- type: "enabled",
1000
- budget_tokens: options.thinkingBudgetTokens || 1024,
1001
- display,
1002
- };
993
+ else if (model.reasoning && options?.thinkingEnabled) {
994
+ // Default to "summarized" so Opus 4.7 and Mythos Preview behave like
995
+ // older Claude 4 models (whose API default is also "summarized").
996
+ const display = options.thinkingDisplay ?? "summarized";
997
+ if (model.compat?.forceAdaptiveThinking === true) {
998
+ // Adaptive thinking: Claude decides when and how much to think.
999
+ params.thinking = { type: "adaptive", display };
1000
+ if (options.effort) {
1001
+ params.output_config = { effort: options.effort };
1003
1002
  }
1004
1003
  }
1005
- else if (options?.thinkingEnabled === false && model.thinkingLevelMap?.off !== null) {
1006
- params.thinking = { type: "disabled" };
1004
+ else {
1005
+ // Budget-based thinking for older models
1006
+ params.thinking = {
1007
+ type: "enabled",
1008
+ budget_tokens: options.thinkingBudgetTokens || 1024,
1009
+ display,
1010
+ };
1007
1011
  }
1008
1012
  }
1009
1013
  if (options?.metadata) {
@@ -1024,17 +1028,17 @@ function buildParams(model, context, isOAuthToken, options, disablePreferredStri
1024
1028
  if (allowedFallbackModels && allowedFallbackModels.length > 0) {
1025
1029
  params.fallbacks = allowedFallbackModels.map((fallback) => ({ model: fallback.model }));
1026
1030
  }
1027
- return { params, hasPreferredStrictTools };
1031
+ return { params, hasPreferredStrictTools, providerThinkingLevel: effortMessages ? activeEffort : undefined };
1028
1032
  }
1029
1033
  // Normalize tool call IDs to match Anthropic's required pattern and length
1030
1034
  function normalizeToolCallId(id) {
1031
1035
  return id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
1032
1036
  }
1033
- function convertToolResult(msg) {
1037
+ function convertToolResult(msg, content = msg.content) {
1034
1038
  return {
1035
1039
  type: "tool_result",
1036
1040
  tool_use_id: msg.toolCallId,
1037
- content: convertContentBlocks(msg.content),
1041
+ content: convertContentBlocks(content),
1038
1042
  is_error: msg.isError,
1039
1043
  };
1040
1044
  }
@@ -1099,14 +1103,7 @@ convertToolDefinitions) {
1099
1103
  };
1100
1104
  }
1101
1105
  else {
1102
- return {
1103
- type: "image",
1104
- source: {
1105
- type: "base64",
1106
- media_type: item.mimeType,
1107
- data: item.data,
1108
- },
1109
- };
1106
+ return convertImageBlock(item);
1110
1107
  }
1111
1108
  });
1112
1109
  const filteredBlocks = blocks.filter((b) => {
@@ -1197,16 +1194,24 @@ convertToolDefinitions) {
1197
1194
  else if (msg.role === "toolResult") {
1198
1195
  // Collect all consecutive toolResult messages, needed for z.ai Anthropic endpoint.
1199
1196
  const toolResults = [];
1197
+ // Anthropic rejects error tool results that contain anything but text, so their images follow
1198
+ // all tool results of the turn, which must come first in the user message (#10794).
1199
+ const errorImages = [];
1200
1200
  let j = i;
1201
1201
  while (j < transformedMessages.length && transformedMessages[j].role === "toolResult") {
1202
- toolResults.push(convertToolResult(transformedMessages[j]));
1202
+ const toolMsg = transformedMessages[j];
1203
+ const split = splitErrorToolResultImages(toolMsg);
1204
+ toolResults.push(convertToolResult(toolMsg, split?.content));
1205
+ if (split) {
1206
+ errorImages.push({ type: "text", text: split.caption }, ...split.images.map(convertImageBlock));
1207
+ }
1203
1208
  j++;
1204
1209
  }
1205
1210
  // Skip the messages we've already processed.
1206
1211
  i = j - 1;
1207
1212
  params.push({
1208
1213
  role: "user",
1209
- content: toolResults,
1214
+ content: [...toolResults, ...errorImages],
1210
1215
  });
1211
1216
  }
1212
1217
  }