@opencode/ai 2.0.15 → 2.0.17

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 (224) hide show
  1. package/README.md +441 -99
  2. package/dist/ai-client.d.ts +8 -0
  3. package/dist/ai-client.js +12 -0
  4. package/dist/experimental/evaluation-client.d.ts +3 -3
  5. package/dist/experimental/evaluation-client.js +1 -1
  6. package/dist/experimental/evaluation.js +1 -1
  7. package/dist/generation.d.ts +39 -29
  8. package/dist/generation.js +62 -36
  9. package/dist/image-client.d.ts +59 -17
  10. package/dist/image-client.js +16 -24
  11. package/dist/image.d.ts +394 -55
  12. package/dist/image.js +48 -57
  13. package/dist/index.d.ts +15 -2
  14. package/dist/index.js +10 -0
  15. package/dist/llm.d.ts +7 -5
  16. package/dist/llm.js +10 -4
  17. package/dist/media-client.d.ts +30 -0
  18. package/dist/media-client.js +51 -0
  19. package/dist/media-model.d.ts +43 -0
  20. package/dist/media-model.js +47 -0
  21. package/dist/media.d.ts +10 -9
  22. package/dist/media.js +11 -12
  23. package/dist/promise.d.ts +473 -15
  24. package/dist/promise.js +71 -13
  25. package/dist/protocols/alibaba-chat.d.ts +12 -0
  26. package/dist/protocols/alibaba-chat.js +4 -1
  27. package/dist/protocols/alibaba-messages.d.ts +1 -1
  28. package/dist/protocols/alibaba-messages.js +6 -4
  29. package/dist/protocols/alibaba-responses.d.ts +2 -2
  30. package/dist/protocols/anthropic-messages.d.ts +34 -34
  31. package/dist/protocols/anthropic-messages.js +14 -9
  32. package/dist/protocols/assemblyai-transcription.d.ts +41 -0
  33. package/dist/protocols/assemblyai-transcription.js +136 -0
  34. package/dist/protocols/bedrock-converse.d.ts +7 -0
  35. package/dist/protocols/bedrock-converse.js +39 -19
  36. package/dist/protocols/bfl-images.d.ts +38 -0
  37. package/dist/protocols/bfl-images.js +153 -0
  38. package/dist/protocols/cartesia-speech.d.ts +143 -0
  39. package/dist/protocols/cartesia-speech.js +120 -0
  40. package/dist/protocols/deepgram-speech.d.ts +135 -0
  41. package/dist/protocols/deepgram-speech.js +91 -0
  42. package/dist/protocols/deepgram-transcription.d.ts +26 -0
  43. package/dist/protocols/deepgram-transcription.js +125 -0
  44. package/dist/protocols/elevenlabs-speech.d.ts +138 -0
  45. package/dist/protocols/elevenlabs-speech.js +111 -0
  46. package/dist/protocols/fal-images.d.ts +25 -0
  47. package/dist/protocols/fal-images.js +104 -0
  48. package/dist/protocols/fal-video.d.ts +29 -0
  49. package/dist/protocols/fal-video.js +73 -0
  50. package/dist/protocols/gemini.d.ts +22 -22
  51. package/dist/protocols/gemini.js +19 -36
  52. package/dist/protocols/google-images.d.ts +3 -3
  53. package/dist/protocols/google-images.js +12 -34
  54. package/dist/protocols/google-speech.d.ts +146 -0
  55. package/dist/protocols/google-speech.js +88 -0
  56. package/dist/protocols/google-transcription.d.ts +174 -0
  57. package/dist/protocols/google-transcription.js +126 -0
  58. package/dist/protocols/google-video.d.ts +26 -0
  59. package/dist/protocols/google-video.js +142 -0
  60. package/dist/protocols/meta-images.d.ts +3 -4
  61. package/dist/protocols/meta-images.js +13 -29
  62. package/dist/protocols/meta-messages.d.ts +5 -5
  63. package/dist/protocols/meta-responses.d.ts +4 -4
  64. package/dist/protocols/meta-responses.js +6 -4
  65. package/dist/protocols/open-responses.d.ts +10 -9
  66. package/dist/protocols/open-responses.js +4 -9
  67. package/dist/protocols/openai-chat.d.ts +84 -0
  68. package/dist/protocols/openai-chat.js +28 -17
  69. package/dist/protocols/openai-compatible-chat.d.ts +12 -0
  70. package/dist/protocols/openai-compatible-responses.d.ts +2 -2
  71. package/dist/protocols/openai-images.d.ts +130 -7
  72. package/dist/protocols/openai-images.js +143 -91
  73. package/dist/protocols/openai-responses.d.ts +20 -20
  74. package/dist/protocols/openai-responses.js +31 -31
  75. package/dist/protocols/openai-speech.d.ts +136 -0
  76. package/dist/protocols/openai-speech.js +97 -0
  77. package/dist/protocols/openai-transcription.d.ts +211 -0
  78. package/dist/protocols/openai-transcription.js +202 -0
  79. package/dist/protocols/replicate-images.d.ts +28 -0
  80. package/dist/protocols/replicate-images.js +127 -0
  81. package/dist/protocols/runway-video.d.ts +38 -0
  82. package/dist/protocols/runway-video.js +140 -0
  83. package/dist/protocols/shared.d.ts +21 -17
  84. package/dist/protocols/shared.js +27 -36
  85. package/dist/protocols/stability-images.d.ts +39 -0
  86. package/dist/protocols/stability-images.js +136 -0
  87. package/dist/protocols/utils/fal-queue.d.ts +26 -0
  88. package/dist/protocols/utils/fal-queue.js +67 -0
  89. package/dist/protocols/utils/gemini-generate-content.d.ts +65 -0
  90. package/dist/protocols/utils/gemini-generate-content.js +65 -0
  91. package/dist/protocols/utils/gemini-json-schema.d.ts +3 -0
  92. package/dist/protocols/utils/gemini-json-schema.js +76 -0
  93. package/dist/protocols/utils/media-input.d.ts +23 -1
  94. package/dist/protocols/utils/media-input.js +40 -0
  95. package/dist/protocols/utils/responses-checkpoint.js +3 -7
  96. package/dist/protocols/utils/responses-compaction.d.ts +3 -1
  97. package/dist/protocols/utils/responses-compaction.js +16 -3
  98. package/dist/protocols/utils/speech-stream.d.ts +49 -0
  99. package/dist/protocols/utils/speech-stream.js +64 -0
  100. package/dist/protocols/utils/tool-schema.d.ts +2 -2
  101. package/dist/protocols/utils/tool-schema.js +62 -19
  102. package/dist/protocols/xai-images.d.ts +4 -4
  103. package/dist/protocols/xai-images.js +14 -40
  104. package/dist/protocols/xai-responses.d.ts +2 -2
  105. package/dist/protocols/xai-responses.js +1 -1
  106. package/dist/protocols/xai-video.d.ts +34 -0
  107. package/dist/protocols/xai-video.js +141 -0
  108. package/dist/protocols/zai-chat.d.ts +13 -1
  109. package/dist/protocols/zai-images.d.ts +2 -2
  110. package/dist/protocols/zai-images.js +11 -14
  111. package/dist/protocols/zai-messages.d.ts +1 -1
  112. package/dist/provider-error.js +10 -1
  113. package/dist/providers/alibaba.d.ts +15 -3
  114. package/dist/providers/amazon-bedrock-mantle.d.ts +14 -2
  115. package/dist/providers/amazon-bedrock.d.ts +2 -0
  116. package/dist/providers/amazon-bedrock.js +1 -0
  117. package/dist/providers/anthropic-compatible.d.ts +5 -5
  118. package/dist/providers/anthropic.d.ts +5 -5
  119. package/dist/providers/assemblyai.d.ts +25 -0
  120. package/dist/providers/assemblyai.js +24 -0
  121. package/dist/providers/azure.d.ts +20 -8
  122. package/dist/providers/azure.js +2 -2
  123. package/dist/providers/baseten.d.ts +24 -0
  124. package/dist/providers/black-forest-labs.d.ts +25 -0
  125. package/dist/providers/black-forest-labs.js +23 -0
  126. package/dist/providers/cartesia.d.ts +24 -0
  127. package/dist/providers/cartesia.js +17 -0
  128. package/dist/providers/cerebras.d.ts +24 -0
  129. package/dist/providers/cloudflare-ai-gateway.d.ts +42 -18
  130. package/dist/providers/cloudflare-workers-ai.d.ts +24 -0
  131. package/dist/providers/deepgram.d.ts +29 -0
  132. package/dist/providers/deepgram.js +25 -0
  133. package/dist/providers/deepinfra.d.ts +24 -0
  134. package/dist/providers/deepseek.d.ts +24 -0
  135. package/dist/providers/elevenlabs.d.ts +24 -0
  136. package/dist/providers/elevenlabs.js +23 -0
  137. package/dist/providers/fal.d.ts +29 -0
  138. package/dist/providers/fal.js +26 -0
  139. package/dist/providers/fireworks.d.ts +24 -0
  140. package/dist/providers/google-vertex-chat.d.ts +12 -0
  141. package/dist/providers/google-vertex-messages.d.ts +5 -5
  142. package/dist/providers/google-vertex-responses.d.ts +2 -2
  143. package/dist/providers/google-vertex.d.ts +6 -6
  144. package/dist/providers/google.d.ts +21 -6
  145. package/dist/providers/google.js +13 -9
  146. package/dist/providers/groq.d.ts +24 -0
  147. package/dist/providers/index.d.ts +9 -0
  148. package/dist/providers/index.js +9 -0
  149. package/dist/providers/meta.d.ts +22 -10
  150. package/dist/providers/meta.js +4 -8
  151. package/dist/providers/minimax.d.ts +19 -7
  152. package/dist/providers/moonshot.d.ts +19 -7
  153. package/dist/providers/moonshot.js +3 -3
  154. package/dist/providers/openai-compatible-responses.d.ts +2 -2
  155. package/dist/providers/openai-compatible.d.ts +12 -0
  156. package/dist/providers/openai-options.d.ts +3 -9
  157. package/dist/providers/openai-options.js +4 -7
  158. package/dist/providers/openai.d.ts +33 -12
  159. package/dist/providers/openai.js +17 -9
  160. package/dist/providers/opencode-zen.js +1 -1
  161. package/dist/providers/openrouter.d.ts +54 -7
  162. package/dist/providers/openrouter.js +10 -5
  163. package/dist/providers/replicate.d.ts +25 -0
  164. package/dist/providers/replicate.js +17 -0
  165. package/dist/providers/runway.d.ts +24 -0
  166. package/dist/providers/runway.js +17 -0
  167. package/dist/providers/stability.d.ts +28 -0
  168. package/dist/providers/stability.js +18 -0
  169. package/dist/providers/togetherai.d.ts +24 -0
  170. package/dist/providers/typesafe-ai.js +1 -1
  171. package/dist/providers/vercel-ai-gateway.js +1 -1
  172. package/dist/providers/xai.d.ts +17 -0
  173. package/dist/providers/xai.js +7 -9
  174. package/dist/providers/zai-coding-plan.d.ts +16 -4
  175. package/dist/providers/zai.d.ts +13 -1
  176. package/dist/providers/zai.js +4 -8
  177. package/dist/route/auth.d.ts +5 -2
  178. package/dist/route/auth.js +20 -13
  179. package/dist/route/client.d.ts +9 -7
  180. package/dist/route/client.js +6 -8
  181. package/dist/route/endpoint.d.ts +1 -0
  182. package/dist/route/endpoint.js +2 -2
  183. package/dist/route/executor-service.d.ts +4 -2
  184. package/dist/route/executor-service.js +2 -1
  185. package/dist/route/executor.d.ts +3 -1
  186. package/dist/route/executor.js +7 -0
  187. package/dist/route/framing.d.ts +15 -2
  188. package/dist/route/framing.js +53 -4
  189. package/dist/route/index.d.ts +1 -1
  190. package/dist/route/media-protocol.d.ts +145 -18
  191. package/dist/route/media-protocol.js +96 -28
  192. package/dist/route/media.d.ts +54 -9
  193. package/dist/route/media.js +194 -38
  194. package/dist/route/protocol.d.ts +3 -1
  195. package/dist/schema/events.d.ts +0 -6
  196. package/dist/schema/messages.d.ts +0 -3
  197. package/dist/schema/options.d.ts +10 -6
  198. package/dist/schema/options.js +10 -5
  199. package/dist/speech-client.d.ts +66 -0
  200. package/dist/speech-client.js +21 -0
  201. package/dist/speech.d.ts +1307 -0
  202. package/dist/speech.js +124 -0
  203. package/dist/testing.d.ts +2 -2
  204. package/dist/transcription-client.d.ts +82 -0
  205. package/dist/transcription-client.js +13 -0
  206. package/dist/transcription.d.ts +1492 -0
  207. package/dist/transcription.js +127 -0
  208. package/dist/utils/bytes.d.ts +1 -0
  209. package/dist/utils/bytes.js +10 -0
  210. package/dist/utils/json.d.ts +4 -0
  211. package/dist/utils/json.js +4 -0
  212. package/dist/utils/media-type.d.ts +3 -1
  213. package/dist/utils/media-type.js +25 -2
  214. package/dist/video-client.d.ts +59 -0
  215. package/dist/video-client.js +20 -0
  216. package/dist/video.d.ts +1351 -0
  217. package/dist/video.js +118 -0
  218. package/package.json +3 -3
  219. package/dist/protocols/utils/gemini-tool-schema.d.ts +0 -2
  220. package/dist/protocols/utils/gemini-tool-schema.js +0 -103
  221. package/dist/protocols/utils/meta-image.d.ts +0 -2
  222. package/dist/protocols/utils/meta-image.js +0 -13
  223. package/dist/protocols/utils/openai-image.d.ts +0 -5
  224. package/dist/protocols/utils/openai-image.js +0 -18
@@ -1,40 +1,108 @@
1
- import { Effect, Schema } from "effect";
1
+ import { Clock, Duration, Effect, Schema } from "effect";
2
2
  import { HttpClientResponse } from "effect/unstable/http";
3
- import { AIError, HttpContext, InvalidProviderOutputError } from "../schema/index.js";
4
- export const json = (value) => ({ type: "json", value });
3
+ import { Media } from "../media.js";
4
+ import { AIError, ContentPolicyError, HttpContext, InvalidProviderOutputError, InvalidRequestError, ProviderID, ProviderInternalError, UnsupportedOperationError, } from "../schema/index.js";
5
+ export const json = (value, query) => ({
6
+ type: "json",
7
+ value,
8
+ query,
9
+ });
5
10
  export const multipart = (value) => ({ type: "multipart", value });
6
- export const inline = (input) => ({
7
- kind: "inline",
8
- ...input,
11
+ export const binary = (value, contentType, query) => ({
12
+ type: "binary",
13
+ value,
14
+ contentType,
15
+ query,
9
16
  });
17
+ export const inline = (route, input) => ({ kind: "inline", id: route.id, provider: route.provider, ...input });
18
+ export const queued = (route, input) => ({ kind: "queued", id: route.id, provider: route.provider, ...input });
19
+ export const stream = (route, input) => ({ kind: "stream", id: route.id, provider: route.provider, ...input });
10
20
  // ---------------------------------------------------------------------------
11
21
  // Response helpers
12
22
  // ---------------------------------------------------------------------------
13
23
  const context = (response) => new HttpContext({ url: response.request.url, status: response.status, headers: response.headers });
14
- /** Read a text body while retaining the original payload and HTTP context on every downstream error. */
15
- export const text = Effect.fn("MediaProtocol.text")(function* (route, name, response) {
16
- const http = context(response);
17
- const body = yield* response.text.pipe(Effect.mapError((cause) => new AIError({
18
- reason: new InvalidProviderOutputError({
19
- route,
20
- message: `Failed to read the ${name} response`,
24
+ /** One protocol's route id, display name, and provider, with the decoders and errors that carry them. */
25
+ export const identity = (input) => {
26
+ const provider = ProviderID.make(input.provider);
27
+ const frameError = (message, body, cause) => new AIError({ reason: new InvalidProviderOutputError({ route: input.id, message, body, cause }) });
28
+ /**
29
+ * Read a text body while retaining the original payload and HTTP context on every downstream error. `invalid` is a
30
+ * malformed provider document; `ended` is a generation that reached a terminal status without output (`failed` is
31
+ * provider-side, `cancelled`/`expired` mean the result will never exist); `pending` is a `result()` read before the
32
+ * generation finished, which is caller misuse; `contentPolicy` is a moderated result.
33
+ */
34
+ const text = Effect.fn("MediaProtocol.text")(function* (response) {
35
+ const http = context(response);
36
+ const body = yield* response.text.pipe(Effect.mapError((cause) => new AIError({
37
+ reason: new InvalidProviderOutputError({
38
+ route: input.id,
39
+ message: `Failed to read the ${input.name} response`,
40
+ http,
41
+ cause,
42
+ }),
43
+ })));
44
+ return {
45
+ body,
21
46
  http,
22
- cause,
23
- }),
24
- })));
47
+ invalid: (message, cause) => new AIError({ reason: new InvalidProviderOutputError({ route: input.id, message, body, http, cause }) }),
48
+ ended: (status, message) => new AIError({
49
+ reason: status === "failed"
50
+ ? new ProviderInternalError({ message, body, http })
51
+ : new InvalidRequestError({ message, body, http }),
52
+ }),
53
+ pending: (id) => new AIError({
54
+ reason: new InvalidRequestError({
55
+ message: `${input.name} generation ${id} has not finished; await it before reading the result`,
56
+ body,
57
+ http,
58
+ }),
59
+ }),
60
+ contentPolicy: (message) => new AIError({ reason: new ContentPolicyError({ message, body, http }) }),
61
+ };
62
+ });
63
+ /** Read and Schema-decode a JSON body. Decode failures keep the raw body as `reason.body`. */
64
+ const decodeJson = (schema) => {
65
+ const decode = Schema.decodeUnknownEffect(Schema.fromJsonString(schema));
66
+ return Effect.fn("MediaProtocol.decodeJson")(function* (response) {
67
+ const output = yield* text(response);
68
+ const value = yield* decode(output.body).pipe(Effect.mapError((cause) => output.invalid(`${input.name} returned an invalid response`, cause)));
69
+ return { ...output, value };
70
+ });
71
+ };
25
72
  return {
26
- body,
27
- http,
28
- invalid: (message, cause) => new AIError({ reason: new InvalidProviderOutputError({ route, message, body, http, cause }) }),
73
+ id: input.id,
74
+ name: input.name,
75
+ provider,
76
+ text,
77
+ decodeJson,
78
+ /** Decode a submission response into the token and first snapshot. */
79
+ decodeStarted: (schema, started) => {
80
+ const decode = decodeJson(schema);
81
+ return (response) => decode(response).pipe(Effect.map((output) => started(output.value)));
82
+ },
83
+ /** Schema-decode one JSON stream frame. Decode failures keep the frame as `reason.body`. */
84
+ decodeFrame: (schema) => {
85
+ const decode = Schema.decodeUnknownEffect(Schema.fromJsonString(schema));
86
+ return (frame) => decode(frame).pipe(Effect.mapError((cause) => frameError(`${input.name} sent an invalid stream event`, frame, cause)));
87
+ },
88
+ /** A stream-time failure; the frame stays on `reason.body`. */
89
+ frameError,
90
+ incomplete: () => new AIError({
91
+ reason: new InvalidProviderOutputError({
92
+ route: input.id,
93
+ message: "The provider response ended unexpectedly.",
94
+ classification: "incomplete-stream",
95
+ }),
96
+ }),
97
+ unsupported: (operation, message) => new AIError({ reason: new UnsupportedOperationError({ operation, provider, route: input.id, message }) }),
29
98
  };
30
- });
31
- /** Read and Schema-decode a JSON body. Decode failures keep the raw body as `reason.body`. */
32
- export const decodeJson = (route, name, schema) => {
33
- const decode = Schema.decodeUnknownEffect(Schema.fromJsonString(schema));
34
- return Effect.fn("MediaProtocol.decodeJson")(function* (response) {
35
- const output = yield* text(route, name, response);
36
- const value = yield* decode(output.body).pipe(Effect.mapError((cause) => output.invalid(`${name} returned an invalid response`, cause)));
37
- return { ...output, value };
38
- });
39
99
  };
100
+ /** Map a provider status string through the protocol's table; unknown values are an invalid provider document. */
101
+ export const status = (table, raw, output) => {
102
+ if (!Object.hasOwn(table, raw))
103
+ return Effect.fail(output.invalid(`Unknown generation status "${raw}"`));
104
+ return Effect.succeed(table[raw]);
105
+ };
106
+ /** A `url` asset whose provider-declared retention window starts now. */
107
+ export const expiringUrl = (url, retention, options) => Clock.currentTimeMillis.pipe(Effect.map((now) => Media.url(url, { ...options, expiresAt: now + Duration.toMillis(retention) })));
40
108
  export * as MediaProtocol from "./media-protocol.js";
@@ -1,8 +1,9 @@
1
- import { Effect } from "effect";
1
+ import { Effect, Stream } from "effect";
2
2
  import { Auth } from "./auth.js";
3
3
  import { Endpoint } from "./endpoint.js";
4
- import type { Interface } from "./executor-service.js";
4
+ import { type Interface } from "./executor-service.js";
5
5
  import { MediaProtocol } from "./media-protocol.js";
6
+ import { Generation } from "../generation.js";
6
7
  import { AIError, HttpOptions, ProviderID } from "../schema/index.js";
7
8
  export type Execute = Interface["execute"];
8
9
  /** The minimum a media request must carry for the route to build a transport request. */
@@ -22,25 +23,69 @@ export interface ModelInput {
22
23
  readonly headers?: Record<string, string>;
23
24
  readonly http?: HttpOptions;
24
25
  }
25
- export interface Route<Request extends MediaRequest, Response> {
26
+ /** A provider facade's `configure(...)` input as the `ModelInput` every media selector shares, minus the model id. */
27
+ export declare const deployment: (input: {
28
+ readonly baseURL?: string;
29
+ readonly headers?: Record<string, string>;
30
+ readonly http?: HttpOptions.Input;
31
+ }, auth: Auth.Definition) => Omit<ModelInput, "id">;
32
+ /** One request, one response. */
33
+ export interface InlineRoute<Request extends MediaRequest, Response> {
34
+ readonly kind: "inline";
26
35
  readonly id: string;
27
36
  readonly provider: ProviderID;
28
37
  readonly protocol: string;
29
38
  readonly generate: (request: Request, execute: Execute) => Effect.Effect<Response, AIError>;
30
39
  }
31
- export interface MakeInput<Request extends MediaRequest, Response> {
40
+ /** Submit, then poll through the returned `Generation`. */
41
+ export interface QueuedRoute<Request extends MediaRequest, Response> {
42
+ readonly kind: "queued";
32
43
  readonly id: string;
33
- readonly provider: string | ProviderID;
34
- readonly protocol: MediaProtocol.Inline<Request, Response>;
44
+ readonly provider: ProviderID;
45
+ readonly protocol: string;
46
+ readonly start: (request: Request, execute: Execute) => Effect.Effect<Generation<Response>, AIError>;
47
+ /** Rebuild a handle from a persisted `Generation.token`; fails typed when the token is not this route's. */
48
+ readonly resume: (model: MediaRequest["model"], token: unknown, execute: Execute) => Effect.Effect<Generation<Response>, AIError>;
49
+ }
50
+ /** One request whose response parses into events; `generate` runs the same stream and collects it. */
51
+ export interface StreamRoute<Request extends MediaRequest, Event, Response> {
52
+ readonly kind: "stream";
53
+ readonly id: string;
54
+ readonly provider: ProviderID;
55
+ readonly protocol: string;
56
+ readonly stream: (request: Request, execute: Execute) => Stream.Stream<Event, AIError>;
57
+ readonly generate: (request: Request, execute: Execute) => Effect.Effect<Response, AIError>;
58
+ }
59
+ export type AnyRoute<Request extends MediaRequest, Event, Response> = InlineRoute<Request, Response> | StreamRoute<Request, Event, Response> | QueuedRoute<Request, Response>;
60
+ export interface Composition<Request extends MediaRequest> {
35
61
  readonly endpoint: Endpoint.Definition<MediaProtocol.Body, Request>;
36
62
  readonly auth: Auth.Definition;
37
63
  /** Deployment headers applied before transport authentication. */
38
64
  readonly headers?: Record<string, string>;
39
65
  }
66
+ export interface InlineInput<Request extends MediaRequest, Response> extends Composition<Request> {
67
+ readonly protocol: MediaProtocol.Inline<Request, Response>;
68
+ }
69
+ export interface QueuedInput<Request extends MediaRequest, Response, Token> extends Composition<Request> {
70
+ readonly protocol: MediaProtocol.Queued<Request, Response, Token>;
71
+ }
72
+ export interface StreamInput<Request extends MediaRequest, Event, Response, Frame, State> extends Composition<MediaProtocol.Addressed<Request>> {
73
+ readonly protocol: MediaProtocol.Streamed<Request, Event, Frame, State>;
74
+ readonly collect: (events: ReadonlyArray<Event>) => Effect.Effect<Response, AIError>;
75
+ }
40
76
  /**
41
77
  * Compose an inline media protocol with an endpoint and auth into a runnable route. The route owns the transport
42
- * plumbing every media protocol would otherwise duplicate: option merging, surrogate sanitizing, unsupported-field rejection, URL and query
43
- * rendering, auth headers, JSON vs multipart encoding, and handing the response back to the protocol for decoding.
78
+ * plumbing every media protocol would otherwise duplicate: option merging, surrogate sanitizing, unsupported-field
79
+ * rejection, URL and query rendering, auth headers, JSON, multipart, or binary encoding, and handing responses back
80
+ * to the protocol.
81
+ */
82
+ export declare const inline: <Request extends MediaRequest, Response>(input: InlineInput<Request, Response>) => InlineRoute<Request, Response>;
83
+ /**
84
+ * Compose a queued media protocol the same way, adding `start`/`resume` handles whose polls reuse the route's auth,
85
+ * deployment headers, and (for `start`) the request's `http` overlay. The token is decoded once at the boundary and
86
+ * closed over by the resulting `Generation.Route`.
44
87
  */
45
- export declare const make: <Request extends MediaRequest, Response>(input: MakeInput<Request, Response>) => Route<Request, Response>;
88
+ export declare const queued: <Request extends MediaRequest, Response, Token>(input: QueuedInput<Request, Response, Token>) => QueuedRoute<Request, Response>;
89
+ /** Compose a streaming media protocol; `generate` runs the same stream in `generate` mode and folds it with `collect`. */
90
+ export declare const stream: <Request extends MediaRequest, Event, Response, Frame, State>(input: StreamInput<Request, Event, Response, Frame, State>) => StreamRoute<Request, Event, Response>;
46
91
  export * as MediaRoute from "./media.js";
@@ -1,52 +1,206 @@
1
- import { Effect } from "effect";
1
+ import { Effect, Schema, Stream } from "effect";
2
2
  import { Headers, HttpClientRequest } from "effect/unstable/http";
3
3
  import { Auth } from "./auth.js";
4
4
  import { Endpoint } from "./endpoint.js";
5
+ import { RequestExecutorService } from "./executor-service.js";
6
+ import { RequestExecutor } from "./executor.js";
5
7
  import { MediaProtocol } from "./media-protocol.js";
6
- import { ProviderShared } from "../protocols/shared.js";
7
- import { AIError, HttpOptions, ProviderID, mergeHttpOptions } from "../schema/index.js";
8
+ import { Generation, isTerminal } from "../generation.js";
9
+ import { AIError, AIErrorReason, HttpOptions, InvalidRequestError, ProviderID, UnsupportedOperationError, mergeHttpOptions, } from "../schema/index.js";
10
+ import { encodeJson } from "../utils/json.js";
8
11
  import { sanitizeSurrogates } from "../utils/sanitize.js";
12
+ /** A provider facade's `configure(...)` input as the `ModelInput` every media selector shares, minus the model id. */
13
+ export const deployment = (input, auth) => ({
14
+ auth,
15
+ baseURL: input.baseURL,
16
+ headers: input.headers,
17
+ http: HttpOptions.make(input.http),
18
+ });
9
19
  /**
10
20
  * Compose an inline media protocol with an endpoint and auth into a runnable route. The route owns the transport
11
- * plumbing every media protocol would otherwise duplicate: option merging, surrogate sanitizing, unsupported-field rejection, URL and query
12
- * rendering, auth headers, JSON vs multipart encoding, and handing the response back to the protocol for decoding.
21
+ * plumbing every media protocol would otherwise duplicate: option merging, surrogate sanitizing, unsupported-field
22
+ * rejection, URL and query rendering, auth headers, JSON, multipart, or binary encoding, and handing responses back
23
+ * to the protocol.
13
24
  */
14
- export const make = (input) => {
15
- const provider = ProviderID.make(input.provider);
16
- const routeHttp = input.headers === undefined ? undefined : new HttpOptions({ headers: input.headers });
17
- const authorize = Auth.toEffect(input.auth);
25
+ export const inline = (input) => {
26
+ const transport = makeTransport(input);
18
27
  return {
19
- id: input.id,
20
- provider,
28
+ kind: "inline",
29
+ id: input.protocol.id,
30
+ provider: input.protocol.provider,
21
31
  protocol: input.protocol.id,
22
32
  generate: Effect.fn(`MediaRoute.generate`)(function* (request, execute) {
23
- yield* rejectUnsupported(input.id, provider, request, input.protocol.unsupported);
33
+ const submitted = yield* transport.submit(request, { unsupported: input.protocol.unsupported, from: input.protocol.body.from }, execute);
34
+ return yield* input.protocol.response.decode(submitted.response, submitted.context);
35
+ }),
36
+ };
37
+ };
38
+ /**
39
+ * Compose a queued media protocol the same way, adding `start`/`resume` handles whose polls reuse the route's auth,
40
+ * deployment headers, and (for `start`) the request's `http` overlay. The token is decoded once at the boundary and
41
+ * closed over by the resulting `Generation.Route`.
42
+ */
43
+ export const queued = (input) => {
44
+ const transport = makeTransport(input);
45
+ const protocol = input.protocol;
46
+ const decodeToken = Schema.decodeUnknownEffect(protocol.token);
47
+ // A protocol producing a token its own codec rejects is a programmer defect, not a provider error.
48
+ const encodeToken = Schema.encodeSync(protocol.token);
49
+ const generationRoute = (token, http, execute) => {
50
+ const materialize = (asset) => asset.materialize().pipe(Effect.provideService(RequestExecutorService, { execute }));
51
+ const poll = (operation) => transport
52
+ .call("GET", operation.path(token), http, execute)
53
+ .pipe(Effect.flatMap((sent) => operation.decode(sent.response, { token, auth: sent.auth, materialize })));
54
+ const status = poll(protocol.status);
55
+ const cancel = protocol.cancel;
56
+ const send = cancel === undefined
57
+ ? undefined
58
+ : transport.call(cancel.method, cancel.path(token), http, execute).pipe(Effect.asVoid);
59
+ return {
60
+ status,
61
+ result: poll(protocol.result),
62
+ cancel: send !== undefined && cancel?.activeOnly
63
+ ? status.pipe(Effect.flatMap((snapshot) => (isTerminal(snapshot.status) ? Effect.void : send)))
64
+ : send,
65
+ };
66
+ };
67
+ const start = Effect.fn("MediaRoute.start")(function* (request, execute) {
68
+ const submitted = yield* transport.submit(request, { unsupported: protocol.unsupported, prepare: protocol.start.prepare, from: protocol.start.body.from }, execute);
69
+ const started = yield* protocol.start.decode(submitted.response, submitted.context);
70
+ const route = generationRoute(started.token, submitted.context.request.http, execute);
71
+ return new Generation(route, encodeToken(started.token), started.snapshot);
72
+ });
73
+ const resume = Effect.fn("MediaRoute.resume")(function* (model, raw, execute) {
74
+ const token = yield* decodeToken(raw).pipe(Effect.mapError((cause) => new AIError({
75
+ reason: new InvalidRequestError({
76
+ message: `${protocol.id} cannot resume a generation from this token`,
77
+ cause,
78
+ }),
79
+ })));
80
+ const route = generationRoute(token, transport.http(model), execute);
81
+ return new Generation(route, encodeToken(token), yield* route.status);
82
+ });
83
+ return { kind: "queued", id: protocol.id, provider: protocol.provider, protocol: protocol.id, start, resume };
84
+ };
85
+ /** Compose a streaming media protocol; `generate` runs the same stream in `generate` mode and folds it with `collect`. */
86
+ export const stream = (input) => {
87
+ const transport = makeTransport(input);
88
+ const protocol = input.protocol;
89
+ const events = (request, execute, mode) => Stream.unwrap(Effect.gen(function* () {
90
+ const submitted = yield* transport.submit({ ...request, mode }, { unsupported: protocol.unsupported, from: protocol.body.from }, execute);
91
+ const http = RequestExecutor.responseHttp(submitted.response);
92
+ return Stream.suspend(() => {
93
+ // Parser state is local to one response, exactly like `Route.make`'s LLM stream loop.
94
+ let state = protocol.initial();
95
+ return protocol.frames(RequestExecutor.responseStream(submitted.response), submitted.context).pipe(Stream.mapEffect((frame) => protocol.step(state, frame).pipe(Effect.map(([next, output]) => {
96
+ state = next;
97
+ return output;
98
+ }))), Stream.flattenIterable, Stream.concat(Stream.suspend(() => Stream.fromIterableEffect(protocol.finish(state, { ...submitted.context, http })))), Stream.mapError((error) => error.reason.http !== undefined
99
+ ? error
100
+ : new AIError({
101
+ reason: AIErrorReason.make({
102
+ ...error.reason,
103
+ message: error.reason.message,
104
+ cause: error.reason.cause,
105
+ http,
106
+ }),
107
+ })));
108
+ });
109
+ }));
110
+ return {
111
+ kind: "stream",
112
+ id: protocol.id,
113
+ provider: protocol.provider,
114
+ protocol: protocol.id,
115
+ stream: (request, execute) => events(request, execute, "stream"),
116
+ generate: (request, execute) => events(request, execute, "generate").pipe(Stream.runCollect, Effect.flatMap(input.collect)),
117
+ };
118
+ };
119
+ // ---------------------------------------------------------------------------
120
+ // Transport plumbing shared by every kind
121
+ // ---------------------------------------------------------------------------
122
+ const makeTransport = (input) => {
123
+ const routeHttp = input.headers === undefined ? undefined : new HttpOptions({ headers: input.headers });
124
+ const authorize = Auth.toEffect(input.auth);
125
+ const baseURL = (path) => new URL(`${Endpoint.trimBaseUrl(input.endpoint.baseURL ?? "")}${path}`);
126
+ /** `auth` is only what `Auth` added or changed, never untouched deployment headers. */
127
+ const send = Effect.fn("MediaRoute.send")(function* (call, execute) {
128
+ const encoded = encode(call.body, call.headers);
129
+ const url = call.url.toString();
130
+ const headers = yield* authorize({
131
+ request: call.request,
132
+ method: call.method,
133
+ url,
134
+ body: encoded.text,
135
+ headers: encoded.headers,
136
+ });
137
+ const response = yield* execute(encoded.apply(HttpClientRequest.make(call.method)(url).pipe(HttpClientRequest.setHeaders(headers))));
138
+ return {
139
+ response,
140
+ auth: Object.fromEntries(Object.entries(headers).filter(([key, value]) => encoded.headers[key] !== value)),
141
+ };
142
+ });
143
+ return {
144
+ /** Route and model overlays; `start` additionally merges the request's own `http`. */
145
+ http: (model) => mergeHttpOptions(routeHttp, model.http),
146
+ /** POST the protocol body to the route endpoint. */
147
+ submit: Effect.fn("MediaRoute.submit")(function* (request, protocol, execute) {
148
+ yield* rejectUnsupported(input.protocol.id, input.protocol.provider, request, protocol.unsupported);
24
149
  const http = mergeHttpOptions(routeHttp, request.model.http, request.http);
150
+ const headers = Headers.fromInput(http?.headers);
151
+ const prepared = protocol.prepare === undefined
152
+ ? request
153
+ : yield* protocol.prepare(request, (path, body) => send({ method: "POST", url: baseURL(path), headers, request, body }, execute).pipe(Effect.map((sent) => sent.response)));
25
154
  // Sanitize after merging so model-level overlays are covered; the model value is restored, not sanitized.
26
- const resolved = { ...sanitizeSurrogates({ ...request, http }), model: request.model };
27
- const body = yield* input.protocol.body.from(resolved);
28
- const url = Endpoint.render(input.endpoint, { request: resolved, body });
29
- for (const [key, value] of Object.entries(http?.query ?? {}))
30
- url.searchParams.set(key, value);
31
- const encoded = body.type === "json" ? ProviderShared.encodeJson(body.value) : "[multipart/form-data]";
32
- const baseHeaders = Headers.fromInput(http?.headers);
33
- const headers = yield* authorize({
34
- request: resolved,
35
- method: "POST",
36
- url: url.toString(),
37
- body: encoded,
38
- // The HTTP client sets the multipart boundary; a caller-supplied content-type would corrupt it.
39
- headers: body.type === "multipart" ? Headers.remove(baseHeaders, "content-type") : baseHeaders,
40
- });
41
- const transport = HttpClientRequest.post(url.toString()).pipe(HttpClientRequest.setHeaders(headers), body.type === "json"
42
- ? HttpClientRequest.bodyText(encoded, "application/json")
43
- : HttpClientRequest.bodyFormData(body.value));
44
- const response = yield* execute(transport);
45
- return yield* input.protocol.response.decode(response, { request: resolved, body });
155
+ const resolved = { ...sanitizeSurrogates({ ...prepared, http }), model: request.model };
156
+ const body = yield* protocol.from(resolved);
157
+ const url = withQuery(withQuery(Endpoint.render(input.endpoint, { request: resolved, body }), body.type === "multipart" ? undefined : body.query), http?.query);
158
+ const sent = yield* send({ method: "POST", url, headers, request: resolved, body }, execute);
159
+ return { response: sent.response, context: { request: resolved, body } };
46
160
  }),
161
+ /** Bodiless follow-up call (status, result, cancel) with the same auth and headers as `submit`. */
162
+ call: (method, path, http, execute) => {
163
+ // Provider-issued absolute URLs (fal `status_url`) are used as-is; everything else resolves against the base.
164
+ const url = withQuery(/^https?:\/\//.test(path) ? new URL(path) : baseURL(path), http?.query);
165
+ for (const [key, value] of Object.entries(input.endpoint.query ?? {}))
166
+ url.searchParams.set(key, value);
167
+ return send({ method, url, headers: Headers.fromInput(http?.headers), request: { http } }, execute);
168
+ },
47
169
  };
48
170
  };
49
- /** Common fields are never silently dropped: a present field the protocol declared unsupported fails typed. */
171
+ const withQuery = (url, query) => {
172
+ for (const [key, value] of Object.entries(query ?? {})) {
173
+ url.searchParams.delete(key);
174
+ for (const item of typeof value === "string" ? [value] : value)
175
+ url.searchParams.append(key, item);
176
+ }
177
+ return url;
178
+ };
179
+ const encode = (body, headers) => {
180
+ if (body === undefined)
181
+ return { text: "", headers, apply: (request) => request };
182
+ if (body.type === "json") {
183
+ const text = encodeJson(body.value);
184
+ return { text, headers, apply: HttpClientRequest.bodyText(text, "application/json") };
185
+ }
186
+ if (body.type === "binary")
187
+ return {
188
+ text: `[${body.contentType}]`,
189
+ headers,
190
+ apply: HttpClientRequest.bodyUint8Array(body.value, body.contentType),
191
+ };
192
+ return {
193
+ text: "[multipart/form-data]",
194
+ // The HTTP client sets the multipart boundary; a caller-supplied content-type would corrupt it.
195
+ headers: Headers.remove(headers, "content-type"),
196
+ apply: HttpClientRequest.bodyFormData(body.value),
197
+ };
198
+ };
199
+ /**
200
+ * Common fields are never silently dropped: a present field the protocol declared unsupported fails typed. `false`
201
+ * counts as present because some booleans mean something when false (video `audio`); protocols reject opt-in
202
+ * booleans such as speech `timestamps` with `=== true` in `body.from` instead of listing them.
203
+ */
50
204
  const rejectUnsupported = (route, provider, request, unsupported) => {
51
205
  const present = (unsupported ?? []).filter((field) => {
52
206
  const value = request[field];
@@ -54,11 +208,13 @@ const rejectUnsupported = (route, provider, request, unsupported) => {
54
208
  });
55
209
  if (present.length === 0)
56
210
  return Effect.void;
57
- return Effect.fail(ProviderShared.unsupportedOperation({
58
- operation: `media.${present[0]}`,
59
- provider,
60
- route,
61
- message: `${provider}/${route} does not support ${present.join(", ")}`,
211
+ return Effect.fail(new AIError({
212
+ reason: new UnsupportedOperationError({
213
+ operation: `media.${present[0]}`,
214
+ provider,
215
+ route,
216
+ message: `${provider}/${route} does not support ${present.join(", ")}`,
217
+ }),
62
218
  }));
63
219
  };
64
220
  export * as MediaRoute from "./media.js";
@@ -1,5 +1,5 @@
1
1
  import { Schema, type Effect } from "effect";
2
- import type { AIError, LLMEvent, LLMRequest, ProtocolID } from "../schema/index.js";
2
+ import type { AIError, LanguageModelSanitizerCompatibility, LLMEvent, LLMRequest, ProtocolID } from "../schema/index.js";
3
3
  /**
4
4
  * The semantic API contract of one model server family.
5
5
  *
@@ -42,6 +42,8 @@ export interface Protocol<Body, Frame, Event, State> {
42
42
  readonly stream: ProtocolStream<Frame, Event, State>;
43
43
  /** Whether `body.from` lowers `Message.effort(...)` markers; wrappers around another `body.from` must forward it. */
44
44
  readonly supportsEffortUpdates?: (request: LLMRequest) => boolean;
45
+ /** Tool schema sanitizer for every model on this protocol unless the model's compatibility sets one; wrappers around another `body.from` must forward it. */
46
+ readonly sanitizer?: LanguageModelSanitizerCompatibility;
45
47
  }
46
48
  export interface ProtocolBody<Body> {
47
49
  /** Schema for the validated provider-native body sent as the JSON request. */
@@ -365,7 +365,6 @@ export declare const MediaEvent: Schema.Struct<{
365
365
  readonly url: Schema.String;
366
366
  readonly mediaType: Schema.optional<Schema.String>;
367
367
  readonly expiresAt: Schema.optional<Schema.Number>;
368
- readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
369
368
  }>, Schema.Struct<{
370
369
  readonly type: Schema.Literal<"ref">;
371
370
  readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
@@ -578,7 +577,6 @@ declare const llmEventTagged: Schema.toTaggedUnion<"type", readonly [Schema.refi
578
577
  readonly url: Schema.String;
579
578
  readonly mediaType: Schema.optional<Schema.String>;
580
579
  readonly expiresAt: Schema.optional<Schema.Number>;
581
- readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
582
580
  }>, Schema.Struct<{
583
581
  readonly type: Schema.Literal<"ref">;
584
582
  readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
@@ -820,7 +818,6 @@ export declare const LLMEvent: Schema.Union<readonly [Schema.refine<{
820
818
  readonly url: Schema.String;
821
819
  readonly mediaType: Schema.optional<Schema.String>;
822
820
  readonly expiresAt: Schema.optional<Schema.Number>;
823
- readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
824
821
  }>, Schema.Struct<{
825
822
  readonly type: Schema.Literal<"ref">;
826
823
  readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
@@ -1064,7 +1061,6 @@ export declare const LLMEvent: Schema.Union<readonly [Schema.refine<{
1064
1061
  readonly url: Schema.String;
1065
1062
  readonly mediaType: Schema.optional<Schema.String>;
1066
1063
  readonly expiresAt: Schema.optional<Schema.Number>;
1067
- readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
1068
1064
  }>, Schema.Struct<{
1069
1065
  readonly type: Schema.Literal<"ref">;
1070
1066
  readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
@@ -4805,7 +4801,6 @@ declare const LLMResponse_base: Schema.Class<LLMResponse, Schema.Struct<{
4805
4801
  readonly url: Schema.String;
4806
4802
  readonly mediaType: Schema.optional<Schema.String>;
4807
4803
  readonly expiresAt: Schema.optional<Schema.Number>;
4808
- readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
4809
4804
  }>, Schema.Struct<{
4810
4805
  readonly type: Schema.Literal<"ref">;
4811
4806
  readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
@@ -5049,7 +5044,6 @@ declare const LLMResponse_base: Schema.Class<LLMResponse, Schema.Struct<{
5049
5044
  readonly url: Schema.String;
5050
5045
  readonly mediaType: Schema.optional<Schema.String>;
5051
5046
  readonly expiresAt: Schema.optional<Schema.Number>;
5052
- readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
5053
5047
  }>, Schema.Struct<{
5054
5048
  readonly type: Schema.Literal<"ref">;
5055
5049
  readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
@@ -44,7 +44,6 @@ export declare const MediaPart: Schema.Struct<{
44
44
  readonly url: Schema.String;
45
45
  readonly mediaType: Schema.optional<Schema.String>;
46
46
  readonly expiresAt: Schema.optional<Schema.Number>;
47
- readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
48
47
  }>, Schema.Struct<{
49
48
  readonly type: Schema.Literal<"ref">;
50
49
  readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
@@ -305,7 +304,6 @@ export declare const ContentPart: Schema.toTaggedUnion<"type", readonly [Schema.
305
304
  readonly url: Schema.String;
306
305
  readonly mediaType: Schema.optional<Schema.String>;
307
306
  readonly expiresAt: Schema.optional<Schema.Number>;
308
- readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
309
307
  }>, Schema.Struct<{
310
308
  readonly type: Schema.Literal<"ref">;
311
309
  readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
@@ -450,7 +448,6 @@ declare const Message_base: Schema.Class<Message, Schema.Struct<{
450
448
  readonly url: Schema.String;
451
449
  readonly mediaType: Schema.optional<Schema.String>;
452
450
  readonly expiresAt: Schema.optional<Schema.Number>;
453
- readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
454
451
  }>, Schema.Struct<{
455
452
  readonly type: Schema.Literal<"ref">;
456
453
  readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
@@ -18,8 +18,9 @@ export declare class HttpOptions extends HttpOptions_base {
18
18
  }
19
19
  export declare namespace HttpOptions {
20
20
  type Input = HttpOptions | ConstructorParameters<typeof HttpOptions>[0];
21
- /** Normalize HTTP option input into the canonical `HttpOptions` class. */
22
- const make: (input: Input) => HttpOptions;
21
+ /** Normalize HTTP option input into the canonical `HttpOptions` class; `undefined` stays `undefined`. */
22
+ function make(input: Input): HttpOptions;
23
+ function make(input: Input | undefined): HttpOptions | undefined;
23
24
  }
24
25
  export declare const mergeHttpOptions: (...items: ReadonlyArray<HttpOptions | undefined>) => HttpOptions | undefined;
25
26
  declare const GenerationOptions_base: Schema.Class<GenerationOptions, Schema.Struct<{
@@ -67,15 +68,18 @@ export declare namespace LanguageModelDefaults {
67
68
  /** Normalize selected-model request defaults without applying precedence. */
68
69
  const make: (input: Input) => LanguageModelDefaults;
69
70
  }
71
+ /** Provider-defined string enum: known values for autocomplete, any string accepted. */
72
+ export type OpenString<Known extends string> = Known | (string & {});
70
73
  export declare const ReasoningEfforts: readonly ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
71
- export type ReasoningEffort = (typeof ReasoningEfforts)[number] | (string & {});
74
+ export type ReasoningEffort = OpenString<(typeof ReasoningEfforts)[number]>;
72
75
  export declare const ReasoningEffort: Schema.declare<ReasoningEffort, ReasoningEffort>;
73
- export declare const LanguageModelToolSchemaCompatibility: Schema.Literals<readonly ["gemini", "moonshot"]>;
74
- export type LanguageModelToolSchemaCompatibility = Schema.Schema.Type<typeof LanguageModelToolSchemaCompatibility>;
76
+ /** Tool schema sanitizer for a model family. `none` opts out of the protocol and model-name defaults. */
77
+ export declare const LanguageModelSanitizerCompatibility: Schema.Literals<readonly ["gemini", "moonshot", "none"]>;
78
+ export type LanguageModelSanitizerCompatibility = Schema.Schema.Type<typeof LanguageModelSanitizerCompatibility>;
75
79
  export declare const LanguageModelMaxTokensFieldCompatibility: Schema.Literals<readonly ["max_completion_tokens", "max_tokens"]>;
76
80
  export type LanguageModelMaxTokensFieldCompatibility = Schema.Schema.Type<typeof LanguageModelMaxTokensFieldCompatibility>;
77
81
  declare const LanguageModelCompatibility_base: Schema.Class<LanguageModelCompatibility, Schema.Struct<{
78
- readonly toolSchema: Schema.optional<Schema.Literals<readonly ["gemini", "moonshot"]>>;
82
+ readonly sanitizer: Schema.optional<Schema.Literals<readonly ["gemini", "moonshot", "none"]>>;
79
83
  readonly reasoningField: Schema.optional<Schema.String>;
80
84
  /** Require every assistant message to include its reasoning field, even when empty. */
81
85
  readonly requireReasoning: Schema.optional<Schema.Boolean>;