@opencode/ai 2.0.16 → 2.0.18

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 (185) hide show
  1. package/README.md +187 -129
  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 +6 -10
  8. package/dist/generation.js +17 -20
  9. package/dist/image-client.d.ts +59 -24
  10. package/dist/image-client.js +16 -40
  11. package/dist/image.d.ts +15 -27
  12. package/dist/image.js +4 -15
  13. package/dist/index.d.ts +2 -1
  14. package/dist/index.js +1 -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 +9 -10
  20. package/dist/media-model.js +10 -12
  21. package/dist/media.js +2 -2
  22. package/dist/promise.d.ts +68 -30
  23. package/dist/promise.js +52 -31
  24. package/dist/protocols/alibaba-chat.js +4 -1
  25. package/dist/protocols/alibaba-messages.d.ts +1 -1
  26. package/dist/protocols/alibaba-messages.js +6 -4
  27. package/dist/protocols/anthropic-messages.d.ts +34 -34
  28. package/dist/protocols/anthropic-messages.js +14 -8
  29. package/dist/protocols/assemblyai-transcription.d.ts +2 -1
  30. package/dist/protocols/assemblyai-transcription.js +14 -16
  31. package/dist/protocols/bedrock-converse.d.ts +7 -0
  32. package/dist/protocols/bedrock-converse.js +37 -11
  33. package/dist/protocols/bfl-images.d.ts +7 -1
  34. package/dist/protocols/bfl-images.js +36 -36
  35. package/dist/protocols/cartesia-speech.d.ts +18 -2
  36. package/dist/protocols/cartesia-speech.js +10 -16
  37. package/dist/protocols/deepgram-speech.d.ts +19 -3
  38. package/dist/protocols/deepgram-speech.js +11 -12
  39. package/dist/protocols/deepgram-transcription.d.ts +2 -1
  40. package/dist/protocols/deepgram-transcription.js +9 -13
  41. package/dist/protocols/elevenlabs-speech.d.ts +19 -3
  42. package/dist/protocols/elevenlabs-speech.js +9 -13
  43. package/dist/protocols/fal-images.d.ts +2 -1
  44. package/dist/protocols/fal-images.js +30 -40
  45. package/dist/protocols/fal-video.d.ts +2 -2
  46. package/dist/protocols/fal-video.js +9 -24
  47. package/dist/protocols/gemini.d.ts +13 -13
  48. package/dist/protocols/gemini.js +16 -7
  49. package/dist/protocols/google-images.d.ts +3 -3
  50. package/dist/protocols/google-images.js +11 -21
  51. package/dist/protocols/google-speech.d.ts +16 -0
  52. package/dist/protocols/google-speech.js +20 -16
  53. package/dist/protocols/google-transcription.d.ts +2 -1
  54. package/dist/protocols/google-transcription.js +8 -20
  55. package/dist/protocols/google-video.d.ts +2 -2
  56. package/dist/protocols/google-video.js +14 -30
  57. package/dist/protocols/meta-images.d.ts +3 -4
  58. package/dist/protocols/meta-images.js +12 -21
  59. package/dist/protocols/meta-messages.d.ts +5 -5
  60. package/dist/protocols/meta-responses.js +6 -4
  61. package/dist/protocols/open-responses.d.ts +4 -3
  62. package/dist/protocols/open-responses.js +4 -8
  63. package/dist/protocols/openai-chat.js +3 -4
  64. package/dist/protocols/openai-images.d.ts +7 -5
  65. package/dist/protocols/openai-images.js +65 -66
  66. package/dist/protocols/openai-responses.d.ts +10 -10
  67. package/dist/protocols/openai-responses.js +31 -30
  68. package/dist/protocols/openai-speech.d.ts +21 -1
  69. package/dist/protocols/openai-speech.js +11 -12
  70. package/dist/protocols/openai-transcription.d.ts +4 -0
  71. package/dist/protocols/openai-transcription.js +44 -32
  72. package/dist/protocols/replicate-images.js +11 -17
  73. package/dist/protocols/runway-video.d.ts +3 -3
  74. package/dist/protocols/runway-video.js +11 -17
  75. package/dist/protocols/shared.d.ts +11 -17
  76. package/dist/protocols/shared.js +11 -40
  77. package/dist/protocols/stability-images.d.ts +2 -1
  78. package/dist/protocols/stability-images.js +24 -36
  79. package/dist/protocols/utils/fal-queue.d.ts +1 -3
  80. package/dist/protocols/utils/fal-queue.js +4 -6
  81. package/dist/protocols/utils/media-input.d.ts +15 -1
  82. package/dist/protocols/utils/media-input.js +22 -0
  83. package/dist/protocols/utils/responses-checkpoint.js +3 -7
  84. package/dist/protocols/utils/responses-compaction.d.ts +3 -1
  85. package/dist/protocols/utils/responses-compaction.js +16 -3
  86. package/dist/protocols/utils/speech-stream.d.ts +3 -3
  87. package/dist/protocols/utils/speech-stream.js +1 -4
  88. package/dist/protocols/utils/tool-schema.d.ts +2 -2
  89. package/dist/protocols/utils/tool-schema.js +29 -9
  90. package/dist/protocols/xai-images.d.ts +4 -4
  91. package/dist/protocols/xai-images.js +14 -29
  92. package/dist/protocols/xai-responses.js +1 -1
  93. package/dist/protocols/xai-video.js +12 -18
  94. package/dist/protocols/zai-images.d.ts +2 -2
  95. package/dist/protocols/zai-images.js +11 -14
  96. package/dist/protocols/zai-messages.d.ts +1 -1
  97. package/dist/provider-error.js +7 -1
  98. package/dist/providers/alibaba.d.ts +1 -1
  99. package/dist/providers/amazon-bedrock.d.ts +2 -0
  100. package/dist/providers/amazon-bedrock.js +1 -0
  101. package/dist/providers/anthropic-compatible.d.ts +5 -5
  102. package/dist/providers/anthropic.d.ts +5 -5
  103. package/dist/providers/assemblyai.d.ts +1 -1
  104. package/dist/providers/assemblyai.js +5 -10
  105. package/dist/providers/azure.d.ts +4 -4
  106. package/dist/providers/azure.js +2 -2
  107. package/dist/providers/black-forest-labs.d.ts +1 -1
  108. package/dist/providers/black-forest-labs.js +5 -10
  109. package/dist/providers/cartesia.d.ts +1 -1
  110. package/dist/providers/cartesia.js +5 -10
  111. package/dist/providers/cloudflare-ai-gateway.d.ts +14 -14
  112. package/dist/providers/deepgram.d.ts +1 -1
  113. package/dist/providers/deepgram.js +6 -12
  114. package/dist/providers/elevenlabs.d.ts +1 -1
  115. package/dist/providers/elevenlabs.js +5 -10
  116. package/dist/providers/fal.d.ts +1 -1
  117. package/dist/providers/fal.js +5 -12
  118. package/dist/providers/google-vertex-messages.d.ts +5 -5
  119. package/dist/providers/google-vertex.d.ts +3 -3
  120. package/dist/providers/google.d.ts +3 -3
  121. package/dist/providers/google.js +7 -12
  122. package/dist/providers/meta.d.ts +8 -8
  123. package/dist/providers/meta.js +4 -8
  124. package/dist/providers/minimax.d.ts +5 -5
  125. package/dist/providers/moonshot.d.ts +5 -5
  126. package/dist/providers/openai-options.d.ts +3 -9
  127. package/dist/providers/openai-options.js +4 -7
  128. package/dist/providers/openai.d.ts +9 -10
  129. package/dist/providers/openai.js +11 -12
  130. package/dist/providers/opencode-zen.js +1 -1
  131. package/dist/providers/openrouter.d.ts +6 -7
  132. package/dist/providers/openrouter.js +10 -5
  133. package/dist/providers/replicate.d.ts +1 -1
  134. package/dist/providers/replicate.js +5 -10
  135. package/dist/providers/runway.d.ts +1 -1
  136. package/dist/providers/runway.js +5 -10
  137. package/dist/providers/stability.d.ts +1 -1
  138. package/dist/providers/stability.js +6 -11
  139. package/dist/providers/typesafe-ai.js +1 -1
  140. package/dist/providers/vercel-ai-gateway.js +1 -1
  141. package/dist/providers/xai.js +5 -10
  142. package/dist/providers/zai-coding-plan.d.ts +1 -1
  143. package/dist/providers/zai.js +4 -8
  144. package/dist/route/auth.d.ts +1 -1
  145. package/dist/route/auth.js +14 -13
  146. package/dist/route/client.d.ts +9 -7
  147. package/dist/route/client.js +6 -8
  148. package/dist/route/endpoint.d.ts +1 -0
  149. package/dist/route/endpoint.js +2 -2
  150. package/dist/route/executor-service.d.ts +4 -2
  151. package/dist/route/executor-service.js +2 -1
  152. package/dist/route/executor.d.ts +3 -1
  153. package/dist/route/executor.js +7 -0
  154. package/dist/route/framing.d.ts +10 -1
  155. package/dist/route/framing.js +45 -5
  156. package/dist/route/index.d.ts +1 -1
  157. package/dist/route/media-protocol.d.ts +49 -35
  158. package/dist/route/media-protocol.js +77 -65
  159. package/dist/route/media.d.ts +10 -16
  160. package/dist/route/media.js +46 -62
  161. package/dist/route/protocol.d.ts +3 -1
  162. package/dist/schema/options.d.ts +6 -3
  163. package/dist/schema/options.js +7 -3
  164. package/dist/speech-client.d.ts +62 -17
  165. package/dist/speech-client.js +17 -21
  166. package/dist/speech.d.ts +178 -21
  167. package/dist/speech.js +13 -8
  168. package/dist/testing.d.ts +2 -2
  169. package/dist/transcription-client.d.ts +78 -24
  170. package/dist/transcription-client.js +9 -40
  171. package/dist/transcription.d.ts +15 -27
  172. package/dist/transcription.js +4 -10
  173. package/dist/utils/json.d.ts +4 -0
  174. package/dist/utils/json.js +4 -0
  175. package/dist/utils/media-type.d.ts +2 -1
  176. package/dist/utils/media-type.js +3 -1
  177. package/dist/video-client.d.ts +55 -24
  178. package/dist/video-client.js +16 -36
  179. package/dist/video.d.ts +18 -26
  180. package/dist/video.js +15 -16
  181. package/package.json +3 -3
  182. package/dist/protocols/utils/meta-image.d.ts +0 -2
  183. package/dist/protocols/utils/meta-image.js +0 -13
  184. package/dist/protocols/utils/openai-image.d.ts +0 -5
  185. package/dist/protocols/utils/openai-image.js +0 -18
@@ -3,7 +3,7 @@ import { HttpClientResponse } from "effect/unstable/http";
3
3
  import type { Snapshot, Status } from "../generation.js";
4
4
  import { Media } from "../media.js";
5
5
  import type { AuthInput } from "./auth.js";
6
- import { AIError, HttpContext } from "../schema/index.js";
6
+ import { AIError, HttpContext, ProviderID } from "../schema/index.js";
7
7
  /** Array values become repeated parameters (`keyterm=a&keyterm=b`). */
8
8
  export type Query = Readonly<Record<string, string | ReadonlyArray<string>>>;
9
9
  /** `query` is appended to the endpoint URL before the route and caller `http.query` overlays. */
@@ -34,7 +34,7 @@ export interface DecodeContext<Request> {
34
34
  export interface Inline<Request, Response> {
35
35
  readonly kind: "inline";
36
36
  readonly id: string;
37
- readonly name: string;
37
+ readonly provider: ProviderID;
38
38
  /** Common request fields this protocol cannot lower; the route rejects them before `body.from` runs. */
39
39
  readonly unsupported?: ReadonlyArray<keyof Request & string>;
40
40
  readonly body: {
@@ -44,7 +44,7 @@ export interface Inline<Request, Response> {
44
44
  readonly decode: (response: HttpClientResponse.HttpClientResponse, context: DecodeContext<Request>) => Effect.Effect<Response, AIError>;
45
45
  };
46
46
  }
47
- export declare const inline: <Request, Response>(input: Omit<Inline<Request, Response>, "kind">) => Inline<Request, Response>;
47
+ export declare const inline: <Request, Response>(route: Identity, input: Omit<Inline<Request, Response>, "kind" | "id" | "provider">) => Inline<Request, Response>;
48
48
  /** What `start` learned from the submission response: the route-owned handle plus the first observation. */
49
49
  export interface Started<Token> {
50
50
  readonly token: Token;
@@ -70,7 +70,7 @@ export interface PollContext<Token> {
70
70
  export interface Queued<Request, Response, Token> {
71
71
  readonly kind: "queued";
72
72
  readonly id: string;
73
- readonly name: string;
73
+ readonly provider: ProviderID;
74
74
  /** Common request fields this protocol cannot lower; the route rejects them before `start.body.from` runs. */
75
75
  readonly unsupported?: ReadonlyArray<keyof Request & string>;
76
76
  /** Serializable handle. `Generation.token` carries the encoded form so it can be persisted and resumed elsewhere. */
@@ -93,9 +93,14 @@ export interface Queued<Request, Response, Token> {
93
93
  readonly cancel?: {
94
94
  readonly method: AuthInput["method"];
95
95
  readonly path: (token: Token) => string;
96
+ /**
97
+ * Fetch a fresh status first and skip the call for terminal generations, for providers whose cancel endpoint
98
+ * destroys finished work (Runway's `DELETE /v1/tasks/{id}` deletes completed tasks and their outputs).
99
+ */
100
+ readonly activeOnly?: boolean;
96
101
  };
97
102
  }
98
- export declare const queued: <Request, Response, Token>(input: Omit<Queued<Request, Response, Token>, "kind">) => Queued<Request, Response, Token>;
103
+ export declare const queued: <Request, Response, Token>(route: Identity, input: Omit<Queued<Request, Response, Token>, "kind" | "id" | "provider">) => Queued<Request, Response, Token>;
99
104
  export type Mode = "generate" | "stream";
100
105
  export type Addressed<Request> = Request & {
101
106
  readonly mode: Mode;
@@ -110,7 +115,7 @@ export interface ResponseContext<Request> extends DecodeContext<Addressed<Reques
110
115
  export interface Streamed<Request, Event, Frame, State> {
111
116
  readonly kind: "stream";
112
117
  readonly id: string;
113
- readonly name: string;
118
+ readonly provider: ProviderID;
114
119
  /** Common request fields this protocol cannot lower; the route rejects them before `body.from` runs. */
115
120
  readonly unsupported?: ReadonlyArray<keyof Request & string>;
116
121
  readonly body: {
@@ -122,37 +127,46 @@ export interface Streamed<Request, Event, Frame, State> {
122
127
  /** Emit exactly one terminal event, or fail when the provider stopped before completing. */
123
128
  readonly finish: (state: State, context: ResponseContext<Request>) => Effect.Effect<ReadonlyArray<Event>, AIError>;
124
129
  }
125
- export declare const stream: <Request, Event, Frame, State>(input: Omit<Streamed<Request, Event, Frame, State>, "kind">) => Streamed<Request, Event, Frame, State>;
126
- /**
127
- * Read a text body while retaining the original payload and HTTP context on every downstream error. `invalid` is a
128
- * malformed provider document; `ended` is a generation that reached a terminal status without output (`failed` is
129
- * provider-side, `cancelled`/`expired` mean the result will never exist); `contentPolicy` is a moderated result.
130
- */
131
- export declare const text: (route: string, name: string, response: HttpClientResponse.HttpClientResponse) => Effect.Effect<{
132
- body: string;
133
- http: HttpContext;
134
- invalid: (message: string, cause?: unknown) => AIError;
135
- ended: (status: Exclude<Status, "queued" | "running" | "completed">, message: string) => AIError;
136
- contentPolicy: (message: string) => AIError;
137
- }, AIError, never>;
138
- export type Output = Effect.Success<ReturnType<typeof text>>;
139
- /** Read and Schema-decode a JSON body. Decode failures keep the raw body as `reason.body`. */
140
- export declare const decodeJson: <A>(route: string, name: string, schema: Schema.Codec<A, unknown>) => (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<{
141
- value: A;
142
- body: string;
143
- http: HttpContext;
144
- invalid: (message: string, cause?: unknown) => AIError;
145
- ended: (status: Exclude<Status, "queued" | "running" | "completed">, message: string) => AIError;
146
- contentPolicy: (message: string) => AIError;
147
- }, AIError, never>;
148
- /** Decode a submission response into the token and first snapshot. */
149
- export declare const decodeStarted: <A, Token>(route: string, name: string, schema: Schema.Codec<A, unknown>, started: (value: A) => Started<Token>) => (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<Started<Token>, AIError, never>;
130
+ export declare const stream: <Request, Event, Frame, State>(route: Identity, input: Omit<Streamed<Request, Event, Frame, State>, "kind" | "id" | "provider">) => Streamed<Request, Event, Frame, State>;
131
+ /** One protocol's route id, display name, and provider, with the decoders and errors that carry them. */
132
+ export declare const identity: (input: {
133
+ readonly id: string;
134
+ readonly name: string;
135
+ readonly provider: string;
136
+ }) => {
137
+ id: string;
138
+ name: string;
139
+ provider: string & import("effect/Brand").Brand<"AI.ProviderID">;
140
+ text: (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<{
141
+ body: string;
142
+ http: HttpContext;
143
+ invalid: (message: string, cause?: unknown) => AIError;
144
+ ended: (status: Exclude<Status, "queued" | "running" | "completed">, message: string) => AIError;
145
+ pending: (id: string) => AIError;
146
+ contentPolicy: (message: string) => AIError;
147
+ }, AIError, never>;
148
+ decodeJson: <A>(schema: Schema.Codec<A, unknown>) => (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<{
149
+ value: A;
150
+ body: string;
151
+ http: HttpContext;
152
+ invalid: (message: string, cause?: unknown) => AIError;
153
+ ended: (status: Exclude<Status, "queued" | "running" | "completed">, message: string) => AIError;
154
+ pending: (id: string) => AIError;
155
+ contentPolicy: (message: string) => AIError;
156
+ }, AIError, never>;
157
+ /** Decode a submission response into the token and first snapshot. */
158
+ decodeStarted: <A, Token>(schema: Schema.Codec<A, unknown>, started: (value: A) => Started<Token>) => (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<Started<Token>, AIError, never>;
159
+ /** Schema-decode one JSON stream frame. Decode failures keep the frame as `reason.body`. */
160
+ decodeFrame: <A>(schema: Schema.Codec<A, unknown>) => (frame: string) => Effect.Effect<A, AIError, never>;
161
+ /** A stream-time failure; the frame stays on `reason.body`. */
162
+ frameError: (message: string, body?: string, cause?: unknown) => AIError;
163
+ incomplete: () => AIError;
164
+ unsupported: (operation: string, message: string) => AIError;
165
+ };
166
+ export type Identity = ReturnType<typeof identity>;
167
+ export type Output = Effect.Success<ReturnType<Identity["text"]>>;
150
168
  /** Map a provider status string through the protocol's table; unknown values are an invalid provider document. */
151
169
  export declare const status: <Table extends Record<string, Status>>(table: Table, raw: string, output: Output) => Effect.Effect<Status, AIError>;
152
- export declare const frameError: (route: string, message: string, body?: string, cause?: unknown) => AIError;
153
- export declare const incomplete: (route: string) => AIError;
154
- /** Schema-decode one JSON stream frame. Decode failures keep the frame as `reason.body`. */
155
- export declare const decodeFrame: <A>(route: string, name: string, schema: Schema.Codec<A, unknown>) => (frame: string) => Effect.Effect<A, AIError, never>;
156
170
  /** A `url` asset whose provider-declared retention window starts now. */
157
171
  export declare const expiringUrl: (url: string, retention: Duration.Duration, options?: Parameters<typeof Media.url>[1]) => Effect.Effect<Media.Asset, never, never>;
158
172
  export * as MediaProtocol from "./media-protocol.js";
@@ -1,7 +1,7 @@
1
1
  import { Clock, Duration, Effect, Schema } from "effect";
2
2
  import { HttpClientResponse } from "effect/unstable/http";
3
3
  import { Media } from "../media.js";
4
- import { AIError, ContentPolicyError, HttpContext, InvalidProviderOutputError, InvalidRequestError, ProviderInternalError, } from "../schema/index.js";
4
+ import { AIError, ContentPolicyError, HttpContext, InvalidProviderOutputError, InvalidRequestError, ProviderID, ProviderInternalError, UnsupportedOperationError, } from "../schema/index.js";
5
5
  export const json = (value, query) => ({
6
6
  type: "json",
7
7
  value,
@@ -14,82 +14,94 @@ export const binary = (value, contentType, query) => ({
14
14
  contentType,
15
15
  query,
16
16
  });
17
- export const inline = (input) => ({
18
- kind: "inline",
19
- ...input,
20
- });
21
- export const queued = (input) => ({
22
- kind: "queued",
23
- ...input,
24
- });
25
- export const stream = (input) => ({
26
- kind: "stream",
27
- ...input,
28
- });
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 });
29
20
  // ---------------------------------------------------------------------------
30
21
  // Response helpers
31
22
  // ---------------------------------------------------------------------------
32
23
  const context = (response) => new HttpContext({ url: response.request.url, status: response.status, headers: response.headers });
33
- /**
34
- * Read a text body while retaining the original payload and HTTP context on every downstream error. `invalid` is a
35
- * malformed provider document; `ended` is a generation that reached a terminal status without output (`failed` is
36
- * provider-side, `cancelled`/`expired` mean the result will never exist); `contentPolicy` is a moderated result.
37
- */
38
- export const text = Effect.fn("MediaProtocol.text")(function* (route, name, response) {
39
- const http = context(response);
40
- const body = yield* response.text.pipe(Effect.mapError((cause) => new AIError({
41
- reason: new InvalidProviderOutputError({
42
- route,
43
- 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,
44
46
  http,
45
- cause,
46
- }),
47
- })));
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
+ };
48
72
  return {
49
- body,
50
- http,
51
- invalid: (message, cause) => new AIError({ reason: new InvalidProviderOutputError({ route, message, body, http, cause }) }),
52
- ended: (status, message) => new AIError({
53
- reason: status === "failed"
54
- ? new ProviderInternalError({ message, body, http })
55
- : new InvalidRequestError({ message, body, http }),
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
+ }),
56
96
  }),
57
- contentPolicy: (message) => new AIError({ reason: new ContentPolicyError({ message, body, http }) }),
97
+ unsupported: (operation, message) => new AIError({ reason: new UnsupportedOperationError({ operation, provider, route: input.id, message }) }),
58
98
  };
59
- });
60
- /** Read and Schema-decode a JSON body. Decode failures keep the raw body as `reason.body`. */
61
- export const decodeJson = (route, name, schema) => {
62
- const decode = Schema.decodeUnknownEffect(Schema.fromJsonString(schema));
63
- return Effect.fn("MediaProtocol.decodeJson")(function* (response) {
64
- const output = yield* text(route, name, response);
65
- const value = yield* decode(output.body).pipe(Effect.mapError((cause) => output.invalid(`${name} returned an invalid response`, cause)));
66
- return { ...output, value };
67
- });
68
- };
69
- /** Decode a submission response into the token and first snapshot. */
70
- export const decodeStarted = (route, name, schema, started) => {
71
- const decode = decodeJson(route, name, schema);
72
- return (response) => decode(response).pipe(Effect.map((output) => started(output.value)));
73
99
  };
74
100
  /** Map a provider status string through the protocol's table; unknown values are an invalid provider document. */
75
101
  export const status = (table, raw, output) => {
76
- const normalized = table[raw];
77
- if (normalized === undefined)
102
+ if (!Object.hasOwn(table, raw))
78
103
  return Effect.fail(output.invalid(`Unknown generation status "${raw}"`));
79
- return Effect.succeed(normalized);
80
- };
81
- export const frameError = (route, message, body, cause) => new AIError({ reason: new InvalidProviderOutputError({ route, message, body, cause }) });
82
- export const incomplete = (route) => new AIError({
83
- reason: new InvalidProviderOutputError({
84
- route,
85
- message: "The provider response ended unexpectedly.",
86
- classification: "incomplete-stream",
87
- }),
88
- });
89
- /** Schema-decode one JSON stream frame. Decode failures keep the frame as `reason.body`. */
90
- export const decodeFrame = (route, name, schema) => {
91
- const decode = Schema.decodeUnknownEffect(Schema.fromJsonString(schema));
92
- return (frame) => decode(frame).pipe(Effect.mapError((cause) => frameError(route, `${name} sent an invalid stream event`, frame, cause)));
104
+ return Effect.succeed(table[raw]);
93
105
  };
94
106
  /** A `url` asset whose provider-declared retention window starts now. */
95
107
  export const expiringUrl = (url, retention, options) => Clock.currentTimeMillis.pipe(Effect.map((now) => Media.url(url, { ...options, expiresAt: now + Duration.toMillis(retention) })));
@@ -3,7 +3,7 @@ import { Auth } from "./auth.js";
3
3
  import { Endpoint } from "./endpoint.js";
4
4
  import { type Interface } from "./executor-service.js";
5
5
  import { MediaProtocol } from "./media-protocol.js";
6
- import { Generation, type AwaitOptions, type Observation } from "../generation.js";
6
+ import { Generation } from "../generation.js";
7
7
  import { AIError, HttpOptions, ProviderID } from "../schema/index.js";
8
8
  export type Execute = Interface["execute"];
9
9
  /** The minimum a media request must carry for the route to build a transport request. */
@@ -23,8 +23,14 @@ export interface ModelInput {
23
23
  readonly headers?: Record<string, string>;
24
24
  readonly http?: HttpOptions;
25
25
  }
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">;
26
32
  /** One request, one response. */
27
- export interface Route<Request extends MediaRequest, Response> {
33
+ export interface InlineRoute<Request extends MediaRequest, Response> {
28
34
  readonly kind: "inline";
29
35
  readonly id: string;
30
36
  readonly provider: ProviderID;
@@ -50,10 +56,8 @@ export interface StreamRoute<Request extends MediaRequest, Event, Response> {
50
56
  readonly stream: (request: Request, execute: Execute) => Stream.Stream<Event, AIError>;
51
57
  readonly generate: (request: Request, execute: Execute) => Effect.Effect<Response, AIError>;
52
58
  }
53
- export type AnyRoute<Request extends MediaRequest, Event, Response> = Route<Request, Response> | StreamRoute<Request, Event, Response> | QueuedRoute<Request, Response>;
59
+ export type AnyRoute<Request extends MediaRequest, Event, Response> = InlineRoute<Request, Response> | StreamRoute<Request, Event, Response> | QueuedRoute<Request, Response>;
54
60
  export interface Composition<Request extends MediaRequest> {
55
- readonly id: string;
56
- readonly provider: string | ProviderID;
57
61
  readonly endpoint: Endpoint.Definition<MediaProtocol.Body, Request>;
58
62
  readonly auth: Auth.Definition;
59
63
  /** Deployment headers applied before transport authentication. */
@@ -75,7 +79,7 @@ export interface StreamInput<Request extends MediaRequest, Event, Response, Fram
75
79
  * rejection, URL and query rendering, auth headers, JSON, multipart, or binary encoding, and handing responses back
76
80
  * to the protocol.
77
81
  */
78
- export declare const inline: <Request extends MediaRequest, Response>(input: InlineInput<Request, Response>) => Route<Request, Response>;
82
+ export declare const inline: <Request extends MediaRequest, Response>(input: InlineInput<Request, Response>) => InlineRoute<Request, Response>;
79
83
  /**
80
84
  * Compose a queued media protocol the same way, adding `start`/`resume` handles whose polls reuse the route's auth,
81
85
  * deployment headers, and (for `start`) the request's `http` overlay. The token is decoded once at the boundary and
@@ -84,14 +88,4 @@ export declare const inline: <Request extends MediaRequest, Response>(input: Inl
84
88
  export declare const queued: <Request extends MediaRequest, Response, Token>(input: QueuedInput<Request, Response, Token>) => QueuedRoute<Request, Response>;
85
89
  /** Compose a streaming media protocol; `generate` runs the same stream in `generate` mode and folds it with `collect`. */
86
90
  export declare const stream: <Request extends MediaRequest, Event, Response, Frame, State>(input: StreamInput<Request, Event, Response, Frame, State>) => StreamRoute<Request, Event, Response>;
87
- export declare const dispatch: <Event, Response>(input: {
88
- readonly modality: string;
89
- readonly execute: Execute;
90
- readonly responseEvents: (response: Response) => ReadonlyArray<Event>;
91
- }) => {
92
- start: <Request extends MediaRequest>(route: AnyRoute<Request, Event, Response>, request: Request) => Effect.Effect<Generation<Response>, AIError, never>;
93
- resume: <Request extends MediaRequest>(route: AnyRoute<Request, Event, Response>, model: MediaRequest["model"], token: unknown) => Effect.Effect<Generation<Response>, AIError, never>;
94
- generate: <Request extends MediaRequest>(route: AnyRoute<Request, Event, Response>, request: Request, options?: AwaitOptions) => Effect.Effect<Response, AIError, never>;
95
- stream: <Request extends MediaRequest>(route: AnyRoute<Request, Event, Response>, request: Request, options?: AwaitOptions) => Stream.Stream<Event | Observation, AIError>;
96
- };
97
91
  export * as MediaRoute from "./media.js";
@@ -2,13 +2,20 @@ 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 { Service as RequestExecutorService } from "./executor-service.js";
5
+ import { RequestExecutorService } from "./executor-service.js";
6
6
  import { RequestExecutor } from "./executor.js";
7
7
  import { MediaProtocol } from "./media-protocol.js";
8
- import { Generation, resultEvents, } from "../generation.js";
9
- import { ProviderShared } from "../protocols/shared.js";
10
- import { AIError, AIErrorReason, HttpOptions, InvalidRequestError, 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";
11
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
+ });
12
19
  /**
13
20
  * Compose an inline media protocol with an endpoint and auth into a runnable route. The route owns the transport
14
21
  * plumbing every media protocol would otherwise duplicate: option merging, surrogate sanitizing, unsupported-field
@@ -19,8 +26,8 @@ export const inline = (input) => {
19
26
  const transport = makeTransport(input);
20
27
  return {
21
28
  kind: "inline",
22
- id: input.id,
23
- provider: transport.provider,
29
+ id: input.protocol.id,
30
+ provider: input.protocol.provider,
24
31
  protocol: input.protocol.id,
25
32
  generate: Effect.fn(`MediaRoute.generate`)(function* (request, execute) {
26
33
  const submitted = yield* transport.submit(request, { unsupported: input.protocol.unsupported, from: input.protocol.body.from }, execute);
@@ -44,15 +51,18 @@ export const queued = (input) => {
44
51
  const poll = (operation) => transport
45
52
  .call("GET", operation.path(token), http, execute)
46
53
  .pipe(Effect.flatMap((sent) => operation.decode(sent.response, { token, auth: sent.auth, materialize })));
54
+ const status = poll(protocol.status);
47
55
  const cancel = protocol.cancel;
48
- const route = {
49
- status: poll(protocol.status),
56
+ const send = cancel === undefined
57
+ ? undefined
58
+ : transport.call(cancel.method, cancel.path(token), http, execute).pipe(Effect.asVoid);
59
+ return {
60
+ status,
50
61
  result: poll(protocol.result),
51
- cancel: cancel === undefined
52
- ? undefined
53
- : transport.call(cancel.method, cancel.path(token), http, execute).pipe(Effect.asVoid),
62
+ cancel: send !== undefined && cancel?.activeOnly
63
+ ? status.pipe(Effect.flatMap((snapshot) => (isTerminal(snapshot.status) ? Effect.void : send)))
64
+ : send,
54
65
  };
55
- return route;
56
66
  };
57
67
  const start = Effect.fn("MediaRoute.start")(function* (request, execute) {
58
68
  const submitted = yield* transport.submit(request, { unsupported: protocol.unsupported, prepare: protocol.start.prepare, from: protocol.start.body.from }, execute);
@@ -63,14 +73,14 @@ export const queued = (input) => {
63
73
  const resume = Effect.fn("MediaRoute.resume")(function* (model, raw, execute) {
64
74
  const token = yield* decodeToken(raw).pipe(Effect.mapError((cause) => new AIError({
65
75
  reason: new InvalidRequestError({
66
- message: `${input.id} cannot resume a generation from this token`,
76
+ message: `${protocol.id} cannot resume a generation from this token`,
67
77
  cause,
68
78
  }),
69
79
  })));
70
80
  const route = generationRoute(token, transport.http(model), execute);
71
81
  return new Generation(route, encodeToken(token), yield* route.status);
72
82
  });
73
- return { kind: "queued", id: input.id, provider: transport.provider, protocol: protocol.id, start, resume };
83
+ return { kind: "queued", id: protocol.id, provider: protocol.provider, protocol: protocol.id, start, resume };
74
84
  };
75
85
  /** Compose a streaming media protocol; `generate` runs the same stream in `generate` mode and folds it with `collect`. */
76
86
  export const stream = (input) => {
@@ -99,55 +109,21 @@ export const stream = (input) => {
99
109
  }));
100
110
  return {
101
111
  kind: "stream",
102
- id: input.id,
103
- provider: transport.provider,
112
+ id: protocol.id,
113
+ provider: protocol.provider,
104
114
  protocol: protocol.id,
105
115
  stream: (request, execute) => events(request, execute, "stream"),
106
116
  generate: (request, execute) => events(request, execute, "generate").pipe(Stream.runCollect, Effect.flatMap(input.collect)),
107
117
  };
108
118
  };
109
- export const dispatch = (input) => {
110
- const notQueued = (route, operation) => ProviderShared.unsupportedOperation({
111
- operation: `${input.modality}.${operation}`,
112
- provider: route.provider,
113
- route: route.id,
114
- message: `${route.provider}/${route.id} is not a queued route; use generate or stream`,
115
- });
116
- const start = (route, request) => {
117
- if (route.kind !== "queued")
118
- return Effect.fail(notQueued(route, "start"));
119
- return route.start(request, input.execute);
120
- };
121
- return {
122
- start,
123
- resume: (route, model, token) => {
124
- if (route.kind !== "queued")
125
- return Effect.fail(notQueued(route, "resume"));
126
- return route.resume(model, token, input.execute);
127
- },
128
- generate: (route, request, options) => {
129
- if (route.kind !== "queued")
130
- return route.generate(request, input.execute);
131
- return start(route, request).pipe(Effect.flatMap((generation) => generation.await(options)));
132
- },
133
- stream: (route, request, options) => {
134
- if (route.kind === "stream")
135
- return route.stream(request, input.execute);
136
- if (route.kind === "queued")
137
- return Stream.unwrap(start(route, request).pipe(Effect.map((generation) => resultEvents(generation, input.responseEvents, options))));
138
- return Stream.fromIterableEffect(Effect.map(route.generate(request, input.execute), input.responseEvents));
139
- },
140
- };
141
- };
142
119
  // ---------------------------------------------------------------------------
143
120
  // Transport plumbing shared by every kind
144
121
  // ---------------------------------------------------------------------------
145
122
  const makeTransport = (input) => {
146
- const provider = ProviderID.make(input.provider);
147
123
  const routeHttp = input.headers === undefined ? undefined : new HttpOptions({ headers: input.headers });
148
124
  const authorize = Auth.toEffect(input.auth);
149
- const baseURL = (path) => new URL(`${ProviderShared.trimBaseUrl(input.endpoint.baseURL ?? "")}${path}`);
150
- /** `auth` is only what `Auth` added, never deployment headers. */
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. */
151
127
  const send = Effect.fn("MediaRoute.send")(function* (call, execute) {
152
128
  const encoded = encode(call.body, call.headers);
153
129
  const url = call.url.toString();
@@ -159,15 +135,17 @@ const makeTransport = (input) => {
159
135
  headers: encoded.headers,
160
136
  });
161
137
  const response = yield* execute(encoded.apply(HttpClientRequest.make(call.method)(url).pipe(HttpClientRequest.setHeaders(headers))));
162
- return { response, auth: Object.fromEntries(Object.entries(headers).filter(([key]) => !(key in call.headers))) };
138
+ return {
139
+ response,
140
+ auth: Object.fromEntries(Object.entries(headers).filter(([key, value]) => encoded.headers[key] !== value)),
141
+ };
163
142
  });
164
143
  return {
165
- provider,
166
144
  /** Route and model overlays; `start` additionally merges the request's own `http`. */
167
145
  http: (model) => mergeHttpOptions(routeHttp, model.http),
168
146
  /** POST the protocol body to the route endpoint. */
169
147
  submit: Effect.fn("MediaRoute.submit")(function* (request, protocol, execute) {
170
- yield* rejectUnsupported(input.id, provider, request, protocol.unsupported);
148
+ yield* rejectUnsupported(input.protocol.id, input.protocol.provider, request, protocol.unsupported);
171
149
  const http = mergeHttpOptions(routeHttp, request.model.http, request.http);
172
150
  const headers = Headers.fromInput(http?.headers);
173
151
  const prepared = protocol.prepare === undefined
@@ -202,7 +180,7 @@ const encode = (body, headers) => {
202
180
  if (body === undefined)
203
181
  return { text: "", headers, apply: (request) => request };
204
182
  if (body.type === "json") {
205
- const text = ProviderShared.encodeJson(body.value);
183
+ const text = encodeJson(body.value);
206
184
  return { text, headers, apply: HttpClientRequest.bodyText(text, "application/json") };
207
185
  }
208
186
  if (body.type === "binary")
@@ -218,7 +196,11 @@ const encode = (body, headers) => {
218
196
  apply: HttpClientRequest.bodyFormData(body.value),
219
197
  };
220
198
  };
221
- /** Common fields are never silently dropped: a present field the protocol declared unsupported fails typed. */
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
+ */
222
204
  const rejectUnsupported = (route, provider, request, unsupported) => {
223
205
  const present = (unsupported ?? []).filter((field) => {
224
206
  const value = request[field];
@@ -226,11 +208,13 @@ const rejectUnsupported = (route, provider, request, unsupported) => {
226
208
  });
227
209
  if (present.length === 0)
228
210
  return Effect.void;
229
- return Effect.fail(ProviderShared.unsupportedOperation({
230
- operation: `media.${present[0]}`,
231
- provider,
232
- route,
233
- 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
+ }),
234
218
  }));
235
219
  };
236
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. */
@@ -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,8 +68,10 @@ 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
76
  /** Tool schema sanitizer for a model family. `none` opts out of the protocol and model-name defaults. */
74
77
  export declare const LanguageModelSanitizerCompatibility: Schema.Literals<readonly ["gemini", "moonshot", "none"]>;
@@ -39,8 +39,12 @@ export class HttpOptions extends Schema.Class("AI.HttpOptions")({
39
39
  }) {
40
40
  }
41
41
  (function (HttpOptions) {
42
- /** Normalize HTTP option input into the canonical `HttpOptions` class. */
43
- HttpOptions.make = (input) => (input instanceof HttpOptions ? input : new HttpOptions(input));
42
+ function make(input) {
43
+ if (input === undefined || input instanceof HttpOptions)
44
+ return input;
45
+ return new HttpOptions(input);
46
+ }
47
+ HttpOptions.make = make;
44
48
  })(HttpOptions || (HttpOptions = {}));
45
49
  export const mergeHttpOptions = (...items) => {
46
50
  const body = mergeJsonRecords(...items.map((item) => item?.body));
@@ -93,7 +97,7 @@ export class LanguageModelDefaults extends Schema.Class("LLM.LanguageModelDefaul
93
97
  return new LanguageModelDefaults({
94
98
  generation: input.generation === undefined ? undefined : GenerationOptions.make(input.generation),
95
99
  providerOptions: input.providerOptions,
96
- http: input.http === undefined ? undefined : HttpOptions.make(input.http),
100
+ http: HttpOptions.make(input.http),
97
101
  });
98
102
  };
99
103
  })(LanguageModelDefaults || (LanguageModelDefaults = {}));