@opencode/ai 0.0.0-beta-19507 → 0.0.0-dev-19276
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 +0 -200
- package/dist/protocols/anthropic-messages.d.ts +1 -24
- package/dist/protocols/anthropic-messages.js +12 -58
- package/dist/protocols/meta-messages.d.ts +0 -6
- package/dist/protocols/open-responses-channel.d.ts +0 -2
- package/dist/protocols/open-responses-channel.js +2 -2
- package/dist/protocols/open-responses-continuation.d.ts +3 -7
- package/dist/protocols/open-responses-continuation.js +7 -16
- package/dist/protocols/open-responses.d.ts +9 -141
- package/dist/protocols/open-responses.js +7 -29
- package/dist/provider-package.d.ts +0 -4
- package/dist/providers/amazon-bedrock-mantle.d.ts +62 -4
- package/dist/providers/amazon-bedrock-mantle.js +20 -23
- package/dist/providers/amazon-bedrock.js +3 -3
- package/dist/providers/anthropic-compatible.d.ts +2 -7
- package/dist/providers/anthropic-compatible.js +12 -18
- package/dist/providers/anthropic.d.ts +2 -7
- package/dist/providers/anthropic.js +9 -12
- package/dist/providers/azure.d.ts +2 -1
- package/dist/providers/azure.js +14 -14
- package/dist/providers/baseten.d.ts +3 -2
- package/dist/providers/baseten.js +6 -6
- package/dist/providers/cerebras.d.ts +3 -2
- package/dist/providers/cerebras.js +6 -6
- package/dist/providers/cloudflare-ai-gateway.d.ts +2 -1
- package/dist/providers/cloudflare-ai-gateway.js +10 -16
- package/dist/providers/cloudflare-workers-ai.d.ts +2 -1
- package/dist/providers/cloudflare-workers-ai.js +9 -15
- package/dist/providers/deepinfra.d.ts +3 -2
- package/dist/providers/deepinfra.js +6 -6
- package/dist/providers/deepseek.d.ts +3 -2
- package/dist/providers/deepseek.js +6 -6
- package/dist/providers/fireworks.d.ts +3 -2
- package/dist/providers/fireworks.js +6 -6
- package/dist/providers/google-vertex-chat.d.ts +3 -2
- package/dist/providers/google-vertex-chat.js +12 -12
- package/dist/providers/google-vertex-messages.d.ts +3 -8
- package/dist/providers/google-vertex-messages.js +12 -12
- package/dist/providers/google-vertex-responses.d.ts +3 -2
- package/dist/providers/google-vertex-responses.js +12 -12
- package/dist/providers/google-vertex-shared.js +3 -14
- package/dist/providers/google-vertex.d.ts +2 -1
- package/dist/providers/google-vertex.js +12 -18
- package/dist/providers/google.d.ts +3 -2
- package/dist/providers/google.js +6 -6
- package/dist/providers/groq.d.ts +3 -2
- package/dist/providers/groq.js +6 -6
- package/dist/providers/index.d.ts +0 -3
- package/dist/providers/index.js +0 -3
- package/dist/providers/meta.d.ts +3 -8
- package/dist/providers/meta.js +6 -6
- package/dist/providers/minimax/chat.js +6 -6
- package/dist/providers/minimax/responses.js +6 -6
- package/dist/providers/minimax.d.ts +3 -8
- package/dist/providers/minimax.js +6 -6
- package/dist/providers/mistral.d.ts +3 -2
- package/dist/providers/mistral.js +6 -6
- package/dist/providers/openai-compatible-responses.d.ts +3 -2
- package/dist/providers/openai-compatible-responses.js +7 -7
- package/dist/providers/openai-compatible.d.ts +3 -2
- package/dist/providers/openai-compatible.js +7 -7
- package/dist/providers/openai.d.ts +3 -2
- package/dist/providers/openai.js +9 -9
- package/dist/providers/openrouter.d.ts +3 -2
- package/dist/providers/openrouter.js +6 -6
- package/dist/providers/togetherai.d.ts +3 -2
- package/dist/providers/togetherai.js +6 -6
- package/dist/providers/xai.d.ts +3 -2
- package/dist/providers/xai.js +6 -10
- package/dist/providers/zai.d.ts +8 -133
- package/dist/providers/zai.js +0 -32
- package/dist/route/client.js +2 -5
- package/dist/route/transport/websocket.js +2 -6
- package/dist/schema/errors.d.ts +0 -12
- package/dist/schema/errors.js +0 -11
- package/dist/schema/options.d.ts +0 -2
- package/dist/schema/options.js +0 -2
- package/package.json +3 -3
- package/dist/protocols/alibaba-chat.d.ts +0 -225
- package/dist/protocols/alibaba-chat.js +0 -75
- package/dist/protocols/alibaba-messages.d.ts +0 -350
- package/dist/protocols/alibaba-messages.js +0 -40
- package/dist/protocols/alibaba-responses.d.ts +0 -255
- package/dist/protocols/alibaba-responses.js +0 -80
- package/dist/protocols/zai-chat.d.ts +0 -202
- package/dist/protocols/zai-chat.js +0 -49
- package/dist/protocols/zai-messages.d.ts +0 -344
- package/dist/protocols/zai-messages.js +0 -27
- package/dist/providers/alibaba/chat.d.ts +0 -1
- package/dist/providers/alibaba/chat.js +0 -1
- package/dist/providers/alibaba/messages.d.ts +0 -3
- package/dist/providers/alibaba/messages.js +0 -1
- package/dist/providers/alibaba/responses.d.ts +0 -3
- package/dist/providers/alibaba/responses.js +0 -1
- package/dist/providers/alibaba.d.ts +0 -711
- package/dist/providers/alibaba.js +0 -94
- package/dist/providers/moonshot/chat.d.ts +0 -1
- package/dist/providers/moonshot/chat.js +0 -1
- package/dist/providers/moonshot/messages.d.ts +0 -4
- package/dist/providers/moonshot/messages.js +0 -8
- package/dist/providers/moonshot/responses.d.ts +0 -4
- package/dist/providers/moonshot/responses.js +0 -8
- package/dist/providers/moonshot.d.ts +0 -596
- package/dist/providers/moonshot.js +0 -90
- package/dist/providers/zai/chat.d.ts +0 -1
- package/dist/providers/zai/chat.js +0 -1
- package/dist/providers/zai-coding-plan/chat.d.ts +0 -1
- package/dist/providers/zai-coding-plan/chat.js +0 -1
- package/dist/providers/zai-coding-plan/messages.d.ts +0 -4
- package/dist/providers/zai-coding-plan/messages.js +0 -8
- package/dist/providers/zai-coding-plan/responses.d.ts +0 -4
- package/dist/providers/zai-coding-plan/responses.js +0 -8
- package/dist/providers/zai-coding-plan.d.ts +0 -577
- package/dist/providers/zai-coding-plan.js +0 -63
package/README.md
CHANGED
|
@@ -29,206 +29,6 @@ await Effect.runPromise(program.pipe(Effect.provide(llmLayer)))
|
|
|
29
29
|
|
|
30
30
|
Run `LLMClient.stream(request)` instead of `generate` when you want incremental `LLMEvent`s. The event stream is provider-neutral — same shape across OpenAI Chat, OpenAI Responses, Anthropic Messages, Gemini, Bedrock Converse, and any OpenAI-compatible deployment.
|
|
31
31
|
|
|
32
|
-
## Alibaba Cloud Model Studio
|
|
33
|
-
|
|
34
|
-
`Alibaba` provides standard Model Studio inference. Configure a region explicitly, then select
|
|
35
|
-
Chat Completions (`.model` or `.chat`), Anthropic-compatible Messages (`.messages`), or OpenAI-compatible
|
|
36
|
-
Responses (`.responses`). These routes use HTTP/SSE.
|
|
37
|
-
|
|
38
|
-
```ts
|
|
39
|
-
import { LLM } from "@opencode/ai"
|
|
40
|
-
import { Alibaba } from "@opencode/ai/providers"
|
|
41
|
-
|
|
42
|
-
const alibaba = Alibaba.configure({
|
|
43
|
-
region: "ap-southeast-1", // Singapore
|
|
44
|
-
apiKey: process.env.DASHSCOPE_API_KEY,
|
|
45
|
-
// workspaceID: "llm-your-workspace", // use a workspace-dedicated endpoint
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
const request = LLM.request({
|
|
49
|
-
model: alibaba.model("qwen3.8-max"),
|
|
50
|
-
prompt: "Explain this design.",
|
|
51
|
-
providerOptions: { reasoningEffort: "medium" },
|
|
52
|
-
})
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### Regions and credentials
|
|
56
|
-
|
|
57
|
-
| Region | `region` | Shared host when `workspaceID` is omitted |
|
|
58
|
-
| ------------------- | ---------------- | ----------------------------------------- |
|
|
59
|
-
| Singapore | `ap-southeast-1` | `dashscope-intl.aliyuncs.com` |
|
|
60
|
-
| China (Beijing) | `cn-beijing` | `dashscope.aliyuncs.com` |
|
|
61
|
-
| China (Hong Kong) | `cn-hongkong` | `cn-hongkong.dashscope.aliyuncs.com` |
|
|
62
|
-
| US (Virginia) | `us-east-1` | `dashscope-us.aliyuncs.com` |
|
|
63
|
-
| Germany (Frankfurt) | `eu-central-1` | Supply `workspaceID` or `baseURL` |
|
|
64
|
-
| Japan (Tokyo) | `ap-northeast-1` | Supply `workspaceID` or `baseURL` |
|
|
65
|
-
|
|
66
|
-
With `workspaceID`, the host is `{workspaceID}.{region}.maas.aliyuncs.com`. A complete `baseURL`
|
|
67
|
-
overrides regional setup, including the API prefix: `/compatible-mode/v1` for Chat/Responses,
|
|
68
|
-
or `/apps/anthropic/v1` for Messages. The selector appends its operation path.
|
|
69
|
-
|
|
70
|
-
Keys and model availability are region-specific. Auth resolves from explicit `auth` or `apiKey`,
|
|
71
|
-
then `DASHSCOPE_API_KEY`, then `ALIBABA_API_KEY`.
|
|
72
|
-
|
|
73
|
-
The access region and inference scope differ: Virginia's `-us` model IDs request US-only inference;
|
|
74
|
-
some regions select scope through their workspace. Model IDs pass through unchanged.
|
|
75
|
-
Alibaba's [regional guide](https://www.alibabacloud.com/help/en/model-studio/regions) and
|
|
76
|
-
[base URL table](https://www.alibabacloud.com/help/en/model-studio/base-url) disagree about Virginia's
|
|
77
|
-
shared host; the entry above follows the base URL table. Dedicated hosts can be copied from the console.
|
|
78
|
-
|
|
79
|
-
### Native options
|
|
80
|
-
|
|
81
|
-
- **Chat:** `reasoningEffort` → `reasoning_effort`, `enableThinking` → `enable_thinking`,
|
|
82
|
-
`thinkingBudget` → `thinking_budget`, and `preserveThinking` → `preserve_thinking`.
|
|
83
|
-
Replay complete `response.message` values to retain `reasoning_content` separately from answer text.
|
|
84
|
-
Qwen 3.8 defaults to preserving thinking; older models have different defaults.
|
|
85
|
-
Additional options include `toolStream`, `parallelToolCalls`, `repetitionPenalty`, `responseFormat`,
|
|
86
|
-
`enableSearch`, and native `searchOptions`. `generation.topK` lowers to `top_k`.
|
|
87
|
-
`clearThinking` is a hosted GLM control, and `thinking.type` is available for hosted MiniMax models.
|
|
88
|
-
- **Messages:** `effort` → `output_config.effort`. `thinking.type` accepts enabled/disabled with an
|
|
89
|
-
optional `budgetTokens` (or native `budget_tokens`). `outputConfig.format` accepts a JSON schema.
|
|
90
|
-
Model Studio's empty thinking signatures are accepted; supplied signatures are replayed unchanged.
|
|
91
|
-
- **Responses:** `reasoningEffort` → `reasoning.effort`, plus `enableThinking`, `store`,
|
|
92
|
-
`previousResponseId`, and `conversation`. Omitted `store` retains the API's default (`true`);
|
|
93
|
-
set it to `false` for client-managed history. `previousResponseId` requires a stored response.
|
|
94
|
-
Hosted tools are `Alibaba.webSearch()`, `Alibaba.webExtractor()`, and `Alibaba.codeInterpreter()`.
|
|
95
|
-
Web extraction is used together with web search. Hosted calls/results carry `providerExecuted: true`.
|
|
96
|
-
|
|
97
|
-
Omitted options preserve provider defaults. Effort values pass through unchanged and accept future
|
|
98
|
-
strings. Qwen 3.8 Chat rejects requests combining a thinking budget with effort.
|
|
99
|
-
|
|
100
|
-
Package entrypoints are `@opencode/ai/providers/alibaba`, `alibaba/chat`, `alibaba/messages`,
|
|
101
|
-
and `alibaba/responses`. Live recordings cover all three APIs in Singapore; regional URL construction
|
|
102
|
-
is unit-tested for all six regions.
|
|
103
|
-
|
|
104
|
-
## Z.AI
|
|
105
|
-
|
|
106
|
-
`ZAI` uses the standard API. Chat Completions is the default language-model API;
|
|
107
|
-
the existing `.image(...)` selector provides image generation.
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
import { LLM } from "@opencode/ai"
|
|
111
|
-
import { ZAI, ZAICodingPlan } from "@opencode/ai/providers"
|
|
112
|
-
|
|
113
|
-
const zai = ZAI.configure({ apiKey: process.env.ZAI_API_KEY })
|
|
114
|
-
const request = LLM.request({
|
|
115
|
-
model: zai.model("glm-5.3"), // also zai.chat("glm-5.3")
|
|
116
|
-
prompt: "Explain this design.",
|
|
117
|
-
providerOptions: {
|
|
118
|
-
reasoningEffort: "high",
|
|
119
|
-
thinking: { type: "enabled", clear_thinking: false },
|
|
120
|
-
},
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
const coding = ZAICodingPlan.configure({ apiKey: process.env.ZAI_API_KEY })
|
|
124
|
-
const messages = LLM.request({
|
|
125
|
-
model: coding.messages("glm-5.3"),
|
|
126
|
-
prompt: "Explain this design.",
|
|
127
|
-
providerOptions: { effort: "high" },
|
|
128
|
-
})
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
The products have distinct provider identities and endpoints:
|
|
132
|
-
|
|
133
|
-
| Provider | Selector | Default base URL |
|
|
134
|
-
| ----------------------------------- | --------------------------- | ------------------------------------- |
|
|
135
|
-
| `ZAI` (`zai`) | `.model`, `.chat`, `.image` | `https://api.z.ai/api/paas/v4` |
|
|
136
|
-
| `ZAICodingPlan` (`zai-coding-plan`) | `.model`, `.chat` | `https://api.z.ai/api/coding/paas/v4` |
|
|
137
|
-
| `ZAICodingPlan` | `.messages` | `https://api.z.ai/api/anthropic/v1` |
|
|
138
|
-
| `ZAICodingPlan` | `.responses` | `https://api.z.ai/api/v1` |
|
|
139
|
-
|
|
140
|
-
Both read `ZAI_API_KEY` when `apiKey` is omitted and support an explicit `auth` override.
|
|
141
|
-
Coding Plan requires an active subscription. `baseURL` overrides the selected API's
|
|
142
|
-
complete base, including its version prefix. Language-model routes use HTTP/SSE.
|
|
143
|
-
|
|
144
|
-
Options retain the selected API's native semantics:
|
|
145
|
-
|
|
146
|
-
- Chat `reasoningEffort` lowers to `reasoning_effort`; Responses lowers it to `reasoning.effort`.
|
|
147
|
-
Messages `effort` lowers to `output_config.effort`. Omission preserves provider defaults.
|
|
148
|
-
- Chat `thinking` passes `type` and `clear_thinking` through unchanged. Set
|
|
149
|
-
`clear_thinking: false` and replay complete `response.message` values to preserve reasoning
|
|
150
|
-
across user messages and tool loops. The standard API defaults to clearing historical thinking;
|
|
151
|
-
Coding Plan documents preservation by default.
|
|
152
|
-
- Messages accepts `thinking: { type: "enabled" | "adaptive" | "disabled" }` without requiring
|
|
153
|
-
an Anthropic token budget. Coding Plan documents a disabled toggle as low-effort thinking
|
|
154
|
-
for GLM-5.3, with explicit effort taking precedence.
|
|
155
|
-
- Chat also offers `toolStream`, `doSample`, `responseFormat`, `requestID`, and `userID`.
|
|
156
|
-
Tool-argument streaming is enabled when tools are present on GLM-4.6/4.7/5.x;
|
|
157
|
-
`toolStream: false` explicitly disables it. Older model families omit the opt-in.
|
|
158
|
-
- Effort and thinking values remain forward-compatible strings. Their meaning is model-specific:
|
|
159
|
-
GLM-5.3 accepts `low`, `high`, and `max` effort and rejects disabled thinking with HTTP 400;
|
|
160
|
-
the direct GLM-5.2 recordings returned reasoning even with `none` and `minimal` effort,
|
|
161
|
-
whereas explicit `thinking.type: "disabled"` disabled it on GLM-5.2 and GLM-4.7.
|
|
162
|
-
|
|
163
|
-
Standard API recordings cover GLM-5.3 efforts and a full preserved-reasoning tool loop with
|
|
164
|
-
a subsequent user follow-up, GLM-5.2 efforts, older-model thinking toggles, GLM-4.5 tool calls,
|
|
165
|
-
GLM-5.3-Flash image input, and JSON output. Coding Plan has unit coverage for routing,
|
|
166
|
-
request options, and reasoning replay; successful live recordings are pending.
|
|
167
|
-
|
|
168
|
-
Package entrypoints are `@opencode/ai/providers/zai`, `zai/chat`, `zai-coding-plan`,
|
|
169
|
-
`zai-coding-plan/chat`, `zai-coding-plan/messages`, and `zai-coding-plan/responses`.
|
|
170
|
-
|
|
171
|
-
## Moonshot
|
|
172
|
-
|
|
173
|
-
Moonshot defaults to Chat Completions, with Messages and Responses selectors for Kimi K3:
|
|
174
|
-
|
|
175
|
-
```ts
|
|
176
|
-
import { LLM } from "@opencode/ai"
|
|
177
|
-
import { Moonshot } from "@opencode/ai/providers"
|
|
178
|
-
|
|
179
|
-
const moonshot = Moonshot.configure({ apiKey: process.env.MOONSHOT_API_KEY })
|
|
180
|
-
|
|
181
|
-
const request = LLM.request({
|
|
182
|
-
model: moonshot.model("kimi-k3"), // also moonshot.chat("kimi-k3")
|
|
183
|
-
prompt: "Explain the tradeoffs in this design.",
|
|
184
|
-
providerOptions: { reasoningEffort: "high" },
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
const messages = LLM.request({
|
|
188
|
-
model: moonshot.messages("kimi-k3"),
|
|
189
|
-
prompt: "Explain the tradeoffs in this design.",
|
|
190
|
-
providerOptions: { effort: "high" },
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
const responses = LLM.request({
|
|
194
|
-
model: moonshot.responses("kimi-k3"),
|
|
195
|
-
prompt: "Explain the tradeoffs in this design.",
|
|
196
|
-
providerOptions: { reasoningEffort: "high" },
|
|
197
|
-
})
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
When `apiKey` is omitted, authentication reads `MOONSHOT_API_KEY`, then `MOONSHOTAI_API_KEY`.
|
|
201
|
-
Chat and Responses use `https://api.moonshot.ai/v1`; Messages uses
|
|
202
|
-
`https://api.moonshot.ai/anthropic/v1`. `baseURL` overrides the selected API's complete base,
|
|
203
|
-
including the version prefix, for regional endpoints or gateways. Each endpoint requires its own valid credentials.
|
|
204
|
-
All three routes use HTTP/SSE.
|
|
205
|
-
|
|
206
|
-
Reasoning options stay native to the selected API and model:
|
|
207
|
-
|
|
208
|
-
| Model/API | Provider options |
|
|
209
|
-
| --------------------------- | --------------------------------------------------------------------------------------- |
|
|
210
|
-
| K3 Chat / Responses | `reasoningEffort: "low" \| "high" \| "max"`; default is `max` |
|
|
211
|
-
| K3 Messages | `effort: "low" \| "high" \| "max"`; default is `max` |
|
|
212
|
-
| K2.6 Chat | `thinking: { type: "enabled" \| "disabled", keep?: "all" \| null }`; default is enabled |
|
|
213
|
-
| K2.7 Code / high-speed Chat | Omit `thinking` to use always-on, preserved reasoning |
|
|
214
|
-
|
|
215
|
-
Omitting options preserves the model's defaults. K3 uses effort rather than the K2.x `thinking`
|
|
216
|
-
parameter. Known effort values have autocomplete while future strings remain accepted.
|
|
217
|
-
For K2.6, `thinking.keep: "all"` enables preservation of reasoning across user messages.
|
|
218
|
-
K3 and both K2.7 Code variants always preserve reasoning. Continue with the returned
|
|
219
|
-
`response.message` and matching tool results so reasoning content and any Messages signatures are retained.
|
|
220
|
-
Leave sampling options such as `temperature` unset to use these models' fixed defaults.
|
|
221
|
-
|
|
222
|
-
The recorded suite covers all three K3 APIs, default and explicit efforts, K2.6 thinking modes,
|
|
223
|
-
both K2.7 Code variants, generated tool loops with a subsequent user follow-up, required/disabled
|
|
224
|
-
tool choice, image-byte input, and native structured output through `http.body` overlays.
|
|
225
|
-
K3 Chat and Messages accept required and disabled tool choice. Responses supports automatic tool
|
|
226
|
-
choice only; explicit `required` and `none` produce a provider `InvalidRequest` error, also covered by recordings.
|
|
227
|
-
The provider targets the Moonshot Open Platform; Kimi Code is a separate product and endpoint.
|
|
228
|
-
|
|
229
|
-
Package entrypoints are `@opencode/ai/providers/moonshot`, `moonshot/chat`, `moonshot/messages`,
|
|
230
|
-
and `moonshot/responses`; each exports `model(modelID, settings)`.
|
|
231
|
-
|
|
232
32
|
## MiniMax
|
|
233
33
|
|
|
234
34
|
MiniMax defaults to its Messages API and reads `MINIMAX_API_KEY` when `apiKey` is omitted:
|
|
@@ -10,19 +10,14 @@ export declare const DEFAULT_BASE_URL = "https://api.anthropic.com/v1";
|
|
|
10
10
|
export declare const PATH = "/messages";
|
|
11
11
|
export declare const DEFAULT_MAX_TOKENS = 32000;
|
|
12
12
|
export declare const framing: Framing.Definition<string>;
|
|
13
|
-
export type ThinkingBlockBinding = {
|
|
14
|
-
readonly prefix_mismatch_behavior?: "error" | "drop_block" | (string & {});
|
|
15
|
-
};
|
|
16
13
|
export type ThinkingInput = {
|
|
17
14
|
readonly type: "adaptive";
|
|
18
15
|
readonly display?: "summarized" | "omitted";
|
|
19
|
-
readonly block_binding?: ThinkingBlockBinding;
|
|
20
16
|
} | {
|
|
21
17
|
readonly type: "disabled";
|
|
22
18
|
} | ({
|
|
23
19
|
readonly type: "enabled";
|
|
24
20
|
readonly display?: "summarized" | "omitted";
|
|
25
|
-
readonly block_binding?: ThinkingBlockBinding;
|
|
26
21
|
} & ({
|
|
27
22
|
readonly budgetTokens: number;
|
|
28
23
|
readonly budget_tokens?: number;
|
|
@@ -314,15 +309,9 @@ export declare const AnthropicMessagesBody: Schema.Struct<{
|
|
|
314
309
|
readonly type: Schema.tag<"enabled">;
|
|
315
310
|
readonly budget_tokens: Schema.Number;
|
|
316
311
|
readonly display: Schema.optional<Schema.Literals<readonly ["summarized", "omitted"]>>;
|
|
317
|
-
readonly block_binding: Schema.optional<Schema.Struct<{
|
|
318
|
-
readonly prefix_mismatch_behavior: Schema.optional<Schema.String>;
|
|
319
|
-
}>>;
|
|
320
312
|
}>, Schema.Struct<{
|
|
321
313
|
readonly type: Schema.tag<"adaptive">;
|
|
322
314
|
readonly display: Schema.optional<Schema.Literals<readonly ["summarized", "omitted"]>>;
|
|
323
|
-
readonly block_binding: Schema.optional<Schema.Struct<{
|
|
324
|
-
readonly prefix_mismatch_behavior: Schema.optional<Schema.String>;
|
|
325
|
-
}>>;
|
|
326
315
|
}>, Schema.Struct<{
|
|
327
316
|
readonly type: Schema.tag<"disabled">;
|
|
328
317
|
}>]>>;
|
|
@@ -567,15 +556,9 @@ export declare const protocol: Protocol<{
|
|
|
567
556
|
readonly type: "enabled";
|
|
568
557
|
readonly budget_tokens: number;
|
|
569
558
|
readonly display?: "summarized" | "omitted" | undefined;
|
|
570
|
-
readonly block_binding?: {
|
|
571
|
-
readonly prefix_mismatch_behavior?: string | undefined;
|
|
572
|
-
} | undefined;
|
|
573
559
|
} | {
|
|
574
560
|
readonly type: "adaptive";
|
|
575
561
|
readonly display?: "summarized" | "omitted" | undefined;
|
|
576
|
-
readonly block_binding?: {
|
|
577
|
-
readonly prefix_mismatch_behavior?: string | undefined;
|
|
578
|
-
} | undefined;
|
|
579
562
|
} | {
|
|
580
563
|
readonly type: "disabled";
|
|
581
564
|
} | undefined;
|
|
@@ -701,7 +684,7 @@ export declare const protocol: Protocol<{
|
|
|
701
684
|
reasoningSignatures: {};
|
|
702
685
|
lifecycle: Lifecycle.State;
|
|
703
686
|
}>;
|
|
704
|
-
export declare const transport: <Body extends Pick<AnthropicMessagesBody, "messages" | "context_management"
|
|
687
|
+
export declare const transport: <Body extends Pick<AnthropicMessagesBody, "messages" | "context_management">>() => {
|
|
705
688
|
prepare: (input: Parameters<(input: import("../route/transport/index.js").TransportPrepareInput<Body>) => Effect.Effect<HttpTransport.HttpPrepared<string>, AIError, never>>[0]) => Effect.Effect<HttpTransport.HttpPrepared<string>, AIError, never>;
|
|
706
689
|
with: (patch: Partial<HttpTransport.HttpJsonInput<Body, string>>) => HttpTransport.HttpJsonTransport<Body, string>;
|
|
707
690
|
id: string;
|
|
@@ -921,15 +904,9 @@ export declare const route: Route<{
|
|
|
921
904
|
readonly type: "enabled";
|
|
922
905
|
readonly budget_tokens: number;
|
|
923
906
|
readonly display?: "summarized" | "omitted" | undefined;
|
|
924
|
-
readonly block_binding?: {
|
|
925
|
-
readonly prefix_mismatch_behavior?: string | undefined;
|
|
926
|
-
} | undefined;
|
|
927
907
|
} | {
|
|
928
908
|
readonly type: "adaptive";
|
|
929
909
|
readonly display?: "summarized" | "omitted" | undefined;
|
|
930
|
-
readonly block_binding?: {
|
|
931
|
-
readonly prefix_mismatch_behavior?: string | undefined;
|
|
932
|
-
} | undefined;
|
|
933
910
|
} | {
|
|
934
911
|
readonly type: "disabled";
|
|
935
912
|
} | undefined;
|
|
@@ -202,20 +202,15 @@ const AnthropicToolChoice = Schema.Union([
|
|
|
202
202
|
disable_parallel_tool_use: Schema.optional(Schema.Boolean),
|
|
203
203
|
}),
|
|
204
204
|
]);
|
|
205
|
-
const AnthropicThinkingBlockBinding = Schema.Struct({
|
|
206
|
-
prefix_mismatch_behavior: Schema.optional(Schema.String),
|
|
207
|
-
});
|
|
208
205
|
const AnthropicThinking = Schema.Union([
|
|
209
206
|
Schema.Struct({
|
|
210
207
|
type: Schema.tag("enabled"),
|
|
211
208
|
budget_tokens: Schema.Number,
|
|
212
209
|
display: Schema.optional(Schema.Literals(["summarized", "omitted"])),
|
|
213
|
-
block_binding: Schema.optional(AnthropicThinkingBlockBinding),
|
|
214
210
|
}),
|
|
215
211
|
Schema.Struct({
|
|
216
212
|
type: Schema.tag("adaptive"),
|
|
217
213
|
display: Schema.optional(Schema.Literals(["summarized", "omitted"])),
|
|
218
|
-
block_binding: Schema.optional(AnthropicThinkingBlockBinding),
|
|
219
214
|
}),
|
|
220
215
|
Schema.Struct({
|
|
221
216
|
type: Schema.tag("disabled"),
|
|
@@ -848,9 +843,8 @@ const resolveOptions = Effect.fn("AnthropicMessages.resolveOptions")(function* (
|
|
|
848
843
|
...(outputConfigEffort === undefined ? {} : { effort: outputConfigEffort }),
|
|
849
844
|
...(outputConfigFormat === undefined ? {} : { format: outputConfigFormat }),
|
|
850
845
|
};
|
|
851
|
-
const thinking = yield* resolveThinking(input?.thinking);
|
|
852
846
|
return {
|
|
853
|
-
thinking:
|
|
847
|
+
thinking: yield* resolveThinking(input?.thinking),
|
|
854
848
|
effort: outputConfigEffort,
|
|
855
849
|
output_config,
|
|
856
850
|
service_tier,
|
|
@@ -860,44 +854,18 @@ const resolveOptions = Effect.fn("AnthropicMessages.resolveOptions")(function* (
|
|
|
860
854
|
cache_control,
|
|
861
855
|
};
|
|
862
856
|
});
|
|
863
|
-
const supportsThinkingBlockBinding = (model) => {
|
|
864
|
-
const override = model.compatibility?.supportsThinkingBlockBinding;
|
|
865
|
-
if (override !== undefined)
|
|
866
|
-
return override;
|
|
867
|
-
// Accept gateway namespaces and Vertex suffixes without treating a snapshot date as a minor version.
|
|
868
|
-
const version = /(?:^|[./])claude-[a-z]+-(?<major>\d+)(?:[.-](?<minor>\d{1,2}))?(?:$|[-:@])/i.exec(model.id)?.groups;
|
|
869
|
-
if (!version)
|
|
870
|
-
return false;
|
|
871
|
-
const major = Number(version.major);
|
|
872
|
-
const minor = Number(version.minor ?? 0);
|
|
873
|
-
return major > 5 || (major === 5 && minor >= 1);
|
|
874
|
-
};
|
|
875
|
-
const applyThinkingBindingDefault = (model, thinking) => {
|
|
876
|
-
if (thinking?.type === "disabled")
|
|
877
|
-
return thinking;
|
|
878
|
-
if (!supportsThinkingBlockBinding(model))
|
|
879
|
-
return thinking;
|
|
880
|
-
return {
|
|
881
|
-
...(thinking ?? { type: "adaptive" }),
|
|
882
|
-
block_binding: {
|
|
883
|
-
prefix_mismatch_behavior: "drop_block",
|
|
884
|
-
...thinking?.block_binding,
|
|
885
|
-
},
|
|
886
|
-
};
|
|
887
|
-
};
|
|
888
857
|
const resolveThinking = Effect.fn("AnthropicMessages.resolveThinking")(function* (input) {
|
|
889
858
|
if (!ProviderShared.isRecord(input))
|
|
890
859
|
return undefined;
|
|
891
|
-
if (input.type === "disabled")
|
|
892
|
-
return { type: "disabled" };
|
|
893
|
-
if (input.type !== "adaptive" && input.type !== "enabled")
|
|
894
|
-
return undefined;
|
|
895
|
-
const block_binding = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Schema.UndefinedOr(AnthropicThinkingBlockBinding)))(input.block_binding);
|
|
896
860
|
const display = input.display === "summarized" || input.display === "omitted"
|
|
897
861
|
? input.display
|
|
898
862
|
: undefined;
|
|
899
863
|
if (input.type === "adaptive")
|
|
900
|
-
return { type: "adaptive", display
|
|
864
|
+
return { type: "adaptive", ...(display === undefined ? {} : { display }) };
|
|
865
|
+
if (input.type === "disabled")
|
|
866
|
+
return { type: "disabled" };
|
|
867
|
+
if (input.type !== "enabled")
|
|
868
|
+
return undefined;
|
|
901
869
|
const budget = typeof input.budgetTokens === "number"
|
|
902
870
|
? input.budgetTokens
|
|
903
871
|
: typeof input.budget_tokens === "number"
|
|
@@ -905,7 +873,7 @@ const resolveThinking = Effect.fn("AnthropicMessages.resolveThinking")(function*
|
|
|
905
873
|
: undefined;
|
|
906
874
|
if (budget === undefined)
|
|
907
875
|
return yield* ProviderShared.invalidRequest("Anthropic thinking provider option requires budgetTokens");
|
|
908
|
-
return { type: "enabled", budget_tokens: budget, display
|
|
876
|
+
return { type: "enabled", budget_tokens: budget, ...(display === undefined ? {} : { display }) };
|
|
909
877
|
});
|
|
910
878
|
const fromRequest = Effect.fn("AnthropicMessages.fromRequest")(function* (request) {
|
|
911
879
|
const management = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Schema.UndefinedOr(ContextManagement)))(request.providerOptions?.contextManagement);
|
|
@@ -1410,15 +1378,15 @@ export const transport = () => {
|
|
|
1410
1378
|
return {
|
|
1411
1379
|
...http,
|
|
1412
1380
|
prepare: (input) => {
|
|
1413
|
-
|
|
1414
|
-
|
|
1381
|
+
if (!input.body.context_management?.edits.length &&
|
|
1382
|
+
!input.body.messages.some((message) => message.content.some((block) => block.type === "compaction")))
|
|
1415
1383
|
return http.prepare(input);
|
|
1416
1384
|
const headers = Headers.fromInput(input.request.http?.headers);
|
|
1417
|
-
const
|
|
1385
|
+
const betas = new Set((headers["anthropic-beta"] ?? "")
|
|
1418
1386
|
.split(",")
|
|
1419
1387
|
.map((item) => item.trim())
|
|
1420
|
-
.filter(Boolean);
|
|
1421
|
-
|
|
1388
|
+
.filter(Boolean));
|
|
1389
|
+
betas.add("compact-2026-01-12");
|
|
1422
1390
|
return http.prepare({
|
|
1423
1391
|
...input,
|
|
1424
1392
|
request: LLMRequest.update(input.request, {
|
|
@@ -1431,20 +1399,6 @@ export const transport = () => {
|
|
|
1431
1399
|
},
|
|
1432
1400
|
};
|
|
1433
1401
|
};
|
|
1434
|
-
function requiredBetaHeaders(body) {
|
|
1435
|
-
// Always request interleaved thinking. The API accepts the header on any
|
|
1436
|
-
// model and ignores it where unsupported, while manual-thinking models need
|
|
1437
|
-
// it for thinking between tool calls.
|
|
1438
|
-
const betas = ["interleaved-thinking-2025-05-14"];
|
|
1439
|
-
const requestsCompaction = (body.context_management?.edits.length ?? 0) > 0;
|
|
1440
|
-
const replaysCompaction = body.messages.some((message) => message.content.some((block) => block.type === "compaction"));
|
|
1441
|
-
if (requestsCompaction || replaysCompaction)
|
|
1442
|
-
betas.push("compact-2026-01-12");
|
|
1443
|
-
const thinking = body.thinking;
|
|
1444
|
-
if (thinking && thinking.type !== "disabled" && thinking.block_binding)
|
|
1445
|
-
betas.push("thinking-binding-controls-2026-08-01");
|
|
1446
|
-
return betas;
|
|
1447
|
-
}
|
|
1448
1402
|
export const route = Route.make({
|
|
1449
1403
|
id: ADAPTER,
|
|
1450
1404
|
provider: "anthropic",
|
|
@@ -219,15 +219,9 @@ export declare const protocol: Protocol<{
|
|
|
219
219
|
readonly type: "enabled";
|
|
220
220
|
readonly budget_tokens: number;
|
|
221
221
|
readonly display?: "summarized" | "omitted" | undefined;
|
|
222
|
-
readonly block_binding?: {
|
|
223
|
-
readonly prefix_mismatch_behavior?: string | undefined;
|
|
224
|
-
} | undefined;
|
|
225
222
|
} | {
|
|
226
223
|
readonly type: "adaptive";
|
|
227
224
|
readonly display?: "summarized" | "omitted" | undefined;
|
|
228
|
-
readonly block_binding?: {
|
|
229
|
-
readonly prefix_mismatch_behavior?: string | undefined;
|
|
230
|
-
} | undefined;
|
|
231
225
|
} | {
|
|
232
226
|
readonly type: "disabled";
|
|
233
227
|
} | undefined;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Headers } from "effect/unstable/http";
|
|
2
2
|
import { HttpTransport, type Transport, type WebSocketChannelDriver } from "../route/transport/index.js";
|
|
3
|
-
import { OpenResponsesContinuation } from "./open-responses-continuation.js";
|
|
4
3
|
export interface Options {
|
|
5
4
|
readonly id: string;
|
|
6
5
|
readonly name: string;
|
|
@@ -8,7 +7,6 @@ export interface Options {
|
|
|
8
7
|
readonly enabled?: (url: string) => boolean;
|
|
9
8
|
readonly url?: (url: string) => string;
|
|
10
9
|
readonly headers?: (headers: Headers.Headers) => Headers.Headers;
|
|
11
|
-
readonly continuation?: OpenResponsesContinuation.Shape;
|
|
12
10
|
}
|
|
13
11
|
export interface Prepared {
|
|
14
12
|
readonly http: HttpTransport.HttpPrepared<string>;
|
|
@@ -10,6 +10,7 @@ const WebSocketResponseCreate = Schema.StructWithRest(Schema.Struct({ type: Sche
|
|
|
10
10
|
]);
|
|
11
11
|
const decodeMessage = ProviderShared.validateWith(Schema.decodeUnknownEffect(WebSocketResponseCreate));
|
|
12
12
|
const encodeMessage = Schema.encodeSync(Schema.fromJsonString(WebSocketResponseCreate));
|
|
13
|
+
const decodeEvent = Schema.decodeUnknownEffect(OpenResponses.protocol.stream.event);
|
|
13
14
|
const message = (body) => Effect.gen(function* () {
|
|
14
15
|
if (!ProviderShared.isRecord(body))
|
|
15
16
|
return yield* ProviderShared.invalidRequest("Open Responses WebSocket body must be a JSON object");
|
|
@@ -27,7 +28,7 @@ const driver = (options, body) => {
|
|
|
27
28
|
return { message: body, mode: "full" };
|
|
28
29
|
}),
|
|
29
30
|
observe: (_create, frame) => Effect.gen(function* () {
|
|
30
|
-
const event = yield*
|
|
31
|
+
const event = yield* decodeEvent(frame).pipe(Effect.mapError((cause) => ProviderShared.eventError(options.id, `Invalid ${options.name} WebSocket event`, frame, cause)));
|
|
31
32
|
if (terminal)
|
|
32
33
|
return yield* ProviderShared.eventError(options.id, `${options.name} emitted ${event.type} after a terminal event`, frame);
|
|
33
34
|
if (event.type === "error") {
|
|
@@ -96,7 +97,6 @@ export const transport = (options) => {
|
|
|
96
97
|
request: create.request,
|
|
97
98
|
message: create.message,
|
|
98
99
|
base,
|
|
99
|
-
continuation: options.continuation,
|
|
100
100
|
}),
|
|
101
101
|
};
|
|
102
102
|
})
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import type { WebSocketChannelDriver } from "../route/transport/index.js";
|
|
2
|
-
/**
|
|
3
|
-
* Fields to send next to `previous_response_id` on an incremental step, or undefined to send the step in full.
|
|
4
|
-
* Whether omitted fields carry over from the continued response is provider behavior the route must know.
|
|
5
|
-
*/
|
|
6
|
-
export type Shape = (request: Readonly<Record<string, unknown>>) => Readonly<Record<string, unknown>> | undefined;
|
|
7
2
|
export interface DriverInput {
|
|
8
3
|
readonly id: string;
|
|
9
4
|
readonly name: string;
|
|
10
5
|
readonly request: Readonly<Record<string, unknown>>;
|
|
11
6
|
readonly message: string;
|
|
12
7
|
readonly base: WebSocketChannelDriver;
|
|
13
|
-
readonly continuation?: Shape;
|
|
14
8
|
}
|
|
15
9
|
export declare const driver: (input: DriverInput) => WebSocketChannelDriver;
|
|
16
|
-
export
|
|
10
|
+
export declare const OpenResponsesContinuation: {
|
|
11
|
+
readonly driver: (input: DriverInput) => WebSocketChannelDriver;
|
|
12
|
+
};
|
|
@@ -4,6 +4,7 @@ import * as ProviderShared from "./shared.js";
|
|
|
4
4
|
import { OpenResponses } from "./open-responses.js";
|
|
5
5
|
const PROTOCOL = "open-responses.websocket.v1";
|
|
6
6
|
const VERSION = 1;
|
|
7
|
+
const decodeEvent = Schema.decodeUnknownEffect(OpenResponses.protocol.stream.event);
|
|
7
8
|
const checkpointValue = (checkpoint) => {
|
|
8
9
|
if (checkpoint?.protocol !== PROTOCOL || !ProviderShared.isRecord(checkpoint.value))
|
|
9
10
|
return undefined;
|
|
@@ -103,24 +104,21 @@ const rejected = (observation, recovery) => ({
|
|
|
103
104
|
});
|
|
104
105
|
export const driver = (input) => {
|
|
105
106
|
const { previous_response_id: _previousResponseID, ...request } = input.request;
|
|
106
|
-
const shape = input.continuation ?? ((fields) => fields);
|
|
107
107
|
let output = [];
|
|
108
108
|
return {
|
|
109
109
|
create: (checkpoint) => Effect.sync(() => {
|
|
110
110
|
output = [];
|
|
111
111
|
const previous = checkpointValue(checkpoint);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const delta = previous && fields ? incremental(request, previous) : undefined;
|
|
115
|
-
if (!previous || !fields || !delta)
|
|
112
|
+
const delta = previous ? incremental(request, previous) : undefined;
|
|
113
|
+
if (!previous || !delta)
|
|
116
114
|
return { message: ProviderShared.encodeJson(request), mode: "full" };
|
|
117
115
|
return {
|
|
118
|
-
message: ProviderShared.encodeJson({ ...
|
|
116
|
+
message: ProviderShared.encodeJson({ ...request, input: delta, previous_response_id: previous.responseID }),
|
|
119
117
|
mode: "incremental",
|
|
120
118
|
};
|
|
121
119
|
}),
|
|
122
120
|
observe: (create, frame) => Effect.gen(function* () {
|
|
123
|
-
const event = yield*
|
|
121
|
+
const event = yield* decodeEvent(frame).pipe(Effect.mapError((cause) => ProviderShared.eventError(input.id, `Invalid ${input.name} WebSocket event`, frame, cause)));
|
|
124
122
|
const observation = yield* input.base.observe(create, frame);
|
|
125
123
|
if (event.type === "response.output_item.done" && event.item)
|
|
126
124
|
output.push(event.item);
|
|
@@ -130,13 +128,6 @@ export const driver = (input) => {
|
|
|
130
128
|
return rejected(observation, "retry-full");
|
|
131
129
|
if (rejection === "websocket_connection_limit_reached")
|
|
132
130
|
return rejected(observation, "rotate-and-retry-full");
|
|
133
|
-
// Only the continuation distinguishes an incremental send from a full one, so an unclassified
|
|
134
|
-
// invalid request there is retried full; Codex reports a stale previous_response_id that way, with
|
|
135
|
-
// no code. Classified failures such as context overflow keep their runner-owned recovery.
|
|
136
|
-
if (create.mode === "incremental" &&
|
|
137
|
-
observation.error.reason._tag === "InvalidRequest" &&
|
|
138
|
-
observation.error.reason.classification === undefined)
|
|
139
|
-
return rejected(observation, "retry-full");
|
|
140
131
|
}
|
|
141
132
|
if (observation.type !== "completed")
|
|
142
133
|
return observation;
|
|
@@ -156,7 +147,7 @@ export const driver = (input) => {
|
|
|
156
147
|
responseID,
|
|
157
148
|
request,
|
|
158
149
|
// Completion can re-encrypt reasoning. Callers replay the item already emitted by output_item.done.
|
|
159
|
-
output: event.response?.output
|
|
150
|
+
output: event.response?.output
|
|
160
151
|
? event.response.output.map((item) => item.type === "reasoning" && item.id !== undefined
|
|
161
152
|
? (output.find((done) => done.type === item.type && done.id === item.id) ?? item)
|
|
162
153
|
: item)
|
|
@@ -167,4 +158,4 @@ export const driver = (input) => {
|
|
|
167
158
|
}),
|
|
168
159
|
};
|
|
169
160
|
};
|
|
170
|
-
export
|
|
161
|
+
export const OpenResponsesContinuation = { driver };
|