@opencode/ai 2.0.15 → 2.0.16

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 (168) hide show
  1. package/README.md +286 -2
  2. package/dist/generation.d.ts +36 -22
  3. package/dist/generation.js +53 -24
  4. package/dist/image-client.d.ts +14 -7
  5. package/dist/image-client.js +24 -8
  6. package/dist/image.d.ts +398 -47
  7. package/dist/image.js +47 -45
  8. package/dist/index.d.ts +13 -1
  9. package/dist/index.js +9 -0
  10. package/dist/media-model.d.ts +44 -0
  11. package/dist/media-model.js +49 -0
  12. package/dist/media.d.ts +10 -9
  13. package/dist/media.js +9 -10
  14. package/dist/promise.d.ts +428 -8
  15. package/dist/promise.js +40 -3
  16. package/dist/protocols/alibaba-chat.d.ts +12 -0
  17. package/dist/protocols/alibaba-responses.d.ts +2 -2
  18. package/dist/protocols/anthropic-messages.js +1 -2
  19. package/dist/protocols/assemblyai-transcription.d.ts +40 -0
  20. package/dist/protocols/assemblyai-transcription.js +138 -0
  21. package/dist/protocols/bedrock-converse.js +5 -11
  22. package/dist/protocols/bfl-images.d.ts +32 -0
  23. package/dist/protocols/bfl-images.js +153 -0
  24. package/dist/protocols/cartesia-speech.d.ts +127 -0
  25. package/dist/protocols/cartesia-speech.js +126 -0
  26. package/dist/protocols/deepgram-speech.d.ts +119 -0
  27. package/dist/protocols/deepgram-speech.js +92 -0
  28. package/dist/protocols/deepgram-transcription.d.ts +25 -0
  29. package/dist/protocols/deepgram-transcription.js +129 -0
  30. package/dist/protocols/elevenlabs-speech.d.ts +122 -0
  31. package/dist/protocols/elevenlabs-speech.js +115 -0
  32. package/dist/protocols/fal-images.d.ts +24 -0
  33. package/dist/protocols/fal-images.js +114 -0
  34. package/dist/protocols/fal-video.d.ts +29 -0
  35. package/dist/protocols/fal-video.js +88 -0
  36. package/dist/protocols/gemini.d.ts +9 -9
  37. package/dist/protocols/gemini.js +8 -34
  38. package/dist/protocols/google-images.js +2 -14
  39. package/dist/protocols/google-speech.d.ts +130 -0
  40. package/dist/protocols/google-speech.js +84 -0
  41. package/dist/protocols/google-transcription.d.ts +173 -0
  42. package/dist/protocols/google-transcription.js +138 -0
  43. package/dist/protocols/google-video.d.ts +26 -0
  44. package/dist/protocols/google-video.js +158 -0
  45. package/dist/protocols/meta-images.js +2 -9
  46. package/dist/protocols/meta-responses.d.ts +4 -4
  47. package/dist/protocols/meta-responses.js +1 -1
  48. package/dist/protocols/open-responses.d.ts +6 -6
  49. package/dist/protocols/open-responses.js +1 -2
  50. package/dist/protocols/openai-chat.d.ts +84 -0
  51. package/dist/protocols/openai-chat.js +26 -14
  52. package/dist/protocols/openai-compatible-chat.d.ts +12 -0
  53. package/dist/protocols/openai-compatible-responses.d.ts +2 -2
  54. package/dist/protocols/openai-images.d.ts +124 -3
  55. package/dist/protocols/openai-images.js +107 -54
  56. package/dist/protocols/openai-responses.d.ts +15 -15
  57. package/dist/protocols/openai-responses.js +5 -6
  58. package/dist/protocols/openai-speech.d.ts +116 -0
  59. package/dist/protocols/openai-speech.js +98 -0
  60. package/dist/protocols/openai-transcription.d.ts +207 -0
  61. package/dist/protocols/openai-transcription.js +190 -0
  62. package/dist/protocols/replicate-images.d.ts +28 -0
  63. package/dist/protocols/replicate-images.js +133 -0
  64. package/dist/protocols/runway-video.d.ts +38 -0
  65. package/dist/protocols/runway-video.js +146 -0
  66. package/dist/protocols/shared.d.ts +13 -3
  67. package/dist/protocols/shared.js +23 -3
  68. package/dist/protocols/stability-images.d.ts +38 -0
  69. package/dist/protocols/stability-images.js +148 -0
  70. package/dist/protocols/utils/fal-queue.d.ts +28 -0
  71. package/dist/protocols/utils/fal-queue.js +69 -0
  72. package/dist/protocols/utils/gemini-generate-content.d.ts +65 -0
  73. package/dist/protocols/utils/gemini-generate-content.js +65 -0
  74. package/dist/protocols/utils/gemini-json-schema.d.ts +3 -0
  75. package/dist/protocols/utils/gemini-json-schema.js +76 -0
  76. package/dist/protocols/utils/media-input.d.ts +8 -0
  77. package/dist/protocols/utils/media-input.js +18 -0
  78. package/dist/protocols/utils/speech-stream.d.ts +49 -0
  79. package/dist/protocols/utils/speech-stream.js +67 -0
  80. package/dist/protocols/utils/tool-schema.d.ts +2 -2
  81. package/dist/protocols/utils/tool-schema.js +40 -17
  82. package/dist/protocols/xai-images.js +1 -12
  83. package/dist/protocols/xai-responses.d.ts +2 -2
  84. package/dist/protocols/xai-video.d.ts +34 -0
  85. package/dist/protocols/xai-video.js +147 -0
  86. package/dist/protocols/zai-chat.d.ts +13 -1
  87. package/dist/provider-error.js +3 -0
  88. package/dist/providers/alibaba.d.ts +14 -2
  89. package/dist/providers/amazon-bedrock-mantle.d.ts +14 -2
  90. package/dist/providers/assemblyai.d.ts +25 -0
  91. package/dist/providers/assemblyai.js +29 -0
  92. package/dist/providers/azure.d.ts +18 -6
  93. package/dist/providers/baseten.d.ts +24 -0
  94. package/dist/providers/black-forest-labs.d.ts +25 -0
  95. package/dist/providers/black-forest-labs.js +28 -0
  96. package/dist/providers/cartesia.d.ts +24 -0
  97. package/dist/providers/cartesia.js +22 -0
  98. package/dist/providers/cerebras.d.ts +24 -0
  99. package/dist/providers/cloudflare-ai-gateway.d.ts +30 -6
  100. package/dist/providers/cloudflare-workers-ai.d.ts +24 -0
  101. package/dist/providers/deepgram.d.ts +29 -0
  102. package/dist/providers/deepgram.js +31 -0
  103. package/dist/providers/deepinfra.d.ts +24 -0
  104. package/dist/providers/deepseek.d.ts +24 -0
  105. package/dist/providers/elevenlabs.d.ts +24 -0
  106. package/dist/providers/elevenlabs.js +28 -0
  107. package/dist/providers/fal.d.ts +29 -0
  108. package/dist/providers/fal.js +33 -0
  109. package/dist/providers/fireworks.d.ts +24 -0
  110. package/dist/providers/google-vertex-chat.d.ts +12 -0
  111. package/dist/providers/google-vertex-responses.d.ts +2 -2
  112. package/dist/providers/google-vertex.d.ts +3 -3
  113. package/dist/providers/google.d.ts +18 -3
  114. package/dist/providers/google.js +11 -2
  115. package/dist/providers/groq.d.ts +24 -0
  116. package/dist/providers/index.d.ts +9 -0
  117. package/dist/providers/index.js +9 -0
  118. package/dist/providers/meta.d.ts +14 -2
  119. package/dist/providers/minimax.d.ts +14 -2
  120. package/dist/providers/moonshot.d.ts +14 -2
  121. package/dist/providers/moonshot.js +3 -3
  122. package/dist/providers/openai-compatible-responses.d.ts +2 -2
  123. package/dist/providers/openai-compatible.d.ts +12 -0
  124. package/dist/providers/openai.d.ts +25 -3
  125. package/dist/providers/openai.js +10 -1
  126. package/dist/providers/openrouter.d.ts +48 -0
  127. package/dist/providers/replicate.d.ts +25 -0
  128. package/dist/providers/replicate.js +22 -0
  129. package/dist/providers/runway.d.ts +24 -0
  130. package/dist/providers/runway.js +22 -0
  131. package/dist/providers/stability.d.ts +28 -0
  132. package/dist/providers/stability.js +23 -0
  133. package/dist/providers/togetherai.d.ts +24 -0
  134. package/dist/providers/xai.d.ts +17 -0
  135. package/dist/providers/xai.js +5 -2
  136. package/dist/providers/zai-coding-plan.d.ts +15 -3
  137. package/dist/providers/zai.d.ts +13 -1
  138. package/dist/route/auth.d.ts +4 -1
  139. package/dist/route/auth.js +6 -0
  140. package/dist/route/framing.d.ts +5 -1
  141. package/dist/route/framing.js +9 -0
  142. package/dist/route/media-protocol.d.ts +116 -3
  143. package/dist/route/media-protocol.js +60 -4
  144. package/dist/route/media.d.ts +58 -7
  145. package/dist/route/media.js +201 -29
  146. package/dist/schema/events.d.ts +0 -6
  147. package/dist/schema/messages.d.ts +0 -3
  148. package/dist/schema/options.d.ts +4 -3
  149. package/dist/schema/options.js +3 -2
  150. package/dist/speech-client.d.ts +21 -0
  151. package/dist/speech-client.js +25 -0
  152. package/dist/speech.d.ts +1150 -0
  153. package/dist/speech.js +119 -0
  154. package/dist/transcription-client.d.ts +28 -0
  155. package/dist/transcription-client.js +44 -0
  156. package/dist/transcription.d.ts +1504 -0
  157. package/dist/transcription.js +133 -0
  158. package/dist/utils/bytes.d.ts +1 -0
  159. package/dist/utils/bytes.js +10 -0
  160. package/dist/utils/media-type.d.ts +1 -0
  161. package/dist/utils/media-type.js +22 -1
  162. package/dist/video-client.d.ts +28 -0
  163. package/dist/video-client.js +40 -0
  164. package/dist/video.d.ts +1359 -0
  165. package/dist/video.js +119 -0
  166. package/package.json +3 -3
  167. package/dist/protocols/utils/gemini-tool-schema.d.ts +0 -2
  168. package/dist/protocols/utils/gemini-tool-schema.js +0 -103
@@ -1,49 +1,221 @@
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 { Service as RequestExecutorService } from "./executor-service.js";
6
+ import { RequestExecutor } from "./executor.js";
5
7
  import { MediaProtocol } from "./media-protocol.js";
8
+ import { Generation, resultEvents, } from "../generation.js";
6
9
  import { ProviderShared } from "../protocols/shared.js";
7
- import { AIError, HttpOptions, ProviderID, mergeHttpOptions } from "../schema/index.js";
10
+ import { AIError, AIErrorReason, HttpOptions, InvalidRequestError, ProviderID, mergeHttpOptions, } from "../schema/index.js";
8
11
  import { sanitizeSurrogates } from "../utils/sanitize.js";
9
12
  /**
10
13
  * 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.
14
+ * plumbing every media protocol would otherwise duplicate: option merging, surrogate sanitizing, unsupported-field
15
+ * rejection, URL and query rendering, auth headers, JSON, multipart, or binary encoding, and handing responses back
16
+ * to the protocol.
13
17
  */
14
- export const make = (input) => {
18
+ export const inline = (input) => {
19
+ const transport = makeTransport(input);
20
+ return {
21
+ kind: "inline",
22
+ id: input.id,
23
+ provider: transport.provider,
24
+ protocol: input.protocol.id,
25
+ generate: Effect.fn(`MediaRoute.generate`)(function* (request, execute) {
26
+ const submitted = yield* transport.submit(request, { unsupported: input.protocol.unsupported, from: input.protocol.body.from }, execute);
27
+ return yield* input.protocol.response.decode(submitted.response, submitted.context);
28
+ }),
29
+ };
30
+ };
31
+ /**
32
+ * Compose a queued media protocol the same way, adding `start`/`resume` handles whose polls reuse the route's auth,
33
+ * deployment headers, and (for `start`) the request's `http` overlay. The token is decoded once at the boundary and
34
+ * closed over by the resulting `Generation.Route`.
35
+ */
36
+ export const queued = (input) => {
37
+ const transport = makeTransport(input);
38
+ const protocol = input.protocol;
39
+ const decodeToken = Schema.decodeUnknownEffect(protocol.token);
40
+ // A protocol producing a token its own codec rejects is a programmer defect, not a provider error.
41
+ const encodeToken = Schema.encodeSync(protocol.token);
42
+ const generationRoute = (token, http, execute) => {
43
+ const materialize = (asset) => asset.materialize().pipe(Effect.provideService(RequestExecutorService, { execute }));
44
+ const poll = (operation) => transport
45
+ .call("GET", operation.path(token), http, execute)
46
+ .pipe(Effect.flatMap((sent) => operation.decode(sent.response, { token, auth: sent.auth, materialize })));
47
+ const cancel = protocol.cancel;
48
+ const route = {
49
+ status: poll(protocol.status),
50
+ result: poll(protocol.result),
51
+ cancel: cancel === undefined
52
+ ? undefined
53
+ : transport.call(cancel.method, cancel.path(token), http, execute).pipe(Effect.asVoid),
54
+ };
55
+ return route;
56
+ };
57
+ const start = Effect.fn("MediaRoute.start")(function* (request, execute) {
58
+ const submitted = yield* transport.submit(request, { unsupported: protocol.unsupported, prepare: protocol.start.prepare, from: protocol.start.body.from }, execute);
59
+ const started = yield* protocol.start.decode(submitted.response, submitted.context);
60
+ const route = generationRoute(started.token, submitted.context.request.http, execute);
61
+ return new Generation(route, encodeToken(started.token), started.snapshot);
62
+ });
63
+ const resume = Effect.fn("MediaRoute.resume")(function* (model, raw, execute) {
64
+ const token = yield* decodeToken(raw).pipe(Effect.mapError((cause) => new AIError({
65
+ reason: new InvalidRequestError({
66
+ message: `${input.id} cannot resume a generation from this token`,
67
+ cause,
68
+ }),
69
+ })));
70
+ const route = generationRoute(token, transport.http(model), execute);
71
+ return new Generation(route, encodeToken(token), yield* route.status);
72
+ });
73
+ return { kind: "queued", id: input.id, provider: transport.provider, protocol: protocol.id, start, resume };
74
+ };
75
+ /** Compose a streaming media protocol; `generate` runs the same stream in `generate` mode and folds it with `collect`. */
76
+ export const stream = (input) => {
77
+ const transport = makeTransport(input);
78
+ const protocol = input.protocol;
79
+ const events = (request, execute, mode) => Stream.unwrap(Effect.gen(function* () {
80
+ const submitted = yield* transport.submit({ ...request, mode }, { unsupported: protocol.unsupported, from: protocol.body.from }, execute);
81
+ const http = RequestExecutor.responseHttp(submitted.response);
82
+ return Stream.suspend(() => {
83
+ // Parser state is local to one response, exactly like `Route.make`'s LLM stream loop.
84
+ let state = protocol.initial();
85
+ return protocol.frames(RequestExecutor.responseStream(submitted.response), submitted.context).pipe(Stream.mapEffect((frame) => protocol.step(state, frame).pipe(Effect.map(([next, output]) => {
86
+ state = next;
87
+ return output;
88
+ }))), Stream.flattenIterable, Stream.concat(Stream.suspend(() => Stream.fromIterableEffect(protocol.finish(state, { ...submitted.context, http })))), Stream.mapError((error) => error.reason.http !== undefined
89
+ ? error
90
+ : new AIError({
91
+ reason: AIErrorReason.make({
92
+ ...error.reason,
93
+ message: error.reason.message,
94
+ cause: error.reason.cause,
95
+ http,
96
+ }),
97
+ })));
98
+ });
99
+ }));
100
+ return {
101
+ kind: "stream",
102
+ id: input.id,
103
+ provider: transport.provider,
104
+ protocol: protocol.id,
105
+ stream: (request, execute) => events(request, execute, "stream"),
106
+ generate: (request, execute) => events(request, execute, "generate").pipe(Stream.runCollect, Effect.flatMap(input.collect)),
107
+ };
108
+ };
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
+ // ---------------------------------------------------------------------------
143
+ // Transport plumbing shared by every kind
144
+ // ---------------------------------------------------------------------------
145
+ const makeTransport = (input) => {
15
146
  const provider = ProviderID.make(input.provider);
16
147
  const routeHttp = input.headers === undefined ? undefined : new HttpOptions({ headers: input.headers });
17
148
  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. */
151
+ const send = Effect.fn("MediaRoute.send")(function* (call, execute) {
152
+ const encoded = encode(call.body, call.headers);
153
+ const url = call.url.toString();
154
+ const headers = yield* authorize({
155
+ request: call.request,
156
+ method: call.method,
157
+ url,
158
+ body: encoded.text,
159
+ headers: encoded.headers,
160
+ });
161
+ 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))) };
163
+ });
18
164
  return {
19
- id: input.id,
20
165
  provider,
21
- protocol: input.protocol.id,
22
- generate: Effect.fn(`MediaRoute.generate`)(function* (request, execute) {
23
- yield* rejectUnsupported(input.id, provider, request, input.protocol.unsupported);
166
+ /** Route and model overlays; `start` additionally merges the request's own `http`. */
167
+ http: (model) => mergeHttpOptions(routeHttp, model.http),
168
+ /** POST the protocol body to the route endpoint. */
169
+ submit: Effect.fn("MediaRoute.submit")(function* (request, protocol, execute) {
170
+ yield* rejectUnsupported(input.id, provider, request, protocol.unsupported);
24
171
  const http = mergeHttpOptions(routeHttp, request.model.http, request.http);
172
+ const headers = Headers.fromInput(http?.headers);
173
+ const prepared = protocol.prepare === undefined
174
+ ? request
175
+ : yield* protocol.prepare(request, (path, body) => send({ method: "POST", url: baseURL(path), headers, request, body }, execute).pipe(Effect.map((sent) => sent.response)));
25
176
  // 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 });
177
+ const resolved = { ...sanitizeSurrogates({ ...prepared, http }), model: request.model };
178
+ const body = yield* protocol.from(resolved);
179
+ const url = withQuery(withQuery(Endpoint.render(input.endpoint, { request: resolved, body }), body.type === "multipart" ? undefined : body.query), http?.query);
180
+ const sent = yield* send({ method: "POST", url, headers, request: resolved, body }, execute);
181
+ return { response: sent.response, context: { request: resolved, body } };
46
182
  }),
183
+ /** Bodiless follow-up call (status, result, cancel) with the same auth and headers as `submit`. */
184
+ call: (method, path, http, execute) => {
185
+ // Provider-issued absolute URLs (fal `status_url`) are used as-is; everything else resolves against the base.
186
+ const url = withQuery(/^https?:\/\//.test(path) ? new URL(path) : baseURL(path), http?.query);
187
+ for (const [key, value] of Object.entries(input.endpoint.query ?? {}))
188
+ url.searchParams.set(key, value);
189
+ return send({ method, url, headers: Headers.fromInput(http?.headers), request: { http } }, execute);
190
+ },
191
+ };
192
+ };
193
+ const withQuery = (url, query) => {
194
+ for (const [key, value] of Object.entries(query ?? {})) {
195
+ url.searchParams.delete(key);
196
+ for (const item of typeof value === "string" ? [value] : value)
197
+ url.searchParams.append(key, item);
198
+ }
199
+ return url;
200
+ };
201
+ const encode = (body, headers) => {
202
+ if (body === undefined)
203
+ return { text: "", headers, apply: (request) => request };
204
+ if (body.type === "json") {
205
+ const text = ProviderShared.encodeJson(body.value);
206
+ return { text, headers, apply: HttpClientRequest.bodyText(text, "application/json") };
207
+ }
208
+ if (body.type === "binary")
209
+ return {
210
+ text: `[${body.contentType}]`,
211
+ headers,
212
+ apply: HttpClientRequest.bodyUint8Array(body.value, body.contentType),
213
+ };
214
+ return {
215
+ text: "[multipart/form-data]",
216
+ // The HTTP client sets the multipart boundary; a caller-supplied content-type would corrupt it.
217
+ headers: Headers.remove(headers, "content-type"),
218
+ apply: HttpClientRequest.bodyFormData(body.value),
47
219
  };
48
220
  };
49
221
  /** Common fields are never silently dropped: a present field the protocol declared unsupported fails typed. */
@@ -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">;
@@ -70,12 +70,13 @@ export declare namespace LanguageModelDefaults {
70
70
  export declare const ReasoningEfforts: readonly ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
71
71
  export type ReasoningEffort = (typeof ReasoningEfforts)[number] | (string & {});
72
72
  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>;
73
+ /** Tool schema sanitizer for a model family. `none` opts out of the protocol and model-name defaults. */
74
+ export declare const LanguageModelSanitizerCompatibility: Schema.Literals<readonly ["gemini", "moonshot", "none"]>;
75
+ export type LanguageModelSanitizerCompatibility = Schema.Schema.Type<typeof LanguageModelSanitizerCompatibility>;
75
76
  export declare const LanguageModelMaxTokensFieldCompatibility: Schema.Literals<readonly ["max_completion_tokens", "max_tokens"]>;
76
77
  export type LanguageModelMaxTokensFieldCompatibility = Schema.Schema.Type<typeof LanguageModelMaxTokensFieldCompatibility>;
77
78
  declare const LanguageModelCompatibility_base: Schema.Class<LanguageModelCompatibility, Schema.Struct<{
78
- readonly toolSchema: Schema.optional<Schema.Literals<readonly ["gemini", "moonshot"]>>;
79
+ readonly sanitizer: Schema.optional<Schema.Literals<readonly ["gemini", "moonshot", "none"]>>;
79
80
  readonly reasoningField: Schema.optional<Schema.String>;
80
81
  /** Require every assistant message to include its reasoning field, even when empty. */
81
82
  readonly requireReasoning: Schema.optional<Schema.Boolean>;
@@ -99,10 +99,11 @@ export class LanguageModelDefaults extends Schema.Class("LLM.LanguageModelDefaul
99
99
  })(LanguageModelDefaults || (LanguageModelDefaults = {}));
100
100
  export const ReasoningEfforts = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
101
101
  export const ReasoningEffort = Schema.declare((value) => typeof value === "string", { title: "ReasoningEffort" });
102
- export const LanguageModelToolSchemaCompatibility = Schema.Literals(["gemini", "moonshot"]);
102
+ /** Tool schema sanitizer for a model family. `none` opts out of the protocol and model-name defaults. */
103
+ export const LanguageModelSanitizerCompatibility = Schema.Literals(["gemini", "moonshot", "none"]);
103
104
  export const LanguageModelMaxTokensFieldCompatibility = Schema.Literals(["max_completion_tokens", "max_tokens"]);
104
105
  export class LanguageModelCompatibility extends Schema.Class("LLM.LanguageModelCompatibility")({
105
- toolSchema: Schema.optional(LanguageModelToolSchemaCompatibility),
106
+ sanitizer: Schema.optional(LanguageModelSanitizerCompatibility),
106
107
  reasoningField: Schema.optional(Schema.String),
107
108
  /** Require every assistant message to include its reasoning field, even when empty. */
108
109
  requireReasoning: Schema.optional(Schema.Boolean),
@@ -0,0 +1,21 @@
1
+ import { Context, Effect, Layer, Stream } from "effect";
2
+ import { RequestExecutor } from "./route/executor.js";
3
+ import type { AIError } from "./schema/index.js";
4
+ import type { SpeechEvent, SpeechOptions, SpeechRequestFor, SpeechResponse } from "./speech.js";
5
+ export interface Interface {
6
+ readonly generate: <Options extends SpeechOptions>(request: SpeechRequestFor<Options>) => Effect.Effect<SpeechResponse, AIError>;
7
+ readonly stream: <Options extends SpeechOptions>(request: SpeechRequestFor<Options>) => Stream.Stream<SpeechEvent, AIError>;
8
+ }
9
+ declare const Service_base: Context.ServiceClass<Service, "@opencode/SpeechClient", Interface>;
10
+ export declare class Service extends Service_base {
11
+ }
12
+ export declare const generate: <Options extends SpeechOptions>(request: SpeechRequestFor<Options>) => Effect.Effect<SpeechResponse, AIError, Service>;
13
+ export declare const stream: <Options extends SpeechOptions>(request: SpeechRequestFor<Options>) => Stream.Stream<SpeechEvent, AIError, Service>;
14
+ export declare const layer: Layer.Layer<Service, never, RequestExecutor.Service>;
15
+ export declare const SpeechClient: {
16
+ readonly Service: typeof Service;
17
+ readonly layer: Layer.Layer<Service, never, RequestExecutor.Service>;
18
+ readonly generate: <Options extends SpeechOptions>(request: SpeechRequestFor<Options>) => Effect.Effect<SpeechResponse, AIError, Service>;
19
+ readonly stream: <Options extends SpeechOptions>(request: SpeechRequestFor<Options>) => Stream.Stream<SpeechEvent, AIError, Service>;
20
+ };
21
+ export {};
@@ -0,0 +1,25 @@
1
+ import { Context, Effect, Layer, Stream } from "effect";
2
+ import { RequestExecutor } from "./route/executor.js";
3
+ export class Service extends Context.Service()("@opencode/SpeechClient") {
4
+ }
5
+ export const generate = (request) => Effect.gen(function* () {
6
+ const client = yield* Service;
7
+ return yield* client.generate(request);
8
+ });
9
+ export const stream = (request) => Stream.unwrap(Effect.gen(function* () {
10
+ const client = yield* Service;
11
+ return client.stream(request);
12
+ }));
13
+ export const layer = Layer.effect(Service, Effect.gen(function* () {
14
+ const executor = yield* RequestExecutor.Service;
15
+ return Service.of({
16
+ generate: (request) => request.model.route.generate(request, executor.execute),
17
+ stream: (request) => request.model.route.stream(request, executor.execute),
18
+ });
19
+ }));
20
+ export const SpeechClient = {
21
+ Service,
22
+ layer,
23
+ generate,
24
+ stream,
25
+ };