@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.
- package/README.md +5 -34
- package/dist/api/anthropic-messages.d.ts.map +1 -1
- package/dist/api/anthropic-messages.js +59 -54
- package/dist/api/anthropic-messages.js.map +1 -1
- package/dist/api/bedrock-converse-stream.js +16 -18
- package/dist/api/bedrock-converse-stream.js.map +1 -1
- package/dist/api/google-antigravity.d.ts +13 -0
- package/dist/api/google-antigravity.d.ts.map +1 -0
- package/dist/api/google-antigravity.js +291 -0
- package/dist/api/google-antigravity.js.map +1 -0
- package/dist/api/google-shared.d.ts +1 -1
- package/dist/api/google-shared.d.ts.map +1 -1
- package/dist/api/google-shared.js +2 -0
- package/dist/api/google-shared.js.map +1 -1
- package/dist/api/mistral-conversations.js +2 -2
- package/dist/api/mistral-conversations.js.map +1 -1
- package/dist/api/openai-completions.d.ts.map +1 -1
- package/dist/api/openai-completions.js +29 -27
- package/dist/api/openai-completions.js.map +1 -1
- package/dist/api/openai-responses-shared.d.ts +10 -0
- package/dist/api/openai-responses-shared.d.ts.map +1 -1
- package/dist/api/openai-responses-shared.js +15 -2
- package/dist/api/openai-responses-shared.js.map +1 -1
- package/dist/api/openai-responses.d.ts.map +1 -1
- package/dist/api/openai-responses.js +21 -4
- package/dist/api/openai-responses.js.map +1 -1
- package/dist/api/transform-messages.d.ts +16 -1
- package/dist/api/transform-messages.d.ts.map +1 -1
- package/dist/api/transform-messages.js +28 -15
- package/dist/api/transform-messages.js.map +1 -1
- package/dist/auth/oauth/google-antigravity.d.ts +5 -0
- package/dist/auth/oauth/google-antigravity.d.ts.map +1 -0
- package/dist/auth/oauth/google-antigravity.js +259 -0
- package/dist/auth/oauth/google-antigravity.js.map +1 -0
- package/dist/auth/oauth/load.d.ts +8 -1
- package/dist/auth/oauth/load.d.ts.map +1 -1
- package/dist/auth/oauth/load.js +11 -0
- package/dist/auth/oauth/load.js.map +1 -1
- package/dist/bun-oauth.d.ts.map +1 -1
- package/dist/bun-oauth.js +3 -0
- package/dist/bun-oauth.js.map +1 -1
- package/dist/compat.d.ts.map +1 -1
- package/dist/compat.js +2 -0
- package/dist/compat.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/providers/all.d.ts.map +1 -1
- package/dist/providers/all.js +2 -0
- package/dist/providers/all.js.map +1 -1
- package/dist/providers/compat-schema.d.ts +3 -0
- package/dist/providers/compat-schema.d.ts.map +1 -1
- package/dist/providers/compat-schema.js +8 -0
- package/dist/providers/compat-schema.js.map +1 -1
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/anthropic.json +1 -1
- package/dist/providers/data/fireworks.json +1 -1
- package/dist/providers/data/github-copilot.json +1 -1
- package/dist/providers/data/kimi-coding.json +1 -1
- package/dist/providers/data/meta.json +1 -1
- package/dist/providers/data/nvidia.json +1 -1
- package/dist/providers/data/openai-codex.json +1 -1
- package/dist/providers/data/openai.json +1 -1
- package/dist/providers/data/opencode-go.json +1 -1
- package/dist/providers/data/opencode.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/data/radius.json +1 -1
- package/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
- package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
- package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
- package/dist/providers/data/xiaomi.json +1 -1
- package/dist/providers/google-antigravity.d.ts +3 -0
- package/dist/providers/google-antigravity.d.ts.map +1 -0
- package/dist/providers/google-antigravity.js +27 -0
- package/dist/providers/google-antigravity.js.map +1 -0
- package/dist/providers/kimi-coding.d.ts +1 -1
- package/dist/providers/kimi-coding.js +3 -3
- package/dist/providers/kimi-coding.js.map +1 -1
- package/dist/providers/opencode-go.d.ts +3 -1
- package/dist/providers/opencode-go.d.ts.map +1 -1
- package/dist/providers/opencode-go.js +2 -0
- package/dist/providers/opencode-go.js.map +1 -1
- package/dist/types.d.ts +9 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/json-parse.d.ts +10 -0
- package/dist/utils/json-parse.d.ts.map +1 -1
- package/dist/utils/json-parse.js +26 -1
- package/dist/utils/json-parse.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +3 -0
- package/dist/utils/validation.js.map +1 -1
- 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
|
|
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
|
|
1536
|
-
- **Assistant messages from different
|
|
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
|
|
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 (
|
|
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;
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
975
|
-
|
|
976
|
-
|
|
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
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
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
|
|
1006
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
}
|