@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,82 @@
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 TranscriptionEvent, type TranscriptionModel, type TranscriptionOptions, type TranscriptionRequestFor, type TranscriptionResponse } from "./transcription.js";
6
- export interface Interface {
7
- readonly generate: <Options extends TranscriptionOptions>(request: TranscriptionRequestFor<Options>, options?: AwaitOptions) => Effect.Effect<TranscriptionResponse, AIError>;
8
- readonly stream: <Options extends TranscriptionOptions>(request: TranscriptionRequestFor<Options>, options?: AwaitOptions) => Stream.Stream<TranscriptionEvent, AIError>;
9
- readonly start: <Options extends TranscriptionOptions>(request: TranscriptionRequestFor<Options>) => Effect.Effect<Generation<TranscriptionResponse>, AIError>;
10
- readonly resume: <Options extends TranscriptionOptions>(model: TranscriptionModel<Options>, token: unknown) => Effect.Effect<Generation<TranscriptionResponse>, AIError>;
11
- }
1
+ import { Context } from "effect";
2
+ import { MediaClient } from "./media-client.js";
3
+ import { type TranscriptionEvent, type TranscriptionRequestFor, type TranscriptionResponse } from "./transcription.js";
4
+ export type Interface = MediaClient.Interface<TranscriptionRequestFor, TranscriptionEvent, TranscriptionResponse>;
12
5
  declare const TranscriptionClientService_base: Context.ServiceClass<TranscriptionClientService, "@opencode/TranscriptionClient", Interface>;
13
6
  export declare class TranscriptionClientService extends TranscriptionClientService_base {
14
7
  }
15
8
  export declare const Service: typeof TranscriptionClientService;
16
9
  export type Service = TranscriptionClientService;
17
- export declare const generate: <Options extends TranscriptionOptions>(request: TranscriptionRequestFor<Options>, options?: AwaitOptions) => Effect.Effect<TranscriptionResponse, AIError, Service>;
18
- export declare const stream: <Options extends TranscriptionOptions>(request: TranscriptionRequestFor<Options>, options?: AwaitOptions) => Stream.Stream<TranscriptionEvent, AIError, Service>;
19
- export declare const start: <Options extends TranscriptionOptions>(request: TranscriptionRequestFor<Options>) => Effect.Effect<Generation<TranscriptionResponse>, AIError, Service>;
20
- export declare const resume: <Options extends TranscriptionOptions>(model: TranscriptionModel<Options>, token: unknown) => Effect.Effect<Generation<TranscriptionResponse>, AIError, Service>;
21
- export declare const layer: Layer.Layer<Service, never, RequestExecutor.Service>;
22
10
  export declare const TranscriptionClient: {
11
+ readonly layer: import("effect/Layer").Layer<TranscriptionClientService, never, import("./route/executor-service.js").RequestExecutorService>;
12
+ readonly generate: (request: TranscriptionRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Effect").Effect<TranscriptionResponse, import("./index.js").AIError, TranscriptionClientService>;
13
+ readonly stream: (request: TranscriptionRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Stream").Stream<import("./generation.js").Observation | {
14
+ readonly type: "text-delta";
15
+ readonly delta: string;
16
+ } | {
17
+ readonly type: "segment";
18
+ readonly segment: {
19
+ readonly text: string;
20
+ readonly startSeconds: number;
21
+ readonly endSeconds: number;
22
+ readonly speaker?: string | undefined;
23
+ };
24
+ } | {
25
+ readonly type: "finish";
26
+ readonly text: string;
27
+ readonly durationSeconds?: number | undefined;
28
+ readonly providerMetadata?: {
29
+ readonly [x: string]: {
30
+ readonly [x: string]: unknown;
31
+ };
32
+ } | undefined;
33
+ readonly usage?: {
34
+ readonly type: "tokens";
35
+ readonly input?: number | undefined;
36
+ readonly output?: number | undefined;
37
+ readonly total?: number | undefined;
38
+ readonly details?: {
39
+ readonly [x: string]: unknown;
40
+ } | undefined;
41
+ } | {
42
+ readonly type: "seconds";
43
+ readonly seconds: number;
44
+ } | {
45
+ readonly type: "characters";
46
+ readonly characters: number;
47
+ } | {
48
+ readonly type: "credits";
49
+ readonly credits: number;
50
+ } | {
51
+ readonly type: "compute";
52
+ readonly seconds: number;
53
+ } | undefined;
54
+ readonly notices?: readonly {
55
+ readonly type: "other" | "moderated" | "filtered";
56
+ readonly message: string;
57
+ readonly providerMetadata?: {
58
+ readonly [x: string]: {
59
+ readonly [x: string]: unknown;
60
+ };
61
+ } | undefined;
62
+ }[] | undefined;
63
+ readonly language?: string | undefined;
64
+ readonly segments?: readonly {
65
+ readonly text: string;
66
+ readonly startSeconds: number;
67
+ readonly endSeconds: number;
68
+ readonly speaker?: string | undefined;
69
+ }[] | undefined;
70
+ readonly words?: readonly {
71
+ readonly text: string;
72
+ readonly startSeconds: number;
73
+ readonly endSeconds: number;
74
+ readonly speaker?: string | undefined;
75
+ readonly confidence?: number | undefined;
76
+ }[] | undefined;
77
+ }, import("./index.js").AIError, TranscriptionClientService>;
78
+ readonly start: (request: TranscriptionRequestFor) => import("effect/Effect").Effect<import("./generation.js").Generation<TranscriptionResponse>, import("./index.js").AIError, TranscriptionClientService>;
79
+ readonly resume: (model: import("./transcription.js").TranscriptionModel<import("./media-model.js").MediaModel.Options>, token: unknown) => import("effect/Effect").Effect<import("./generation.js").Generation<TranscriptionResponse>, import("./index.js").AIError, TranscriptionClientService>;
23
80
  readonly Service: typeof TranscriptionClientService;
24
- readonly layer: Layer.Layer<TranscriptionClientService, never, import("./route/executor-service.js").RequestExecutorService>;
25
- readonly generate: <Options extends TranscriptionOptions>(request: TranscriptionRequestFor<Options>, options?: AwaitOptions) => Effect.Effect<TranscriptionResponse, AIError, Service>;
26
- readonly stream: <Options extends TranscriptionOptions>(request: TranscriptionRequestFor<Options>, options?: AwaitOptions) => Stream.Stream<TranscriptionEvent, AIError, Service>;
27
- readonly start: <Options extends TranscriptionOptions>(request: TranscriptionRequestFor<Options>) => Effect.Effect<Generation<TranscriptionResponse>, AIError, Service>;
28
- readonly resume: <Options extends TranscriptionOptions>(model: TranscriptionModel<Options>, token: unknown) => Effect.Effect<Generation<TranscriptionResponse>, AIError, Service>;
29
81
  };
30
82
  export {};
@@ -1,45 +1,13 @@
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 "./transcription.js";
1
+ import { Context } from "effect";
2
+ import { MediaClient } from "./media-client.js";
3
+ import { TranscriptionFinishEvent, } from "./transcription.js";
5
4
  export class TranscriptionClientService extends Context.Service()("@opencode/TranscriptionClient") {
6
5
  }
7
6
  export const Service = TranscriptionClientService;
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: "transcription",
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 TranscriptionClient = {
39
8
  Service,
40
- layer,
41
- generate,
42
- stream,
43
- start,
44
- resume,
9
+ ...MediaClient.make(Service, {
10
+ modality: "transcription",
11
+ responseEvents: (response) => [TranscriptionFinishEvent.make({ ...response })],
12
+ }),
45
13
  };
@@ -3,30 +3,22 @@ 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 } from "./schema/index.js";
8
7
  import { Service } from "./transcription-client.js";
9
- export type TranscriptionOptions = Record<string, unknown>;
10
- export type TranscriptionRoute<Options extends TranscriptionOptions = TranscriptionOptions> = MediaRoute.AnyRoute<TranscriptionRequestFor<Options>, TranscriptionEvent, TranscriptionResponse>;
11
- export declare class TranscriptionModel<Options extends TranscriptionOptions = TranscriptionOptions> extends MediaModel<TranscriptionRoute<Options>, Options> {
8
+ export type TranscriptionOptions = MediaModel.Options;
9
+ export type TranscriptionRoute = MediaRoute.AnyRoute<TranscriptionRequestFor, TranscriptionEvent, TranscriptionResponse>;
10
+ export declare class TranscriptionModel<Options extends TranscriptionOptions = TranscriptionOptions> extends MediaModel<TranscriptionRoute, Options> {
12
11
  protected readonly _TranscriptionModel: void;
13
- static make<Options extends TranscriptionOptions = TranscriptionOptions>(input: MediaModel.Input<TranscriptionRoute<Options>>): TranscriptionModel<Options>;
14
12
  /** The number of type arguments selects the kind: `<Options>`, `<Options, Frame, State>`, or `<Options, Token>`. */
15
- static fromRoute<Options extends TranscriptionOptions>(route: TranscriptionModel.InlineRouteInput<Options>, input: MediaRoute.ModelInput): TranscriptionModel<Options>;
16
- static fromRoute<Options extends TranscriptionOptions, Frame, State>(route: TranscriptionModel.StreamRouteInput<Options, Frame, State>, input: MediaRoute.ModelInput): TranscriptionModel<Options>;
17
- static fromRoute<Options extends TranscriptionOptions, Token>(route: TranscriptionModel.QueuedRouteInput<Options, Token>, input: MediaRoute.ModelInput): TranscriptionModel<Options>;
13
+ static fromRoute<Options extends TranscriptionOptions>(route: MediaModel.InlineRouteInput<TranscriptionRequestFor<Options>, TranscriptionResponse>, input: MediaRoute.ModelInput): TranscriptionModel<Options>;
14
+ static fromRoute<Options extends TranscriptionOptions, Frame, State>(route: MediaModel.StreamRouteInput<TranscriptionRequestFor<Options>, TranscriptionEvent, Frame, State>, input: MediaRoute.ModelInput): TranscriptionModel<Options>;
15
+ static fromRoute<Options extends TranscriptionOptions, Token>(route: MediaModel.QueuedRouteInput<TranscriptionRequestFor<Options>, TranscriptionResponse, Token>, input: MediaRoute.ModelInput): TranscriptionModel<Options>;
18
16
  }
19
- export declare namespace TranscriptionModel {
20
- type InlineRouteInput<Options extends TranscriptionOptions = TranscriptionOptions> = MediaModel.RouteInput<TranscriptionRequestFor<Options>, MediaProtocol.Inline<TranscriptionRequestFor<Options>, TranscriptionResponse>>;
21
- type StreamRouteInput<Options extends TranscriptionOptions = TranscriptionOptions, Frame = unknown, State = unknown> = MediaModel.RouteInput<MediaProtocol.Addressed<TranscriptionRequestFor<Options>>, MediaProtocol.Streamed<TranscriptionRequestFor<Options>, TranscriptionEvent, Frame, State>>;
22
- type QueuedRouteInput<Options extends TranscriptionOptions = TranscriptionOptions, Token = unknown> = MediaModel.RouteInput<TranscriptionRequestFor<Options>, MediaProtocol.Queued<TranscriptionRequestFor<Options>, TranscriptionResponse, Token>>;
23
- type RouteInput<Options extends TranscriptionOptions = TranscriptionOptions, Frame = unknown, State = unknown, Token = unknown> = MediaModel.AnyRouteInput<TranscriptionRequestFor<Options>, TranscriptionEvent, TranscriptionResponse, Frame, State, Token>;
24
- }
25
- export declare const TranscriptionModelSchema: Schema.declare<TranscriptionModel<TranscriptionOptions>, TranscriptionModel<TranscriptionOptions>>;
17
+ export declare const TranscriptionModelSchema: Schema.declare<TranscriptionModel<MediaModel.Options>, TranscriptionModel<MediaModel.Options>>;
26
18
  export declare const TranscriptionTimestamps: Schema.Literals<readonly ["none", "segment", "word"]>;
27
19
  export type TranscriptionTimestamps = Schema.Schema.Type<typeof TranscriptionTimestamps>;
28
20
  declare const TranscriptionRequest_base: Schema.Class<TranscriptionRequest, Schema.Struct<{
29
- readonly model: Schema.declare<TranscriptionModel<TranscriptionOptions>, TranscriptionModel<TranscriptionOptions>>;
21
+ readonly model: Schema.declare<TranscriptionModel<MediaModel.Options>, TranscriptionModel<MediaModel.Options>>;
30
22
  readonly audio: Schema.decodeTo<Schema.declare<Media.Asset, Media.Asset>, Schema.Struct<{
31
23
  readonly source: Schema.Union<readonly [Schema.Struct<{
32
24
  readonly type: Schema.Literal<"bytes">;
@@ -1483,22 +1475,18 @@ export declare const TranscriptionEvent: Schema.Union<readonly [Schema.Struct<{
1483
1475
  };
1484
1476
  };
1485
1477
  export type TranscriptionEvent = Schema.Schema.Type<typeof transcriptionEventTagged>;
1486
- export declare const responseEvents: (response: TranscriptionResponse) => ReadonlyArray<TranscriptionEvent>;
1487
1478
  export declare function request<const Model extends TranscriptionModel>(input: TranscriptionRequestInput<Model>): TranscriptionRequestFor<TranscriptionModelOptions<Model>>;
1488
1479
  export declare function request(input: TranscriptionRequest): TranscriptionRequest;
1489
- export declare function generate<const Model extends TranscriptionModel>(input: TranscriptionRequestInput<Model>, options?: AwaitOptions): Effect.Effect<TranscriptionResponse, AIError, Service>;
1490
- export declare function generate(input: TranscriptionRequest, options?: AwaitOptions): Effect.Effect<TranscriptionResponse, AIError, Service>;
1491
- export declare function stream<const Model extends TranscriptionModel>(input: TranscriptionRequestInput<Model>, options?: AwaitOptions): Stream.Stream<TranscriptionEvent, AIError, Service>;
1492
- export declare function stream(input: TranscriptionRequest, options?: AwaitOptions): Stream.Stream<TranscriptionEvent, AIError, Service>;
1480
+ export declare function generate<const Model extends TranscriptionModel>(input: TranscriptionRequest | TranscriptionRequestInput<Model>, options?: AwaitOptions): Effect.Effect<TranscriptionResponse, AIError, Service>;
1481
+ export declare function stream<const Model extends TranscriptionModel>(input: TranscriptionRequest | TranscriptionRequestInput<Model>, options?: AwaitOptions): Stream.Stream<TranscriptionEvent, AIError, Service>;
1493
1482
  /** Inline and streaming routes fail with `UnsupportedOperation`. */
1494
- export declare function start<const Model extends TranscriptionModel>(input: TranscriptionRequestInput<Model>): Effect.Effect<Generation<TranscriptionResponse>, AIError, Service>;
1495
- export declare function start(input: TranscriptionRequest): Effect.Effect<Generation<TranscriptionResponse>, AIError, Service>;
1496
- export declare const resume: <Options extends TranscriptionOptions>(model: TranscriptionModel<Options>, token: unknown) => Effect.Effect<Generation<TranscriptionResponse>, AIError, Service>;
1483
+ export declare function start<const Model extends TranscriptionModel>(input: TranscriptionRequest | TranscriptionRequestInput<Model>): Effect.Effect<Generation<TranscriptionResponse>, AIError, Service>;
1484
+ export declare const resume: (model: TranscriptionModel, token: unknown) => Effect.Effect<Generation<TranscriptionResponse>, AIError, Service>;
1497
1485
  export declare const Transcription: {
1498
1486
  readonly request: typeof request;
1499
1487
  readonly generate: typeof generate;
1500
1488
  readonly stream: typeof stream;
1501
1489
  readonly start: typeof start;
1502
- readonly resume: <Options extends TranscriptionOptions>(model: TranscriptionModel<Options>, token: unknown) => Effect.Effect<Generation<TranscriptionResponse>, AIError, Service>;
1490
+ readonly resume: (model: TranscriptionModel, token: unknown) => Effect.Effect<Generation<TranscriptionResponse>, AIError, Service>;
1503
1491
  };
1504
1492
  export {};
@@ -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 { TranscriptionClient, Service } from "./transcription-client.js";
8
8
  export class TranscriptionModel extends MediaModel {
9
- static make(input) {
10
- return new TranscriptionModel(input);
11
- }
12
9
  static fromRoute(route, input) {
13
10
  return new TranscriptionModel({
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
  }
@@ -94,9 +91,6 @@ export const TranscriptionEvent = Object.assign(transcriptionEventTagged, {
94
91
  finish: transcriptionEventTagged.guards.finish,
95
92
  },
96
93
  });
97
- export const responseEvents = (response) => [
98
- TranscriptionFinishEvent.make({ ...response }),
99
- ];
100
94
  const collectResponse = (events) => {
101
95
  const finish = events.find(TranscriptionEvent.is.finish);
102
96
  // Every transcription protocol's `finish` emits the terminal event or fails, so a completed stream always has one.
@@ -1,30 +1,59 @@
1
- import { Context, Effect, Layer, Stream } from "effect";
2
- import { type AwaitOptions, type Generation } from "./generation.js";
3
- import { RequestExecutor } from "./route/executor.js";
4
- import type { AIError } from "./schema/index.js";
5
- import { type VideoEvent, type VideoModel, type VideoOptions, type VideoRequestFor, type VideoResponse } from "./video.js";
6
- export interface Interface {
7
- readonly start: <Options extends VideoOptions>(request: VideoRequestFor<Options>) => Effect.Effect<Generation<VideoResponse>, AIError>;
8
- readonly resume: <Options extends VideoOptions>(model: VideoModel<Options>, token: unknown) => Effect.Effect<Generation<VideoResponse>, AIError>;
9
- readonly generate: <Options extends VideoOptions>(request: VideoRequestFor<Options>, options?: AwaitOptions) => Effect.Effect<VideoResponse, AIError>;
10
- readonly stream: <Options extends VideoOptions>(request: VideoRequestFor<Options>, options?: AwaitOptions) => Stream.Stream<VideoEvent, AIError>;
11
- }
1
+ import { Context } from "effect";
2
+ import { MediaClient } from "./media-client.js";
3
+ import { type VideoEvent, type VideoRequestFor, type VideoResponse } from "./video.js";
4
+ export type Interface = MediaClient.Interface<VideoRequestFor, VideoEvent, VideoResponse>;
12
5
  declare const VideoClientService_base: Context.ServiceClass<VideoClientService, "@opencode/VideoClient", Interface>;
13
6
  export declare class VideoClientService extends VideoClientService_base {
14
7
  }
15
8
  export declare const Service: typeof VideoClientService;
16
9
  export type Service = VideoClientService;
17
- export declare const start: <Options extends VideoOptions>(request: VideoRequestFor<Options>) => Effect.Effect<Generation<VideoResponse>, AIError, Service>;
18
- export declare const resume: <Options extends VideoOptions>(model: VideoModel<Options>, token: unknown) => Effect.Effect<Generation<VideoResponse>, AIError, Service>;
19
- export declare const generate: <Options extends VideoOptions>(request: VideoRequestFor<Options>, options?: AwaitOptions) => Effect.Effect<VideoResponse, AIError, Service>;
20
- export declare const stream: <Options extends VideoOptions>(request: VideoRequestFor<Options>, options?: AwaitOptions) => Stream.Stream<VideoEvent, AIError, Service>;
21
- export declare const layer: Layer.Layer<Service, never, RequestExecutor.Service>;
22
10
  export declare const VideoClient: {
11
+ readonly layer: import("effect/Layer").Layer<VideoClientService, never, import("./route/executor-service.js").RequestExecutorService>;
12
+ readonly generate: (request: VideoRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Effect").Effect<VideoResponse, import("./index.js").AIError, VideoClientService>;
13
+ readonly stream: (request: VideoRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Stream").Stream<import("./generation.js").Observation | {
14
+ readonly type: "video";
15
+ readonly index: number;
16
+ readonly video: import("./media.js").Asset;
17
+ } | {
18
+ readonly type: "finish";
19
+ readonly providerMetadata?: {
20
+ readonly [x: string]: {
21
+ readonly [x: string]: unknown;
22
+ };
23
+ } | undefined;
24
+ readonly usage?: {
25
+ readonly type: "tokens";
26
+ readonly input?: number | undefined;
27
+ readonly output?: number | undefined;
28
+ readonly total?: number | undefined;
29
+ readonly details?: {
30
+ readonly [x: string]: unknown;
31
+ } | undefined;
32
+ } | {
33
+ readonly type: "seconds";
34
+ readonly seconds: number;
35
+ } | {
36
+ readonly type: "characters";
37
+ readonly characters: number;
38
+ } | {
39
+ readonly type: "credits";
40
+ readonly credits: number;
41
+ } | {
42
+ readonly type: "compute";
43
+ readonly seconds: number;
44
+ } | undefined;
45
+ readonly notices?: readonly {
46
+ readonly type: "other" | "moderated" | "filtered";
47
+ readonly message: string;
48
+ readonly providerMetadata?: {
49
+ readonly [x: string]: {
50
+ readonly [x: string]: unknown;
51
+ };
52
+ } | undefined;
53
+ }[] | undefined;
54
+ }, import("./index.js").AIError, VideoClientService>;
55
+ readonly start: (request: VideoRequestFor) => import("effect/Effect").Effect<import("./generation.js").Generation<VideoResponse>, import("./index.js").AIError, VideoClientService>;
56
+ readonly resume: (model: import("./video.js").VideoModel<import("./media-model.js").MediaModel.Options>, token: unknown) => import("effect/Effect").Effect<import("./generation.js").Generation<VideoResponse>, import("./index.js").AIError, VideoClientService>;
23
57
  readonly Service: typeof VideoClientService;
24
- readonly layer: Layer.Layer<VideoClientService, never, import("./route/executor-service.js").RequestExecutorService>;
25
- readonly start: <Options extends VideoOptions>(request: VideoRequestFor<Options>) => Effect.Effect<Generation<VideoResponse>, AIError, Service>;
26
- readonly resume: <Options extends VideoOptions>(model: VideoModel<Options>, token: unknown) => Effect.Effect<Generation<VideoResponse>, AIError, Service>;
27
- readonly generate: <Options extends VideoOptions>(request: VideoRequestFor<Options>, options?: AwaitOptions) => Effect.Effect<VideoResponse, AIError, Service>;
28
- readonly stream: <Options extends VideoOptions>(request: VideoRequestFor<Options>, options?: AwaitOptions) => Stream.Stream<VideoEvent, AIError, Service>;
29
58
  };
30
59
  export {};
@@ -1,41 +1,20 @@
1
- import { Context, Effect, Layer, Stream } from "effect";
2
- import { resultEvents } from "./generation.js";
3
- import { RequestExecutor } from "./route/executor.js";
4
- import { responseEvents, } from "./video.js";
1
+ import { Context } from "effect";
2
+ import { MediaClient } from "./media-client.js";
3
+ import { VideoOutputEvent, VideoFinishEvent, } from "./video.js";
5
4
  export class VideoClientService extends Context.Service()("@opencode/VideoClient") {
6
5
  }
7
6
  export const Service = VideoClientService;
8
- export const start = (request) => Effect.gen(function* () {
9
- const client = yield* Service;
10
- return yield* client.start(request);
11
- });
12
- export const resume = (model, token) => Effect.gen(function* () {
13
- const client = yield* Service;
14
- return yield* client.resume(model, token);
15
- });
16
- export const generate = (request, options) => Effect.gen(function* () {
17
- const client = yield* Service;
18
- return yield* client.generate(request, options);
19
- });
20
- export const stream = (request, options) => Stream.unwrap(Effect.gen(function* () {
21
- const client = yield* Service;
22
- return client.stream(request, options);
23
- }));
24
- export const layer = Layer.effect(Service, Effect.gen(function* () {
25
- const executor = yield* RequestExecutor.Service;
26
- const start = (request) => request.model.route.start(request, executor.execute);
27
- return Service.of({
28
- start,
29
- resume: (model, token) => model.route.resume(model, token, executor.execute),
30
- generate: (request, options) => start(request).pipe(Effect.flatMap((generation) => generation.await(options))),
31
- stream: (request, options) => Stream.unwrap(start(request).pipe(Effect.map((generation) => resultEvents(generation, responseEvents, options)))),
32
- });
33
- }));
34
7
  export const VideoClient = {
35
8
  Service,
36
- layer,
37
- start,
38
- resume,
39
- generate,
40
- stream,
9
+ ...MediaClient.make(Service, {
10
+ modality: "video",
11
+ responseEvents: (response) => [
12
+ ...response.videos.map((video, index) => VideoOutputEvent.make({ index, video })),
13
+ VideoFinishEvent.make({
14
+ usage: response.usage,
15
+ notices: response.notices,
16
+ providerMetadata: response.providerMetadata,
17
+ }),
18
+ ],
19
+ }),
41
20
  };
package/dist/video.d.ts CHANGED
@@ -3,21 +3,18 @@ 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 "./video-client.js";
9
- export type VideoOptions = Record<string, unknown>;
10
- export type VideoRoute<Options extends VideoOptions = VideoOptions> = MediaRoute.QueuedRoute<VideoRequestFor<Options>, VideoResponse>;
11
- export declare class VideoModel<Options extends VideoOptions = VideoOptions> extends MediaModel<VideoRoute<Options>, Options> {
8
+ export type VideoOptions = MediaModel.Options;
9
+ export type VideoRoute = MediaRoute.AnyRoute<VideoRequestFor, VideoEvent, VideoResponse>;
10
+ export declare class VideoModel<Options extends VideoOptions = VideoOptions> extends MediaModel<VideoRoute, Options> {
12
11
  protected readonly _VideoModel: void;
13
- static make<Options extends VideoOptions = VideoOptions>(input: MediaModel.Input<VideoRoute<Options>>): VideoModel<Options>;
14
- /** Compose a queued video protocol with its canonical start path into a model for one deployment. */
15
- static fromRoute<Options extends VideoOptions = VideoOptions, Token = unknown>(route: VideoModel.RouteInput<Options, Token>, input: MediaRoute.ModelInput): VideoModel<Options>;
12
+ /** The number of type arguments selects the kind: `<Options>`, `<Options, Frame, State>`, or `<Options, Token>`. */
13
+ static fromRoute<Options extends VideoOptions>(route: MediaModel.InlineRouteInput<VideoRequestFor<Options>, VideoResponse>, input: MediaRoute.ModelInput): VideoModel<Options>;
14
+ static fromRoute<Options extends VideoOptions, Frame, State>(route: MediaModel.StreamRouteInput<VideoRequestFor<Options>, VideoEvent, Frame, State>, input: MediaRoute.ModelInput): VideoModel<Options>;
15
+ static fromRoute<Options extends VideoOptions, Token>(route: MediaModel.QueuedRouteInput<VideoRequestFor<Options>, VideoResponse, Token>, input: MediaRoute.ModelInput): VideoModel<Options>;
16
16
  }
17
- export declare namespace VideoModel {
18
- type RouteInput<Options extends VideoOptions = VideoOptions, Token = unknown> = MediaModel.RouteInput<VideoRequestFor<Options>, MediaProtocol.Queued<VideoRequestFor<Options>, VideoResponse, Token>>;
19
- }
20
- export declare const VideoModelSchema: Schema.declare<VideoModel<VideoOptions>, VideoModel<VideoOptions>>;
17
+ export declare const VideoModelSchema: Schema.declare<VideoModel<MediaModel.Options>, VideoModel<MediaModel.Options>>;
21
18
  export type VideoAspectRatio = Media.AspectRatio;
22
19
  export declare const VideoAspectRatio: Schema.declare<`${number}:${number}`, `${number}:${number}`>;
23
20
  export type VideoResolution = OpenString<"480p" | "720p" | "1080p" | "4k">;
@@ -88,7 +85,7 @@ export declare const VideoFrames: Schema.Struct<{
88
85
  }>;
89
86
  export type VideoFrames = Schema.Schema.Type<typeof VideoFrames>;
90
87
  declare const VideoRequest_base: Schema.Class<VideoRequest, Schema.Struct<{
91
- readonly model: Schema.declare<VideoModel<VideoOptions>, VideoModel<VideoOptions>>;
88
+ readonly model: Schema.declare<VideoModel<MediaModel.Options>, VideoModel<MediaModel.Options>>;
92
89
  readonly prompt: Schema.String;
93
90
  readonly frames: Schema.optional<Schema.Struct<{
94
91
  readonly first: Schema.optional<Schema.decodeTo<Schema.declare<Media.Asset, Media.Asset>, Schema.Struct<{
@@ -1337,23 +1334,18 @@ export declare const VideoEvent: Schema.Union<readonly [Schema.Struct<{
1337
1334
  };
1338
1335
  };
1339
1336
  export type VideoEvent = Schema.Schema.Type<typeof videoEventTagged>;
1340
- /** A completed response expanded into the streaming event shape. */
1341
- export declare const responseEvents: (response: VideoResponse) => ReadonlyArray<VideoEvent>;
1342
1337
  export declare function request<const Model extends VideoModel>(input: VideoRequestInput<Model>): VideoRequestFor<VideoModelOptions<Model>>;
1343
1338
  export declare function request(input: VideoRequest): VideoRequest;
1344
- export declare function start<const Model extends VideoModel>(input: VideoRequestInput<Model>): Effect.Effect<Generation<VideoResponse>, AIError, Service>;
1345
- export declare function start(input: VideoRequest): Effect.Effect<Generation<VideoResponse>, AIError, Service>;
1346
- export declare function generate<const Model extends VideoModel>(input: VideoRequestInput<Model>, options?: AwaitOptions): Effect.Effect<VideoResponse, AIError, Service>;
1347
- export declare function generate(input: VideoRequest, options?: AwaitOptions): Effect.Effect<VideoResponse, AIError, Service>;
1339
+ export declare function start<const Model extends VideoModel>(input: VideoRequest | VideoRequestInput<Model>): Effect.Effect<Generation<VideoResponse>, AIError, Service>;
1340
+ export declare function generate<const Model extends VideoModel>(input: VideoRequest | VideoRequestInput<Model>, options?: AwaitOptions): Effect.Effect<VideoResponse, AIError, Service>;
1348
1341
  /** Rebuild a generation handle from a persisted `Generation.token`, refreshing its status once. */
1349
- export declare const resume: <Options extends VideoOptions>(model: VideoModel<Options>, token: unknown) => Effect.Effect<Generation<VideoResponse>, AIError, Service>;
1350
- export declare function stream<const Model extends VideoModel>(input: VideoRequestInput<Model>, options?: AwaitOptions): Stream.Stream<VideoEvent, AIError, Service>;
1351
- export declare function stream(input: VideoRequest, options?: AwaitOptions): Stream.Stream<VideoEvent, AIError, Service>;
1342
+ export declare const resume: (model: VideoModel, token: unknown) => Effect.Effect<Generation<VideoResponse>, AIError, Service>;
1343
+ export declare function stream<const Model extends VideoModel>(input: VideoRequest | VideoRequestInput<Model>, options?: AwaitOptions): Stream.Stream<VideoEvent, AIError, Service>;
1352
1344
  export declare const Video: {
1353
1345
  readonly request: typeof request;
1354
1346
  readonly start: typeof start;
1355
1347
  readonly generate: typeof generate;
1356
- readonly resume: <Options extends VideoOptions>(model: VideoModel<Options>, token: unknown) => Effect.Effect<Generation<VideoResponse>, AIError, Service>;
1348
+ readonly resume: (model: VideoModel, token: unknown) => Effect.Effect<Generation<VideoResponse>, AIError, Service>;
1357
1349
  readonly stream: typeof stream;
1358
1350
  };
1359
1351
  export {};
package/dist/video.js CHANGED
@@ -6,16 +6,12 @@ import { MediaRoute } from "./route/media.js";
6
6
  import { AIError, HttpOptions, MediaUsage, ProviderMetadata } from "./schema/index.js";
7
7
  import { VideoClient, Service } from "./video-client.js";
8
8
  export class VideoModel extends MediaModel {
9
- static make(input) {
10
- return new VideoModel(input);
11
- }
12
- /** Compose a queued video protocol with its canonical start path into a model for one deployment. */
13
9
  static fromRoute(route, input) {
14
10
  return new VideoModel({
15
11
  id: input.id,
16
12
  provider: route.protocol.provider,
17
13
  http: input.http,
18
- route: composeRoute(MediaRoute.queued, route, input),
14
+ route: composeRoute(route, input, collectResponse),
19
15
  });
20
16
  }
21
17
  }
@@ -81,15 +77,18 @@ export const VideoEvent = Object.assign(videoEventTagged, {
81
77
  finish: videoEventTagged.guards.finish,
82
78
  },
83
79
  });
84
- /** A completed response expanded into the streaming event shape. */
85
- export const responseEvents = (response) => [
86
- ...response.videos.map((video, index) => VideoOutputEvent.make({ index, video })),
87
- VideoFinishEvent.make({
88
- usage: response.usage,
89
- notices: response.notices,
90
- providerMetadata: response.providerMetadata,
91
- }),
92
- ];
80
+ const collectResponse = (events) => {
81
+ const finish = events.find(VideoEvent.is.finish);
82
+ // A streaming video protocol's `finish` emits the terminal event or fails, so a completed stream always has one.
83
+ if (finish === undefined)
84
+ return Effect.die(new Error("The video stream completed without a finish event"));
85
+ return Effect.succeed(new VideoResponse({
86
+ videos: events.filter(VideoEvent.is.video).map((event) => event.video),
87
+ usage: finish.usage,
88
+ notices: finish.notices,
89
+ providerMetadata: finish.providerMetadata,
90
+ }));
91
+ };
93
92
  export function request(input) {
94
93
  if (input instanceof VideoRequest)
95
94
  return input;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "0.0.0-dev-20138",
3
+ "version": "0.0.0-dev-20141",
4
4
  "name": "@opencode/ai",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,7 +34,7 @@
34
34
  "devDependencies": {
35
35
  "@clack/prompts": "1.0.0-alpha.1",
36
36
  "@effect/platform-node": "4.0.0-rc.112",
37
- "@opencode/http-recorder": "0.0.0-dev-20138",
37
+ "@opencode/http-recorder": "0.0.0-dev-20141",
38
38
  "@tsconfig/bun": "1.0.9",
39
39
  "@types/bun": "1.4.0",
40
40
  "@typescript/native-preview": "7.0.0-dev.20251207.1",
@@ -44,7 +44,7 @@
44
44
  "@aws-sdk/credential-providers": "3.1057.0",
45
45
  "@smithy/eventstream-codec": "4.2.14",
46
46
  "@smithy/util-utf8": "4.2.2",
47
- "@opencode/schema": "0.0.0-dev-20138",
47
+ "@opencode/schema": "0.0.0-dev-20141",
48
48
  "aws4fetch": "1.0.20",
49
49
  "effect": "4.0.0-rc.112",
50
50
  "google-auth-library": "10.5.0"