@opencode/ai 0.0.0-dev-20138 → 0.0.0-dev-20141

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 (44) hide show
  1. package/dist/image-client.d.ts +54 -21
  2. package/dist/image-client.js +14 -39
  3. package/dist/image.d.ts +13 -25
  4. package/dist/image.js +2 -13
  5. package/dist/media-client.d.ts +30 -0
  6. package/dist/media-client.js +51 -0
  7. package/dist/media-model.d.ts +9 -8
  8. package/dist/media-model.js +10 -10
  9. package/dist/promise.d.ts +26 -10
  10. package/dist/promise.js +15 -20
  11. package/dist/protocols/anthropic-messages.js +3 -6
  12. package/dist/protocols/bedrock-converse.js +5 -6
  13. package/dist/protocols/cartesia-speech.d.ts +16 -0
  14. package/dist/protocols/deepgram-speech.d.ts +16 -0
  15. package/dist/protocols/elevenlabs-speech.d.ts +16 -0
  16. package/dist/protocols/gemini.js +6 -6
  17. package/dist/protocols/google-speech.d.ts +16 -0
  18. package/dist/protocols/meta-responses.js +1 -2
  19. package/dist/protocols/mistral-chat.js +3 -6
  20. package/dist/protocols/open-responses.d.ts +1 -3
  21. package/dist/protocols/open-responses.js +3 -4
  22. package/dist/protocols/openai-chat.js +3 -4
  23. package/dist/protocols/openai-responses.js +7 -10
  24. package/dist/protocols/openai-speech.d.ts +16 -0
  25. package/dist/protocols/utils/tool-schema.d.ts +2 -2
  26. package/dist/protocols/utils/tool-schema.js +10 -3
  27. package/dist/route/client.d.ts +2 -1
  28. package/dist/route/client.js +3 -1
  29. package/dist/route/media.d.ts +4 -14
  30. package/dist/route/media.js +1 -36
  31. package/dist/route/protocol.d.ts +3 -1
  32. package/dist/speech-client.d.ts +57 -14
  33. package/dist/speech-client.js +15 -20
  34. package/dist/speech.d.ts +176 -19
  35. package/dist/speech.js +11 -6
  36. package/dist/transcription-client.d.ts +73 -21
  37. package/dist/transcription-client.js +7 -39
  38. package/dist/transcription.d.ts +13 -25
  39. package/dist/transcription.js +2 -8
  40. package/dist/video-client.d.ts +50 -21
  41. package/dist/video-client.js +14 -35
  42. package/dist/video.d.ts +14 -22
  43. package/dist/video.js +13 -14
  44. package/package.json +3 -3
@@ -1,30 +1,63 @@
1
- import { Context, Effect, Layer, Stream } from "effect";
2
- import type { AwaitOptions, Generation } from "./generation.js";
3
- import { RequestExecutor } from "./route/executor.js";
4
- import type { AIError } from "./schema/index.js";
5
- import { type ImageEvent, type ImageModel, type ImageOptions, type ImageRequestFor, type ImageResponse } from "./image.js";
6
- export interface Interface {
7
- readonly generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>, options?: AwaitOptions) => Effect.Effect<ImageResponse, AIError>;
8
- readonly stream: <Options extends ImageOptions>(request: ImageRequestFor<Options>, options?: AwaitOptions) => Stream.Stream<ImageEvent, AIError>;
9
- readonly start: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<Generation<ImageResponse>, AIError>;
10
- readonly resume: <Options extends ImageOptions>(model: ImageModel<Options>, token: unknown) => Effect.Effect<Generation<ImageResponse>, AIError>;
11
- }
1
+ import { Context } from "effect";
2
+ import { MediaClient } from "./media-client.js";
3
+ import { type ImageEvent, type ImageRequestFor, type ImageResponse } from "./image.js";
4
+ export type Interface = MediaClient.Interface<ImageRequestFor, ImageEvent, ImageResponse>;
12
5
  declare const ImageClientService_base: Context.ServiceClass<ImageClientService, "@opencode/ImageClient", Interface>;
13
6
  export declare class ImageClientService extends ImageClientService_base {
14
7
  }
15
8
  export declare const Service: typeof ImageClientService;
16
9
  export type Service = ImageClientService;
17
- export declare const generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>, options?: AwaitOptions) => Effect.Effect<ImageResponse, AIError, Service>;
18
- export declare const stream: <Options extends ImageOptions>(request: ImageRequestFor<Options>, options?: AwaitOptions) => Stream.Stream<ImageEvent, AIError, Service>;
19
- export declare const start: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
20
- export declare const resume: <Options extends ImageOptions>(model: ImageModel<Options>, token: unknown) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
21
- export declare const layer: Layer.Layer<Service, never, RequestExecutor.Service>;
22
10
  export declare const ImageClient: {
11
+ readonly layer: import("effect/Layer").Layer<ImageClientService, never, import("./route/executor-service.js").RequestExecutorService>;
12
+ readonly generate: (request: ImageRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Effect").Effect<ImageResponse, import("./index.js").AIError, ImageClientService>;
13
+ readonly stream: (request: ImageRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Stream").Stream<import("./generation.js").Observation | {
14
+ readonly type: "image-partial";
15
+ readonly index: number;
16
+ readonly image: import("./media.js").Asset;
17
+ } | {
18
+ readonly type: "image";
19
+ readonly index: number;
20
+ readonly image: import("./media.js").Asset;
21
+ } | {
22
+ readonly type: "finish";
23
+ readonly providerMetadata?: {
24
+ readonly [x: string]: {
25
+ readonly [x: string]: unknown;
26
+ };
27
+ } | undefined;
28
+ readonly usage?: {
29
+ readonly type: "tokens";
30
+ readonly input?: number | undefined;
31
+ readonly output?: number | undefined;
32
+ readonly total?: number | undefined;
33
+ readonly details?: {
34
+ readonly [x: string]: unknown;
35
+ } | undefined;
36
+ } | {
37
+ readonly type: "seconds";
38
+ readonly seconds: number;
39
+ } | {
40
+ readonly type: "characters";
41
+ readonly characters: number;
42
+ } | {
43
+ readonly type: "credits";
44
+ readonly credits: number;
45
+ } | {
46
+ readonly type: "compute";
47
+ readonly seconds: number;
48
+ } | undefined;
49
+ readonly notices?: readonly {
50
+ readonly type: "other" | "moderated" | "filtered";
51
+ readonly message: string;
52
+ readonly providerMetadata?: {
53
+ readonly [x: string]: {
54
+ readonly [x: string]: unknown;
55
+ };
56
+ } | undefined;
57
+ }[] | undefined;
58
+ }, import("./index.js").AIError, ImageClientService>;
59
+ readonly start: (request: ImageRequestFor) => import("effect/Effect").Effect<import("./generation.js").Generation<ImageResponse>, import("./index.js").AIError, ImageClientService>;
60
+ readonly resume: (model: import("./image.js").ImageModel<import("./media-model.js").MediaModel.Options>, token: unknown) => import("effect/Effect").Effect<import("./generation.js").Generation<ImageResponse>, import("./index.js").AIError, ImageClientService>;
23
61
  readonly Service: typeof ImageClientService;
24
- readonly layer: Layer.Layer<ImageClientService, never, import("./route/executor-service.js").RequestExecutorService>;
25
- readonly generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>, options?: AwaitOptions) => Effect.Effect<ImageResponse, AIError, Service>;
26
- readonly stream: <Options extends ImageOptions>(request: ImageRequestFor<Options>, options?: AwaitOptions) => Stream.Stream<ImageEvent, AIError, Service>;
27
- readonly start: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
28
- readonly resume: <Options extends ImageOptions>(model: ImageModel<Options>, token: unknown) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
29
62
  };
30
63
  export {};
@@ -1,45 +1,20 @@
1
- import { Context, Effect, Layer, Stream } from "effect";
2
- import { RequestExecutor } from "./route/executor.js";
3
- import { MediaRoute } from "./route/media.js";
4
- import { responseEvents, } from "./image.js";
1
+ import { Context } from "effect";
2
+ import { MediaClient } from "./media-client.js";
3
+ import { ImageOutputEvent, ImageFinishEvent, } from "./image.js";
5
4
  export class ImageClientService extends Context.Service()("@opencode/ImageClient") {
6
5
  }
7
6
  export const Service = ImageClientService;
8
- export const generate = (request, options) => Effect.gen(function* () {
9
- const client = yield* Service;
10
- return yield* client.generate(request, options);
11
- });
12
- export const stream = (request, options) => Stream.unwrap(Effect.gen(function* () {
13
- const client = yield* Service;
14
- return client.stream(request, options);
15
- }));
16
- export const start = (request) => Effect.gen(function* () {
17
- const client = yield* Service;
18
- return yield* client.start(request);
19
- });
20
- export const resume = (model, token) => Effect.gen(function* () {
21
- const client = yield* Service;
22
- return yield* client.resume(model, token);
23
- });
24
- export const layer = Layer.effect(Service, Effect.gen(function* () {
25
- const executor = yield* RequestExecutor.Service;
26
- const dispatch = MediaRoute.dispatch({
27
- modality: "image",
28
- execute: executor.execute,
29
- responseEvents,
30
- });
31
- return Service.of({
32
- start: (request) => dispatch.start(request.model.route, request),
33
- resume: (model, token) => dispatch.resume(model.route, model, token),
34
- generate: (request, options) => dispatch.generate(request.model.route, request, options),
35
- stream: (request, options) => dispatch.stream(request.model.route, request, options),
36
- });
37
- }));
38
7
  export const ImageClient = {
39
8
  Service,
40
- layer,
41
- generate,
42
- stream,
43
- start,
44
- resume,
9
+ ...MediaClient.make(Service, {
10
+ modality: "image",
11
+ responseEvents: (response) => [
12
+ ...response.images.map((image, index) => ImageOutputEvent.make({ index, image })),
13
+ ImageFinishEvent.make({
14
+ usage: response.usage,
15
+ notices: response.notices,
16
+ providerMetadata: response.providerMetadata,
17
+ }),
18
+ ],
19
+ }),
45
20
  };
package/dist/image.d.ts CHANGED
@@ -3,33 +3,25 @@ import { Generation, type AwaitOptions } from "./generation.js";
3
3
  import { Media } from "./media.js";
4
4
  import { MediaModel } from "./media-model.js";
5
5
  import { MediaRoute } from "./route/media.js";
6
- import type { MediaProtocol } from "./route/media-protocol.js";
7
6
  import { AIError, HttpOptions, type OpenString } from "./schema/index.js";
8
7
  import { Service } from "./image-client.js";
9
- export type ImageOptions = Record<string, unknown>;
10
- export type ImageRoute<Options extends ImageOptions = ImageOptions> = MediaRoute.AnyRoute<ImageRequestFor<Options>, ImageEvent, ImageResponse>;
11
- export declare class ImageModel<Options extends ImageOptions = ImageOptions> extends MediaModel<ImageRoute<Options>, Options> {
8
+ export type ImageOptions = MediaModel.Options;
9
+ export type ImageRoute = MediaRoute.AnyRoute<ImageRequestFor, ImageEvent, ImageResponse>;
10
+ export declare class ImageModel<Options extends ImageOptions = ImageOptions> extends MediaModel<ImageRoute, Options> {
12
11
  protected readonly _ImageModel: void;
13
- static make<Options extends ImageOptions = ImageOptions>(input: MediaModel.Input<ImageRoute<Options>>): ImageModel<Options>;
14
12
  /** The number of type arguments selects the kind: `<Options>`, `<Options, Frame, State>`, or `<Options, Token>`. */
15
- static fromRoute<Options extends ImageOptions>(route: ImageModel.InlineRouteInput<Options>, input: MediaRoute.ModelInput): ImageModel<Options>;
16
- static fromRoute<Options extends ImageOptions, Frame, State>(route: ImageModel.StreamRouteInput<Options, Frame, State>, input: MediaRoute.ModelInput): ImageModel<Options>;
17
- static fromRoute<Options extends ImageOptions, Token>(route: ImageModel.QueuedRouteInput<Options, Token>, input: MediaRoute.ModelInput): ImageModel<Options>;
13
+ static fromRoute<Options extends ImageOptions>(route: MediaModel.InlineRouteInput<ImageRequestFor<Options>, ImageResponse>, input: MediaRoute.ModelInput): ImageModel<Options>;
14
+ static fromRoute<Options extends ImageOptions, Frame, State>(route: MediaModel.StreamRouteInput<ImageRequestFor<Options>, ImageEvent, Frame, State>, input: MediaRoute.ModelInput): ImageModel<Options>;
15
+ static fromRoute<Options extends ImageOptions, Token>(route: MediaModel.QueuedRouteInput<ImageRequestFor<Options>, ImageResponse, Token>, input: MediaRoute.ModelInput): ImageModel<Options>;
18
16
  }
19
- export declare namespace ImageModel {
20
- type InlineRouteInput<Options extends ImageOptions = ImageOptions> = MediaModel.RouteInput<ImageRequestFor<Options>, MediaProtocol.Inline<ImageRequestFor<Options>, ImageResponse>>;
21
- type StreamRouteInput<Options extends ImageOptions = ImageOptions, Frame = unknown, State = unknown> = MediaModel.RouteInput<MediaProtocol.Addressed<ImageRequestFor<Options>>, MediaProtocol.Streamed<ImageRequestFor<Options>, ImageEvent, Frame, State>>;
22
- type QueuedRouteInput<Options extends ImageOptions = ImageOptions, Token = unknown> = MediaModel.RouteInput<ImageRequestFor<Options>, MediaProtocol.Queued<ImageRequestFor<Options>, ImageResponse, Token>>;
23
- type RouteInput<Options extends ImageOptions = ImageOptions, Frame = unknown, State = unknown, Token = unknown> = MediaModel.AnyRouteInput<ImageRequestFor<Options>, ImageEvent, ImageResponse, Frame, State, Token>;
24
- }
25
- export declare const ImageModelSchema: Schema.declare<ImageModel<ImageOptions>, ImageModel<ImageOptions>>;
17
+ export declare const ImageModelSchema: Schema.declare<ImageModel<MediaModel.Options>, ImageModel<MediaModel.Options>>;
26
18
  export type ImageSize = `${number}x${number}`;
27
19
  export declare const ImageSize: Schema.declare<`${number}x${number}`, `${number}x${number}`>;
28
20
  export type ImageAspectRatio = Media.AspectRatio;
29
21
  export declare const ImageAspectRatio: Schema.declare<`${number}:${number}`, `${number}:${number}`>;
30
22
  export type ImageFormat = OpenString<"png" | "jpeg" | "webp">;
31
23
  declare const ImageRequest_base: Schema.Class<ImageRequest, Schema.Struct<{
32
- readonly model: Schema.declare<ImageModel<ImageOptions>, ImageModel<ImageOptions>>;
24
+ readonly model: Schema.declare<ImageModel<MediaModel.Options>, ImageModel<MediaModel.Options>>;
33
25
  readonly prompt: Schema.String;
34
26
  /** Edit sources or style/subject references, in order. */
35
27
  readonly images: Schema.optional<Schema.$Array<Schema.decodeTo<Schema.declare<Media.Asset, Media.Asset>, Schema.Struct<{
@@ -1418,22 +1410,18 @@ export declare const ImageEvent: Schema.Union<readonly [Schema.Struct<{
1418
1410
  };
1419
1411
  };
1420
1412
  export type ImageEvent = Schema.Schema.Type<typeof imageEventTagged>;
1421
- export declare const responseEvents: (response: ImageResponse) => ReadonlyArray<ImageEvent>;
1422
1413
  export declare function request<const Model extends ImageModel>(input: ImageRequestInput<Model>): ImageRequestFor<ImageModelOptions<Model>>;
1423
1414
  export declare function request(input: ImageRequest): ImageRequest;
1424
- export declare function generate<const Model extends ImageModel>(input: ImageRequestInput<Model>, options?: AwaitOptions): Effect.Effect<ImageResponse, AIError, Service>;
1425
- export declare function generate(input: ImageRequest, options?: AwaitOptions): Effect.Effect<ImageResponse, AIError, Service>;
1426
- export declare function stream<const Model extends ImageModel>(input: ImageRequestInput<Model>, options?: AwaitOptions): Stream.Stream<ImageEvent, AIError, Service>;
1427
- export declare function stream(input: ImageRequest, options?: AwaitOptions): Stream.Stream<ImageEvent, AIError, Service>;
1415
+ export declare function generate<const Model extends ImageModel>(input: ImageRequest | ImageRequestInput<Model>, options?: AwaitOptions): Effect.Effect<ImageResponse, AIError, Service>;
1416
+ export declare function stream<const Model extends ImageModel>(input: ImageRequest | ImageRequestInput<Model>, options?: AwaitOptions): Stream.Stream<ImageEvent, AIError, Service>;
1428
1417
  /** Inline and streaming routes fail with `UnsupportedOperation`. */
1429
- export declare function start<const Model extends ImageModel>(input: ImageRequestInput<Model>): Effect.Effect<Generation<ImageResponse>, AIError, Service>;
1430
- export declare function start(input: ImageRequest): Effect.Effect<Generation<ImageResponse>, AIError, Service>;
1431
- export declare const resume: <Options extends ImageOptions>(model: ImageModel<Options>, token: unknown) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
1418
+ export declare function start<const Model extends ImageModel>(input: ImageRequest | ImageRequestInput<Model>): Effect.Effect<Generation<ImageResponse>, AIError, Service>;
1419
+ export declare const resume: (model: ImageModel, token: unknown) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
1432
1420
  export declare const Image: {
1433
1421
  readonly request: typeof request;
1434
1422
  readonly generate: typeof generate;
1435
1423
  readonly stream: typeof stream;
1436
1424
  readonly start: typeof start;
1437
- readonly resume: <Options extends ImageOptions>(model: ImageModel<Options>, token: unknown) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
1425
+ readonly resume: (model: ImageModel, token: unknown) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
1438
1426
  };
1439
1427
  export {};
package/dist/image.js CHANGED
@@ -1,20 +1,17 @@
1
1
  import { Effect, Schema, Stream } from "effect";
2
2
  import { Generation, ProgressEvent, QueuedEvent } from "./generation.js";
3
3
  import { Media } from "./media.js";
4
- import { MediaModel, composeAnyRoute, tryRequest } from "./media-model.js";
4
+ import { MediaModel, composeRoute, tryRequest } from "./media-model.js";
5
5
  import { MediaRoute } from "./route/media.js";
6
6
  import { AIError, HttpOptions, MediaUsage, ProviderMetadata } from "./schema/index.js";
7
7
  import { ImageClient, Service } from "./image-client.js";
8
8
  export class ImageModel extends MediaModel {
9
- static make(input) {
10
- return new ImageModel(input);
11
- }
12
9
  static fromRoute(route, input) {
13
10
  return new ImageModel({
14
11
  id: input.id,
15
12
  provider: route.protocol.provider,
16
13
  http: input.http,
17
- route: composeAnyRoute(route, input, collectResponse),
14
+ route: composeRoute(route, input, collectResponse),
18
15
  });
19
16
  }
20
17
  }
@@ -85,14 +82,6 @@ export const ImageEvent = Object.assign(imageEventTagged, {
85
82
  finish: imageEventTagged.guards.finish,
86
83
  },
87
84
  });
88
- export const responseEvents = (response) => [
89
- ...response.images.map((image, index) => ImageOutputEvent.make({ index, image })),
90
- ImageFinishEvent.make({
91
- usage: response.usage,
92
- notices: response.notices,
93
- providerMetadata: response.providerMetadata,
94
- }),
95
- ];
96
85
  const collectResponse = (events) => {
97
86
  const finish = events.find(ImageEvent.is.finish);
98
87
  // Every image protocol's `finish` emits the terminal event or fails, so a completed stream always has one.
@@ -0,0 +1,30 @@
1
+ import { type Context, Effect, Layer, Stream } from "effect";
2
+ import { type AwaitOptions, type Generation, type Observation } from "./generation.js";
3
+ import type { MediaRoute } from "./route/media.js";
4
+ import { AIError } from "./schema/index.js";
5
+ /** A media request whose model carries the route that executes it. */
6
+ export interface RoutedRequest<Self extends MediaRoute.MediaRequest, Event, Response> extends MediaRoute.MediaRequest {
7
+ readonly model: MediaRoute.MediaRequest["model"] & {
8
+ readonly route: MediaRoute.AnyRoute<Self, Event, Response>;
9
+ };
10
+ }
11
+ /** `start` and `resume` fail with `UnsupportedOperation` on inline and stream routes. */
12
+ export interface Interface<Req extends RoutedRequest<Req, Event, Response>, Event, Response> {
13
+ readonly generate: (request: Req, options?: AwaitOptions) => Effect.Effect<Response, AIError>;
14
+ readonly stream: (request: Req, options?: AwaitOptions) => Stream.Stream<Event | Observation, AIError>;
15
+ readonly start: (request: Req) => Effect.Effect<Generation<Response>, AIError>;
16
+ readonly resume: (model: Req["model"], token: unknown) => Effect.Effect<Generation<Response>, AIError>;
17
+ }
18
+ /** One modality's layer and service accessors, dispatching each request on its route's `kind`. */
19
+ export declare const make: <Self, Req extends RoutedRequest<Req, Event, Response>, Event, Response>(service: Context.Service<Self, Interface<Req, Event, Response>>, input: {
20
+ readonly modality: string;
21
+ /** A completed response expanded into the streaming event shape. */
22
+ readonly responseEvents: (response: Response) => ReadonlyArray<Event>;
23
+ }) => {
24
+ layer: Layer.Layer<Self, never, import("./route/executor-service.js").RequestExecutorService>;
25
+ generate: (request: Req, options?: AwaitOptions) => Effect.Effect<Response, AIError, Self>;
26
+ stream: (request: Req, options?: AwaitOptions) => Stream.Stream<Observation | Event, AIError, Exclude<Self, import("effect/Scope").Scope>>;
27
+ start: (request: Req) => Effect.Effect<Generation<Response>, AIError, Self>;
28
+ resume: (model: Req["model"], token: unknown) => Effect.Effect<Generation<Response>, AIError, Self>;
29
+ };
30
+ export * as MediaClient from "./media-client.js";
@@ -0,0 +1,51 @@
1
+ import { Effect, Layer, Stream } from "effect";
2
+ import { resultEvents } from "./generation.js";
3
+ import { RequestExecutor } from "./route/executor.js";
4
+ import { AIError, UnsupportedOperationError } from "./schema/index.js";
5
+ /** One modality's layer and service accessors, dispatching each request on its route's `kind`. */
6
+ export const make = (service, input) => ({
7
+ layer: Layer.effect(service, Effect.gen(function* () {
8
+ const executor = yield* RequestExecutor.Service;
9
+ const notQueued = (route, operation) => new AIError({
10
+ reason: new UnsupportedOperationError({
11
+ operation: `${input.modality}.${operation}`,
12
+ provider: route.provider,
13
+ route: route.id,
14
+ message: `${route.provider}/${route.id} is not a queued route; use generate or stream`,
15
+ }),
16
+ });
17
+ const start = (request) => {
18
+ const route = request.model.route;
19
+ if (route.kind !== "queued")
20
+ return Effect.fail(notQueued(route, "start"));
21
+ return route.start(request, executor.execute);
22
+ };
23
+ return service.of({
24
+ start,
25
+ resume: (model, token) => {
26
+ if (model.route.kind !== "queued")
27
+ return Effect.fail(notQueued(model.route, "resume"));
28
+ return model.route.resume(model, token, executor.execute);
29
+ },
30
+ generate: (request, options) => {
31
+ const route = request.model.route;
32
+ if (route.kind !== "queued")
33
+ return route.generate(request, executor.execute);
34
+ return start(request).pipe(Effect.flatMap((generation) => generation.await(options)));
35
+ },
36
+ stream: (request, options) => {
37
+ const route = request.model.route;
38
+ if (route.kind === "stream")
39
+ return route.stream(request, executor.execute);
40
+ if (route.kind === "queued")
41
+ return Stream.unwrap(start(request).pipe(Effect.map((generation) => resultEvents(generation, input.responseEvents, options))));
42
+ return Stream.fromIterableEffect(Effect.map(route.generate(request, executor.execute), input.responseEvents));
43
+ },
44
+ });
45
+ })),
46
+ generate: (request, options) => service.use((client) => client.generate(request, options)),
47
+ stream: (request, options) => Stream.unwrap(service.useSync((client) => client.stream(request, options))),
48
+ start: (request) => service.use((client) => client.start(request)),
49
+ resume: (model, token) => service.use((client) => client.resume(model, token)),
50
+ });
51
+ export * as MediaClient from "./media-client.js";
@@ -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;
@@ -31,12 +32,12 @@ export declare namespace MediaModel {
31
32
  /** Headers the protocol requires on every call, such as a pinned API version; deployment headers win. */
32
33
  readonly headers?: Record<string, string>;
33
34
  }
34
- 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>;
35
39
  }
36
40
  /** Compose a protocol route input with one deployment through `MediaRoute.inline`, `queued`, or `stream`. */
37
- export declare const composeRoute: <Request extends MediaRoute.MediaRequest, Protocol, Route>(compose: (input: MediaRoute.Composition<Request> & {
38
- readonly protocol: Protocol;
39
- }) => Route, route: MediaModel.RouteInput<Request, Protocol>, input: MediaRoute.ModelInput) => Route;
40
- 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>;
41
42
  /** Lift a synchronous Schema-class constructor into a typed `InvalidRequest` failure. */
42
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,19 +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({
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) => ({
24
31
  protocol: route.protocol,
25
32
  endpoint: Endpoint.path(route.path, { baseURL: input.baseURL ?? route.baseURL }),
26
33
  auth: input.auth,
27
34
  headers: route.headers === undefined && input.headers === undefined ? undefined : { ...route.headers, ...input.headers },
28
35
  });
29
- export const composeAnyRoute = (route, input, collect) => {
30
- if (isStreamInput(route))
31
- return composeRoute((composition) => MediaRoute.stream({ ...composition, collect }), route, input);
32
- if (isQueuedInput(route))
33
- return composeRoute(MediaRoute.queued, route, input);
34
- return composeRoute(MediaRoute.inline, route, input);
35
- };
36
36
  const isStreamInput = (route) => route.protocol.kind === "stream";
37
37
  const isQueuedInput = (route) => route.protocol.kind === "queued";
38
38
  /** Lift a synchronous Schema-class constructor into a typed `InvalidRequest` failure. */
package/dist/promise.d.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  import { Effect, Layer } from "effect";
2
2
  import { AIClient } from "./ai-client.js";
3
3
  import type { AwaitOptions, Event, Snapshot } from "./generation.js";
4
- import { ImageModel, ImageRequest, type ImageOptions, type ImageRequestInput } from "./image.js";
4
+ import { type ImageModel, type ImageRequest, type ImageRequestInput } from "./image.js";
5
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 { TranscriptionModel, TranscriptionRequest, type TranscriptionOptions, type TranscriptionRequestInput } from "./transcription.js";
11
- import { VideoModel, VideoRequest, type VideoOptions, type VideoRequestInput } from "./video.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";
12
12
  /**
13
13
  * Promise-first entrypoint for scripts and non-Effect callers. One `ManagedRuntime` hosts the LLM, image, video, speech,
14
14
  * and transcription clients over a request executor; every method runs the corresponding Effect API and rethrows
@@ -322,13 +322,13 @@ export declare const make: (options?: Options) => {
322
322
  }[] | undefined;
323
323
  }>;
324
324
  start: <const Model extends ImageModel>(input: ImageRequestInput<Model> | ImageRequest, options?: RunOptions) => Promise<GenerationHandle<import("./image.js").ImageResponse>>;
325
- 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>>;
326
326
  };
327
327
  video: {
328
328
  request: typeof import("./video.js").request;
329
329
  start: <const Model extends VideoModel>(input: VideoRequestInput<Model> | VideoRequest, options?: RunOptions) => Promise<GenerationHandle<import("./video.js").VideoResponse>>;
330
330
  generate: <const Model extends VideoModel>(input: VideoRequestInput<Model> | VideoRequest, options?: AwaitOptions & RunOptions) => Promise<import("./video.js").VideoResponse>;
331
- 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>>;
332
332
  stream: <const Model extends VideoModel>(input: VideoRequestInput<Model> | VideoRequest, options?: AwaitOptions & RunOptions) => AsyncIterable<{
333
333
  readonly id: string;
334
334
  readonly type: "generation-queued";
@@ -384,6 +384,14 @@ export declare const make: (options?: Options) => {
384
384
  request: typeof import("./speech.js").request;
385
385
  generate: <const Model extends SpeechModel>(input: SpeechRequestInput<Model> | SpeechRequest, options?: RunOptions) => Promise<import("./speech.js").SpeechResponse>;
386
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
+ } | {
387
395
  readonly type: "audio-delta";
388
396
  readonly chunk: Uint8Array<ArrayBufferLike>;
389
397
  } | {
@@ -510,7 +518,7 @@ export declare const make: (options?: Options) => {
510
518
  }[] | undefined;
511
519
  }>;
512
520
  start: <const Model extends TranscriptionModel>(input: TranscriptionRequestInput<Model> | TranscriptionRequest, options?: RunOptions) => Promise<GenerationHandle<import("./transcription.js").TranscriptionResponse>>;
513
- 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>>;
514
522
  };
515
523
  dispose: () => Promise<void>;
516
524
  };
@@ -801,13 +809,13 @@ export declare const ai: {
801
809
  }[] | undefined;
802
810
  }>;
803
811
  start: <const Model extends ImageModel>(input: ImageRequestInput<Model> | ImageRequest, options?: RunOptions) => Promise<GenerationHandle<import("./image.js").ImageResponse>>;
804
- 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>>;
805
813
  };
806
814
  video: {
807
815
  request: typeof import("./video.js").request;
808
816
  start: <const Model extends VideoModel>(input: VideoRequestInput<Model> | VideoRequest, options?: RunOptions) => Promise<GenerationHandle<import("./video.js").VideoResponse>>;
809
817
  generate: <const Model extends VideoModel>(input: VideoRequestInput<Model> | VideoRequest, options?: AwaitOptions & RunOptions) => Promise<import("./video.js").VideoResponse>;
810
- 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>>;
811
819
  stream: <const Model extends VideoModel>(input: VideoRequestInput<Model> | VideoRequest, options?: AwaitOptions & RunOptions) => AsyncIterable<{
812
820
  readonly id: string;
813
821
  readonly type: "generation-queued";
@@ -863,6 +871,14 @@ export declare const ai: {
863
871
  request: typeof import("./speech.js").request;
864
872
  generate: <const Model extends SpeechModel>(input: SpeechRequestInput<Model> | SpeechRequest, options?: RunOptions) => Promise<import("./speech.js").SpeechResponse>;
865
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
+ } | {
866
882
  readonly type: "audio-delta";
867
883
  readonly chunk: Uint8Array<ArrayBufferLike>;
868
884
  } | {
@@ -989,7 +1005,7 @@ export declare const ai: {
989
1005
  }[] | undefined;
990
1006
  }>;
991
1007
  start: <const Model extends TranscriptionModel>(input: TranscriptionRequestInput<Model> | TranscriptionRequest, options?: RunOptions) => Promise<GenerationHandle<import("./transcription.js").TranscriptionResponse>>;
992
- 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>>;
993
1009
  };
994
1010
  dispose: () => Promise<void>;
995
1011
  };
package/dist/promise.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import { Effect, Layer, ManagedRuntime, Stream } from "effect";
2
2
  import { AIClient } from "./ai-client.js";
3
- import { Image, ImageModel, ImageRequest } from "./image.js";
3
+ import { Image } from "./image.js";
4
4
  import { LLM } from "./index.js";
5
5
  import { Media } from "./media.js";
6
6
  import { tryRequest } from "./media-model.js";
7
7
  import { RequestExecutor } from "./route/executor.js";
8
8
  import { AIError, InvalidRequestError, LanguageModel, LLMRequest } from "./schema/index.js";
9
- import { Speech, SpeechModel, SpeechRequest } from "./speech.js";
10
- import { Transcription, TranscriptionModel, TranscriptionRequest, } from "./transcription.js";
9
+ import { Speech } from "./speech.js";
10
+ import { Transcription, } from "./transcription.js";
11
11
  import { fileMediaType } from "./utils/media-type.js";
12
- import { Video, VideoModel, VideoRequest } from "./video.js";
12
+ import { Video } from "./video.js";
13
13
  const abortEffect = (signal) => signal === undefined
14
14
  ? Effect.never
15
15
  : Effect.callback((resume) => {
@@ -35,12 +35,7 @@ export const make = (options = {}) => {
35
35
  refresh: (options) => run(generation.refresh(), options).then(handle),
36
36
  cancel: (options) => run(generation.cancel(), options),
37
37
  });
38
- // The typed `generate`/`stream` overloads take a concrete input or a request, not the union; normalize once here.
39
38
  const llmRequest = (input) => input instanceof LLMRequest ? Effect.succeed(input) : tryRequest(() => LLM.request(input));
40
- const imageRequest = (input) => input instanceof ImageRequest ? input : Image.request(input);
41
- const videoRequest = (input) => input instanceof VideoRequest ? input : Video.request(input);
42
- const speechRequest = (input) => input instanceof SpeechRequest ? input : Speech.request(input);
43
- const transcriptionRequest = (input) => input instanceof TranscriptionRequest ? input : Transcription.request(input);
44
39
  return {
45
40
  run,
46
41
  /** Decoded asset bytes, downloading `url` sources through the executor. */
@@ -74,28 +69,28 @@ export const make = (options = {}) => {
74
69
  },
75
70
  image: {
76
71
  request: Image.request,
77
- generate: (input, options) => run(Image.generate(imageRequest(input), { poll: options?.poll }), options),
78
- stream: (input, options) => iterate(Image.stream(imageRequest(input), { poll: options?.poll }), options),
79
- 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),
80
75
  resume: (model, token, options) => run(Image.resume(model, token), options).then(handle),
81
76
  },
82
77
  video: {
83
78
  request: Video.request,
84
- start: (input, options) => run(Video.start(videoRequest(input)), options).then(handle),
85
- 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),
86
81
  resume: (model, token, options) => run(Video.resume(model, token), options).then(handle),
87
- stream: (input, options) => iterate(Video.stream(videoRequest(input), { poll: options?.poll }), options),
82
+ stream: (input, options) => iterate(Video.stream(input, { poll: options?.poll }), options),
88
83
  },
89
84
  speech: {
90
85
  request: Speech.request,
91
- generate: (input, options) => run(Speech.generate(speechRequest(input)), options),
92
- 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),
93
88
  },
94
89
  transcription: {
95
90
  request: Transcription.request,
96
- generate: (input, options) => run(Transcription.generate(transcriptionRequest(input), { poll: options?.poll }), options),
97
- stream: (input, options) => iterate(Transcription.stream(transcriptionRequest(input), { poll: options?.poll }), options),
98
- 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),
99
94
  resume: (model, token, options) => run(Transcription.resume(model, token), options).then(handle),
100
95
  },
101
96
  dispose: () => runtime.dispose(),