@opencode/ai 0.0.0-dev-20035 → 0.0.0-dev-20039
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.
- package/README.md +80 -0
- package/dist/generation.d.ts +23 -13
- package/dist/generation.js +42 -24
- package/dist/image-client.js +1 -1
- package/dist/image.d.ts +7 -36
- package/dist/image.js +8 -38
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -0
- package/dist/media-model.d.ts +42 -0
- package/dist/media-model.js +39 -0
- package/dist/media.d.ts +10 -9
- package/dist/media.js +9 -10
- package/dist/promise.d.ts +126 -3
- package/dist/promise.js +18 -1
- package/dist/protocols/fal-video.d.ts +39 -0
- package/dist/protocols/fal-video.js +139 -0
- package/dist/protocols/google-video.d.ts +26 -0
- package/dist/protocols/google-video.js +158 -0
- package/dist/protocols/meta-images.js +2 -9
- package/dist/protocols/openai-images.js +1 -12
- package/dist/protocols/runway-video.d.ts +38 -0
- package/dist/protocols/runway-video.js +146 -0
- package/dist/protocols/shared.d.ts +10 -0
- package/dist/protocols/shared.js +17 -0
- package/dist/protocols/xai-images.js +1 -12
- package/dist/protocols/xai-video.d.ts +34 -0
- package/dist/protocols/xai-video.js +147 -0
- package/dist/protocols/zai-chat.d.ts +1 -1
- package/dist/providers/fal.d.ts +24 -0
- package/dist/providers/fal.js +29 -0
- package/dist/providers/google.d.ts +5 -0
- package/dist/providers/google.js +5 -2
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.js +2 -0
- package/dist/providers/runway.d.ts +24 -0
- package/dist/providers/runway.js +22 -0
- package/dist/providers/xai.d.ts +5 -0
- package/dist/providers/xai.js +5 -2
- package/dist/providers/zai-coding-plan.d.ts +1 -1
- package/dist/providers/zai.d.ts +1 -1
- package/dist/route/auth.d.ts +4 -1
- package/dist/route/auth.js +6 -0
- package/dist/route/media-protocol.d.ts +68 -2
- package/dist/route/media-protocol.js +32 -3
- package/dist/route/media.d.ts +28 -5
- package/dist/route/media.js +95 -14
- package/dist/schema/events.d.ts +0 -6
- package/dist/schema/messages.d.ts +0 -3
- package/dist/video-client.d.ts +28 -0
- package/dist/video-client.js +43 -0
- package/dist/video.d.ts +1373 -0
- package/dist/video.js +131 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -602,6 +602,86 @@ const program = Effect.gen(function* () {
|
|
|
602
602
|
|
|
603
603
|
The hosted result is represented as a provider-executed tool call and tool result, and the generated image is also emitted as a first-class `media` `LLMEvent` (`response.message` then carries a `media` part). Gemini image-capable models emit the same `media` event for inline image output. Retaining `response.message` preserves the generated image for continuation on both routes.
|
|
604
604
|
|
|
605
|
+
## Video generation
|
|
606
|
+
|
|
607
|
+
Video mirrors `Image` with one difference: every provider is asynchronous, so the route is a submit-then-poll
|
|
608
|
+
`Generation`. Models come from `.video(...)` selectors on the `Google` (Veo), `XAI`, `Fal`, and `Runway` facades.
|
|
609
|
+
Common fields (`frames`, `references`, `video`, `durationSeconds`, `aspectRatio`, `resolution`, `audio`, `n`, `seed`,
|
|
610
|
+
`negativePrompt`) lower natively or fail with a typed `AIError` before any network call; provider-native controls live
|
|
611
|
+
under `providerOptions`, inferred from the selected model.
|
|
612
|
+
|
|
613
|
+
```ts
|
|
614
|
+
import { Video, VideoClient } from "@opencode/ai"
|
|
615
|
+
import { Google } from "@opencode/ai/providers"
|
|
616
|
+
|
|
617
|
+
const google = Google.configure({ apiKey: process.env.GOOGLE_GENERATIVE_AI_API_KEY })
|
|
618
|
+
|
|
619
|
+
// Simple: submit and wait.
|
|
620
|
+
const program = Effect.gen(function* () {
|
|
621
|
+
const response = yield* Video.generate(
|
|
622
|
+
{
|
|
623
|
+
model: google.video("veo-3.1-generate-preview"),
|
|
624
|
+
prompt: "Panning wide shot of a calico kitten sleeping in the sunshine",
|
|
625
|
+
aspectRatio: "16:9",
|
|
626
|
+
resolution: "1080p",
|
|
627
|
+
durationSeconds: 8,
|
|
628
|
+
providerOptions: { personGeneration: "allow_adult" },
|
|
629
|
+
},
|
|
630
|
+
{ poll: { interval: "10 seconds", timeout: "10 minutes" } },
|
|
631
|
+
)
|
|
632
|
+
// Veo serves files for two days behind the API key. The asset knows the deadline (`expiresAt`) and carries the
|
|
633
|
+
// download credentials only on the live instance (`asset.headers`), never in `source` or JSON: materialize
|
|
634
|
+
// before persisting, or the persisted URL cannot be fetched again.
|
|
635
|
+
return yield* response.video.materialize()
|
|
636
|
+
})
|
|
637
|
+
|
|
638
|
+
// Explicit control: keep the handle, persist the token, resume elsewhere.
|
|
639
|
+
const controlled = Effect.gen(function* () {
|
|
640
|
+
const generation = yield* Video.start({ model: google.video("veo-3.1-generate-preview"), prompt })
|
|
641
|
+
generation.id // provider operation / task / request id
|
|
642
|
+
generation.status // "queued" | "running" | "completed" | "failed" | "cancelled" | "expired"
|
|
643
|
+
generation.token // route-owned JSON: `{ operation }`, `{ requestID }`, `{ taskID }`, or fal's follow-up URLs
|
|
644
|
+
const saved = JSON.stringify(generation.token)
|
|
645
|
+
|
|
646
|
+
const resumed = yield* Video.resume(google.video("veo-3.1-generate-preview"), JSON.parse(saved))
|
|
647
|
+
return yield* resumed.await({ poll: { interval: "10 seconds" } })
|
|
648
|
+
})
|
|
649
|
+
|
|
650
|
+
// Progress as a stream: generation-queued | generation-progress | video | finish.
|
|
651
|
+
const events = Video.stream({ model: Runway.configure({ apiKey }).video("gen4.5"), prompt }, { poll })
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
`VideoClient.layer` needs `RequestExecutor.Service`, and status polls, result fetches, cancels, and asset downloads
|
|
655
|
+
all run through the same executor with the route's auth. `Generation.await` and `Generation.events` fail with a
|
|
656
|
+
`Timeout` reason when `poll.timeout` (default 10 minutes) elapses. Failed,
|
|
657
|
+
cancelled, and expired generations fail typed with the provider's terminal document on `reason.body`; moderation
|
|
658
|
+
outcomes (Veo `raiMediaFilteredReasons`, xAI `respect_moderation`, Runway `SAFETY.*` codes) surface as `notices` when
|
|
659
|
+
a video is still returned and as a `ContentPolicy` reason when nothing is.
|
|
660
|
+
|
|
661
|
+
Provider notes:
|
|
662
|
+
|
|
663
|
+
- **Google Veo** takes inline bytes only (materialize `url` assets first); `frames.last` requires `frames.first`;
|
|
664
|
+
audio is always on, so `audio: false` fails typed; one video per request. Output URLs need the API key to
|
|
665
|
+
download, which the returned asset holds transiently (see above).
|
|
666
|
+
- **xAI** sends a `video` input to `/videos/edits`, or `/videos/extensions` with `providerOptions.mode: "extend"`.
|
|
667
|
+
`seed` and `negativePrompt` are not supported.
|
|
668
|
+
- **fal** endpoints are model-specific: `durationSeconds`, `references`, and `frames.last` fail typed and belong in
|
|
669
|
+
`providerOptions` under the model's own names (`duration: "8s"`, `end_image_url`, …). Auth is
|
|
670
|
+
`Authorization: Key <FAL_KEY>`.
|
|
671
|
+
- **Runway** expects pixel ratios in `aspectRatio` for most models (`"1280:720"`), pins `X-Runway-Version`, reports
|
|
672
|
+
`usage: { type: "credits" }`, and its output URLs expire after 24–48 hours.
|
|
673
|
+
|
|
674
|
+
The promise client exposes the same surface: `ai.video.start(...)` resolves to a handle with `await`, `refresh`,
|
|
675
|
+
`cancel`, and `token`; `ai.video.generate`, `ai.video.resume(model, token)`, and `ai.video.stream` mirror the Effect
|
|
676
|
+
API.
|
|
677
|
+
|
|
678
|
+
```ts
|
|
679
|
+
import { ai } from "@opencode/ai/promise"
|
|
680
|
+
|
|
681
|
+
const generation = await ai.video.start({ model, prompt })
|
|
682
|
+
const video = await generation.await({ poll: { interval: 10_000 }, signal })
|
|
683
|
+
```
|
|
684
|
+
|
|
605
685
|
## Public API
|
|
606
686
|
|
|
607
687
|
- **`LLM.request({...})`** — build a provider-neutral `LLMRequest`. Accepts ergonomic inputs (`system: string`, `prompt: string`) that normalize into the canonical Schema classes.
|
package/dist/generation.d.ts
CHANGED
|
@@ -12,13 +12,13 @@ export interface Snapshot {
|
|
|
12
12
|
readonly expiresAt?: number;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* Route-owned generation operations.
|
|
16
|
-
*
|
|
15
|
+
* Route-owned generation operations for one generation. The media route decodes its serializable token once (from the
|
|
16
|
+
* submission response or a `resume` input) and closes over it, so `Generation` never sees the token's shape.
|
|
17
17
|
*/
|
|
18
18
|
export interface Route<Response> {
|
|
19
|
-
readonly status:
|
|
20
|
-
readonly result:
|
|
21
|
-
readonly cancel?:
|
|
19
|
+
readonly status: Effect.Effect<Snapshot, AIError>;
|
|
20
|
+
readonly result: Effect.Effect<Response, AIError>;
|
|
21
|
+
readonly cancel?: Effect.Effect<void, AIError>;
|
|
22
22
|
/** Provider polling hint (e.g. `openai-poll-after-ms`) that overrides the default interval for the next poll. */
|
|
23
23
|
readonly pollHint?: (snapshot: Snapshot) => Duration.Duration | undefined;
|
|
24
24
|
}
|
|
@@ -28,6 +28,9 @@ export interface Poll {
|
|
|
28
28
|
/** Full override of the polling schedule; `interval` and `pollHint` are ignored when supplied. */
|
|
29
29
|
readonly schedule?: Schedule.Schedule<unknown, Snapshot>;
|
|
30
30
|
}
|
|
31
|
+
export interface AwaitOptions {
|
|
32
|
+
readonly poll?: Poll;
|
|
33
|
+
}
|
|
31
34
|
export declare const DEFAULT_POLL_INTERVAL: Duration.Duration;
|
|
32
35
|
export declare const DEFAULT_POLL_TIMEOUT: Duration.Duration;
|
|
33
36
|
export type Event = {
|
|
@@ -45,25 +48,32 @@ export type Event = {
|
|
|
45
48
|
};
|
|
46
49
|
export declare class Generation<Response> {
|
|
47
50
|
readonly route: Route<Response>;
|
|
51
|
+
/** Route-owned serializable JSON; pass it to the modality's `resume` from another process. */
|
|
48
52
|
readonly token: unknown;
|
|
49
53
|
readonly id: string;
|
|
50
54
|
readonly status: Status;
|
|
51
55
|
readonly progress?: number;
|
|
52
56
|
readonly position?: number;
|
|
53
57
|
readonly expiresAt?: number;
|
|
54
|
-
constructor(route: Route<Response>,
|
|
58
|
+
constructor(route: Route<Response>,
|
|
59
|
+
/** Route-owned serializable JSON; pass it to the modality's `resume` from another process. */
|
|
60
|
+
token: unknown, snapshot: Snapshot);
|
|
55
61
|
get snapshot(): Snapshot;
|
|
56
62
|
get terminal(): boolean;
|
|
57
63
|
refresh(): Effect.Effect<Generation<Response>, AIError>;
|
|
64
|
+
/** Fetch the result without polling; non-completed terminal generations fail with the provider's terminal body. */
|
|
65
|
+
result(): Effect.Effect<Response, AIError>;
|
|
58
66
|
/** Poll until the generation reaches a terminal status, then fetch the result. Fails with a `Timeout` reason on deadline. */
|
|
59
|
-
await(options?:
|
|
60
|
-
readonly poll?: Poll;
|
|
61
|
-
}): Effect.Effect<Response, AIError>;
|
|
67
|
+
await(options?: AwaitOptions): Effect.Effect<Response, AIError>;
|
|
62
68
|
cancel(): Effect.Effect<void, AIError>;
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Status observations as a stream, ending after the first terminal observation. Each poll is bounded by the time
|
|
71
|
+
* remaining until `poll.timeout`, so a hung status request fails the stream instead of stalling it. (`Stream.interruptWhen`
|
|
72
|
+
* would express this directly but deadlocks under `TestClock` when the source completes while the timer sleeps.)
|
|
73
|
+
*/
|
|
74
|
+
events(options?: AwaitOptions): Stream.Stream<Event, AIError>;
|
|
75
|
+
private event;
|
|
76
|
+
private timeoutError;
|
|
67
77
|
private poll;
|
|
68
78
|
private schedule;
|
|
69
79
|
}
|
package/dist/generation.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Duration, Effect, Schedule, Schema, Stream } from "effect";
|
|
1
|
+
import { Clock, Duration, Effect, Schedule, Schema, Stream } from "effect";
|
|
2
2
|
import { AIError, TimeoutError } from "./schema/errors.js";
|
|
3
3
|
export const Status = Schema.Literals(["queued", "running", "completed", "failed", "cancelled", "expired"]);
|
|
4
4
|
export const DEFAULT_POLL_INTERVAL = Duration.seconds(5);
|
|
@@ -12,7 +12,9 @@ export class Generation {
|
|
|
12
12
|
progress;
|
|
13
13
|
position;
|
|
14
14
|
expiresAt;
|
|
15
|
-
constructor(route,
|
|
15
|
+
constructor(route,
|
|
16
|
+
/** Route-owned serializable JSON; pass it to the modality's `resume` from another process. */
|
|
17
|
+
token, snapshot) {
|
|
16
18
|
this.route = route;
|
|
17
19
|
this.token = token;
|
|
18
20
|
this.id = snapshot.id;
|
|
@@ -34,7 +36,11 @@ export class Generation {
|
|
|
34
36
|
return TERMINAL.has(this.status);
|
|
35
37
|
}
|
|
36
38
|
refresh() {
|
|
37
|
-
return this.route.status
|
|
39
|
+
return this.route.status.pipe(Effect.map((snapshot) => new Generation(this.route, this.token, snapshot)));
|
|
40
|
+
}
|
|
41
|
+
/** Fetch the result without polling; non-completed terminal generations fail with the provider's terminal body. */
|
|
42
|
+
result() {
|
|
43
|
+
return this.route.result;
|
|
38
44
|
}
|
|
39
45
|
/** Poll until the generation reaches a terminal status, then fetch the result. Fails with a `Timeout` reason on deadline. */
|
|
40
46
|
await(options) {
|
|
@@ -42,31 +48,43 @@ export class Generation {
|
|
|
42
48
|
const settled = this.terminal ? Effect.succeed(this) : this.poll(options?.poll);
|
|
43
49
|
return settled.pipe(
|
|
44
50
|
// Non-completed terminal states also go through `result` so the route can surface its provider failure body.
|
|
45
|
-
Effect.flatMap((generation) => generation.
|
|
46
|
-
duration: timeout,
|
|
47
|
-
orElse: () => new AIError({
|
|
48
|
-
reason: new TimeoutError({
|
|
49
|
-
message: `Generation ${this.id} did not finish within ${Duration.format(timeout)}`,
|
|
50
|
-
timeoutMs: Duration.toMillis(timeout),
|
|
51
|
-
}),
|
|
52
|
-
}),
|
|
53
|
-
}));
|
|
51
|
+
Effect.flatMap((generation) => generation.result()), Effect.timeoutOrElse({ duration: timeout, orElse: () => this.timeoutError(timeout) }));
|
|
54
52
|
}
|
|
55
53
|
cancel() {
|
|
56
|
-
return this.route.cancel
|
|
54
|
+
return this.route.cancel ?? Effect.void;
|
|
57
55
|
}
|
|
58
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* Status observations as a stream, ending after the first terminal observation. Each poll is bounded by the time
|
|
58
|
+
* remaining until `poll.timeout`, so a hung status request fails the stream instead of stalling it. (`Stream.interruptWhen`
|
|
59
|
+
* would express this directly but deadlocks under `TestClock` when the source completes while the timer sleeps.)
|
|
60
|
+
*/
|
|
59
61
|
events(options) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
62
|
+
if (this.terminal)
|
|
63
|
+
return Stream.make(this.event());
|
|
64
|
+
const timeout = Duration.fromInputUnsafe(options?.poll?.timeout ?? DEFAULT_POLL_TIMEOUT);
|
|
65
|
+
return Stream.unwrap(Clock.currentTimeMillis.pipe(Effect.map((start) => {
|
|
66
|
+
const deadline = start + Duration.toMillis(timeout);
|
|
67
|
+
const refresh = Clock.currentTimeMillis.pipe(Effect.flatMap((now) => this.refresh().pipe(Effect.timeoutOrElse({
|
|
68
|
+
duration: Duration.millis(Math.max(0, deadline - now)),
|
|
69
|
+
orElse: () => this.timeoutError(timeout),
|
|
70
|
+
}))));
|
|
71
|
+
return Stream.fromEffectSchedule(refresh, this.schedule(options?.poll)).pipe(Stream.takeUntil((generation) => generation.terminal), Stream.map((generation) => generation.event()));
|
|
72
|
+
})));
|
|
73
|
+
}
|
|
74
|
+
event() {
|
|
75
|
+
if (this.terminal)
|
|
76
|
+
return { type: "generation-finished", id: this.id, status: this.status };
|
|
77
|
+
if (this.status === "queued")
|
|
78
|
+
return { type: "generation-queued", id: this.id, position: this.position };
|
|
79
|
+
return { type: "generation-progress", id: this.id, progress: this.progress };
|
|
80
|
+
}
|
|
81
|
+
timeoutError(timeout) {
|
|
82
|
+
return new AIError({
|
|
83
|
+
reason: new TimeoutError({
|
|
84
|
+
message: `Generation ${this.id} did not finish within ${Duration.format(timeout)}`,
|
|
85
|
+
timeoutMs: Duration.toMillis(timeout),
|
|
86
|
+
}),
|
|
87
|
+
});
|
|
70
88
|
}
|
|
71
89
|
poll(poll) {
|
|
72
90
|
return this.refresh().pipe(Effect.repeat({ schedule: this.schedule(poll), until: (generation) => generation.terminal }));
|
package/dist/image-client.js
CHANGED
|
@@ -17,7 +17,7 @@ export const layer = Layer.effect(Service, Effect.gen(function* () {
|
|
|
17
17
|
return Service.of({
|
|
18
18
|
generate,
|
|
19
19
|
// Inline routes have no partial frames yet; the stream is the completed response expanded into events.
|
|
20
|
-
stream: (request) => Stream.
|
|
20
|
+
stream: (request) => Stream.fromIterableEffect(Effect.map(generate(request), responseEvents)),
|
|
21
21
|
});
|
|
22
22
|
}));
|
|
23
23
|
export const ImageClient = {
|
package/dist/image.d.ts
CHANGED
|
@@ -1,47 +1,25 @@
|
|
|
1
1
|
import { Effect, Schema, Stream } from "effect";
|
|
2
2
|
import { Media } from "./media.js";
|
|
3
|
-
import {
|
|
3
|
+
import { MediaModel } from "./media-model.js";
|
|
4
4
|
import { MediaRoute } from "./route/media.js";
|
|
5
5
|
import type { MediaProtocol } from "./route/media-protocol.js";
|
|
6
|
-
import { AIError, HttpOptions
|
|
6
|
+
import { AIError, HttpOptions } from "./schema/index.js";
|
|
7
7
|
import { Service } from "./image-client.js";
|
|
8
8
|
export type ImageOptions = Record<string, unknown>;
|
|
9
9
|
export type ImageRoute<Options extends ImageOptions = ImageOptions> = MediaRoute.Route<ImageRequestFor<Options>, ImageResponse>;
|
|
10
|
-
export declare class ImageModel<Options extends ImageOptions = ImageOptions> {
|
|
11
|
-
protected readonly
|
|
12
|
-
|
|
13
|
-
readonly provider: ProviderID;
|
|
14
|
-
readonly route: ImageRoute<Options>;
|
|
15
|
-
readonly http?: HttpOptions;
|
|
16
|
-
constructor(input: ImageModel.Input<Options>);
|
|
17
|
-
static make<Options extends ImageOptions = ImageOptions>(input: ImageModel.MakeInput<Options>): ImageModel<Options>;
|
|
10
|
+
export declare class ImageModel<Options extends ImageOptions = ImageOptions> extends MediaModel<ImageRoute<Options>, Options> {
|
|
11
|
+
protected readonly _ImageModel: void;
|
|
12
|
+
static make<Options extends ImageOptions = ImageOptions>(input: MediaModel.Input<ImageRoute<Options>>): ImageModel<Options>;
|
|
18
13
|
/** Compose an inline image protocol with its canonical path into a model for one deployment. */
|
|
19
14
|
static fromRoute<Options extends ImageOptions = ImageOptions>(route: ImageModel.RouteInput<Options>, input: MediaRoute.ModelInput): ImageModel<Options>;
|
|
20
15
|
}
|
|
21
16
|
export declare namespace ImageModel {
|
|
22
|
-
|
|
23
|
-
readonly id: ModelID;
|
|
24
|
-
readonly provider: ProviderID;
|
|
25
|
-
readonly route: ImageRoute<Options>;
|
|
26
|
-
readonly http?: HttpOptions;
|
|
27
|
-
}
|
|
28
|
-
interface MakeInput<Options extends ImageOptions = ImageOptions> extends Omit<Input<Options>, "id" | "provider"> {
|
|
29
|
-
readonly id: string | ModelID;
|
|
30
|
-
readonly provider: string | ProviderID;
|
|
31
|
-
}
|
|
32
|
-
interface RouteInput<Options extends ImageOptions = ImageOptions> {
|
|
33
|
-
readonly id: string;
|
|
34
|
-
readonly provider: string | ProviderID;
|
|
35
|
-
readonly protocol: MediaProtocol.Inline<ImageRequestFor<Options>, ImageResponse>;
|
|
36
|
-
readonly path: Endpoint.EndpointPart<MediaProtocol.Body, ImageRequestFor<Options>>;
|
|
37
|
-
/** Canonical base URL; `ModelInput.baseURL` overrides it per deployment. */
|
|
38
|
-
readonly baseURL?: string;
|
|
39
|
-
}
|
|
17
|
+
type RouteInput<Options extends ImageOptions = ImageOptions> = MediaModel.RouteInput<ImageRequestFor<Options>, MediaProtocol.Inline<ImageRequestFor<Options>, ImageResponse>>;
|
|
40
18
|
}
|
|
41
19
|
export declare const ImageModelSchema: Schema.declare<ImageModel<ImageOptions>, ImageModel<ImageOptions>>;
|
|
42
20
|
export type ImageSize = `${number}x${number}`;
|
|
43
21
|
export declare const ImageSize: Schema.declare<`${number}x${number}`, `${number}x${number}`>;
|
|
44
|
-
export type ImageAspectRatio =
|
|
22
|
+
export type ImageAspectRatio = Media.AspectRatio;
|
|
45
23
|
export declare const ImageAspectRatio: Schema.declare<`${number}:${number}`, `${number}:${number}`>;
|
|
46
24
|
export type ImageFormat = "png" | "jpeg" | "webp" | (string & {});
|
|
47
25
|
declare const ImageRequest_base: Schema.Class<ImageRequest, Schema.Struct<{
|
|
@@ -62,7 +40,6 @@ declare const ImageRequest_base: Schema.Class<ImageRequest, Schema.Struct<{
|
|
|
62
40
|
readonly url: Schema.String;
|
|
63
41
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
64
42
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
65
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
66
43
|
}>, Schema.Struct<{
|
|
67
44
|
readonly type: Schema.Literal<"ref">;
|
|
68
45
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
|
@@ -95,7 +72,6 @@ declare const ImageRequest_base: Schema.Class<ImageRequest, Schema.Struct<{
|
|
|
95
72
|
readonly url: Schema.String;
|
|
96
73
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
97
74
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
98
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
99
75
|
}>, Schema.Struct<{
|
|
100
76
|
readonly type: Schema.Literal<"ref">;
|
|
101
77
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
|
@@ -150,7 +126,6 @@ declare const ImageResponse_base: Schema.Class<ImageResponse, Schema.Struct<{
|
|
|
150
126
|
readonly url: Schema.String;
|
|
151
127
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
152
128
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
153
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
154
129
|
}>, Schema.Struct<{
|
|
155
130
|
readonly type: Schema.Literal<"ref">;
|
|
156
131
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
|
@@ -214,7 +189,6 @@ export declare const ImageOutputEvent: Schema.Struct<{
|
|
|
214
189
|
readonly url: Schema.String;
|
|
215
190
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
216
191
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
217
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
218
192
|
}>, Schema.Struct<{
|
|
219
193
|
readonly type: Schema.Literal<"ref">;
|
|
220
194
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
|
@@ -278,7 +252,6 @@ declare const imageEventTagged: Schema.toTaggedUnion<"type", readonly [Schema.St
|
|
|
278
252
|
readonly url: Schema.String;
|
|
279
253
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
280
254
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
281
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
282
255
|
}>, Schema.Struct<{
|
|
283
256
|
readonly type: Schema.Literal<"ref">;
|
|
284
257
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
|
@@ -341,7 +314,6 @@ export declare const ImageEvent: Schema.Union<readonly [Schema.Struct<{
|
|
|
341
314
|
readonly url: Schema.String;
|
|
342
315
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
343
316
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
344
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
345
317
|
}>, Schema.Struct<{
|
|
346
318
|
readonly type: Schema.Literal<"ref">;
|
|
347
319
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
|
@@ -406,7 +378,6 @@ export declare const ImageEvent: Schema.Union<readonly [Schema.Struct<{
|
|
|
406
378
|
readonly url: Schema.String;
|
|
407
379
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
408
380
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
409
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
410
381
|
}>, Schema.Struct<{
|
|
411
382
|
readonly type: Schema.Literal<"ref">;
|
|
412
383
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
package/dist/image.js
CHANGED
|
@@ -1,42 +1,20 @@
|
|
|
1
1
|
import { Effect, Schema, Stream } from "effect";
|
|
2
2
|
import { Media } from "./media.js";
|
|
3
|
-
import {
|
|
3
|
+
import { MediaModel, composeRoute, tryRequest } from "./media-model.js";
|
|
4
4
|
import { MediaRoute } from "./route/media.js";
|
|
5
|
-
import { AIError, HttpOptions,
|
|
5
|
+
import { AIError, HttpOptions, MediaUsage, ProviderMetadata } from "./schema/index.js";
|
|
6
6
|
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
|
-
}
|
|
7
|
+
export class ImageModel extends MediaModel {
|
|
18
8
|
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
|
-
});
|
|
9
|
+
return new ImageModel(input);
|
|
25
10
|
}
|
|
26
11
|
/** Compose an inline image protocol with its canonical path into a model for one deployment. */
|
|
27
12
|
static fromRoute(route, input) {
|
|
28
|
-
return ImageModel
|
|
13
|
+
return new ImageModel({
|
|
29
14
|
id: input.id,
|
|
30
15
|
provider: route.provider,
|
|
31
16
|
http: input.http,
|
|
32
|
-
route: MediaRoute.
|
|
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: composeRoute(MediaRoute.inline, route, input),
|
|
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 =
|
|
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,
|
|
@@ -109,15 +87,7 @@ export function request(input) {
|
|
|
109
87
|
http: input.http === undefined ? undefined : HttpOptions.make(input.http),
|
|
110
88
|
});
|
|
111
89
|
}
|
|
112
|
-
const requestEffect = (input) =>
|
|
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
|
-
});
|
|
90
|
+
const requestEffect = (input) => tryRequest(() => request(input));
|
|
121
91
|
export function generate(input) {
|
|
122
92
|
return requestEffect(input).pipe(Effect.flatMap((request) => ImageClient.generate(request)));
|
|
123
93
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,9 +9,13 @@ 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";
|
|
12
16
|
export { Media } from "./media.js";
|
|
13
17
|
export { Generation } from "./generation.js";
|
|
14
|
-
export type { Event as GenerationEvent, Poll, Route as GenerationRoute, Snapshot as GenerationSnapshot, Status as GenerationStatus } from "./generation.js";
|
|
18
|
+
export type { AwaitOptions as GenerationAwaitOptions, Event as GenerationEvent, Poll, Route as GenerationRoute, Snapshot as GenerationSnapshot, Status as GenerationStatus, } from "./generation.js";
|
|
15
19
|
export { Tool, ToolFailure, toDefinitions } from "./tool.js";
|
|
16
20
|
export { ToolRuntime } from "./tool-runtime.js";
|
|
17
21
|
export type { DispatchResult as ToolDispatchResult, ToolSettlement } from "./tool-runtime.js";
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,9 @@ 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";
|
|
10
13
|
export { Media } from "./media.js";
|
|
11
14
|
export { Generation } from "./generation.js";
|
|
12
15
|
export { Tool, ToolFailure, toDefinitions } from "./tool.js";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Endpoint } from "./route/endpoint.js";
|
|
3
|
+
import type { 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`) extend it with their route type and a nominal marker so one cannot stand in for the
|
|
9
|
+
* 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
|
+
}
|
|
37
|
+
/** Compose a protocol route input with one deployment through `MediaRoute.inline` or `MediaRoute.queued`. */
|
|
38
|
+
export declare const composeRoute: <Request extends MediaRoute.MediaRequest, Protocol, Route>(compose: (input: MediaRoute.Composition<Request> & {
|
|
39
|
+
readonly protocol: Protocol;
|
|
40
|
+
}) => Route, route: MediaModel.RouteInput<Request, Protocol>, input: MediaRoute.ModelInput) => Route;
|
|
41
|
+
/** Lift a synchronous Schema-class constructor into a typed `InvalidRequest` failure. */
|
|
42
|
+
export declare const tryRequest: <A>(make: () => A) => Effect.Effect<A, AIError>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Endpoint } from "./route/endpoint.js";
|
|
3
|
+
import { AIError, HttpOptions, InvalidRequestError, ModelID, ProviderID } from "./schema/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* What every media model carries: ids, the configured route, and deployment `http` overlays. Modality classes
|
|
6
|
+
* (`ImageModel`, `VideoModel`) extend it with their route type and a nominal marker so one cannot stand in for the
|
|
7
|
+
* other in requests.
|
|
8
|
+
*/
|
|
9
|
+
export class MediaModel {
|
|
10
|
+
id;
|
|
11
|
+
provider;
|
|
12
|
+
route;
|
|
13
|
+
http;
|
|
14
|
+
constructor(input) {
|
|
15
|
+
this.id = ModelID.make(input.id);
|
|
16
|
+
this.provider = ProviderID.make(input.provider);
|
|
17
|
+
this.route = input.route;
|
|
18
|
+
this.http = input.http;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Compose a protocol route input with one deployment through `MediaRoute.inline` or `MediaRoute.queued`. */
|
|
22
|
+
export const composeRoute = (compose, route, input) => compose({
|
|
23
|
+
id: route.id,
|
|
24
|
+
provider: route.provider,
|
|
25
|
+
protocol: route.protocol,
|
|
26
|
+
endpoint: Endpoint.path(route.path, { baseURL: input.baseURL ?? route.baseURL }),
|
|
27
|
+
auth: input.auth,
|
|
28
|
+
headers: route.headers === undefined && input.headers === undefined ? undefined : { ...route.headers, ...input.headers },
|
|
29
|
+
});
|
|
30
|
+
/** Lift a synchronous Schema-class constructor into a typed `InvalidRequest` failure. */
|
|
31
|
+
export const tryRequest = (make) => Effect.try({
|
|
32
|
+
try: make,
|
|
33
|
+
catch: (error) => new AIError({
|
|
34
|
+
reason: new InvalidRequestError({
|
|
35
|
+
message: error instanceof Error ? error.message : String(error),
|
|
36
|
+
cause: error,
|
|
37
|
+
}),
|
|
38
|
+
}),
|
|
39
|
+
});
|
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">;
|