@opencode/ai 2.0.2 → 2.0.4
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/cache-policy.js +10 -2
- package/dist/effort-updates.d.ts +12 -0
- package/dist/effort-updates.js +23 -0
- package/dist/protocols/alibaba-messages.d.ts +4 -1
- package/dist/protocols/alibaba-responses.d.ts +3 -1
- package/dist/protocols/anthropic-messages.d.ts +11 -2
- package/dist/protocols/anthropic-messages.js +48 -17
- package/dist/protocols/bedrock-converse.js +17 -12
- package/dist/protocols/meta-messages.d.ts +4 -1
- package/dist/protocols/meta-responses.d.ts +8 -4
- package/dist/protocols/mistral-chat.js +5 -33
- package/dist/protocols/open-responses-channel.js +4 -0
- package/dist/protocols/open-responses.d.ts +27 -9
- package/dist/protocols/open-responses.js +24 -8
- package/dist/protocols/openai-chat.d.ts +7 -7
- package/dist/protocols/openai-chat.js +10 -4
- package/dist/protocols/openai-compatible-chat.d.ts +1 -1
- package/dist/protocols/openai-compatible-responses.d.ts +4 -2
- package/dist/protocols/openai-responses.d.ts +74 -31
- package/dist/protocols/openai-responses.js +27 -6
- package/dist/protocols/utils/bedrock-media.d.ts +10 -8
- package/dist/protocols/utils/bedrock-media.js +29 -5
- package/dist/protocols/utils/mistral-tool-id.d.ts +5 -0
- package/dist/protocols/utils/mistral-tool-id.js +31 -0
- package/dist/protocols/utils/open-responses-options.d.ts +2 -4
- package/dist/protocols/utils/open-responses-options.js +2 -2
- package/dist/protocols/utils/responses-compaction.js +3 -1
- package/dist/protocols/xai-responses.d.ts +4 -2
- package/dist/protocols/zai-chat.d.ts +1 -1
- package/dist/protocols/zai-messages.d.ts +4 -1
- package/dist/provider-error.js +40 -4
- package/dist/providers/alibaba.d.ts +15 -10
- package/dist/providers/amazon-bedrock-mantle.d.ts +5 -3
- package/dist/providers/anthropic-compatible.d.ts +4 -1
- package/dist/providers/anthropic.d.ts +4 -1
- package/dist/providers/azure.d.ts +19 -5
- package/dist/providers/azure.js +1 -8
- package/dist/providers/baseten.d.ts +2 -2
- package/dist/providers/cerebras.d.ts +2 -2
- package/dist/providers/cerebras.js +1 -1
- package/dist/providers/cloudflare-ai-gateway.d.ts +1024 -13
- package/dist/providers/cloudflare-ai-gateway.js +64 -27
- package/dist/providers/cloudflare-workers-ai.d.ts +3 -3
- package/dist/providers/cloudflare-workers-ai.js +1 -1
- package/dist/providers/deepinfra.d.ts +2 -2
- package/dist/providers/deepinfra.js +1 -1
- package/dist/providers/deepseek.d.ts +2 -2
- package/dist/providers/fireworks.d.ts +2 -2
- package/dist/providers/fireworks.js +1 -1
- package/dist/providers/google-vertex-chat.d.ts +1 -1
- package/dist/providers/google-vertex-messages.d.ts +4 -1
- package/dist/providers/google-vertex-responses.d.ts +4 -2
- package/dist/providers/google-vertex.js +5 -1
- package/dist/providers/groq.d.ts +2 -2
- package/dist/providers/meta.d.ts +9 -4
- package/dist/providers/meta.js +1 -1
- package/dist/providers/minimax.d.ts +9 -4
- package/dist/providers/moonshot.d.ts +9 -4
- package/dist/providers/moonshot.js +1 -0
- package/dist/providers/openai-compatible-responses.d.ts +4 -2
- package/dist/providers/openai-compatible.d.ts +1 -1
- package/dist/providers/openai.d.ts +10 -3
- package/dist/providers/openai.js +4 -1
- package/dist/providers/openrouter.d.ts +4 -4
- package/dist/providers/openrouter.js +1 -1
- package/dist/providers/togetherai.d.ts +2 -2
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/zai-coding-plan.d.ts +9 -4
- package/dist/providers/zai.d.ts +1 -1
- package/dist/route/client.d.ts +1 -0
- package/dist/route/client.js +3 -1
- package/dist/route/protocol.d.ts +2 -0
- package/dist/schema/messages.d.ts +22 -3
- package/dist/schema/messages.js +9 -1
- package/dist/schema/options.d.ts +6 -0
- package/dist/schema/options.js +7 -0
- package/package.json +3 -3
package/dist/cache-policy.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
// count against the four-breakpoint budget; auto only fills remaining slots.
|
|
13
13
|
import { CacheHint } from "./schema/options.js";
|
|
14
14
|
import { LLMRequest, Message, ToolDefinition } from "./schema/messages.js";
|
|
15
|
+
import { effortUpdate } from "./effort-updates.js";
|
|
15
16
|
const AUTO = {
|
|
16
17
|
tools: true,
|
|
17
18
|
system: true,
|
|
@@ -100,10 +101,17 @@ const markMessages = (messages, strategy, hint, budget) => {
|
|
|
100
101
|
return markMessageAt(messages, lastIndexOfRole(messages, "user"), hint, budget);
|
|
101
102
|
if (strategy === "latest-assistant")
|
|
102
103
|
return markMessageAt(messages, lastIndexOfRole(messages, "assistant"), hint, budget);
|
|
103
|
-
|
|
104
|
+
let start = messages.length;
|
|
105
|
+
let remaining = strategy.tail;
|
|
106
|
+
while (remaining > 0 && start > 0) {
|
|
107
|
+
start -= 1;
|
|
108
|
+
if (effortUpdate(messages[start]) === undefined)
|
|
109
|
+
remaining -= 1;
|
|
110
|
+
}
|
|
104
111
|
let next = messages;
|
|
105
112
|
for (let i = start; i < messages.length; i++)
|
|
106
|
-
|
|
113
|
+
if (effortUpdate(messages[i]) === undefined)
|
|
114
|
+
next = markMessageAt(next, i, hint, budget);
|
|
107
115
|
return next;
|
|
108
116
|
};
|
|
109
117
|
const countHints = (request) => countToolHints(request.tools) +
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { LLMRequest, type EffortPart, type Message } from "./schema/messages.js";
|
|
2
|
+
export declare const effortUpdate: (message: Message) => EffortPart | undefined;
|
|
3
|
+
export declare const stripEffortUpdates: (request: LLMRequest) => LLMRequest<import("./index.js").LanguageModel<{
|
|
4
|
+
readonly [x: string]: unknown;
|
|
5
|
+
}, import("./route.js").CompactionOperations | undefined>>;
|
|
6
|
+
export declare const applyEffortUpdates: (request: LLMRequest) => LLMRequest;
|
|
7
|
+
export declare const resolveEffortUpdates: (request: LLMRequest, current: string | undefined) => {
|
|
8
|
+
request: LLMRequest<import("./index.js").LanguageModel<{
|
|
9
|
+
readonly [x: string]: unknown;
|
|
10
|
+
}, import("./route.js").CompactionOperations | undefined>>;
|
|
11
|
+
effort: string | undefined;
|
|
12
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Changing the top-level reasoning effort invalidates the provider prompt cache. Protocols with a native
|
|
2
|
+
// per-message update keep it frozen and lower `Message.effort(...)` markers instead; other routes strip them.
|
|
3
|
+
import { LLMRequest } from "./schema/messages.js";
|
|
4
|
+
export const effortUpdate = (message) => {
|
|
5
|
+
if (message.role !== "system" || message.content.length !== 1)
|
|
6
|
+
return undefined;
|
|
7
|
+
const part = message.content[0];
|
|
8
|
+
return part.type === "effort" ? part : undefined;
|
|
9
|
+
};
|
|
10
|
+
export const stripEffortUpdates = (request) => {
|
|
11
|
+
const messages = request.messages.filter((message) => effortUpdate(message) === undefined);
|
|
12
|
+
return messages.length === request.messages.length ? request : LLMRequest.update(request, { messages });
|
|
13
|
+
};
|
|
14
|
+
export const applyEffortUpdates = (request) => request.model.route.supportsEffortUpdates?.(request) ? request : stripEffortUpdates(request);
|
|
15
|
+
// Reverted or forked history can leave the last marker disagreeing with the requested effort.
|
|
16
|
+
export const resolveEffortUpdates = (request, current) => {
|
|
17
|
+
const updates = request.messages.flatMap((message) => effortUpdate(message) ?? []);
|
|
18
|
+
if (updates.length === 0)
|
|
19
|
+
return { request, effort: current };
|
|
20
|
+
if (updates.at(-1)?.effort !== current)
|
|
21
|
+
return { request: stripEffortUpdates(request), effort: current };
|
|
22
|
+
return { request, effort: updates[0]?.previous };
|
|
23
|
+
};
|
|
@@ -187,7 +187,6 @@ export declare const protocol: Protocol<{
|
|
|
187
187
|
readonly content: string | null;
|
|
188
188
|
})[];
|
|
189
189
|
} | {
|
|
190
|
-
readonly role: "system";
|
|
191
190
|
readonly content: readonly {
|
|
192
191
|
readonly type: "text";
|
|
193
192
|
readonly text: string;
|
|
@@ -196,6 +195,10 @@ export declare const protocol: Protocol<{
|
|
|
196
195
|
readonly ttl?: "1h" | "5m" | undefined;
|
|
197
196
|
} | undefined;
|
|
198
197
|
}[];
|
|
198
|
+
readonly role: "system";
|
|
199
|
+
readonly output_config?: {
|
|
200
|
+
readonly effort: string;
|
|
201
|
+
} | undefined;
|
|
199
202
|
})[];
|
|
200
203
|
readonly stream: true;
|
|
201
204
|
readonly metadata?: {
|
|
@@ -61,12 +61,15 @@ export declare const protocol: Protocol<{
|
|
|
61
61
|
readonly encrypted_content: string;
|
|
62
62
|
readonly id?: string | null | undefined;
|
|
63
63
|
} | {
|
|
64
|
+
readonly type: "message";
|
|
64
65
|
readonly role: "system";
|
|
65
66
|
readonly content: string;
|
|
66
67
|
} | {
|
|
68
|
+
readonly type: "message";
|
|
67
69
|
readonly role: "developer";
|
|
68
70
|
readonly content: string;
|
|
69
71
|
} | {
|
|
72
|
+
readonly type: "message";
|
|
70
73
|
readonly content: readonly ({
|
|
71
74
|
readonly type: "input_image";
|
|
72
75
|
readonly image_url: string;
|
|
@@ -83,7 +86,6 @@ export declare const protocol: Protocol<{
|
|
|
83
86
|
})[];
|
|
84
87
|
readonly role: "user";
|
|
85
88
|
readonly id?: string | undefined;
|
|
86
|
-
readonly type?: "message" | undefined;
|
|
87
89
|
readonly status?: string | undefined;
|
|
88
90
|
} | {
|
|
89
91
|
readonly type: "message";
|
|
@@ -286,6 +286,9 @@ export declare const AnthropicMessagesBody: Schema.Struct<{
|
|
|
286
286
|
readonly ttl: Schema.optional<Schema.Literals<readonly ["5m", "1h"]>>;
|
|
287
287
|
}>>;
|
|
288
288
|
}>>;
|
|
289
|
+
readonly output_config: Schema.optional<Schema.Struct<{
|
|
290
|
+
readonly effort: Schema.String;
|
|
291
|
+
}>>;
|
|
289
292
|
}>]>>;
|
|
290
293
|
tools: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
291
294
|
readonly name: Schema.String;
|
|
@@ -529,7 +532,6 @@ export declare const protocol: Protocol<{
|
|
|
529
532
|
readonly content: string | null;
|
|
530
533
|
})[];
|
|
531
534
|
} | {
|
|
532
|
-
readonly role: "system";
|
|
533
535
|
readonly content: readonly {
|
|
534
536
|
readonly type: "text";
|
|
535
537
|
readonly text: string;
|
|
@@ -538,6 +540,10 @@ export declare const protocol: Protocol<{
|
|
|
538
540
|
readonly ttl?: "1h" | "5m" | undefined;
|
|
539
541
|
} | undefined;
|
|
540
542
|
}[];
|
|
543
|
+
readonly role: "system";
|
|
544
|
+
readonly output_config?: {
|
|
545
|
+
readonly effort: string;
|
|
546
|
+
} | undefined;
|
|
541
547
|
})[];
|
|
542
548
|
readonly stream: true;
|
|
543
549
|
readonly metadata?: {
|
|
@@ -883,7 +889,6 @@ export declare const route: Route<{
|
|
|
883
889
|
readonly content: string | null;
|
|
884
890
|
})[];
|
|
885
891
|
} | {
|
|
886
|
-
readonly role: "system";
|
|
887
892
|
readonly content: readonly {
|
|
888
893
|
readonly type: "text";
|
|
889
894
|
readonly text: string;
|
|
@@ -892,6 +897,10 @@ export declare const route: Route<{
|
|
|
892
897
|
readonly ttl?: "1h" | "5m" | undefined;
|
|
893
898
|
} | undefined;
|
|
894
899
|
}[];
|
|
900
|
+
readonly role: "system";
|
|
901
|
+
readonly output_config?: {
|
|
902
|
+
readonly effort: string;
|
|
903
|
+
} | undefined;
|
|
895
904
|
})[];
|
|
896
905
|
readonly stream: true;
|
|
897
906
|
readonly metadata?: {
|
|
@@ -11,6 +11,7 @@ import { HttpTransport } from "../route/transport/index.js";
|
|
|
11
11
|
import { AIError, HttpOptions, LLMRequest, LLMEvent, mergeJsonRecords, Usage, } from "../schema/index.js";
|
|
12
12
|
import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js";
|
|
13
13
|
import { classifyProviderFailure } from "../provider-error.js";
|
|
14
|
+
import { effortUpdate, resolveEffortUpdates } from "../effort-updates.js";
|
|
14
15
|
import * as Cache from "./utils/cache.js";
|
|
15
16
|
import { Lifecycle } from "./utils/lifecycle.js";
|
|
16
17
|
import { ToolSchemaProjection } from "./utils/tool-schema.js";
|
|
@@ -19,6 +20,7 @@ const ADAPTER = "anthropic-messages";
|
|
|
19
20
|
export const DEFAULT_BASE_URL = "https://api.anthropic.com/v1";
|
|
20
21
|
export const PATH = "/messages";
|
|
21
22
|
export const DEFAULT_MAX_TOKENS = 32_000;
|
|
23
|
+
const DEFAULT_EFFORT = "high";
|
|
22
24
|
const SSE_EVENTS = new Set([
|
|
23
25
|
"message",
|
|
24
26
|
"message_start",
|
|
@@ -183,7 +185,11 @@ const AnthropicAssistantBlock = Schema.Union([
|
|
|
183
185
|
const AnthropicMessage = Schema.Union([
|
|
184
186
|
Schema.Struct({ role: Schema.Literal("user"), content: Schema.Array(AnthropicUserBlock) }),
|
|
185
187
|
Schema.Struct({ role: Schema.Literal("assistant"), content: Schema.Array(AnthropicAssistantBlock) }),
|
|
186
|
-
Schema.Struct({
|
|
188
|
+
Schema.Struct({
|
|
189
|
+
role: Schema.Literal("system"),
|
|
190
|
+
content: Schema.Array(AnthropicTextBlock),
|
|
191
|
+
output_config: Schema.optional(Schema.Struct({ effort: Schema.String })),
|
|
192
|
+
}),
|
|
187
193
|
]).pipe(Schema.toTaggedUnion("role"));
|
|
188
194
|
const AnthropicTool = Schema.Struct({
|
|
189
195
|
name: Schema.String,
|
|
@@ -697,6 +703,12 @@ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* (re
|
|
|
697
703
|
const providerMetadataKey = request.model.route.providerMetadataKey ?? String(request.model.provider);
|
|
698
704
|
for (const [index, message] of request.messages.entries()) {
|
|
699
705
|
if (message.role === "system") {
|
|
706
|
+
const update = effortUpdate(message);
|
|
707
|
+
if (update) {
|
|
708
|
+
// Accepted at any position, so the text-update placement rules do not apply.
|
|
709
|
+
messages.push({ role: "system", content: [], output_config: { effort: update.effort ?? DEFAULT_EFFORT } });
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
700
712
|
if (splitsLocalToolResults(request.messages, index))
|
|
701
713
|
return yield* invalid("Anthropic Messages system updates cannot split a local tool call from its tool result");
|
|
702
714
|
if (supportsNativeSystemUpdates(request) && canUseNativeSystemUpdate(request, index)) {
|
|
@@ -842,17 +854,11 @@ const resolveOptions = Effect.fn("AnthropicMessages.resolveOptions")(function* (
|
|
|
842
854
|
const outputConfigFormat = ProviderShared.isRecord(rawOutputConfig) && ProviderShared.isRecord(rawOutputConfig.format)
|
|
843
855
|
? rawOutputConfig.format
|
|
844
856
|
: undefined;
|
|
845
|
-
const output_config = outputConfigEffort === undefined && outputConfigFormat === undefined
|
|
846
|
-
? undefined
|
|
847
|
-
: {
|
|
848
|
-
...(outputConfigEffort === undefined ? {} : { effort: outputConfigEffort }),
|
|
849
|
-
...(outputConfigFormat === undefined ? {} : { format: outputConfigFormat }),
|
|
850
|
-
};
|
|
851
857
|
const thinking = yield* resolveThinking(input?.thinking);
|
|
852
858
|
return {
|
|
853
859
|
thinking: applyThinkingBindingDefault(request.model, thinking),
|
|
854
860
|
effort: outputConfigEffort,
|
|
855
|
-
|
|
861
|
+
format: outputConfigFormat,
|
|
856
862
|
service_tier,
|
|
857
863
|
metadata,
|
|
858
864
|
container,
|
|
@@ -860,17 +866,32 @@ const resolveOptions = Effect.fn("AnthropicMessages.resolveOptions")(function* (
|
|
|
860
866
|
cache_control,
|
|
861
867
|
};
|
|
862
868
|
});
|
|
869
|
+
// Accept gateway namespaces and Vertex suffixes without treating a snapshot date as a minor version.
|
|
870
|
+
const claudeVersion = (id) => {
|
|
871
|
+
const match = /(?:^|[./])claude-(?<family>[a-z]+)-(?<major>\d+)(?:[.-](?<minor>\d{1,2}))?(?:$|[-:@])/.exec(id.toLowerCase())?.groups;
|
|
872
|
+
if (!match)
|
|
873
|
+
return undefined;
|
|
874
|
+
return { family: match.family, major: Number(match.major), minor: Number(match.minor ?? 0) };
|
|
875
|
+
};
|
|
863
876
|
const supportsThinkingBlockBinding = (model) => {
|
|
864
877
|
const override = model.compatibility?.supportsThinkingBlockBinding;
|
|
865
878
|
if (override !== undefined)
|
|
866
879
|
return override;
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
880
|
+
const version = claudeVersion(model.id);
|
|
881
|
+
return version !== undefined && (version.major > 5 || (version.major === 5 && version.minor >= 1));
|
|
882
|
+
};
|
|
883
|
+
const supportsEffortUpdates = (model) => {
|
|
884
|
+
const override = model.compatibility?.supportsEffortUpdates;
|
|
885
|
+
if (override !== undefined)
|
|
886
|
+
return override;
|
|
887
|
+
const version = claudeVersion(model.id);
|
|
888
|
+
if (version === undefined)
|
|
870
889
|
return false;
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
890
|
+
if (version.family === "opus")
|
|
891
|
+
return version.major >= 5;
|
|
892
|
+
if (version.family !== "fable" && version.family !== "mythos")
|
|
893
|
+
return false;
|
|
894
|
+
return version.major > 5 || (version.major === 5 && version.minor >= 1);
|
|
874
895
|
};
|
|
875
896
|
const applyThinkingBindingDefault = (model, thinking) => {
|
|
876
897
|
if (thinking?.type === "disabled")
|
|
@@ -909,13 +930,15 @@ const resolveThinking = Effect.fn("AnthropicMessages.resolveThinking")(function*
|
|
|
909
930
|
});
|
|
910
931
|
const fromRequest = Effect.fn("AnthropicMessages.fromRequest")(function* (request) {
|
|
911
932
|
const management = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Schema.UndefinedOr(ContextManagement)))(request.providerOptions?.contextManagement);
|
|
933
|
+
const options = yield* resolveOptions(request);
|
|
934
|
+
const updates = resolveEffortUpdates(request, options.effort);
|
|
912
935
|
const generation = request.generation;
|
|
913
936
|
const toolSchemaCompatibility = request.model.compatibility?.toolSchema;
|
|
914
937
|
// Allocate the 4-breakpoint budget in invalidation order: tools → system →
|
|
915
938
|
// messages. Tools live highest in the cache hierarchy, so when callers
|
|
916
939
|
// over-mark we keep their tool hints and shed the message-tail ones first.
|
|
917
940
|
const breakpoints = Cache.newBreakpoints(ANTHROPIC_BREAKPOINT_CAP);
|
|
918
|
-
const flattened = ProviderShared.flattenToolRequest(request);
|
|
941
|
+
const flattened = ProviderShared.flattenToolRequest(updates.request);
|
|
919
942
|
const tools = flattened.tools.length === 0
|
|
920
943
|
? undefined
|
|
921
944
|
: flattened.tools.map((tool) => lowerTool(breakpoints, tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, toolSchemaCompatibility)));
|
|
@@ -933,7 +956,12 @@ const fromRequest = Effect.fn("AnthropicMessages.fromRequest")(function* (reques
|
|
|
933
956
|
if (breakpoints.dropped > 0) {
|
|
934
957
|
yield* Effect.logWarning(`Anthropic Messages: dropped ${breakpoints.dropped} cache breakpoint(s); the API allows at most ${ANTHROPIC_BREAKPOINT_CAP} per request.`);
|
|
935
958
|
}
|
|
936
|
-
const
|
|
959
|
+
const output_config = updates.effort === undefined && options.format === undefined
|
|
960
|
+
? undefined
|
|
961
|
+
: {
|
|
962
|
+
...(updates.effort === undefined ? {} : { effort: updates.effort }),
|
|
963
|
+
...(options.format === undefined ? {} : { format: options.format }),
|
|
964
|
+
};
|
|
937
965
|
const body = {
|
|
938
966
|
model: request.model.id,
|
|
939
967
|
system,
|
|
@@ -947,7 +975,7 @@ const fromRequest = Effect.fn("AnthropicMessages.fromRequest")(function* (reques
|
|
|
947
975
|
top_k: generation?.topK,
|
|
948
976
|
stop_sequences: generation?.stop,
|
|
949
977
|
thinking: options.thinking,
|
|
950
|
-
output_config
|
|
978
|
+
output_config,
|
|
951
979
|
// top-level passthrough per SDK MessageCreateParamsBase:4638,4643,4649,4654,4670
|
|
952
980
|
cache_control: options.cache_control,
|
|
953
981
|
container: options.container,
|
|
@@ -1404,6 +1432,7 @@ export const protocol = Protocol.make({
|
|
|
1404
1432
|
}),
|
|
1405
1433
|
step,
|
|
1406
1434
|
},
|
|
1435
|
+
supportsEffortUpdates: (request) => supportsEffortUpdates(request.model),
|
|
1407
1436
|
});
|
|
1408
1437
|
export const transport = () => {
|
|
1409
1438
|
const http = HttpTransport.httpJson({ framing });
|
|
@@ -1440,6 +1469,8 @@ function requiredBetaHeaders(body) {
|
|
|
1440
1469
|
const replaysCompaction = body.messages.some((message) => message.content.some((block) => block.type === "compaction"));
|
|
1441
1470
|
if (requestsCompaction || replaysCompaction)
|
|
1442
1471
|
betas.push("compact-2026-01-12");
|
|
1472
|
+
if (body.messages.some((message) => message.role === "system" && message.output_config !== undefined))
|
|
1473
|
+
betas.push("mid-conversation-output-config-2026-07-01");
|
|
1443
1474
|
const thinking = body.thinking;
|
|
1444
1475
|
if (thinking && thinking.type !== "disabled" && thinking.block_binding)
|
|
1445
1476
|
betas.push("thinking-binding-controls-2026-08-01");
|
|
@@ -10,6 +10,7 @@ import { BedrockAuth } from "./utils/bedrock-auth.js";
|
|
|
10
10
|
import { BedrockCache } from "./utils/bedrock-cache.js";
|
|
11
11
|
import { BedrockMedia } from "./utils/bedrock-media.js";
|
|
12
12
|
import { Lifecycle } from "./utils/lifecycle.js";
|
|
13
|
+
import { MistralToolID } from "./utils/mistral-tool-id.js";
|
|
13
14
|
import { ToolSchemaProjection } from "./utils/tool-schema.js";
|
|
14
15
|
import { ToolStream } from "./utils/tool-stream.js";
|
|
15
16
|
const ADAPTER = "bedrock-converse";
|
|
@@ -201,14 +202,15 @@ const removeEmptyToolInputKeys = (input) => {
|
|
|
201
202
|
return input;
|
|
202
203
|
return Object.fromEntries(Object.entries(input).flatMap(([key, value]) => (key === "" ? [] : [[key, removeEmptyToolInputKeys(value)]])));
|
|
203
204
|
};
|
|
204
|
-
const lowerToolCall = (part) => ({
|
|
205
|
+
const lowerToolCall = (part, normalizeID) => ({
|
|
205
206
|
toolUse: {
|
|
206
|
-
toolUseId: part.id,
|
|
207
|
-
|
|
207
|
+
toolUseId: normalizeID(part.id),
|
|
208
|
+
// Models can emit names that Converse rejects when replayed in history.
|
|
209
|
+
name: part.name.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64) || "_",
|
|
208
210
|
input: removeEmptyToolInputKeys(part.input),
|
|
209
211
|
},
|
|
210
212
|
});
|
|
211
|
-
const lowerToolResultContent = Effect.fn("BedrockConverse.lowerToolResultContent")(function* (part) {
|
|
213
|
+
const lowerToolResultContent = Effect.fn("BedrockConverse.lowerToolResultContent")(function* (part, documentNames) {
|
|
212
214
|
if (part.result.type === "text" || part.result.type === "error")
|
|
213
215
|
return [{ text: ProviderShared.toolResultText(part) }];
|
|
214
216
|
if (part.result.type === "json")
|
|
@@ -224,22 +226,25 @@ const lowerToolResultContent = Effect.fn("BedrockConverse.lowerToolResultContent
|
|
|
224
226
|
mediaType: item.mime,
|
|
225
227
|
data: item.uri,
|
|
226
228
|
filename: item.name,
|
|
227
|
-
});
|
|
228
|
-
content.push(media);
|
|
229
|
+
}, documentNames);
|
|
230
|
+
content.push(...media);
|
|
229
231
|
}
|
|
230
232
|
return content;
|
|
231
233
|
});
|
|
232
|
-
const lowerToolResult = Effect.fn("BedrockConverse.lowerToolResult")(function* (part) {
|
|
234
|
+
const lowerToolResult = Effect.fn("BedrockConverse.lowerToolResult")(function* (part, documentNames, normalizeID) {
|
|
233
235
|
return {
|
|
234
236
|
toolResult: {
|
|
235
|
-
toolUseId: part.id,
|
|
236
|
-
content: yield* lowerToolResultContent(part),
|
|
237
|
+
toolUseId: normalizeID(part.id),
|
|
238
|
+
content: yield* lowerToolResultContent(part, documentNames),
|
|
237
239
|
status: part.result.type === "error" ? "error" : "success",
|
|
238
240
|
},
|
|
239
241
|
};
|
|
240
242
|
});
|
|
241
243
|
const lowerMessages = Effect.fn("BedrockConverse.lowerMessages")(function* (request, breakpoints) {
|
|
242
244
|
const messages = [];
|
|
245
|
+
const documentNames = new Set();
|
|
246
|
+
// Mistral can reject replay IDs even when they satisfy Converse's broader ID syntax.
|
|
247
|
+
const normalizeID = request.model.id.includes("mistral.") ? MistralToolID.normalizer(request) : (id) => id;
|
|
243
248
|
const providerMetadataKey = request.model.route.providerMetadataKey ?? String(request.model.provider);
|
|
244
249
|
for (const message of request.messages) {
|
|
245
250
|
if (message.role === "system") {
|
|
@@ -262,7 +267,7 @@ const lowerMessages = Effect.fn("BedrockConverse.lowerMessages")(function* (requ
|
|
|
262
267
|
continue;
|
|
263
268
|
}
|
|
264
269
|
if (part.type === "media") {
|
|
265
|
-
content.push(yield* BedrockMedia.lower(part));
|
|
270
|
+
content.push(...(yield* BedrockMedia.lower(part, documentNames)));
|
|
266
271
|
continue;
|
|
267
272
|
}
|
|
268
273
|
}
|
|
@@ -304,7 +309,7 @@ const lowerMessages = Effect.fn("BedrockConverse.lowerMessages")(function* (requ
|
|
|
304
309
|
continue;
|
|
305
310
|
}
|
|
306
311
|
if (part.type === "tool-call") {
|
|
307
|
-
content.push(lowerToolCall(part));
|
|
312
|
+
content.push(lowerToolCall(part, normalizeID));
|
|
308
313
|
continue;
|
|
309
314
|
}
|
|
310
315
|
}
|
|
@@ -316,7 +321,7 @@ const lowerMessages = Effect.fn("BedrockConverse.lowerMessages")(function* (requ
|
|
|
316
321
|
for (const part of message.content) {
|
|
317
322
|
if (!ProviderShared.supportsContent(part, ["tool-result"]))
|
|
318
323
|
return yield* ProviderShared.unsupportedContent("Bedrock Converse", "tool", ["tool-result"]);
|
|
319
|
-
content.push(yield* lowerToolResult(part));
|
|
324
|
+
content.push(yield* lowerToolResult(part, documentNames, normalizeID));
|
|
320
325
|
const cachePoint = BedrockCache.block(breakpoints, part.cache);
|
|
321
326
|
if (cachePoint)
|
|
322
327
|
content.push(cachePoint);
|
|
@@ -175,7 +175,6 @@ export declare const protocol: Protocol<{
|
|
|
175
175
|
readonly content: string | null;
|
|
176
176
|
})[];
|
|
177
177
|
} | {
|
|
178
|
-
readonly role: "system";
|
|
179
178
|
readonly content: readonly {
|
|
180
179
|
readonly type: "text";
|
|
181
180
|
readonly text: string;
|
|
@@ -184,6 +183,10 @@ export declare const protocol: Protocol<{
|
|
|
184
183
|
readonly ttl?: "1h" | "5m" | undefined;
|
|
185
184
|
} | undefined;
|
|
186
185
|
}[];
|
|
186
|
+
readonly role: "system";
|
|
187
|
+
readonly output_config?: {
|
|
188
|
+
readonly effort: string;
|
|
189
|
+
} | undefined;
|
|
187
190
|
})[];
|
|
188
191
|
readonly stream: true;
|
|
189
192
|
readonly metadata?: {
|
|
@@ -76,12 +76,15 @@ export declare const protocol: Protocol<{
|
|
|
76
76
|
readonly encrypted_content: string;
|
|
77
77
|
readonly id?: string | null | undefined;
|
|
78
78
|
} | {
|
|
79
|
+
readonly type: "message";
|
|
79
80
|
readonly role: "system";
|
|
80
81
|
readonly content: string;
|
|
81
82
|
} | {
|
|
83
|
+
readonly type: "message";
|
|
82
84
|
readonly role: "developer";
|
|
83
85
|
readonly content: string;
|
|
84
86
|
} | {
|
|
87
|
+
readonly type: "message";
|
|
85
88
|
readonly content: readonly ({
|
|
86
89
|
readonly type: "input_image";
|
|
87
90
|
readonly image_url: string;
|
|
@@ -98,7 +101,6 @@ export declare const protocol: Protocol<{
|
|
|
98
101
|
})[];
|
|
99
102
|
readonly role: "user";
|
|
100
103
|
readonly id?: string | undefined;
|
|
101
|
-
readonly type?: "message" | undefined;
|
|
102
104
|
readonly status?: string | undefined;
|
|
103
105
|
} | {
|
|
104
106
|
readonly type: "message";
|
|
@@ -153,7 +155,7 @@ export declare const protocol: Protocol<{
|
|
|
153
155
|
readonly instructions?: string | undefined;
|
|
154
156
|
readonly reasoning?: {
|
|
155
157
|
readonly summary?: "auto" | "concise" | "detailed" | undefined;
|
|
156
|
-
readonly effort?: import("
|
|
158
|
+
readonly effort?: import("../schema/options.js").ReasoningEffort | undefined;
|
|
157
159
|
} | undefined;
|
|
158
160
|
readonly tools?: readonly ({
|
|
159
161
|
readonly type: "function";
|
|
@@ -332,12 +334,15 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
|
|
|
332
334
|
readonly encrypted_content: string;
|
|
333
335
|
readonly id?: string | null | undefined;
|
|
334
336
|
} | {
|
|
337
|
+
readonly type: "message";
|
|
335
338
|
readonly role: "system";
|
|
336
339
|
readonly content: string;
|
|
337
340
|
} | {
|
|
341
|
+
readonly type: "message";
|
|
338
342
|
readonly role: "developer";
|
|
339
343
|
readonly content: string;
|
|
340
344
|
} | {
|
|
345
|
+
readonly type: "message";
|
|
341
346
|
readonly content: readonly ({
|
|
342
347
|
readonly type: "input_image";
|
|
343
348
|
readonly image_url: string;
|
|
@@ -354,7 +359,6 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
|
|
|
354
359
|
})[];
|
|
355
360
|
readonly role: "user";
|
|
356
361
|
readonly id?: string | undefined;
|
|
357
|
-
readonly type?: "message" | undefined;
|
|
358
362
|
readonly status?: string | undefined;
|
|
359
363
|
} | {
|
|
360
364
|
readonly type: "message";
|
|
@@ -409,7 +413,7 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
|
|
|
409
413
|
readonly instructions?: string | undefined;
|
|
410
414
|
readonly reasoning?: {
|
|
411
415
|
readonly summary?: "auto" | "concise" | "detailed" | undefined;
|
|
412
|
-
readonly effort?: import("
|
|
416
|
+
readonly effort?: import("../schema/options.js").ReasoningEffort | undefined;
|
|
413
417
|
} | undefined;
|
|
414
418
|
readonly tools?: readonly ({
|
|
415
419
|
readonly type: "function";
|
|
@@ -9,10 +9,10 @@ import { AIError, InvalidProviderOutputError, LLMEvent, ProviderInternalError, U
|
|
|
9
9
|
import { classifyProviderFailure } from "../provider-error.js";
|
|
10
10
|
import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js";
|
|
11
11
|
import { Lifecycle } from "./utils/lifecycle.js";
|
|
12
|
+
import { MistralToolID } from "./utils/mistral-tool-id.js";
|
|
12
13
|
import { ToolStream } from "./utils/tool-stream.js";
|
|
13
14
|
const ADAPTER = "mistral-chat";
|
|
14
15
|
const DONE = "[DONE]";
|
|
15
|
-
const TOOL_ID = /^[A-Za-z0-9]{9}$/;
|
|
16
16
|
export const DEFAULT_BASE_URL = "https://api.mistral.ai/v1";
|
|
17
17
|
export const PATH = "/chat/completions";
|
|
18
18
|
const MistralTextContent = Schema.Struct({
|
|
@@ -140,34 +140,6 @@ const MistralEvent = Schema.StructWithRest(Schema.Struct({
|
|
|
140
140
|
error: optionalNull(MistralError),
|
|
141
141
|
}), [Schema.Record(Schema.String, Schema.Unknown)]);
|
|
142
142
|
const MistralStreamEvent = Schema.Union([Schema.Literal(DONE), Protocol.jsonEvent(MistralEvent)]);
|
|
143
|
-
const hashID = (value) => {
|
|
144
|
-
const hash = (seed) => {
|
|
145
|
-
let result = seed;
|
|
146
|
-
for (const char of value)
|
|
147
|
-
result = Math.imul(result ^ char.charCodeAt(0), 16777619);
|
|
148
|
-
return (result >>> 0).toString(36);
|
|
149
|
-
};
|
|
150
|
-
return `${hash(2166136261).padStart(7, "0")}${hash(2246822519).padStart(7, "0")}`.slice(-9);
|
|
151
|
-
};
|
|
152
|
-
const toolIDNormalizer = (request) => {
|
|
153
|
-
const ids = request.messages.flatMap((message) => message.content.flatMap((part) => (part.type === "tool-call" || part.type === "tool-result" ? [part.id] : [])));
|
|
154
|
-
const used = new Set(ids.filter((id) => TOOL_ID.test(id)));
|
|
155
|
-
const normalized = new Map();
|
|
156
|
-
return (id) => {
|
|
157
|
-
if (TOOL_ID.test(id))
|
|
158
|
-
return id;
|
|
159
|
-
const previous = normalized.get(id);
|
|
160
|
-
if (previous)
|
|
161
|
-
return previous;
|
|
162
|
-
let attempt = 0;
|
|
163
|
-
let candidate = hashID(id);
|
|
164
|
-
while (used.has(candidate))
|
|
165
|
-
candidate = hashID(`${id}:${++attempt}`);
|
|
166
|
-
used.add(candidate);
|
|
167
|
-
normalized.set(id, candidate);
|
|
168
|
-
return candidate;
|
|
169
|
-
};
|
|
170
|
-
};
|
|
171
143
|
const lowerMedia = Effect.fn("MistralChat.lowerMedia")(function* (part) {
|
|
172
144
|
const media = ProviderShared.normalizeMedia(part);
|
|
173
145
|
const url = typeof part.data === "string" && /^(?:https?:|data:)/.test(part.data) ? part.data : media.dataUrl;
|
|
@@ -269,7 +241,7 @@ const lowerToolResults = Effect.fn("MistralChat.lowerToolResults")(function* (me
|
|
|
269
241
|
return output;
|
|
270
242
|
});
|
|
271
243
|
const lowerMessages = Effect.fn("MistralChat.lowerMessages")(function* (request) {
|
|
272
|
-
const normalizeID =
|
|
244
|
+
const normalizeID = MistralToolID.normalizer(request);
|
|
273
245
|
const messages = request.system.length === 0 ? [] : [{ role: "system", content: ProviderShared.joinText(request.system) }];
|
|
274
246
|
for (const message of request.messages) {
|
|
275
247
|
if (message.role === "system") {
|
|
@@ -439,15 +411,15 @@ const appendContent = (state, events, content) => {
|
|
|
439
411
|
}, state);
|
|
440
412
|
};
|
|
441
413
|
const normalizeStreamToolID = (state, source) => {
|
|
442
|
-
if (
|
|
414
|
+
if (MistralToolID.valid.test(source))
|
|
443
415
|
return { id: source, state: { ...state, usedToolIDs: new Set([...state.usedToolIDs, source]) } };
|
|
444
416
|
const previous = state.toolIDs.get(source);
|
|
445
417
|
if (previous)
|
|
446
418
|
return { id: previous, state };
|
|
447
419
|
let attempt = 0;
|
|
448
|
-
let id =
|
|
420
|
+
let id = MistralToolID.hash(source);
|
|
449
421
|
while (state.usedToolIDs.has(id))
|
|
450
|
-
id =
|
|
422
|
+
id = MistralToolID.hash(`${source}:${++attempt}`);
|
|
451
423
|
return {
|
|
452
424
|
id,
|
|
453
425
|
state: {
|
|
@@ -101,6 +101,10 @@ export const transport = (options) => {
|
|
|
101
101
|
};
|
|
102
102
|
})
|
|
103
103
|
: undefined;
|
|
104
|
+
if (input.webSocket && !channel)
|
|
105
|
+
yield* Effect.logWarning(`${options.name} does not offer WebSocket for this endpoint; using HTTP`, {
|
|
106
|
+
url: parts.url,
|
|
107
|
+
});
|
|
104
108
|
return {
|
|
105
109
|
http: {
|
|
106
110
|
request: ProviderShared.jsonPost({ url: parts.url, body: parts.bodyText, headers: parts.headers }),
|