@opencode/ai 0.0.0-beta-19289 → 0.0.0-beta-19365

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +128 -0
  2. package/dist/protocols/anthropic-messages.d.ts +24 -1
  3. package/dist/protocols/anthropic-messages.js +55 -12
  4. package/dist/protocols/meta-messages.d.ts +6 -0
  5. package/dist/protocols/zai-chat.d.ts +202 -0
  6. package/dist/protocols/zai-chat.js +49 -0
  7. package/dist/protocols/zai-messages.d.ts +344 -0
  8. package/dist/protocols/zai-messages.js +27 -0
  9. package/dist/providers/anthropic-compatible.d.ts +6 -0
  10. package/dist/providers/anthropic.d.ts +6 -0
  11. package/dist/providers/google-vertex-messages.d.ts +6 -0
  12. package/dist/providers/index.d.ts +2 -0
  13. package/dist/providers/index.js +2 -0
  14. package/dist/providers/meta.d.ts +6 -0
  15. package/dist/providers/minimax.d.ts +6 -0
  16. package/dist/providers/moonshot/chat.d.ts +1 -0
  17. package/dist/providers/moonshot/chat.js +1 -0
  18. package/dist/providers/moonshot/messages.d.ts +4 -0
  19. package/dist/providers/moonshot/messages.js +8 -0
  20. package/dist/providers/moonshot/responses.d.ts +4 -0
  21. package/dist/providers/moonshot/responses.js +8 -0
  22. package/dist/providers/moonshot.d.ts +597 -0
  23. package/dist/providers/moonshot.js +90 -0
  24. package/dist/providers/zai/chat.d.ts +1 -0
  25. package/dist/providers/zai/chat.js +1 -0
  26. package/dist/providers/zai-coding-plan/chat.d.ts +1 -0
  27. package/dist/providers/zai-coding-plan/chat.js +1 -0
  28. package/dist/providers/zai-coding-plan/messages.d.ts +4 -0
  29. package/dist/providers/zai-coding-plan/messages.js +8 -0
  30. package/dist/providers/zai-coding-plan/responses.d.ts +4 -0
  31. package/dist/providers/zai-coding-plan/responses.js +8 -0
  32. package/dist/providers/zai-coding-plan.d.ts +578 -0
  33. package/dist/providers/zai-coding-plan.js +63 -0
  34. package/dist/providers/zai.d.ts +134 -8
  35. package/dist/providers/zai.js +32 -0
  36. package/dist/schema/options.d.ts +2 -0
  37. package/dist/schema/options.js +2 -0
  38. package/package.json +3 -3
package/README.md CHANGED
@@ -29,6 +29,134 @@ 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
+ ## Z.AI
33
+
34
+ `ZAI` uses the standard API. Chat Completions is the default language-model API;
35
+ the existing `.image(...)` selector provides image generation.
36
+
37
+ ```ts
38
+ import { LLM } from "@opencode/ai"
39
+ import { ZAI, ZAICodingPlan } from "@opencode/ai/providers"
40
+
41
+ const zai = ZAI.configure({ apiKey: process.env.ZAI_API_KEY })
42
+ const request = LLM.request({
43
+ model: zai.model("glm-5.3"), // also zai.chat("glm-5.3")
44
+ prompt: "Explain this design.",
45
+ providerOptions: {
46
+ reasoningEffort: "high",
47
+ thinking: { type: "enabled", clear_thinking: false },
48
+ },
49
+ })
50
+
51
+ const coding = ZAICodingPlan.configure({ apiKey: process.env.ZAI_API_KEY })
52
+ const messages = LLM.request({
53
+ model: coding.messages("glm-5.3"),
54
+ prompt: "Explain this design.",
55
+ providerOptions: { effort: "high" },
56
+ })
57
+ ```
58
+
59
+ The products have distinct provider identities and endpoints:
60
+
61
+ | Provider | Selector | Default base URL |
62
+ | ----------------------------------- | --------------------------- | ------------------------------------- |
63
+ | `ZAI` (`zai`) | `.model`, `.chat`, `.image` | `https://api.z.ai/api/paas/v4` |
64
+ | `ZAICodingPlan` (`zai-coding-plan`) | `.model`, `.chat` | `https://api.z.ai/api/coding/paas/v4` |
65
+ | `ZAICodingPlan` | `.messages` | `https://api.z.ai/api/anthropic/v1` |
66
+ | `ZAICodingPlan` | `.responses` | `https://api.z.ai/api/v1` |
67
+
68
+ Both read `ZAI_API_KEY` when `apiKey` is omitted and support an explicit `auth` override.
69
+ Coding Plan requires an active subscription. `baseURL` overrides the selected API's
70
+ complete base, including its version prefix. Language-model routes use HTTP/SSE.
71
+
72
+ Options retain the selected API's native semantics:
73
+
74
+ - Chat `reasoningEffort` lowers to `reasoning_effort`; Responses lowers it to `reasoning.effort`.
75
+ Messages `effort` lowers to `output_config.effort`. Omission preserves provider defaults.
76
+ - Chat `thinking` passes `type` and `clear_thinking` through unchanged. Set
77
+ `clear_thinking: false` and replay complete `response.message` values to preserve reasoning
78
+ across user messages and tool loops. The standard API defaults to clearing historical thinking;
79
+ Coding Plan documents preservation by default.
80
+ - Messages accepts `thinking: { type: "enabled" | "adaptive" | "disabled" }` without requiring
81
+ an Anthropic token budget. Coding Plan documents a disabled toggle as low-effort thinking
82
+ for GLM-5.3, with explicit effort taking precedence.
83
+ - Chat also offers `toolStream`, `doSample`, `responseFormat`, `requestID`, and `userID`.
84
+ Tool-argument streaming is enabled when tools are present on GLM-4.6/4.7/5.x;
85
+ `toolStream: false` explicitly disables it. Older model families omit the opt-in.
86
+ - Effort and thinking values remain forward-compatible strings. Their meaning is model-specific:
87
+ GLM-5.3 accepts `low`, `high`, and `max` effort and rejects disabled thinking with HTTP 400;
88
+ the direct GLM-5.2 recordings returned reasoning even with `none` and `minimal` effort,
89
+ whereas explicit `thinking.type: "disabled"` disabled it on GLM-5.2 and GLM-4.7.
90
+
91
+ Standard API recordings cover GLM-5.3 efforts and a full preserved-reasoning tool loop with
92
+ a subsequent user follow-up, GLM-5.2 efforts, older-model thinking toggles, GLM-4.5 tool calls,
93
+ GLM-5.3-Flash image input, and JSON output. Coding Plan has unit coverage for routing,
94
+ request options, and reasoning replay; successful live recordings are pending.
95
+
96
+ Package entrypoints are `@opencode/ai/providers/zai`, `zai/chat`, `zai-coding-plan`,
97
+ `zai-coding-plan/chat`, `zai-coding-plan/messages`, and `zai-coding-plan/responses`.
98
+
99
+ ## Moonshot
100
+
101
+ Moonshot defaults to Chat Completions, with Messages and Responses selectors for Kimi K3:
102
+
103
+ ```ts
104
+ import { LLM } from "@opencode/ai"
105
+ import { Moonshot } from "@opencode/ai/providers"
106
+
107
+ const moonshot = Moonshot.configure({ apiKey: process.env.MOONSHOT_API_KEY })
108
+
109
+ const request = LLM.request({
110
+ model: moonshot.model("kimi-k3"), // also moonshot.chat("kimi-k3")
111
+ prompt: "Explain the tradeoffs in this design.",
112
+ providerOptions: { reasoningEffort: "high" },
113
+ })
114
+
115
+ const messages = LLM.request({
116
+ model: moonshot.messages("kimi-k3"),
117
+ prompt: "Explain the tradeoffs in this design.",
118
+ providerOptions: { effort: "high" },
119
+ })
120
+
121
+ const responses = LLM.request({
122
+ model: moonshot.responses("kimi-k3"),
123
+ prompt: "Explain the tradeoffs in this design.",
124
+ providerOptions: { reasoningEffort: "high" },
125
+ })
126
+ ```
127
+
128
+ When `apiKey` is omitted, authentication reads `MOONSHOT_API_KEY`, then `MOONSHOTAI_API_KEY`.
129
+ Chat and Responses use `https://api.moonshot.ai/v1`; Messages uses
130
+ `https://api.moonshot.ai/anthropic/v1`. `baseURL` overrides the selected API's complete base,
131
+ including the version prefix, for regional endpoints or gateways. Each endpoint requires its own valid credentials.
132
+ All three routes use HTTP/SSE.
133
+
134
+ Reasoning options stay native to the selected API and model:
135
+
136
+ | Model/API | Provider options |
137
+ | --------------------------- | --------------------------------------------------------------------------------------- |
138
+ | K3 Chat / Responses | `reasoningEffort: "low" \| "high" \| "max"`; default is `max` |
139
+ | K3 Messages | `effort: "low" \| "high" \| "max"`; default is `max` |
140
+ | K2.6 Chat | `thinking: { type: "enabled" \| "disabled", keep?: "all" \| null }`; default is enabled |
141
+ | K2.7 Code / high-speed Chat | Omit `thinking` to use always-on, preserved reasoning |
142
+
143
+ Omitting options preserves the model's defaults. K3 uses effort rather than the K2.x `thinking`
144
+ parameter. Known effort values have autocomplete while future strings remain accepted.
145
+ For K2.6, `thinking.keep: "all"` enables preservation of reasoning across user messages.
146
+ K3 and both K2.7 Code variants always preserve reasoning. Continue with the returned
147
+ `response.message` and matching tool results so reasoning content and any Messages signatures are retained.
148
+ Leave sampling options such as `temperature` unset to use these models' fixed defaults.
149
+
150
+ The recorded suite covers all three K3 APIs, default and explicit efforts, K2.6 thinking modes,
151
+ both K2.7 Code variants, generated tool loops with a subsequent user follow-up, required/disabled
152
+ tool choice, image-byte input, and native structured output through `http.body` overlays.
153
+ K3 Chat and Messages accept required and disabled tool choice. Responses supports automatic tool
154
+ choice only; explicit `required` and `none` produce a provider `InvalidRequest` error, also covered by recordings.
155
+ The provider targets the Moonshot Open Platform; Kimi Code is a separate product and endpoint.
156
+
157
+ Package entrypoints are `@opencode/ai/providers/moonshot`, `moonshot/chat`, `moonshot/messages`,
158
+ and `moonshot/responses`; each exports `model(modelID, settings)`.
159
+
32
160
  ## MiniMax
33
161
 
34
162
  MiniMax defaults to its Messages API and reads `MINIMAX_API_KEY` when `apiKey` is omitted:
@@ -10,14 +10,19 @@ 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
+ };
13
16
  export type ThinkingInput = {
14
17
  readonly type: "adaptive";
15
18
  readonly display?: "summarized" | "omitted";
19
+ readonly block_binding?: ThinkingBlockBinding;
16
20
  } | {
17
21
  readonly type: "disabled";
18
22
  } | ({
19
23
  readonly type: "enabled";
20
24
  readonly display?: "summarized" | "omitted";
25
+ readonly block_binding?: ThinkingBlockBinding;
21
26
  } & ({
22
27
  readonly budgetTokens: number;
23
28
  readonly budget_tokens?: number;
@@ -309,9 +314,15 @@ export declare const AnthropicMessagesBody: Schema.Struct<{
309
314
  readonly type: Schema.tag<"enabled">;
310
315
  readonly budget_tokens: Schema.Number;
311
316
  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
+ }>>;
312
320
  }>, Schema.Struct<{
313
321
  readonly type: Schema.tag<"adaptive">;
314
322
  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
+ }>>;
315
326
  }>, Schema.Struct<{
316
327
  readonly type: Schema.tag<"disabled">;
317
328
  }>]>>;
@@ -556,9 +567,15 @@ export declare const protocol: Protocol<{
556
567
  readonly type: "enabled";
557
568
  readonly budget_tokens: number;
558
569
  readonly display?: "summarized" | "omitted" | undefined;
570
+ readonly block_binding?: {
571
+ readonly prefix_mismatch_behavior?: string | undefined;
572
+ } | undefined;
559
573
  } | {
560
574
  readonly type: "adaptive";
561
575
  readonly display?: "summarized" | "omitted" | undefined;
576
+ readonly block_binding?: {
577
+ readonly prefix_mismatch_behavior?: string | undefined;
578
+ } | undefined;
562
579
  } | {
563
580
  readonly type: "disabled";
564
581
  } | undefined;
@@ -684,7 +701,7 @@ export declare const protocol: Protocol<{
684
701
  reasoningSignatures: {};
685
702
  lifecycle: Lifecycle.State;
686
703
  }>;
687
- export declare const transport: <Body extends Pick<AnthropicMessagesBody, "messages" | "context_management">>() => {
704
+ export declare const transport: <Body extends Pick<AnthropicMessagesBody, "messages" | "context_management" | "thinking">>() => {
688
705
  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>;
689
706
  with: (patch: Partial<HttpTransport.HttpJsonInput<Body, string>>) => HttpTransport.HttpJsonTransport<Body, string>;
690
707
  id: string;
@@ -904,9 +921,15 @@ export declare const route: Route<{
904
921
  readonly type: "enabled";
905
922
  readonly budget_tokens: number;
906
923
  readonly display?: "summarized" | "omitted" | undefined;
924
+ readonly block_binding?: {
925
+ readonly prefix_mismatch_behavior?: string | undefined;
926
+ } | undefined;
907
927
  } | {
908
928
  readonly type: "adaptive";
909
929
  readonly display?: "summarized" | "omitted" | undefined;
930
+ readonly block_binding?: {
931
+ readonly prefix_mismatch_behavior?: string | undefined;
932
+ } | undefined;
910
933
  } | {
911
934
  readonly type: "disabled";
912
935
  } | undefined;
@@ -202,15 +202,20 @@ 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
+ });
205
208
  const AnthropicThinking = Schema.Union([
206
209
  Schema.Struct({
207
210
  type: Schema.tag("enabled"),
208
211
  budget_tokens: Schema.Number,
209
212
  display: Schema.optional(Schema.Literals(["summarized", "omitted"])),
213
+ block_binding: Schema.optional(AnthropicThinkingBlockBinding),
210
214
  }),
211
215
  Schema.Struct({
212
216
  type: Schema.tag("adaptive"),
213
217
  display: Schema.optional(Schema.Literals(["summarized", "omitted"])),
218
+ block_binding: Schema.optional(AnthropicThinkingBlockBinding),
214
219
  }),
215
220
  Schema.Struct({
216
221
  type: Schema.tag("disabled"),
@@ -843,8 +848,9 @@ const resolveOptions = Effect.fn("AnthropicMessages.resolveOptions")(function* (
843
848
  ...(outputConfigEffort === undefined ? {} : { effort: outputConfigEffort }),
844
849
  ...(outputConfigFormat === undefined ? {} : { format: outputConfigFormat }),
845
850
  };
851
+ const thinking = yield* resolveThinking(input?.thinking);
846
852
  return {
847
- thinking: yield* resolveThinking(input?.thinking),
853
+ thinking: applyThinkingBindingDefault(request.model, thinking),
848
854
  effort: outputConfigEffort,
849
855
  output_config,
850
856
  service_tier,
@@ -854,18 +860,44 @@ const resolveOptions = Effect.fn("AnthropicMessages.resolveOptions")(function* (
854
860
  cache_control,
855
861
  };
856
862
  });
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
+ };
857
888
  const resolveThinking = Effect.fn("AnthropicMessages.resolveThinking")(function* (input) {
858
889
  if (!ProviderShared.isRecord(input))
859
890
  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);
860
896
  const display = input.display === "summarized" || input.display === "omitted"
861
897
  ? input.display
862
898
  : undefined;
863
899
  if (input.type === "adaptive")
864
- return { type: "adaptive", ...(display === undefined ? {} : { display }) };
865
- if (input.type === "disabled")
866
- return { type: "disabled" };
867
- if (input.type !== "enabled")
868
- return undefined;
900
+ return { type: "adaptive", display, block_binding };
869
901
  const budget = typeof input.budgetTokens === "number"
870
902
  ? input.budgetTokens
871
903
  : typeof input.budget_tokens === "number"
@@ -873,7 +905,7 @@ const resolveThinking = Effect.fn("AnthropicMessages.resolveThinking")(function*
873
905
  : undefined;
874
906
  if (budget === undefined)
875
907
  return yield* ProviderShared.invalidRequest("Anthropic thinking provider option requires budgetTokens");
876
- return { type: "enabled", budget_tokens: budget, ...(display === undefined ? {} : { display }) };
908
+ return { type: "enabled", budget_tokens: budget, display, block_binding };
877
909
  });
878
910
  const fromRequest = Effect.fn("AnthropicMessages.fromRequest")(function* (request) {
879
911
  const management = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Schema.UndefinedOr(ContextManagement)))(request.providerOptions?.contextManagement);
@@ -1378,15 +1410,15 @@ export const transport = () => {
1378
1410
  return {
1379
1411
  ...http,
1380
1412
  prepare: (input) => {
1381
- if (!input.body.context_management?.edits.length &&
1382
- !input.body.messages.some((message) => message.content.some((block) => block.type === "compaction")))
1413
+ const requiredBetas = requiredBetaHeaders(input.body);
1414
+ if (requiredBetas.length === 0)
1383
1415
  return http.prepare(input);
1384
1416
  const headers = Headers.fromInput(input.request.http?.headers);
1385
- const betas = new Set((headers["anthropic-beta"] ?? "")
1417
+ const existingBetas = (headers["anthropic-beta"] ?? "")
1386
1418
  .split(",")
1387
1419
  .map((item) => item.trim())
1388
- .filter(Boolean));
1389
- betas.add("compact-2026-01-12");
1420
+ .filter(Boolean);
1421
+ const betas = new Set([...existingBetas, ...requiredBetas]);
1390
1422
  return http.prepare({
1391
1423
  ...input,
1392
1424
  request: LLMRequest.update(input.request, {
@@ -1399,6 +1431,17 @@ export const transport = () => {
1399
1431
  },
1400
1432
  };
1401
1433
  };
1434
+ function requiredBetaHeaders(body) {
1435
+ const betas = [];
1436
+ const requestsCompaction = (body.context_management?.edits.length ?? 0) > 0;
1437
+ const replaysCompaction = body.messages.some((message) => message.content.some((block) => block.type === "compaction"));
1438
+ if (requestsCompaction || replaysCompaction)
1439
+ betas.push("compact-2026-01-12");
1440
+ const thinking = body.thinking;
1441
+ if (thinking && thinking.type !== "disabled" && thinking.block_binding)
1442
+ betas.push("thinking-binding-controls-2026-08-01");
1443
+ return betas;
1444
+ }
1402
1445
  export const route = Route.make({
1403
1446
  id: ADAPTER,
1404
1447
  provider: "anthropic",
@@ -219,9 +219,15 @@ 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;
222
225
  } | {
223
226
  readonly type: "adaptive";
224
227
  readonly display?: "summarized" | "omitted" | undefined;
228
+ readonly block_binding?: {
229
+ readonly prefix_mismatch_behavior?: string | undefined;
230
+ } | undefined;
225
231
  } | {
226
232
  readonly type: "disabled";
227
233
  } | undefined;
@@ -0,0 +1,202 @@
1
+ import { Protocol } from "../route/protocol.js";
2
+ import { OpenAIChat } from "./openai-chat.js";
3
+ export type ReasoningEffort = "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | (string & {});
4
+ export type OptionsInput = {
5
+ readonly reasoningEffort?: ReasoningEffort;
6
+ readonly thinking?: {
7
+ readonly type?: "enabled" | "disabled" | (string & {});
8
+ /** False retains historical reasoning; omission preserves the endpoint's default. */
9
+ readonly clear_thinking?: boolean;
10
+ };
11
+ readonly toolStream?: boolean;
12
+ readonly doSample?: boolean;
13
+ readonly responseFormat?: {
14
+ readonly type: "text" | "json_object" | (string & {});
15
+ };
16
+ readonly requestID?: string;
17
+ readonly userID?: string;
18
+ };
19
+ export declare const compatibility: {
20
+ maxTokensField: "max_tokens";
21
+ supportsStore: false;
22
+ supportsStrictMode: false;
23
+ reasoningField: string;
24
+ zaiToolStream: false;
25
+ };
26
+ export declare const protocol: Protocol<{
27
+ readonly model: string;
28
+ readonly messages: readonly ({
29
+ readonly role: "system";
30
+ readonly content: string | readonly ({
31
+ readonly type: "text";
32
+ readonly text: string;
33
+ readonly cache_control?: {
34
+ readonly type: "ephemeral";
35
+ readonly ttl?: string | undefined;
36
+ } | undefined;
37
+ } | {
38
+ readonly type: "image_url";
39
+ readonly image_url: {
40
+ readonly url: string;
41
+ };
42
+ })[];
43
+ } | {
44
+ readonly role: "user";
45
+ readonly content: string | readonly ({
46
+ readonly type: "text";
47
+ readonly text: string;
48
+ readonly cache_control?: {
49
+ readonly type: "ephemeral";
50
+ readonly ttl?: string | undefined;
51
+ } | undefined;
52
+ } | {
53
+ readonly type: "image_url";
54
+ readonly image_url: {
55
+ readonly url: string;
56
+ };
57
+ })[];
58
+ } | {
59
+ readonly [x: string]: unknown;
60
+ readonly content: string | null;
61
+ readonly role: "assistant";
62
+ readonly reasoning?: string | undefined;
63
+ readonly reasoning_content?: string | undefined;
64
+ readonly reasoning_text?: string | undefined;
65
+ readonly cache_control?: {
66
+ readonly type: "ephemeral";
67
+ readonly ttl?: string | undefined;
68
+ } | undefined;
69
+ readonly tool_calls?: readonly {
70
+ readonly id: string;
71
+ readonly type: "function";
72
+ readonly function: {
73
+ readonly name: string;
74
+ readonly arguments: string;
75
+ };
76
+ }[] | undefined;
77
+ readonly reasoning_details?: unknown;
78
+ } | {
79
+ readonly content: string;
80
+ readonly role: "tool";
81
+ readonly tool_call_id: string;
82
+ readonly cache_control?: {
83
+ readonly type: "ephemeral";
84
+ readonly ttl?: string | undefined;
85
+ } | undefined;
86
+ })[];
87
+ readonly stream: true;
88
+ readonly max_completion_tokens?: number | undefined;
89
+ readonly max_tokens?: number | undefined;
90
+ readonly tools?: readonly {
91
+ readonly function: {
92
+ readonly description: string;
93
+ readonly name: string;
94
+ readonly parameters: {
95
+ readonly [x: string]: unknown;
96
+ };
97
+ readonly strict?: boolean | undefined;
98
+ };
99
+ readonly type: "function";
100
+ readonly cache_control?: {
101
+ readonly type: "ephemeral";
102
+ readonly ttl?: string | undefined;
103
+ } | undefined;
104
+ }[] | undefined;
105
+ readonly stop?: readonly string[] | undefined;
106
+ readonly temperature?: number | undefined;
107
+ readonly seed?: number | undefined;
108
+ readonly thinking?: {
109
+ readonly type?: string | undefined;
110
+ readonly clear_thinking?: boolean | undefined;
111
+ } | undefined;
112
+ readonly user_id?: string | undefined;
113
+ readonly tool_choice?: "required" | "auto" | "none" | {
114
+ readonly type: "function";
115
+ readonly function: {
116
+ readonly name: string;
117
+ };
118
+ } | undefined;
119
+ readonly top_p?: number | undefined;
120
+ readonly frequency_penalty?: number | undefined;
121
+ readonly presence_penalty?: number | undefined;
122
+ readonly prompt_cache_key?: string | undefined;
123
+ readonly reasoning_effort?: import("./utils/open-responses-options.js").ReasoningEffort | undefined;
124
+ readonly store?: boolean | undefined;
125
+ readonly stream_options?: {
126
+ readonly include_usage: boolean;
127
+ } | undefined;
128
+ readonly tool_stream?: boolean | undefined;
129
+ readonly response_format?: {
130
+ readonly type: string;
131
+ } | undefined;
132
+ readonly do_sample?: boolean | undefined;
133
+ readonly request_id?: string | undefined;
134
+ }, string, "[DONE]" | {
135
+ readonly [x: string]: unknown;
136
+ readonly error?: {
137
+ readonly [x: string]: unknown;
138
+ readonly message: string;
139
+ readonly code?: string | number | null | undefined;
140
+ } | null | undefined;
141
+ readonly usage?: {
142
+ readonly [x: string]: unknown;
143
+ readonly cached_tokens?: number | null | undefined;
144
+ readonly prompt_tokens?: number | null | undefined;
145
+ readonly completion_tokens?: number | null | undefined;
146
+ readonly total_tokens?: number | null | undefined;
147
+ readonly prompt_tokens_details?: {
148
+ readonly [x: string]: unknown;
149
+ readonly cached_tokens?: number | null | undefined;
150
+ readonly cache_write_tokens?: number | null | undefined;
151
+ } | null | undefined;
152
+ readonly prompt_cache_hit_tokens?: number | null | undefined;
153
+ readonly completion_tokens_details?: {
154
+ readonly [x: string]: unknown;
155
+ readonly reasoning_tokens?: number | null | undefined;
156
+ readonly accepted_prediction_tokens?: number | null | undefined;
157
+ readonly rejected_prediction_tokens?: number | null | undefined;
158
+ } | null | undefined;
159
+ } | null | undefined;
160
+ readonly choices?: readonly {
161
+ readonly [x: string]: unknown;
162
+ readonly delta?: {
163
+ readonly [x: string]: unknown;
164
+ readonly reasoning?: string | null | undefined;
165
+ readonly reasoning_content?: string | null | undefined;
166
+ readonly reasoning_text?: string | null | undefined;
167
+ readonly content?: string | null | undefined;
168
+ readonly refusal?: string | null | undefined;
169
+ readonly tool_calls?: readonly {
170
+ readonly function?: {
171
+ readonly name?: string | null | undefined;
172
+ readonly arguments?: string | null | undefined;
173
+ } | null | undefined;
174
+ readonly id?: string | null | undefined;
175
+ readonly index?: number | null | undefined;
176
+ }[] | null | undefined;
177
+ readonly reasoning_details?: unknown;
178
+ } | null | undefined;
179
+ readonly usage?: {
180
+ readonly [x: string]: unknown;
181
+ readonly cached_tokens?: number | null | undefined;
182
+ readonly prompt_tokens?: number | null | undefined;
183
+ readonly completion_tokens?: number | null | undefined;
184
+ readonly total_tokens?: number | null | undefined;
185
+ readonly prompt_tokens_details?: {
186
+ readonly [x: string]: unknown;
187
+ readonly cached_tokens?: number | null | undefined;
188
+ readonly cache_write_tokens?: number | null | undefined;
189
+ } | null | undefined;
190
+ readonly prompt_cache_hit_tokens?: number | null | undefined;
191
+ readonly completion_tokens_details?: {
192
+ readonly [x: string]: unknown;
193
+ readonly reasoning_tokens?: number | null | undefined;
194
+ readonly accepted_prediction_tokens?: number | null | undefined;
195
+ readonly rejected_prediction_tokens?: number | null | undefined;
196
+ } | null | undefined;
197
+ } | null | undefined;
198
+ readonly finish_reason?: string | null | undefined;
199
+ readonly native_finish_reason?: string | null | undefined;
200
+ }[] | null | undefined;
201
+ }, OpenAIChat.ParserState>;
202
+ export * as ZAIChat from "./zai-chat.js";
@@ -0,0 +1,49 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { Protocol } from "../route/protocol.js";
3
+ import { OpenAIChat } from "./openai-chat.js";
4
+ import { ProviderShared } from "./shared.js";
5
+ const Options = Schema.Struct({
6
+ reasoningEffort: Schema.optional(Schema.String),
7
+ thinking: Schema.optional(Schema.Struct({ type: Schema.optional(Schema.String), clear_thinking: Schema.optional(Schema.Boolean) })),
8
+ toolStream: Schema.optional(Schema.Boolean),
9
+ doSample: Schema.optional(Schema.Boolean),
10
+ responseFormat: Schema.optional(Schema.Struct({ type: Schema.String })),
11
+ requestID: Schema.optional(Schema.String),
12
+ userID: Schema.optional(Schema.String),
13
+ });
14
+ const Body = Schema.Struct({
15
+ ...OpenAIChat.bodyFields,
16
+ thinking: Options.fields.thinking,
17
+ do_sample: Options.fields.doSample,
18
+ response_format: Options.fields.responseFormat,
19
+ request_id: Options.fields.requestID,
20
+ user_id: Options.fields.userID,
21
+ });
22
+ const fromRequest = Effect.fn("ZAIChat.fromRequest")(function* (request) {
23
+ const options = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Options))(request.providerOptions ?? {});
24
+ const body = yield* OpenAIChat.protocol.body.from(request);
25
+ return {
26
+ ...body,
27
+ thinking: options.thinking,
28
+ // Tool streaming was introduced in GLM-4.6; older models must not receive the opt-in.
29
+ tool_stream: options.toolStream ??
30
+ (body.tools?.length && /^glm-(?:4\.[67]|5(?:[.-]|$))/i.test(request.model.id) ? true : undefined),
31
+ do_sample: options.doSample,
32
+ response_format: options.responseFormat,
33
+ request_id: options.requestID,
34
+ user_id: options.userID,
35
+ };
36
+ });
37
+ export const compatibility = {
38
+ maxTokensField: "max_tokens",
39
+ supportsStore: false,
40
+ supportsStrictMode: false,
41
+ reasoningField: "reasoning_content",
42
+ zaiToolStream: false,
43
+ };
44
+ export const protocol = Protocol.make({
45
+ id: "zai-chat",
46
+ body: { schema: Body, from: fromRequest },
47
+ stream: OpenAIChat.protocol.stream,
48
+ });
49
+ export * as ZAIChat from "./zai-chat.js";