@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
@@ -5,11 +5,11 @@ import type { MediaProtocol } from "./route/media-protocol.js";
5
5
  import { AIError, HttpOptions, ModelID, ProviderID } from "./schema/index.js";
6
6
  /**
7
7
  * What every media model carries: ids, the configured route, and deployment `http` overlays. Modality classes
8
- * (`ImageModel`, `VideoModel`, `SpeechModel`) extend it with their route type and a nominal marker so one cannot stand
9
- * in for the other in requests.
8
+ * (`ImageModel`, `VideoModel`, `SpeechModel`, `TranscriptionModel`) extend it with their route type and a nominal
9
+ * marker so one cannot stand in for the other in requests.
10
10
  */
11
11
  export declare class MediaModel<Route, Options> {
12
- protected readonly _Options: (options: Options) => Options;
12
+ protected readonly _Options: Options;
13
13
  readonly id: ModelID;
14
14
  readonly provider: ProviderID;
15
15
  readonly route: Route;
@@ -17,6 +17,7 @@ export declare class MediaModel<Route, Options> {
17
17
  constructor(input: MediaModel.Input<Route>);
18
18
  }
19
19
  export declare namespace MediaModel {
20
+ type Options = Record<string, unknown>;
20
21
  interface Input<Route> {
21
22
  readonly id: string | ModelID;
22
23
  readonly provider: string | ProviderID;
@@ -25,20 +26,18 @@ export declare namespace MediaModel {
25
26
  }
26
27
  /** A protocol plus its canonical start path; `ModelInput.baseURL` overrides `baseURL` per deployment. */
27
28
  interface RouteInput<Request extends MediaRoute.MediaRequest, Protocol> {
28
- readonly id: string;
29
- readonly provider: string | ProviderID;
30
29
  readonly protocol: Protocol;
31
30
  readonly path: Endpoint.EndpointPart<MediaProtocol.Body, Request>;
32
31
  readonly baseURL?: string;
33
32
  /** Headers the protocol requires on every call, such as a pinned API version; deployment headers win. */
34
33
  readonly headers?: Record<string, string>;
35
34
  }
36
- type AnyRouteInput<Request extends MediaRoute.MediaRequest, Event, Response, Frame, State, Token> = RouteInput<Request, MediaProtocol.Inline<Request, Response>> | RouteInput<MediaProtocol.Addressed<Request>, MediaProtocol.Streamed<Request, Event, Frame, State>> | RouteInput<Request, MediaProtocol.Queued<Request, Response, Token>>;
35
+ type InlineRouteInput<Request extends MediaRoute.MediaRequest, Response> = RouteInput<Request, MediaProtocol.Inline<Request, Response>>;
36
+ type StreamRouteInput<Request extends MediaRoute.MediaRequest, Event, Frame, State> = RouteInput<MediaProtocol.Addressed<Request>, MediaProtocol.Streamed<Request, Event, Frame, State>>;
37
+ type QueuedRouteInput<Request extends MediaRoute.MediaRequest, Response, Token> = RouteInput<Request, MediaProtocol.Queued<Request, Response, Token>>;
38
+ type AnyRouteInput<Request extends MediaRoute.MediaRequest, Event, Response, Frame, State, Token> = InlineRouteInput<Request, Response> | StreamRouteInput<Request, Event, Frame, State> | QueuedRouteInput<Request, Response, Token>;
37
39
  }
38
40
  /** Compose a protocol route input with one deployment through `MediaRoute.inline`, `queued`, or `stream`. */
39
- export declare const composeRoute: <Request extends MediaRoute.MediaRequest, Protocol, Route>(compose: (input: MediaRoute.Composition<Request> & {
40
- readonly protocol: Protocol;
41
- }) => Route, route: MediaModel.RouteInput<Request, Protocol>, input: MediaRoute.ModelInput) => Route;
42
- export declare const composeAnyRoute: <Request extends MediaRoute.MediaRequest, Event, Response, Frame, State, Token>(route: MediaModel.AnyRouteInput<Request, Event, Response, Frame, State, Token>, input: MediaRoute.ModelInput, collect: (events: ReadonlyArray<Event>) => Effect.Effect<Response, AIError>) => MediaRoute.AnyRoute<Request, Event, Response>;
41
+ export declare const composeRoute: <Request extends MediaRoute.MediaRequest, Event, Response, Frame, State, Token>(route: MediaModel.AnyRouteInput<Request, Event, Response, Frame, State, Token>, input: MediaRoute.ModelInput, collect: (events: ReadonlyArray<Event>) => Effect.Effect<Response, AIError>) => MediaRoute.AnyRoute<Request, Event, Response>;
43
42
  /** Lift a synchronous Schema-class constructor into a typed `InvalidRequest` failure. */
44
43
  export declare const tryRequest: <A>(make: () => A) => Effect.Effect<A, AIError>;
@@ -4,8 +4,8 @@ import { MediaRoute } from "./route/media.js";
4
4
  import { AIError, HttpOptions, InvalidRequestError, ModelID, ProviderID } from "./schema/index.js";
5
5
  /**
6
6
  * What every media model carries: ids, the configured route, and deployment `http` overlays. Modality classes
7
- * (`ImageModel`, `VideoModel`, `SpeechModel`) extend it with their route type and a nominal marker so one cannot stand
8
- * in for the other in requests.
7
+ * (`ImageModel`, `VideoModel`, `SpeechModel`, `TranscriptionModel`) extend it with their route type and a nominal
8
+ * marker so one cannot stand in for the other in requests.
9
9
  */
10
10
  export class MediaModel {
11
11
  id;
@@ -20,21 +20,19 @@ export class MediaModel {
20
20
  }
21
21
  }
22
22
  /** Compose a protocol route input with one deployment through `MediaRoute.inline`, `queued`, or `stream`. */
23
- export const composeRoute = (compose, route, input) => compose({
24
- id: route.id,
25
- provider: route.provider,
23
+ export const composeRoute = (route, input, collect) => {
24
+ if (isStreamInput(route))
25
+ return MediaRoute.stream({ ...composition(route, input), collect });
26
+ if (isQueuedInput(route))
27
+ return MediaRoute.queued(composition(route, input));
28
+ return MediaRoute.inline(composition(route, input));
29
+ };
30
+ const composition = (route, input) => ({
26
31
  protocol: route.protocol,
27
32
  endpoint: Endpoint.path(route.path, { baseURL: input.baseURL ?? route.baseURL }),
28
33
  auth: input.auth,
29
34
  headers: route.headers === undefined && input.headers === undefined ? undefined : { ...route.headers, ...input.headers },
30
35
  });
31
- export const composeAnyRoute = (route, input, collect) => {
32
- if (isStreamInput(route))
33
- return composeRoute((composition) => MediaRoute.stream({ ...composition, collect }), route, input);
34
- if (isQueuedInput(route))
35
- return composeRoute(MediaRoute.queued, route, input);
36
- return composeRoute(MediaRoute.inline, route, input);
37
- };
38
36
  const isStreamInput = (route) => route.protocol.kind === "stream";
39
37
  const isQueuedInput = (route) => route.protocol.kind === "queued";
40
38
  /** Lift a synchronous Schema-class constructor into a typed `InvalidRequest` failure. */
package/dist/media.js CHANGED
@@ -5,7 +5,7 @@ import { ProviderID } from "./schema/ids.js";
5
5
  import { AIError, HttpContext, InvalidProviderOutputError, InvalidRequestError } from "./schema/errors.js";
6
6
  import { ProviderMetadata } from "./schema/options.js";
7
7
  import { Service } from "./route/executor-service.js";
8
- import { detectMediaType, extensionMediaType } from "./utils/media-type.js";
8
+ import { detectMediaType, fileMediaType } from "./utils/media-type.js";
9
9
  export { detectMediaType } from "./utils/media-type.js";
10
10
  const OCTET_STREAM = "application/octet-stream";
11
11
  // ---------------------------------------------------------------------------
@@ -215,7 +215,7 @@ export const file = (path, options) => Effect.gen(function* () {
215
215
  const data = yield* fs
216
216
  .readFile(path)
217
217
  .pipe(Effect.mapError((cause) => invalid(`Failed to read media file ${path}`, cause)));
218
- return bytes(data, detectMediaType(data) ?? extensionMediaType(path), options);
218
+ return bytes(data, fileMediaType(data, path), options);
219
219
  });
220
220
  /** Materialize an asset and write its bytes through `FileSystem`. */
221
221
  export const write = (asset, path) => Effect.gen(function* () {
package/dist/promise.d.ts CHANGED
@@ -1,40 +1,53 @@
1
1
  import { Effect, Layer } from "effect";
2
- import type { AwaitOptions, Snapshot } from "./generation.js";
3
- import { ImageModel, ImageRequest, type ImageOptions, type ImageRequestInput } from "./image.js";
4
- import { ImageClient } from "./image-client.js";
5
- import { LLMClient } from "./route/client.js";
2
+ import { AIClient } from "./ai-client.js";
3
+ import type { AwaitOptions, Event, Snapshot } from "./generation.js";
4
+ import { type ImageModel, type ImageRequest, type ImageRequestInput } from "./image.js";
5
+ import { Media } from "./media.js";
6
6
  import { RequestExecutor } from "./route/executor.js";
7
7
  import { LanguageModel, LLMRequest } from "./schema/index.js";
8
8
  import type { RequestInput } from "./llm.js";
9
- import { SpeechModel, SpeechRequest, type SpeechRequestInput } from "./speech.js";
10
- import { SpeechClient } from "./speech-client.js";
11
- import { TranscriptionModel, TranscriptionRequest, type TranscriptionOptions, type TranscriptionRequestInput } from "./transcription.js";
12
- import { TranscriptionClient } from "./transcription-client.js";
13
- import { VideoModel, VideoRequest, type VideoOptions, type VideoRequestInput } from "./video.js";
14
- import { VideoClient } from "./video-client.js";
9
+ import { type SpeechModel, type SpeechRequest, type SpeechRequestInput } from "./speech.js";
10
+ import { type TranscriptionModel, type TranscriptionRequest, type TranscriptionRequestInput } from "./transcription.js";
11
+ import { type VideoModel, type VideoRequest, type VideoRequestInput } from "./video.js";
15
12
  /**
16
13
  * Promise-first entrypoint for scripts and non-Effect callers. One `ManagedRuntime` hosts the LLM, image, video, speech,
17
14
  * and transcription clients over a request executor; every method runs the corresponding Effect API and rethrows
18
- * `AIError` unchanged.
15
+ * `AIError` unchanged. `file` and `write` load `node:fs/promises` on first use, so importing this module does not.
19
16
  */
20
17
  export interface Options {
21
- /** Executor layer; defaults to `RequestExecutor.fetchLayer`. Inject a recorder or middleware here. */
18
+ /** Executor layer; defaults to `RequestExecutor.fetchLayer`. Inject a recorder or `RequestExecutor.middleware(fn)` here. */
22
19
  readonly layer?: Layer.Layer<RequestExecutor.Service>;
23
20
  }
24
21
  export interface RunOptions {
25
22
  readonly signal?: AbortSignal;
26
23
  }
27
- export type Services = Layer.Success<typeof LLMClient.layer> | Layer.Success<typeof ImageClient.layer> | Layer.Success<typeof VideoClient.layer> | Layer.Success<typeof SpeechClient.layer> | Layer.Success<typeof TranscriptionClient.layer> | RequestExecutor.Service;
28
- /** Promise view of a `Generation`: its snapshot plus `await`, `refresh`, and `cancel` returning promises. */
24
+ export type Services = AIClient.Services;
25
+ /**
26
+ * Promise view of a `Generation`. Its fields are a snapshot taken when the handle was created; `refresh()` resolves to a
27
+ * new handle rather than updating this one.
28
+ */
29
29
  export type GenerationHandle<Response> = Snapshot & {
30
30
  /** Serializable JSON; pass it back to `resume` from another process. */
31
31
  readonly token: unknown;
32
32
  readonly await: (options?: AwaitOptions & RunOptions) => Promise<Response>;
33
+ /** Status observations until the first terminal one, polling like `await`; abort ends iteration without throwing. */
34
+ readonly events: (options?: AwaitOptions & RunOptions) => AsyncIterable<Event>;
35
+ /** The result without polling; fails when the generation has not completed. */
36
+ readonly result: (options?: RunOptions) => Promise<Response>;
33
37
  readonly refresh: (options?: RunOptions) => Promise<GenerationHandle<Response>>;
34
38
  readonly cancel: (options?: RunOptions) => Promise<void>;
35
39
  };
36
40
  export declare const make: (options?: Options) => {
37
41
  run: <A, E>(effect: Effect.Effect<A, E, Services>, options?: RunOptions) => Promise<A>;
42
+ /** Decoded asset bytes, downloading `url` sources through the executor. */
43
+ bytes: (asset: Media.Asset, options?: RunOptions) => Promise<Uint8Array<ArrayBufferLike>>;
44
+ base64: (asset: Media.Asset, options?: RunOptions) => Promise<string>;
45
+ /** Pull a `url` asset into owned bytes before the provider URL expires. */
46
+ materialize: (asset: Media.Asset, options?: RunOptions) => Promise<Media.Asset>;
47
+ /** Read a file into an asset like `Media.file`: sniffed media type, then the extension's. */
48
+ file: (path: string, options?: Media.AssetOptions & RunOptions) => Promise<Media.Asset>;
49
+ /** Write an asset's bytes like `Media.write`, downloading `url` sources through the executor. */
50
+ write: (asset: Media.Asset, path: string, options?: RunOptions) => Promise<void>;
38
51
  llm: {
39
52
  request: <const SelectedLanguageModel extends LanguageModel>(input: RequestInput<SelectedLanguageModel>) => LLMRequest<SelectedLanguageModel>;
40
53
  generate: <const Model extends LanguageModel>(input: RequestInput<Model> | LLMRequest, options?: RunOptions) => Promise<import("./index.js").LLMResponse>;
@@ -209,7 +222,7 @@ export declare const make: (options?: Options) => {
209
222
  readonly namespace?: string | undefined;
210
223
  } | {
211
224
  readonly type: "media";
212
- readonly media: import("./media.js").Asset;
225
+ readonly media: Media.Asset;
213
226
  readonly providerMetadata?: {
214
227
  readonly [x: string]: {
215
228
  readonly [x: string]: unknown;
@@ -265,11 +278,11 @@ export declare const make: (options?: Options) => {
265
278
  } | {
266
279
  readonly type: "image-partial";
267
280
  readonly index: number;
268
- readonly image: import("./media.js").Asset;
281
+ readonly image: Media.Asset;
269
282
  } | {
270
283
  readonly type: "image";
271
284
  readonly index: number;
272
- readonly image: import("./media.js").Asset;
285
+ readonly image: Media.Asset;
273
286
  } | {
274
287
  readonly type: "finish";
275
288
  readonly providerMetadata?: {
@@ -309,13 +322,13 @@ export declare const make: (options?: Options) => {
309
322
  }[] | undefined;
310
323
  }>;
311
324
  start: <const Model extends ImageModel>(input: ImageRequestInput<Model> | ImageRequest, options?: RunOptions) => Promise<GenerationHandle<import("./image.js").ImageResponse>>;
312
- resume: <Options extends ImageOptions>(model: ImageModel<Options>, token: unknown, options?: RunOptions) => Promise<GenerationHandle<import("./image.js").ImageResponse>>;
325
+ resume: (model: ImageModel, token: unknown, options?: RunOptions) => Promise<GenerationHandle<import("./image.js").ImageResponse>>;
313
326
  };
314
327
  video: {
315
328
  request: typeof import("./video.js").request;
316
329
  start: <const Model extends VideoModel>(input: VideoRequestInput<Model> | VideoRequest, options?: RunOptions) => Promise<GenerationHandle<import("./video.js").VideoResponse>>;
317
330
  generate: <const Model extends VideoModel>(input: VideoRequestInput<Model> | VideoRequest, options?: AwaitOptions & RunOptions) => Promise<import("./video.js").VideoResponse>;
318
- resume: <Options extends VideoOptions>(model: VideoModel<Options>, token: unknown, options?: RunOptions) => Promise<GenerationHandle<import("./video.js").VideoResponse>>;
331
+ resume: (model: VideoModel, token: unknown, options?: RunOptions) => Promise<GenerationHandle<import("./video.js").VideoResponse>>;
319
332
  stream: <const Model extends VideoModel>(input: VideoRequestInput<Model> | VideoRequest, options?: AwaitOptions & RunOptions) => AsyncIterable<{
320
333
  readonly id: string;
321
334
  readonly type: "generation-queued";
@@ -327,7 +340,7 @@ export declare const make: (options?: Options) => {
327
340
  } | {
328
341
  readonly type: "video";
329
342
  readonly index: number;
330
- readonly video: import("./media.js").Asset;
343
+ readonly video: Media.Asset;
331
344
  } | {
332
345
  readonly type: "finish";
333
346
  readonly providerMetadata?: {
@@ -371,6 +384,14 @@ export declare const make: (options?: Options) => {
371
384
  request: typeof import("./speech.js").request;
372
385
  generate: <const Model extends SpeechModel>(input: SpeechRequestInput<Model> | SpeechRequest, options?: RunOptions) => Promise<import("./speech.js").SpeechResponse>;
373
386
  stream: <const Model extends SpeechModel>(input: SpeechRequestInput<Model> | SpeechRequest, options?: RunOptions) => AsyncIterable<{
387
+ readonly id: string;
388
+ readonly type: "generation-queued";
389
+ readonly position?: number | undefined;
390
+ } | {
391
+ readonly id: string;
392
+ readonly type: "generation-progress";
393
+ readonly progress?: number | undefined;
394
+ } | {
374
395
  readonly type: "audio-delta";
375
396
  readonly chunk: Uint8Array<ArrayBufferLike>;
376
397
  } | {
@@ -382,7 +403,7 @@ export declare const make: (options?: Options) => {
382
403
  }[];
383
404
  } | {
384
405
  readonly type: "finish";
385
- readonly audio: import("./media.js").Asset;
406
+ readonly audio: Media.Asset;
386
407
  readonly providerMetadata?: {
387
408
  readonly [x: string]: {
388
409
  readonly [x: string]: unknown;
@@ -497,7 +518,7 @@ export declare const make: (options?: Options) => {
497
518
  }[] | undefined;
498
519
  }>;
499
520
  start: <const Model extends TranscriptionModel>(input: TranscriptionRequestInput<Model> | TranscriptionRequest, options?: RunOptions) => Promise<GenerationHandle<import("./transcription.js").TranscriptionResponse>>;
500
- resume: <Options extends TranscriptionOptions>(model: TranscriptionModel<Options>, token: unknown, options?: RunOptions) => Promise<GenerationHandle<import("./transcription.js").TranscriptionResponse>>;
521
+ resume: (model: TranscriptionModel, token: unknown, options?: RunOptions) => Promise<GenerationHandle<import("./transcription.js").TranscriptionResponse>>;
501
522
  };
502
523
  dispose: () => Promise<void>;
503
524
  };
@@ -505,6 +526,15 @@ export type Client = ReturnType<typeof make>;
505
526
  /** Default client over `RequestExecutor.fetchLayer` for scripts; the runtime builds its layer on first use. */
506
527
  export declare const ai: {
507
528
  run: <A, E>(effect: Effect.Effect<A, E, Services>, options?: RunOptions) => Promise<A>;
529
+ /** Decoded asset bytes, downloading `url` sources through the executor. */
530
+ bytes: (asset: Media.Asset, options?: RunOptions) => Promise<Uint8Array<ArrayBufferLike>>;
531
+ base64: (asset: Media.Asset, options?: RunOptions) => Promise<string>;
532
+ /** Pull a `url` asset into owned bytes before the provider URL expires. */
533
+ materialize: (asset: Media.Asset, options?: RunOptions) => Promise<Media.Asset>;
534
+ /** Read a file into an asset like `Media.file`: sniffed media type, then the extension's. */
535
+ file: (path: string, options?: Media.AssetOptions & RunOptions) => Promise<Media.Asset>;
536
+ /** Write an asset's bytes like `Media.write`, downloading `url` sources through the executor. */
537
+ write: (asset: Media.Asset, path: string, options?: RunOptions) => Promise<void>;
508
538
  llm: {
509
539
  request: <const SelectedLanguageModel extends LanguageModel>(input: RequestInput<SelectedLanguageModel>) => LLMRequest<SelectedLanguageModel>;
510
540
  generate: <const Model extends LanguageModel>(input: RequestInput<Model> | LLMRequest, options?: RunOptions) => Promise<import("./index.js").LLMResponse>;
@@ -679,7 +709,7 @@ export declare const ai: {
679
709
  readonly namespace?: string | undefined;
680
710
  } | {
681
711
  readonly type: "media";
682
- readonly media: import("./media.js").Asset;
712
+ readonly media: Media.Asset;
683
713
  readonly providerMetadata?: {
684
714
  readonly [x: string]: {
685
715
  readonly [x: string]: unknown;
@@ -735,11 +765,11 @@ export declare const ai: {
735
765
  } | {
736
766
  readonly type: "image-partial";
737
767
  readonly index: number;
738
- readonly image: import("./media.js").Asset;
768
+ readonly image: Media.Asset;
739
769
  } | {
740
770
  readonly type: "image";
741
771
  readonly index: number;
742
- readonly image: import("./media.js").Asset;
772
+ readonly image: Media.Asset;
743
773
  } | {
744
774
  readonly type: "finish";
745
775
  readonly providerMetadata?: {
@@ -779,13 +809,13 @@ export declare const ai: {
779
809
  }[] | undefined;
780
810
  }>;
781
811
  start: <const Model extends ImageModel>(input: ImageRequestInput<Model> | ImageRequest, options?: RunOptions) => Promise<GenerationHandle<import("./image.js").ImageResponse>>;
782
- resume: <Options extends ImageOptions>(model: ImageModel<Options>, token: unknown, options?: RunOptions) => Promise<GenerationHandle<import("./image.js").ImageResponse>>;
812
+ resume: (model: ImageModel, token: unknown, options?: RunOptions) => Promise<GenerationHandle<import("./image.js").ImageResponse>>;
783
813
  };
784
814
  video: {
785
815
  request: typeof import("./video.js").request;
786
816
  start: <const Model extends VideoModel>(input: VideoRequestInput<Model> | VideoRequest, options?: RunOptions) => Promise<GenerationHandle<import("./video.js").VideoResponse>>;
787
817
  generate: <const Model extends VideoModel>(input: VideoRequestInput<Model> | VideoRequest, options?: AwaitOptions & RunOptions) => Promise<import("./video.js").VideoResponse>;
788
- resume: <Options extends VideoOptions>(model: VideoModel<Options>, token: unknown, options?: RunOptions) => Promise<GenerationHandle<import("./video.js").VideoResponse>>;
818
+ resume: (model: VideoModel, token: unknown, options?: RunOptions) => Promise<GenerationHandle<import("./video.js").VideoResponse>>;
789
819
  stream: <const Model extends VideoModel>(input: VideoRequestInput<Model> | VideoRequest, options?: AwaitOptions & RunOptions) => AsyncIterable<{
790
820
  readonly id: string;
791
821
  readonly type: "generation-queued";
@@ -797,7 +827,7 @@ export declare const ai: {
797
827
  } | {
798
828
  readonly type: "video";
799
829
  readonly index: number;
800
- readonly video: import("./media.js").Asset;
830
+ readonly video: Media.Asset;
801
831
  } | {
802
832
  readonly type: "finish";
803
833
  readonly providerMetadata?: {
@@ -841,6 +871,14 @@ export declare const ai: {
841
871
  request: typeof import("./speech.js").request;
842
872
  generate: <const Model extends SpeechModel>(input: SpeechRequestInput<Model> | SpeechRequest, options?: RunOptions) => Promise<import("./speech.js").SpeechResponse>;
843
873
  stream: <const Model extends SpeechModel>(input: SpeechRequestInput<Model> | SpeechRequest, options?: RunOptions) => AsyncIterable<{
874
+ readonly id: string;
875
+ readonly type: "generation-queued";
876
+ readonly position?: number | undefined;
877
+ } | {
878
+ readonly id: string;
879
+ readonly type: "generation-progress";
880
+ readonly progress?: number | undefined;
881
+ } | {
844
882
  readonly type: "audio-delta";
845
883
  readonly chunk: Uint8Array<ArrayBufferLike>;
846
884
  } | {
@@ -852,7 +890,7 @@ export declare const ai: {
852
890
  }[];
853
891
  } | {
854
892
  readonly type: "finish";
855
- readonly audio: import("./media.js").Asset;
893
+ readonly audio: Media.Asset;
856
894
  readonly providerMetadata?: {
857
895
  readonly [x: string]: {
858
896
  readonly [x: string]: unknown;
@@ -967,7 +1005,7 @@ export declare const ai: {
967
1005
  }[] | undefined;
968
1006
  }>;
969
1007
  start: <const Model extends TranscriptionModel>(input: TranscriptionRequestInput<Model> | TranscriptionRequest, options?: RunOptions) => Promise<GenerationHandle<import("./transcription.js").TranscriptionResponse>>;
970
- resume: <Options extends TranscriptionOptions>(model: TranscriptionModel<Options>, token: unknown, options?: RunOptions) => Promise<GenerationHandle<import("./transcription.js").TranscriptionResponse>>;
1008
+ resume: (model: TranscriptionModel, token: unknown, options?: RunOptions) => Promise<GenerationHandle<import("./transcription.js").TranscriptionResponse>>;
971
1009
  };
972
1010
  dispose: () => Promise<void>;
973
1011
  };
package/dist/promise.js CHANGED
@@ -1,16 +1,15 @@
1
1
  import { Effect, Layer, ManagedRuntime, Stream } from "effect";
2
- import { Image, ImageModel, ImageRequest } from "./image.js";
3
- import { ImageClient } from "./image-client.js";
2
+ import { AIClient } from "./ai-client.js";
3
+ import { Image } from "./image.js";
4
4
  import { LLM } from "./index.js";
5
- import { LLMClient } from "./route/client.js";
5
+ import { Media } from "./media.js";
6
+ import { tryRequest } from "./media-model.js";
6
7
  import { RequestExecutor } from "./route/executor.js";
7
- import { LanguageModel, LLMRequest } from "./schema/index.js";
8
- import { Speech, SpeechModel, SpeechRequest } from "./speech.js";
9
- import { SpeechClient } from "./speech-client.js";
10
- import { Transcription, TranscriptionModel, TranscriptionRequest, } from "./transcription.js";
11
- import { TranscriptionClient } from "./transcription-client.js";
12
- import { Video, VideoModel, VideoRequest } from "./video.js";
13
- import { VideoClient } from "./video-client.js";
8
+ import { AIError, InvalidRequestError, LanguageModel, LLMRequest } from "./schema/index.js";
9
+ import { Speech } from "./speech.js";
10
+ import { Transcription, } from "./transcription.js";
11
+ import { fileMediaType } from "./utils/media-type.js";
12
+ import { Video } from "./video.js";
14
13
  const abortEffect = (signal) => signal === undefined
15
14
  ? Effect.never
16
15
  : Effect.callback((resume) => {
@@ -23,59 +22,81 @@ const abortEffect = (signal) => signal === undefined
23
22
  return Effect.sync(() => signal.removeEventListener("abort", onAbort));
24
23
  });
25
24
  export const make = (options = {}) => {
26
- const runtime = ManagedRuntime.make(Layer.mergeAll(LLMClient.layer, ImageClient.layer, VideoClient.layer, SpeechClient.layer, TranscriptionClient.layer).pipe(Layer.provideMerge(options.layer ?? RequestExecutor.fetchLayer)));
27
- /** Run any package Effect (for example `asset.bytes()`) inside this runtime. */
25
+ const runtime = ManagedRuntime.make(AIClient.layerWith(options.layer ?? RequestExecutor.fetchLayer));
26
+ /** Run any package Effect (for example `LLMClient.compact(...)`) inside this runtime. */
28
27
  const run = (effect, options) => runtime.runPromise(effect, { signal: options?.signal });
29
28
  const iterate = (stream, options) => Stream.toAsyncIterable(Stream.unwrap(runtime.contextEffect.pipe(Effect.map((context) => stream.pipe(Stream.interruptWhen(abortEffect(options?.signal)), Stream.provideContext(context))))));
30
29
  const handle = (generation) => ({
31
30
  ...generation.snapshot,
32
31
  token: generation.token,
33
32
  await: (options) => run(generation.await({ poll: options?.poll }), options),
33
+ events: (options) => iterate(generation.events({ poll: options?.poll }), options),
34
+ result: (options) => run(generation.result(), options),
34
35
  refresh: (options) => run(generation.refresh(), options).then(handle),
35
36
  cancel: (options) => run(generation.cancel(), options),
36
37
  });
37
- // The typed `generate`/`stream` overloads take a concrete input or a request, not the union; normalize once here.
38
- const llmRequest = (input) => (input instanceof LLMRequest ? input : LLM.request(input));
39
- const imageRequest = (input) => input instanceof ImageRequest ? input : Image.request(input);
40
- const videoRequest = (input) => input instanceof VideoRequest ? input : Video.request(input);
41
- const speechRequest = (input) => input instanceof SpeechRequest ? input : Speech.request(input);
42
- const transcriptionRequest = (input) => input instanceof TranscriptionRequest ? input : Transcription.request(input);
38
+ const llmRequest = (input) => input instanceof LLMRequest ? Effect.succeed(input) : tryRequest(() => LLM.request(input));
43
39
  return {
44
40
  run,
41
+ /** Decoded asset bytes, downloading `url` sources through the executor. */
42
+ bytes: (asset, options) => run(asset.bytes(), options),
43
+ base64: (asset, options) => run(asset.base64(), options),
44
+ /** Pull a `url` asset into owned bytes before the provider URL expires. */
45
+ materialize: (asset, options) => run(asset.materialize(), options),
46
+ /** Read a file into an asset like `Media.file`: sniffed media type, then the extension's. */
47
+ file: async (path, options) => {
48
+ const { readFile } = await import("node:fs/promises");
49
+ return run(Effect.tryPromise({
50
+ try: (signal) => readFile(path, { signal }),
51
+ catch: (cause) => fileError(`Failed to read media file ${path}`, cause),
52
+ }).pipe(Effect.map((buffer) => {
53
+ const data = new Uint8Array(buffer);
54
+ return Media.bytes(data, fileMediaType(data, path), options);
55
+ })), options);
56
+ },
57
+ /** Write an asset's bytes like `Media.write`, downloading `url` sources through the executor. */
58
+ write: async (asset, path, options) => {
59
+ const { writeFile } = await import("node:fs/promises");
60
+ return run(asset.bytes().pipe(Effect.flatMap((data) => Effect.tryPromise({
61
+ try: (signal) => writeFile(path, data, { signal }),
62
+ catch: (cause) => fileError(`Failed to write media file ${path}`, cause),
63
+ }))), options);
64
+ },
45
65
  llm: {
46
66
  request: LLM.request,
47
- generate: (input, options) => run(LLM.generate(llmRequest(input)), options),
48
- stream: (input, options) => iterate(LLM.stream(llmRequest(input)), options),
67
+ generate: (input, options) => run(Effect.flatMap(llmRequest(input), LLM.generate), options),
68
+ stream: (input, options) => iterate(Stream.unwrap(Effect.map(llmRequest(input), LLM.stream)), options),
49
69
  },
50
70
  image: {
51
71
  request: Image.request,
52
- generate: (input, options) => run(Image.generate(imageRequest(input), { poll: options?.poll }), options),
53
- stream: (input, options) => iterate(Image.stream(imageRequest(input), { poll: options?.poll }), options),
54
- start: (input, options) => run(Image.start(imageRequest(input)), options).then(handle),
72
+ generate: (input, options) => run(Image.generate(input, { poll: options?.poll }), options),
73
+ stream: (input, options) => iterate(Image.stream(input, { poll: options?.poll }), options),
74
+ start: (input, options) => run(Image.start(input), options).then(handle),
55
75
  resume: (model, token, options) => run(Image.resume(model, token), options).then(handle),
56
76
  },
57
77
  video: {
58
78
  request: Video.request,
59
- start: (input, options) => run(Video.start(videoRequest(input)), options).then(handle),
60
- generate: (input, options) => run(Video.generate(videoRequest(input), { poll: options?.poll }), options),
79
+ start: (input, options) => run(Video.start(input), options).then(handle),
80
+ generate: (input, options) => run(Video.generate(input, { poll: options?.poll }), options),
61
81
  resume: (model, token, options) => run(Video.resume(model, token), options).then(handle),
62
- stream: (input, options) => iterate(Video.stream(videoRequest(input), { poll: options?.poll }), options),
82
+ stream: (input, options) => iterate(Video.stream(input, { poll: options?.poll }), options),
63
83
  },
64
84
  speech: {
65
85
  request: Speech.request,
66
- generate: (input, options) => run(Speech.generate(speechRequest(input)), options),
67
- stream: (input, options) => iterate(Speech.stream(speechRequest(input)), options),
86
+ generate: (input, options) => run(Speech.generate(input), options),
87
+ stream: (input, options) => iterate(Speech.stream(input), options),
68
88
  },
69
89
  transcription: {
70
90
  request: Transcription.request,
71
- generate: (input, options) => run(Transcription.generate(transcriptionRequest(input), { poll: options?.poll }), options),
72
- stream: (input, options) => iterate(Transcription.stream(transcriptionRequest(input), { poll: options?.poll }), options),
73
- start: (input, options) => run(Transcription.start(transcriptionRequest(input)), options).then(handle),
91
+ generate: (input, options) => run(Transcription.generate(input, { poll: options?.poll }), options),
92
+ stream: (input, options) => iterate(Transcription.stream(input, { poll: options?.poll }), options),
93
+ start: (input, options) => run(Transcription.start(input), options).then(handle),
74
94
  resume: (model, token, options) => run(Transcription.resume(model, token), options).then(handle),
75
95
  },
76
96
  dispose: () => runtime.dispose(),
77
97
  };
78
98
  };
99
+ const fileError = (message, cause) => new AIError({ reason: new InvalidRequestError({ message, cause }) });
79
100
  /** Default client over `RequestExecutor.fetchLayer` for scripts; the runtime builds its layer on first use. */
80
101
  export const ai = make();
81
102
  export * as AI from "./promise.js";
@@ -55,7 +55,10 @@ export const protocol = Protocol.make({
55
55
  return {
56
56
  ...(yield* OpenAIChat.protocol.body.from(req)),
57
57
  enable_thinking: opts.enableThinking,
58
- thinking_budget: opts.thinkingBudget,
58
+ // Alibaba also rejects an explicit budget that is not below `max_completion_tokens`.
59
+ thinking_budget: opts.thinkingBudget === undefined
60
+ ? undefined
61
+ : ProviderShared.fitThinkingBudget(opts.thinkingBudget, req.generation?.maxTokens),
59
62
  preserve_thinking: opts.preserveThinking,
60
63
  clear_thinking: opts.clearThinking,
61
64
  thinking: opts.thinking,
@@ -228,7 +228,7 @@ export declare const protocol: Protocol<{
228
228
  readonly type: string;
229
229
  readonly budget_tokens?: number | undefined;
230
230
  } | undefined;
231
- readonly service_tier?: "auto" | (string & {}) | "standard_only" | undefined;
231
+ readonly service_tier?: import("../schema/options.js").OpenString<"auto" | "standard_only"> | undefined;
232
232
  readonly container?: string | {
233
233
  readonly id?: string | null | undefined;
234
234
  readonly skills?: readonly {
@@ -22,15 +22,17 @@ export const protocol = Protocol.make({
22
22
  from: Effect.fn("AlibabaMessages.fromRequest")(function* (req) {
23
23
  const opts = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Options))(req.providerOptions ?? {});
24
24
  // Model Studio accepts enabled thinking without Anthropic's mandatory token budget.
25
+ const body = yield* AnthropicMessages.protocol.body.from(LLMRequest.update(req, {
26
+ providerOptions: { ...req.providerOptions, thinking: undefined },
27
+ }));
28
+ const budget = opts.thinking?.budgetTokens ?? opts.thinking?.budget_tokens;
25
29
  return {
26
- ...(yield* AnthropicMessages.protocol.body.from(LLMRequest.update(req, {
27
- providerOptions: { ...req.providerOptions, thinking: undefined },
28
- }))),
30
+ ...body,
29
31
  thinking: opts.thinking === undefined
30
32
  ? undefined
31
33
  : {
32
34
  type: opts.thinking.type,
33
- budget_tokens: opts.thinking.budgetTokens ?? opts.thinking.budget_tokens,
35
+ budget_tokens: budget === undefined ? undefined : ProviderShared.fitThinkingBudget(budget, body.max_tokens),
34
36
  },
35
37
  };
36
38
  }),