@opencode/ai 2.0.15 → 2.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/README.md +286 -2
  2. package/dist/generation.d.ts +36 -22
  3. package/dist/generation.js +53 -24
  4. package/dist/image-client.d.ts +14 -7
  5. package/dist/image-client.js +24 -8
  6. package/dist/image.d.ts +398 -47
  7. package/dist/image.js +47 -45
  8. package/dist/index.d.ts +13 -1
  9. package/dist/index.js +9 -0
  10. package/dist/media-model.d.ts +44 -0
  11. package/dist/media-model.js +49 -0
  12. package/dist/media.d.ts +10 -9
  13. package/dist/media.js +9 -10
  14. package/dist/promise.d.ts +428 -8
  15. package/dist/promise.js +40 -3
  16. package/dist/protocols/alibaba-chat.d.ts +12 -0
  17. package/dist/protocols/alibaba-responses.d.ts +2 -2
  18. package/dist/protocols/anthropic-messages.js +1 -2
  19. package/dist/protocols/assemblyai-transcription.d.ts +40 -0
  20. package/dist/protocols/assemblyai-transcription.js +138 -0
  21. package/dist/protocols/bedrock-converse.js +5 -11
  22. package/dist/protocols/bfl-images.d.ts +32 -0
  23. package/dist/protocols/bfl-images.js +153 -0
  24. package/dist/protocols/cartesia-speech.d.ts +127 -0
  25. package/dist/protocols/cartesia-speech.js +126 -0
  26. package/dist/protocols/deepgram-speech.d.ts +119 -0
  27. package/dist/protocols/deepgram-speech.js +92 -0
  28. package/dist/protocols/deepgram-transcription.d.ts +25 -0
  29. package/dist/protocols/deepgram-transcription.js +129 -0
  30. package/dist/protocols/elevenlabs-speech.d.ts +122 -0
  31. package/dist/protocols/elevenlabs-speech.js +115 -0
  32. package/dist/protocols/fal-images.d.ts +24 -0
  33. package/dist/protocols/fal-images.js +114 -0
  34. package/dist/protocols/fal-video.d.ts +29 -0
  35. package/dist/protocols/fal-video.js +88 -0
  36. package/dist/protocols/gemini.d.ts +9 -9
  37. package/dist/protocols/gemini.js +8 -34
  38. package/dist/protocols/google-images.js +2 -14
  39. package/dist/protocols/google-speech.d.ts +130 -0
  40. package/dist/protocols/google-speech.js +84 -0
  41. package/dist/protocols/google-transcription.d.ts +173 -0
  42. package/dist/protocols/google-transcription.js +138 -0
  43. package/dist/protocols/google-video.d.ts +26 -0
  44. package/dist/protocols/google-video.js +158 -0
  45. package/dist/protocols/meta-images.js +2 -9
  46. package/dist/protocols/meta-responses.d.ts +4 -4
  47. package/dist/protocols/meta-responses.js +1 -1
  48. package/dist/protocols/open-responses.d.ts +6 -6
  49. package/dist/protocols/open-responses.js +1 -2
  50. package/dist/protocols/openai-chat.d.ts +84 -0
  51. package/dist/protocols/openai-chat.js +26 -14
  52. package/dist/protocols/openai-compatible-chat.d.ts +12 -0
  53. package/dist/protocols/openai-compatible-responses.d.ts +2 -2
  54. package/dist/protocols/openai-images.d.ts +124 -3
  55. package/dist/protocols/openai-images.js +107 -54
  56. package/dist/protocols/openai-responses.d.ts +15 -15
  57. package/dist/protocols/openai-responses.js +5 -6
  58. package/dist/protocols/openai-speech.d.ts +116 -0
  59. package/dist/protocols/openai-speech.js +98 -0
  60. package/dist/protocols/openai-transcription.d.ts +207 -0
  61. package/dist/protocols/openai-transcription.js +190 -0
  62. package/dist/protocols/replicate-images.d.ts +28 -0
  63. package/dist/protocols/replicate-images.js +133 -0
  64. package/dist/protocols/runway-video.d.ts +38 -0
  65. package/dist/protocols/runway-video.js +146 -0
  66. package/dist/protocols/shared.d.ts +13 -3
  67. package/dist/protocols/shared.js +23 -3
  68. package/dist/protocols/stability-images.d.ts +38 -0
  69. package/dist/protocols/stability-images.js +148 -0
  70. package/dist/protocols/utils/fal-queue.d.ts +28 -0
  71. package/dist/protocols/utils/fal-queue.js +69 -0
  72. package/dist/protocols/utils/gemini-generate-content.d.ts +65 -0
  73. package/dist/protocols/utils/gemini-generate-content.js +65 -0
  74. package/dist/protocols/utils/gemini-json-schema.d.ts +3 -0
  75. package/dist/protocols/utils/gemini-json-schema.js +76 -0
  76. package/dist/protocols/utils/media-input.d.ts +8 -0
  77. package/dist/protocols/utils/media-input.js +18 -0
  78. package/dist/protocols/utils/speech-stream.d.ts +49 -0
  79. package/dist/protocols/utils/speech-stream.js +67 -0
  80. package/dist/protocols/utils/tool-schema.d.ts +2 -2
  81. package/dist/protocols/utils/tool-schema.js +40 -17
  82. package/dist/protocols/xai-images.js +1 -12
  83. package/dist/protocols/xai-responses.d.ts +2 -2
  84. package/dist/protocols/xai-video.d.ts +34 -0
  85. package/dist/protocols/xai-video.js +147 -0
  86. package/dist/protocols/zai-chat.d.ts +13 -1
  87. package/dist/provider-error.js +3 -0
  88. package/dist/providers/alibaba.d.ts +14 -2
  89. package/dist/providers/amazon-bedrock-mantle.d.ts +14 -2
  90. package/dist/providers/assemblyai.d.ts +25 -0
  91. package/dist/providers/assemblyai.js +29 -0
  92. package/dist/providers/azure.d.ts +18 -6
  93. package/dist/providers/baseten.d.ts +24 -0
  94. package/dist/providers/black-forest-labs.d.ts +25 -0
  95. package/dist/providers/black-forest-labs.js +28 -0
  96. package/dist/providers/cartesia.d.ts +24 -0
  97. package/dist/providers/cartesia.js +22 -0
  98. package/dist/providers/cerebras.d.ts +24 -0
  99. package/dist/providers/cloudflare-ai-gateway.d.ts +30 -6
  100. package/dist/providers/cloudflare-workers-ai.d.ts +24 -0
  101. package/dist/providers/deepgram.d.ts +29 -0
  102. package/dist/providers/deepgram.js +31 -0
  103. package/dist/providers/deepinfra.d.ts +24 -0
  104. package/dist/providers/deepseek.d.ts +24 -0
  105. package/dist/providers/elevenlabs.d.ts +24 -0
  106. package/dist/providers/elevenlabs.js +28 -0
  107. package/dist/providers/fal.d.ts +29 -0
  108. package/dist/providers/fal.js +33 -0
  109. package/dist/providers/fireworks.d.ts +24 -0
  110. package/dist/providers/google-vertex-chat.d.ts +12 -0
  111. package/dist/providers/google-vertex-responses.d.ts +2 -2
  112. package/dist/providers/google-vertex.d.ts +3 -3
  113. package/dist/providers/google.d.ts +18 -3
  114. package/dist/providers/google.js +11 -2
  115. package/dist/providers/groq.d.ts +24 -0
  116. package/dist/providers/index.d.ts +9 -0
  117. package/dist/providers/index.js +9 -0
  118. package/dist/providers/meta.d.ts +14 -2
  119. package/dist/providers/minimax.d.ts +14 -2
  120. package/dist/providers/moonshot.d.ts +14 -2
  121. package/dist/providers/moonshot.js +3 -3
  122. package/dist/providers/openai-compatible-responses.d.ts +2 -2
  123. package/dist/providers/openai-compatible.d.ts +12 -0
  124. package/dist/providers/openai.d.ts +25 -3
  125. package/dist/providers/openai.js +10 -1
  126. package/dist/providers/openrouter.d.ts +48 -0
  127. package/dist/providers/replicate.d.ts +25 -0
  128. package/dist/providers/replicate.js +22 -0
  129. package/dist/providers/runway.d.ts +24 -0
  130. package/dist/providers/runway.js +22 -0
  131. package/dist/providers/stability.d.ts +28 -0
  132. package/dist/providers/stability.js +23 -0
  133. package/dist/providers/togetherai.d.ts +24 -0
  134. package/dist/providers/xai.d.ts +17 -0
  135. package/dist/providers/xai.js +5 -2
  136. package/dist/providers/zai-coding-plan.d.ts +15 -3
  137. package/dist/providers/zai.d.ts +13 -1
  138. package/dist/route/auth.d.ts +4 -1
  139. package/dist/route/auth.js +6 -0
  140. package/dist/route/framing.d.ts +5 -1
  141. package/dist/route/framing.js +9 -0
  142. package/dist/route/media-protocol.d.ts +116 -3
  143. package/dist/route/media-protocol.js +60 -4
  144. package/dist/route/media.d.ts +58 -7
  145. package/dist/route/media.js +201 -29
  146. package/dist/schema/events.d.ts +0 -6
  147. package/dist/schema/messages.d.ts +0 -3
  148. package/dist/schema/options.d.ts +4 -3
  149. package/dist/schema/options.js +3 -2
  150. package/dist/speech-client.d.ts +21 -0
  151. package/dist/speech-client.js +25 -0
  152. package/dist/speech.d.ts +1150 -0
  153. package/dist/speech.js +119 -0
  154. package/dist/transcription-client.d.ts +28 -0
  155. package/dist/transcription-client.js +44 -0
  156. package/dist/transcription.d.ts +1504 -0
  157. package/dist/transcription.js +133 -0
  158. package/dist/utils/bytes.d.ts +1 -0
  159. package/dist/utils/bytes.js +10 -0
  160. package/dist/utils/media-type.d.ts +1 -0
  161. package/dist/utils/media-type.js +22 -1
  162. package/dist/video-client.d.ts +28 -0
  163. package/dist/video-client.js +40 -0
  164. package/dist/video.d.ts +1359 -0
  165. package/dist/video.js +119 -0
  166. package/package.json +3 -3
  167. package/dist/protocols/utils/gemini-tool-schema.d.ts +0 -2
  168. package/dist/protocols/utils/gemini-tool-schema.js +0 -103
package/dist/image.js CHANGED
@@ -1,42 +1,20 @@
1
1
  import { Effect, Schema, Stream } from "effect";
2
+ import { Generation, ProgressEvent, QueuedEvent } from "./generation.js";
2
3
  import { Media } from "./media.js";
3
- import { Endpoint } from "./route/endpoint.js";
4
+ import { MediaModel, composeAnyRoute, tryRequest } from "./media-model.js";
4
5
  import { MediaRoute } from "./route/media.js";
5
- import { AIError, HttpOptions, InvalidRequestError, MediaUsage, ModelID, ProviderID, ProviderMetadata, } from "./schema/index.js";
6
+ import { AIError, HttpOptions, MediaUsage, ProviderMetadata } from "./schema/index.js";
6
7
  import { ImageClient, Service } from "./image-client.js";
7
- export class ImageModel {
8
- id;
9
- provider;
10
- route;
11
- http;
12
- constructor(input) {
13
- this.id = input.id;
14
- this.provider = input.provider;
15
- this.route = input.route;
16
- this.http = input.http;
17
- }
8
+ export class ImageModel extends MediaModel {
18
9
  static make(input) {
19
- return new ImageModel({
20
- id: ModelID.make(input.id),
21
- provider: ProviderID.make(input.provider),
22
- route: input.route,
23
- http: input.http,
24
- });
10
+ return new ImageModel(input);
25
11
  }
26
- /** Compose an inline image protocol with its canonical path into a model for one deployment. */
27
12
  static fromRoute(route, input) {
28
- return ImageModel.make({
13
+ return new ImageModel({
29
14
  id: input.id,
30
15
  provider: route.provider,
31
16
  http: input.http,
32
- route: MediaRoute.make({
33
- id: route.id,
34
- provider: route.provider,
35
- protocol: route.protocol,
36
- endpoint: Endpoint.path(route.path, { baseURL: input.baseURL ?? route.baseURL }),
37
- auth: input.auth,
38
- headers: input.headers,
39
- }),
17
+ route: composeAnyRoute(route, input, collectResponse),
40
18
  });
41
19
  }
42
20
  }
@@ -44,7 +22,7 @@ export const ImageModelSchema = Schema.declare((value) => value instanceof Image
44
22
  expected: "Image.Model",
45
23
  });
46
24
  export const ImageSize = Schema.declare((value) => typeof value === "string" && /^\d+x\d+$/.test(value), { title: "ImageSize" });
47
- export const ImageAspectRatio = Schema.declare((value) => typeof value === "string" && /^\d+(?:\.\d+)?:\d+(?:\.\d+)?$/.test(value), { title: "ImageAspectRatio" });
25
+ export const ImageAspectRatio = Media.AspectRatio;
48
26
  export class ImageRequest extends Schema.Class("Image.Request")({
49
27
  model: ImageModelSchema,
50
28
  prompt: Schema.String,
@@ -74,6 +52,12 @@ export class ImageResponse extends Schema.Class("Image.Response")({
74
52
  return this.images[0];
75
53
  }
76
54
  }
55
+ /** The `index`-th lower-fidelity preview of an image still generating; the `image` event supersedes it. */
56
+ export const ImagePartialEvent = Schema.Struct({
57
+ type: Schema.tag("image-partial"),
58
+ index: Schema.Number,
59
+ image: Media.AssetSchema,
60
+ }).annotate({ identifier: "Image.Event.Partial" });
77
61
  export const ImageOutputEvent = Schema.Struct({
78
62
  type: Schema.tag("image"),
79
63
  index: Schema.Number,
@@ -85,14 +69,22 @@ export const ImageFinishEvent = Schema.Struct({
85
69
  notices: Schema.optional(Schema.Array(Media.Notice)),
86
70
  providerMetadata: Schema.optional(ProviderMetadata),
87
71
  }).annotate({ identifier: "Image.Event.Finish" });
88
- const imageEventTagged = Schema.Union([ImageOutputEvent, ImageFinishEvent]).pipe(Schema.toTaggedUnion("type"));
72
+ const imageEventTagged = Schema.Union([
73
+ QueuedEvent,
74
+ ProgressEvent,
75
+ ImagePartialEvent,
76
+ ImageOutputEvent,
77
+ ImageFinishEvent,
78
+ ]).pipe(Schema.toTaggedUnion("type"));
89
79
  export const ImageEvent = Object.assign(imageEventTagged, {
90
80
  is: {
81
+ generationQueued: imageEventTagged.guards["generation-queued"],
82
+ generationProgress: imageEventTagged.guards["generation-progress"],
83
+ imagePartial: imageEventTagged.guards["image-partial"],
91
84
  image: imageEventTagged.guards.image,
92
85
  finish: imageEventTagged.guards.finish,
93
86
  },
94
87
  });
95
- /** Inline routes produce every image at once; expand the response into the streaming event shape. */
96
88
  export const responseEvents = (response) => [
97
89
  ...response.images.map((image, index) => ImageOutputEvent.make({ index, image })),
98
90
  ImageFinishEvent.make({
@@ -101,6 +93,18 @@ export const responseEvents = (response) => [
101
93
  providerMetadata: response.providerMetadata,
102
94
  }),
103
95
  ];
96
+ const collectResponse = (events) => {
97
+ const finish = events.find(ImageEvent.is.finish);
98
+ // Every image protocol's `finish` emits the terminal event or fails, so a completed stream always has one.
99
+ if (finish === undefined)
100
+ return Effect.die(new Error("The image stream completed without a finish event"));
101
+ return Effect.succeed(new ImageResponse({
102
+ images: events.filter(ImageEvent.is.image).map((event) => event.image),
103
+ usage: finish.usage,
104
+ notices: finish.notices,
105
+ providerMetadata: finish.providerMetadata,
106
+ }));
107
+ };
104
108
  export function request(input) {
105
109
  if (input instanceof ImageRequest)
106
110
  return input;
@@ -109,23 +113,21 @@ export function request(input) {
109
113
  http: input.http === undefined ? undefined : HttpOptions.make(input.http),
110
114
  });
111
115
  }
112
- const requestEffect = (input) => Effect.try({
113
- try: () => request(input),
114
- catch: (error) => new AIError({
115
- reason: new InvalidRequestError({
116
- message: error instanceof Error ? error.message : String(error),
117
- cause: error,
118
- }),
119
- }),
120
- });
121
- export function generate(input) {
122
- return requestEffect(input).pipe(Effect.flatMap((request) => ImageClient.generate(request)));
116
+ const requestEffect = (input) => tryRequest(() => request(input));
117
+ export function generate(input, options) {
118
+ return requestEffect(input).pipe(Effect.flatMap((request) => ImageClient.generate(request, options)));
119
+ }
120
+ export function stream(input, options) {
121
+ return Stream.unwrap(requestEffect(input).pipe(Effect.map((request) => ImageClient.stream(request, options))));
123
122
  }
124
- export function stream(input) {
125
- return Stream.unwrap(requestEffect(input).pipe(Effect.map((request) => ImageClient.stream(request))));
123
+ export function start(input) {
124
+ return requestEffect(input).pipe(Effect.flatMap((request) => ImageClient.start(request)));
126
125
  }
126
+ export const resume = (model, token) => ImageClient.resume(model, token);
127
127
  export const Image = {
128
128
  request,
129
129
  generate,
130
130
  stream,
131
+ start,
132
+ resume,
131
133
  };
package/dist/index.d.ts CHANGED
@@ -9,9 +9,21 @@ export * from "./schema/index.js";
9
9
  export { ImageAspectRatio, ImageEvent, ImageModel, ImageModelSchema, ImageRequest, ImageResponse, ImageSize, } from "./image.js";
10
10
  export type { ImageFormat, ImageModelOptions, ImageOptions, ImageRequestFor, ImageRequestInput, ImageRoute, } from "./image.js";
11
11
  export { Image } from "./image.js";
12
+ export { VideoClient } from "./video-client.js";
13
+ export { VideoAspectRatio, VideoEvent, VideoFrames, VideoModel, VideoModelSchema, VideoRequest, VideoResponse, } from "./video.js";
14
+ export type { VideoModelOptions, VideoOptions, VideoRequestFor, VideoRequestInput, VideoResolution, VideoRoute, } from "./video.js";
15
+ export { Video } from "./video.js";
16
+ export { SpeechClient } from "./speech-client.js";
17
+ export { SpeechEvent, SpeechModel, SpeechModelSchema, SpeechRequest, SpeechResponse, SpeechTimestamp, SpeechVoice, } from "./speech.js";
18
+ export type { SpeechFormat, SpeechModelOptions, SpeechOptions, SpeechRequestFor, SpeechRequestInput, SpeechRoute, } from "./speech.js";
19
+ export { Speech } from "./speech.js";
20
+ export { TranscriptionClient } from "./transcription-client.js";
21
+ export { TranscriptionEvent, TranscriptionModel, TranscriptionModelSchema, TranscriptionRequest, TranscriptionResponse, TranscriptionSegment, TranscriptionTimestamps, TranscriptionWord, } from "./transcription.js";
22
+ export type { TranscriptionModelOptions, TranscriptionOptions, TranscriptionRequestFor, TranscriptionRequestInput, TranscriptionRoute, } from "./transcription.js";
23
+ export { Transcription } from "./transcription.js";
12
24
  export { Media } from "./media.js";
13
25
  export { Generation } from "./generation.js";
14
- export type { Event as GenerationEvent, Poll, Route as GenerationRoute, Snapshot as GenerationSnapshot, Status as GenerationStatus } from "./generation.js";
26
+ export type { AwaitOptions as GenerationAwaitOptions, Event as GenerationEvent, Poll, Route as GenerationRoute, Snapshot as GenerationSnapshot, Status as GenerationStatus, } from "./generation.js";
15
27
  export { Tool, ToolFailure, toDefinitions } from "./tool.js";
16
28
  export { ToolRuntime } from "./tool-runtime.js";
17
29
  export type { DispatchResult as ToolDispatchResult, ToolSettlement } from "./tool-runtime.js";
package/dist/index.js CHANGED
@@ -7,6 +7,15 @@ export { isContextOverflow, isContextOverflowFailure } from "./provider-error.js
7
7
  export * from "./schema/index.js";
8
8
  export { ImageAspectRatio, ImageEvent, ImageModel, ImageModelSchema, ImageRequest, ImageResponse, ImageSize, } from "./image.js";
9
9
  export { Image } from "./image.js";
10
+ export { VideoClient } from "./video-client.js";
11
+ export { VideoAspectRatio, VideoEvent, VideoFrames, VideoModel, VideoModelSchema, VideoRequest, VideoResponse, } from "./video.js";
12
+ export { Video } from "./video.js";
13
+ export { SpeechClient } from "./speech-client.js";
14
+ export { SpeechEvent, SpeechModel, SpeechModelSchema, SpeechRequest, SpeechResponse, SpeechTimestamp, SpeechVoice, } from "./speech.js";
15
+ export { Speech } from "./speech.js";
16
+ export { TranscriptionClient } from "./transcription-client.js";
17
+ export { TranscriptionEvent, TranscriptionModel, TranscriptionModelSchema, TranscriptionRequest, TranscriptionResponse, TranscriptionSegment, TranscriptionTimestamps, TranscriptionWord, } from "./transcription.js";
18
+ export { Transcription } from "./transcription.js";
10
19
  export { Media } from "./media.js";
11
20
  export { Generation } from "./generation.js";
12
21
  export { Tool, ToolFailure, toDefinitions } from "./tool.js";
@@ -0,0 +1,44 @@
1
+ import { Effect } from "effect";
2
+ import { Endpoint } from "./route/endpoint.js";
3
+ import { MediaRoute } from "./route/media.js";
4
+ import type { MediaProtocol } from "./route/media-protocol.js";
5
+ import { AIError, HttpOptions, ModelID, ProviderID } from "./schema/index.js";
6
+ /**
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.
10
+ */
11
+ export declare class MediaModel<Route, Options> {
12
+ protected readonly _Options: (options: Options) => Options;
13
+ readonly id: ModelID;
14
+ readonly provider: ProviderID;
15
+ readonly route: Route;
16
+ readonly http?: HttpOptions;
17
+ constructor(input: MediaModel.Input<Route>);
18
+ }
19
+ export declare namespace MediaModel {
20
+ interface Input<Route> {
21
+ readonly id: string | ModelID;
22
+ readonly provider: string | ProviderID;
23
+ readonly route: Route;
24
+ readonly http?: HttpOptions;
25
+ }
26
+ /** A protocol plus its canonical start path; `ModelInput.baseURL` overrides `baseURL` per deployment. */
27
+ interface RouteInput<Request extends MediaRoute.MediaRequest, Protocol> {
28
+ readonly id: string;
29
+ readonly provider: string | ProviderID;
30
+ readonly protocol: Protocol;
31
+ readonly path: Endpoint.EndpointPart<MediaProtocol.Body, Request>;
32
+ readonly baseURL?: string;
33
+ /** Headers the protocol requires on every call, such as a pinned API version; deployment headers win. */
34
+ readonly headers?: Record<string, string>;
35
+ }
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>>;
37
+ }
38
+ /** 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>;
43
+ /** Lift a synchronous Schema-class constructor into a typed `InvalidRequest` failure. */
44
+ export declare const tryRequest: <A>(make: () => A) => Effect.Effect<A, AIError>;
@@ -0,0 +1,49 @@
1
+ import { Effect } from "effect";
2
+ import { Endpoint } from "./route/endpoint.js";
3
+ import { MediaRoute } from "./route/media.js";
4
+ import { AIError, HttpOptions, InvalidRequestError, ModelID, ProviderID } from "./schema/index.js";
5
+ /**
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.
9
+ */
10
+ export class MediaModel {
11
+ id;
12
+ provider;
13
+ route;
14
+ http;
15
+ constructor(input) {
16
+ this.id = ModelID.make(input.id);
17
+ this.provider = ProviderID.make(input.provider);
18
+ this.route = input.route;
19
+ this.http = input.http;
20
+ }
21
+ }
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,
26
+ protocol: route.protocol,
27
+ endpoint: Endpoint.path(route.path, { baseURL: input.baseURL ?? route.baseURL }),
28
+ auth: input.auth,
29
+ headers: route.headers === undefined && input.headers === undefined ? undefined : { ...route.headers, ...input.headers },
30
+ });
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
+ const isStreamInput = (route) => route.protocol.kind === "stream";
39
+ const isQueuedInput = (route) => route.protocol.kind === "queued";
40
+ /** Lift a synchronous Schema-class constructor into a typed `InvalidRequest` failure. */
41
+ export const tryRequest = (make) => Effect.try({
42
+ try: make,
43
+ catch: (error) => new AIError({
44
+ reason: new InvalidRequestError({
45
+ message: error instanceof Error ? error.message : String(error),
46
+ cause: error,
47
+ }),
48
+ }),
49
+ });
package/dist/media.d.ts CHANGED
@@ -19,8 +19,6 @@ export declare const Source: Schema.Union<readonly [Schema.Struct<{
19
19
  readonly mediaType: Schema.optional<Schema.String>;
20
20
  /** Epoch milliseconds after which the provider no longer serves the URL. */
21
21
  readonly expiresAt: Schema.optional<Schema.Number>;
22
- /** Headers required to fetch the URL, such as provider auth for Veo downloads. */
23
- readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
24
22
  }>, Schema.Struct<{
25
23
  readonly type: Schema.Literal<"ref">;
26
24
  readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
@@ -28,6 +26,8 @@ export declare const Source: Schema.Union<readonly [Schema.Struct<{
28
26
  readonly mediaType: Schema.optional<Schema.String>;
29
27
  }>]>;
30
28
  export type Source = Schema.Schema.Type<typeof Source>;
29
+ export type AspectRatio = `${number}:${number}`;
30
+ export declare const AspectRatio: Schema.declare<`${number}:${number}`, `${number}:${number}`>;
31
31
  export declare const Kind: Schema.Literals<readonly ["image", "video", "audio", "document", "other"]>;
32
32
  export type Kind = Schema.Schema.Type<typeof Kind>;
33
33
  export declare const kindOf: (mediaType: string) => Kind;
@@ -65,6 +65,8 @@ export declare class Asset {
65
65
  /** Epoch milliseconds after which a `url` source stops resolving. */
66
66
  readonly expiresAt?: number;
67
67
  readonly providerMetadata?: ProviderMetadata;
68
+ /** Transient download credentials for `url` sources; see `Asset.Input.headers`. */
69
+ readonly headers?: Record<string, string>;
68
70
  constructor(input: Asset.Input);
69
71
  /** Inline payload without effects, for protocols that embed base64 or data URLs directly. */
70
72
  inline(): Inline | undefined;
@@ -84,9 +86,6 @@ export declare class Asset {
84
86
  } | {
85
87
  readonly type: "url";
86
88
  readonly url: string;
87
- readonly headers?: {
88
- readonly [x: string]: string;
89
- } | undefined;
90
89
  readonly mediaType?: string | undefined;
91
90
  readonly expiresAt?: number | undefined;
92
91
  } | {
@@ -122,6 +121,12 @@ export declare namespace Asset {
122
121
  readonly source: Source;
123
122
  readonly info?: Info;
124
123
  readonly providerMetadata?: ProviderMetadata;
124
+ /**
125
+ * Headers required to download a `url` source, such as the provider API key Veo demands for its file URIs.
126
+ * They are runtime-only: never part of `source`, `toJSON()`, or `AssetSchema`, so a persisted asset cannot leak
127
+ * credentials and cannot be downloaded again after a round-trip. Call `materialize()` before persisting.
128
+ */
129
+ readonly headers?: Record<string, string>;
125
130
  }
126
131
  }
127
132
  /** JSON form of an asset: the serializable `Source` plus caller-supplied metadata. `bytes` sources encode as base64. */
@@ -140,8 +145,6 @@ export declare const AssetEncoded: Schema.Struct<{
140
145
  readonly mediaType: Schema.optional<Schema.String>;
141
146
  /** Epoch milliseconds after which the provider no longer serves the URL. */
142
147
  readonly expiresAt: Schema.optional<Schema.Number>;
143
- /** Headers required to fetch the URL, such as provider auth for Veo downloads. */
144
- readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
145
148
  }>, Schema.Struct<{
146
149
  readonly type: Schema.Literal<"ref">;
147
150
  readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
@@ -175,8 +178,6 @@ export declare const AssetSchema: Schema.decodeTo<Schema.declare<Asset, Asset>,
175
178
  readonly mediaType: Schema.optional<Schema.String>;
176
179
  /** Epoch milliseconds after which the provider no longer serves the URL. */
177
180
  readonly expiresAt: Schema.optional<Schema.Number>;
178
- /** Headers required to fetch the URL, such as provider auth for Veo downloads. */
179
- readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
180
181
  }>, Schema.Struct<{
181
182
  readonly type: Schema.Literal<"ref">;
182
183
  readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
package/dist/media.js CHANGED
@@ -27,8 +27,6 @@ const UrlSource = Schema.Struct({
27
27
  mediaType: Schema.optional(Schema.String),
28
28
  /** Epoch milliseconds after which the provider no longer serves the URL. */
29
29
  expiresAt: Schema.optional(Schema.Number),
30
- /** Headers required to fetch the URL, such as provider auth for Veo downloads. */
31
- headers: Schema.optional(Schema.Record(Schema.String, Schema.String)),
32
30
  });
33
31
  /** A provider-side handle: OpenAI `file_id`, Gemini file URI, `gs://`, `runway://`, or a prior generation id. */
34
32
  const RefSource = Schema.Struct({
@@ -40,9 +38,7 @@ const RefSource = Schema.Struct({
40
38
  export const Source = Schema.Union([BytesSource, Base64Source, UrlSource, RefSource])
41
39
  .pipe(Schema.toTaggedUnion("type"))
42
40
  .annotate({ identifier: "Media.Source" });
43
- // ---------------------------------------------------------------------------
44
- // Kind, Info, Notice
45
- // ---------------------------------------------------------------------------
41
+ export const AspectRatio = Schema.declare((value) => typeof value === "string" && /^\d+(?:\.\d+)?:\d+(?:\.\d+)?$/.test(value), { title: "Media.AspectRatio" });
46
42
  export const Kind = Schema.Literals(["image", "video", "audio", "document", "other"]);
47
43
  export const kindOf = (mediaType) => {
48
44
  const lower = mediaType.toLowerCase();
@@ -85,6 +81,8 @@ export class Asset {
85
81
  /** Epoch milliseconds after which a `url` source stops resolving. */
86
82
  expiresAt;
87
83
  providerMetadata;
84
+ /** Transient download credentials for `url` sources; see `Asset.Input.headers`. */
85
+ headers;
88
86
  // Derived payload forms are cached on the instance because every protocol lowering re-reads the same payload. The
89
87
  // cache is check-then-set (concurrent first reads of a `url` source may both download) and is never observable
90
88
  // through `source`, so round-tripping through `Media.from(asset.source)` stays lossless.
@@ -100,6 +98,7 @@ export class Asset {
100
98
  this.info = input.info;
101
99
  this.expiresAt = input.source.type === "url" ? input.source.expiresAt : undefined;
102
100
  this.providerMetadata = input.providerMetadata;
101
+ this.headers = input.source.type === "url" ? input.headers : undefined;
103
102
  }
104
103
  /** Inline payload without effects, for protocols that embed base64 or data URLs directly. */
105
104
  inline() {
@@ -122,7 +121,7 @@ export class Asset {
122
121
  return Effect.fail(invalid(`Cannot materialize provider ref ${source.provider}:${source.id}`));
123
122
  const decoded = source.type === "base64"
124
123
  ? Effect.fromResult(Encoding.decodeBase64(source.data)).pipe(Effect.mapError((cause) => invalid(`Media asset contains invalid base64 data`, cause)))
125
- : download(source);
124
+ : download(source, this.headers);
126
125
  return decoded.pipe(Effect.tap((data) => Effect.sync(() => (this.#bytes = data))));
127
126
  });
128
127
  }
@@ -177,9 +176,9 @@ export const AssetSchema = AssetEncoded.pipe(Schema.decodeTo(AssetInstance, {
177
176
  decode: SchemaGetter.transform((encoded) => new Asset(encoded)),
178
177
  encode: SchemaGetter.transform(encodeAsset),
179
178
  }));
180
- const download = Effect.fn("Media.download")(function* (source) {
179
+ const download = Effect.fn("Media.download")(function* (source, headers) {
181
180
  const executor = yield* Service;
182
- const response = yield* executor.execute(HttpClientRequest.get(source.url).pipe(HttpClientRequest.setHeaders(source.headers ?? {})));
181
+ const response = yield* executor.execute(HttpClientRequest.get(source.url).pipe(HttpClientRequest.setHeaders(headers ?? {})));
183
182
  const buffer = yield* response.arrayBuffer.pipe(Effect.mapError((cause) => new AIError({
184
183
  reason: new InvalidProviderOutputError({
185
184
  message: `Failed to read media from ${source.url}`,
@@ -193,8 +192,8 @@ export const from = (source, options) => new Asset({ ...options, source });
193
192
  export const bytes = (data, mediaType, options) => from({ type: "bytes", data, mediaType: mediaType ?? detectMediaType(data) ?? OCTET_STREAM }, options);
194
193
  export const base64 = (data, mediaType, options) => from({ type: "base64", data, mediaType }, options);
195
194
  export const url = (value, options) => {
196
- const { mediaType, expiresAt, headers, ...rest } = options ?? {};
197
- return from({ type: "url", url: value, mediaType, expiresAt, headers }, rest);
195
+ const { mediaType, expiresAt, ...rest } = options ?? {};
196
+ return from({ type: "url", url: value, mediaType, expiresAt }, rest);
198
197
  };
199
198
  export const ref = (provider, id, mediaType, options) => from({ type: "ref", provider: ProviderID.make(provider), id, mediaType }, options);
200
199
  const DATA_URL = /^data:([^;,]+)(?:;[^,]*)*;base64,(.*)$/s;