@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.
- package/README.md +286 -2
- package/dist/generation.d.ts +36 -22
- package/dist/generation.js +53 -24
- package/dist/image-client.d.ts +14 -7
- package/dist/image-client.js +24 -8
- package/dist/image.d.ts +398 -47
- package/dist/image.js +47 -45
- package/dist/index.d.ts +13 -1
- package/dist/index.js +9 -0
- package/dist/media-model.d.ts +44 -0
- package/dist/media-model.js +49 -0
- package/dist/media.d.ts +10 -9
- package/dist/media.js +9 -10
- package/dist/promise.d.ts +428 -8
- package/dist/promise.js +40 -3
- package/dist/protocols/alibaba-chat.d.ts +12 -0
- package/dist/protocols/alibaba-responses.d.ts +2 -2
- package/dist/protocols/anthropic-messages.js +1 -2
- package/dist/protocols/assemblyai-transcription.d.ts +40 -0
- package/dist/protocols/assemblyai-transcription.js +138 -0
- package/dist/protocols/bedrock-converse.js +5 -11
- package/dist/protocols/bfl-images.d.ts +32 -0
- package/dist/protocols/bfl-images.js +153 -0
- package/dist/protocols/cartesia-speech.d.ts +127 -0
- package/dist/protocols/cartesia-speech.js +126 -0
- package/dist/protocols/deepgram-speech.d.ts +119 -0
- package/dist/protocols/deepgram-speech.js +92 -0
- package/dist/protocols/deepgram-transcription.d.ts +25 -0
- package/dist/protocols/deepgram-transcription.js +129 -0
- package/dist/protocols/elevenlabs-speech.d.ts +122 -0
- package/dist/protocols/elevenlabs-speech.js +115 -0
- package/dist/protocols/fal-images.d.ts +24 -0
- package/dist/protocols/fal-images.js +114 -0
- package/dist/protocols/fal-video.d.ts +29 -0
- package/dist/protocols/fal-video.js +88 -0
- package/dist/protocols/gemini.d.ts +9 -9
- package/dist/protocols/gemini.js +8 -34
- package/dist/protocols/google-images.js +2 -14
- package/dist/protocols/google-speech.d.ts +130 -0
- package/dist/protocols/google-speech.js +84 -0
- package/dist/protocols/google-transcription.d.ts +173 -0
- package/dist/protocols/google-transcription.js +138 -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/meta-responses.d.ts +4 -4
- package/dist/protocols/meta-responses.js +1 -1
- package/dist/protocols/open-responses.d.ts +6 -6
- package/dist/protocols/open-responses.js +1 -2
- package/dist/protocols/openai-chat.d.ts +84 -0
- package/dist/protocols/openai-chat.js +26 -14
- package/dist/protocols/openai-compatible-chat.d.ts +12 -0
- package/dist/protocols/openai-compatible-responses.d.ts +2 -2
- package/dist/protocols/openai-images.d.ts +124 -3
- package/dist/protocols/openai-images.js +107 -54
- package/dist/protocols/openai-responses.d.ts +15 -15
- package/dist/protocols/openai-responses.js +5 -6
- package/dist/protocols/openai-speech.d.ts +116 -0
- package/dist/protocols/openai-speech.js +98 -0
- package/dist/protocols/openai-transcription.d.ts +207 -0
- package/dist/protocols/openai-transcription.js +190 -0
- package/dist/protocols/replicate-images.d.ts +28 -0
- package/dist/protocols/replicate-images.js +133 -0
- package/dist/protocols/runway-video.d.ts +38 -0
- package/dist/protocols/runway-video.js +146 -0
- package/dist/protocols/shared.d.ts +13 -3
- package/dist/protocols/shared.js +23 -3
- package/dist/protocols/stability-images.d.ts +38 -0
- package/dist/protocols/stability-images.js +148 -0
- package/dist/protocols/utils/fal-queue.d.ts +28 -0
- package/dist/protocols/utils/fal-queue.js +69 -0
- package/dist/protocols/utils/gemini-generate-content.d.ts +65 -0
- package/dist/protocols/utils/gemini-generate-content.js +65 -0
- package/dist/protocols/utils/gemini-json-schema.d.ts +3 -0
- package/dist/protocols/utils/gemini-json-schema.js +76 -0
- package/dist/protocols/utils/media-input.d.ts +8 -0
- package/dist/protocols/utils/media-input.js +18 -0
- package/dist/protocols/utils/speech-stream.d.ts +49 -0
- package/dist/protocols/utils/speech-stream.js +67 -0
- package/dist/protocols/utils/tool-schema.d.ts +2 -2
- package/dist/protocols/utils/tool-schema.js +40 -17
- package/dist/protocols/xai-images.js +1 -12
- package/dist/protocols/xai-responses.d.ts +2 -2
- package/dist/protocols/xai-video.d.ts +34 -0
- package/dist/protocols/xai-video.js +147 -0
- package/dist/protocols/zai-chat.d.ts +13 -1
- package/dist/provider-error.js +3 -0
- package/dist/providers/alibaba.d.ts +14 -2
- package/dist/providers/amazon-bedrock-mantle.d.ts +14 -2
- package/dist/providers/assemblyai.d.ts +25 -0
- package/dist/providers/assemblyai.js +29 -0
- package/dist/providers/azure.d.ts +18 -6
- package/dist/providers/baseten.d.ts +24 -0
- package/dist/providers/black-forest-labs.d.ts +25 -0
- package/dist/providers/black-forest-labs.js +28 -0
- package/dist/providers/cartesia.d.ts +24 -0
- package/dist/providers/cartesia.js +22 -0
- package/dist/providers/cerebras.d.ts +24 -0
- package/dist/providers/cloudflare-ai-gateway.d.ts +30 -6
- package/dist/providers/cloudflare-workers-ai.d.ts +24 -0
- package/dist/providers/deepgram.d.ts +29 -0
- package/dist/providers/deepgram.js +31 -0
- package/dist/providers/deepinfra.d.ts +24 -0
- package/dist/providers/deepseek.d.ts +24 -0
- package/dist/providers/elevenlabs.d.ts +24 -0
- package/dist/providers/elevenlabs.js +28 -0
- package/dist/providers/fal.d.ts +29 -0
- package/dist/providers/fal.js +33 -0
- package/dist/providers/fireworks.d.ts +24 -0
- package/dist/providers/google-vertex-chat.d.ts +12 -0
- package/dist/providers/google-vertex-responses.d.ts +2 -2
- package/dist/providers/google-vertex.d.ts +3 -3
- package/dist/providers/google.d.ts +18 -3
- package/dist/providers/google.js +11 -2
- package/dist/providers/groq.d.ts +24 -0
- package/dist/providers/index.d.ts +9 -0
- package/dist/providers/index.js +9 -0
- package/dist/providers/meta.d.ts +14 -2
- package/dist/providers/minimax.d.ts +14 -2
- package/dist/providers/moonshot.d.ts +14 -2
- package/dist/providers/moonshot.js +3 -3
- package/dist/providers/openai-compatible-responses.d.ts +2 -2
- package/dist/providers/openai-compatible.d.ts +12 -0
- package/dist/providers/openai.d.ts +25 -3
- package/dist/providers/openai.js +10 -1
- package/dist/providers/openrouter.d.ts +48 -0
- package/dist/providers/replicate.d.ts +25 -0
- package/dist/providers/replicate.js +22 -0
- package/dist/providers/runway.d.ts +24 -0
- package/dist/providers/runway.js +22 -0
- package/dist/providers/stability.d.ts +28 -0
- package/dist/providers/stability.js +23 -0
- package/dist/providers/togetherai.d.ts +24 -0
- package/dist/providers/xai.d.ts +17 -0
- package/dist/providers/xai.js +5 -2
- package/dist/providers/zai-coding-plan.d.ts +15 -3
- package/dist/providers/zai.d.ts +13 -1
- package/dist/route/auth.d.ts +4 -1
- package/dist/route/auth.js +6 -0
- package/dist/route/framing.d.ts +5 -1
- package/dist/route/framing.js +9 -0
- package/dist/route/media-protocol.d.ts +116 -3
- package/dist/route/media-protocol.js +60 -4
- package/dist/route/media.d.ts +58 -7
- package/dist/route/media.js +201 -29
- package/dist/schema/events.d.ts +0 -6
- package/dist/schema/messages.d.ts +0 -3
- package/dist/schema/options.d.ts +4 -3
- package/dist/schema/options.js +3 -2
- package/dist/speech-client.d.ts +21 -0
- package/dist/speech-client.js +25 -0
- package/dist/speech.d.ts +1150 -0
- package/dist/speech.js +119 -0
- package/dist/transcription-client.d.ts +28 -0
- package/dist/transcription-client.js +44 -0
- package/dist/transcription.d.ts +1504 -0
- package/dist/transcription.js +133 -0
- package/dist/utils/bytes.d.ts +1 -0
- package/dist/utils/bytes.js +10 -0
- package/dist/utils/media-type.d.ts +1 -0
- package/dist/utils/media-type.js +22 -1
- package/dist/video-client.d.ts +28 -0
- package/dist/video-client.js +40 -0
- package/dist/video.d.ts +1359 -0
- package/dist/video.js +119 -0
- package/package.json +3 -3
- package/dist/protocols/utils/gemini-tool-schema.d.ts +0 -2
- package/dist/protocols/utils/gemini-tool-schema.js +0 -103
|
@@ -86,8 +86,8 @@ export declare const routes: (Route<{
|
|
|
86
86
|
} | {
|
|
87
87
|
readonly type: "input_file";
|
|
88
88
|
readonly filename: string;
|
|
89
|
-
readonly detail?: string | undefined;
|
|
90
89
|
readonly file_data?: string | undefined;
|
|
90
|
+
readonly detail?: string | undefined;
|
|
91
91
|
readonly file_url?: string | undefined;
|
|
92
92
|
} | {
|
|
93
93
|
readonly type: "input_text";
|
|
@@ -123,8 +123,8 @@ export declare const routes: (Route<{
|
|
|
123
123
|
} | {
|
|
124
124
|
readonly type: "input_file";
|
|
125
125
|
readonly filename: string;
|
|
126
|
-
readonly detail?: string | undefined;
|
|
127
126
|
readonly file_data?: string | undefined;
|
|
127
|
+
readonly detail?: string | undefined;
|
|
128
128
|
readonly file_url?: string | undefined;
|
|
129
129
|
} | {
|
|
130
130
|
readonly type: "input_text";
|
|
@@ -200,6 +200,12 @@ export declare const routes: (Route<{
|
|
|
200
200
|
readonly image_url: {
|
|
201
201
|
readonly url: string;
|
|
202
202
|
};
|
|
203
|
+
} | {
|
|
204
|
+
readonly type: "file";
|
|
205
|
+
readonly file: {
|
|
206
|
+
readonly filename: string;
|
|
207
|
+
readonly file_data: string;
|
|
208
|
+
};
|
|
203
209
|
})[];
|
|
204
210
|
} | {
|
|
205
211
|
readonly role: "user";
|
|
@@ -215,6 +221,12 @@ export declare const routes: (Route<{
|
|
|
215
221
|
readonly image_url: {
|
|
216
222
|
readonly url: string;
|
|
217
223
|
};
|
|
224
|
+
} | {
|
|
225
|
+
readonly type: "file";
|
|
226
|
+
readonly file: {
|
|
227
|
+
readonly filename: string;
|
|
228
|
+
readonly file_data: string;
|
|
229
|
+
};
|
|
218
230
|
})[];
|
|
219
231
|
} | {
|
|
220
232
|
readonly [x: string]: unknown;
|
|
@@ -290,8 +302,8 @@ export declare const routes: (Route<{
|
|
|
290
302
|
readonly response_format?: {
|
|
291
303
|
readonly type: string;
|
|
292
304
|
} | undefined;
|
|
293
|
-
readonly do_sample?: boolean | undefined;
|
|
294
305
|
readonly request_id?: string | undefined;
|
|
306
|
+
readonly do_sample?: boolean | undefined;
|
|
295
307
|
}, import("../route/transport/http.js").HttpPrepared<string>, import("../route/client.js").CompactionOperations | undefined> | Route<{
|
|
296
308
|
readonly max_tokens: number;
|
|
297
309
|
readonly model: string;
|
package/dist/providers/zai.d.ts
CHANGED
|
@@ -30,6 +30,12 @@ export declare const routes: Route<{
|
|
|
30
30
|
readonly image_url: {
|
|
31
31
|
readonly url: string;
|
|
32
32
|
};
|
|
33
|
+
} | {
|
|
34
|
+
readonly type: "file";
|
|
35
|
+
readonly file: {
|
|
36
|
+
readonly filename: string;
|
|
37
|
+
readonly file_data: string;
|
|
38
|
+
};
|
|
33
39
|
})[];
|
|
34
40
|
} | {
|
|
35
41
|
readonly role: "user";
|
|
@@ -45,6 +51,12 @@ export declare const routes: Route<{
|
|
|
45
51
|
readonly image_url: {
|
|
46
52
|
readonly url: string;
|
|
47
53
|
};
|
|
54
|
+
} | {
|
|
55
|
+
readonly type: "file";
|
|
56
|
+
readonly file: {
|
|
57
|
+
readonly filename: string;
|
|
58
|
+
readonly file_data: string;
|
|
59
|
+
};
|
|
48
60
|
})[];
|
|
49
61
|
} | {
|
|
50
62
|
readonly [x: string]: unknown;
|
|
@@ -120,8 +132,8 @@ export declare const routes: Route<{
|
|
|
120
132
|
readonly response_format?: {
|
|
121
133
|
readonly type: string;
|
|
122
134
|
} | undefined;
|
|
123
|
-
readonly do_sample?: boolean | undefined;
|
|
124
135
|
readonly request_id?: string | undefined;
|
|
136
|
+
readonly do_sample?: boolean | undefined;
|
|
125
137
|
}, import("../route/transport/http.js").HttpPrepared<string>, import("../route/client.js").CompactionOperations | undefined>[];
|
|
126
138
|
export declare const configure: (input?: Config) => {
|
|
127
139
|
id: string & import("effect/Brand").Brand<"AI.ProviderID">;
|
package/dist/route/auth.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export interface AuthInput {
|
|
|
13
13
|
readonly request: {
|
|
14
14
|
readonly http?: HttpOptions;
|
|
15
15
|
};
|
|
16
|
-
readonly method: "POST" | "GET";
|
|
16
|
+
readonly method: "POST" | "GET" | "PUT" | "DELETE";
|
|
17
17
|
readonly url: string;
|
|
18
18
|
readonly body: string;
|
|
19
19
|
readonly headers: Headers.Headers;
|
|
@@ -47,5 +47,8 @@ export declare function header(name: string): (source: Secret | Credential) => D
|
|
|
47
47
|
export declare function header(name: string, source: Secret | Credential): Definition;
|
|
48
48
|
export declare function bearerHeader(name: string): (source: Secret | Credential) => Definition;
|
|
49
49
|
export declare function bearerHeader(name: string, source: Secret | Credential): Definition;
|
|
50
|
+
/** `Authorization: <scheme> <secret>` for providers whose scheme is not `Bearer`, such as fal's `Key`. */
|
|
51
|
+
export declare function scheme(name: string): (source: Secret | Credential) => Definition;
|
|
52
|
+
export declare function scheme(name: string, source: Secret | Credential): Definition;
|
|
50
53
|
export declare const toEffect: (input: Definition) => (authInput: AuthInput) => Effect.Effect<Headers.Headers, AIError>;
|
|
51
54
|
export * as Auth from "./auth.js";
|
package/dist/route/auth.js
CHANGED
|
@@ -73,6 +73,12 @@ export function bearerHeader(name, source) {
|
|
|
73
73
|
return render;
|
|
74
74
|
return render(source);
|
|
75
75
|
}
|
|
76
|
+
export function scheme(name, source) {
|
|
77
|
+
const render = (input) => fromCredential(credentialInput(input), (secret) => ({ authorization: `${name} ${secret}` }));
|
|
78
|
+
if (source === undefined)
|
|
79
|
+
return render;
|
|
80
|
+
return render(source);
|
|
81
|
+
}
|
|
76
82
|
const toAIError = (error) => {
|
|
77
83
|
if (error instanceof MissingCredentialError || error instanceof Config.ConfigError) {
|
|
78
84
|
return new AIError({
|
package/dist/route/framing.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Stream } from "effect";
|
|
2
2
|
import type { AIError } from "../schema/index.js";
|
|
3
3
|
/**
|
|
4
4
|
* Decode a streaming HTTP response body into provider-protocol frames.
|
|
@@ -10,6 +10,8 @@ import type { AIError } from "../schema/index.js";
|
|
|
10
10
|
* `[DONE]`; protocols that use it as a terminal select `sseWithDone`.
|
|
11
11
|
* - AWS event stream — length-prefixed binary frames with CRC checksums.
|
|
12
12
|
* Each emitted frame is one parsed binary event record.
|
|
13
|
+
* - Media streams — newline-delimited JSON (`lines`) or the whole body as one
|
|
14
|
+
* frame (`document`); chunked binary bodies need no framing.
|
|
13
15
|
*
|
|
14
16
|
* The frame type is opaque to this layer; the protocol's event schema decodes
|
|
15
17
|
* each frame before its state machine handles it.
|
|
@@ -26,4 +28,6 @@ export declare const sse: Definition<string>;
|
|
|
26
28
|
export declare const sseWithDone: Definition<string>;
|
|
27
29
|
/** SSE framing restricted to protocol-recognized event names. */
|
|
28
30
|
export declare const sseEvents: (events: ReadonlySet<string>) => Definition<string>;
|
|
31
|
+
export declare const lines: Definition<string>;
|
|
32
|
+
export declare const document: Definition<string>;
|
|
29
33
|
export * as Framing from "./framing.js";
|
package/dist/route/framing.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Stream } from "effect";
|
|
1
2
|
import * as ProviderShared from "../protocols/shared.js";
|
|
2
3
|
/** Server-Sent Events framing. Used by every JSON-streaming HTTP provider. */
|
|
3
4
|
export const sse = { id: "sse", frame: ProviderShared.sseFraming };
|
|
@@ -11,4 +12,12 @@ export const sseEvents = (events) => ({
|
|
|
11
12
|
id: "sse",
|
|
12
13
|
frame: (bytes) => ProviderShared.sseFraming(bytes, events),
|
|
13
14
|
});
|
|
15
|
+
export const lines = {
|
|
16
|
+
id: "lines",
|
|
17
|
+
frame: (bytes) => bytes.pipe(Stream.decodeText(), Stream.splitLines, Stream.filter((line) => line.trim().length > 0)),
|
|
18
|
+
};
|
|
19
|
+
export const document = {
|
|
20
|
+
id: "document",
|
|
21
|
+
frame: (bytes) => Stream.fromEffect(Stream.mkString(bytes.pipe(Stream.decodeText()))),
|
|
22
|
+
};
|
|
14
23
|
export * as Framing from "./framing.js";
|
|
@@ -1,15 +1,31 @@
|
|
|
1
|
-
import { Effect, Schema } from "effect";
|
|
1
|
+
import { Duration, Effect, Schema, type Stream } from "effect";
|
|
2
2
|
import { HttpClientResponse } from "effect/unstable/http";
|
|
3
|
+
import type { Snapshot, Status } from "../generation.js";
|
|
4
|
+
import { Media } from "../media.js";
|
|
5
|
+
import type { AuthInput } from "./auth.js";
|
|
3
6
|
import { AIError, HttpContext } from "../schema/index.js";
|
|
7
|
+
/** Array values become repeated parameters (`keyterm=a&keyterm=b`). */
|
|
8
|
+
export type Query = Readonly<Record<string, string | ReadonlyArray<string>>>;
|
|
9
|
+
/** `query` is appended to the endpoint URL before the route and caller `http.query` overlays. */
|
|
4
10
|
export type Body = {
|
|
5
11
|
readonly type: "json";
|
|
6
12
|
readonly value: Record<string, unknown>;
|
|
13
|
+
readonly query?: Query;
|
|
7
14
|
} | {
|
|
8
15
|
readonly type: "multipart";
|
|
9
16
|
readonly value: FormData;
|
|
17
|
+
} | {
|
|
18
|
+
readonly type: "binary";
|
|
19
|
+
readonly value: Uint8Array;
|
|
20
|
+
readonly contentType: string;
|
|
21
|
+
readonly query?: Query;
|
|
10
22
|
};
|
|
11
|
-
export declare const json: (value: Record<string, unknown
|
|
23
|
+
export declare const json: (value: Record<string, unknown>, query?: Query) => Body;
|
|
12
24
|
export declare const multipart: (value: FormData) => Body;
|
|
25
|
+
export declare const binary: (value: Uint8Array, contentType: string, query?: Query) => Body;
|
|
26
|
+
export type Send = (path: string, body: Body) => Effect.Effect<HttpClientResponse.HttpClientResponse, AIError>;
|
|
27
|
+
/** Runs after unsupported-field rejection and before `body.from`, for providers that need an upload first. */
|
|
28
|
+
export type Prepare<Request> = (request: Request, send: Send) => Effect.Effect<Request, AIError>;
|
|
13
29
|
export interface DecodeContext<Request> {
|
|
14
30
|
readonly request: Request;
|
|
15
31
|
readonly body: Body;
|
|
@@ -29,17 +45,114 @@ export interface Inline<Request, Response> {
|
|
|
29
45
|
};
|
|
30
46
|
}
|
|
31
47
|
export declare const inline: <Request, Response>(input: Omit<Inline<Request, Response>, "kind">) => Inline<Request, Response>;
|
|
32
|
-
/**
|
|
48
|
+
/** What `start` learned from the submission response: the route-owned handle plus the first observation. */
|
|
49
|
+
export interface Started<Token> {
|
|
50
|
+
readonly token: Token;
|
|
51
|
+
readonly snapshot: Snapshot;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A follow-up call's inputs: the decoded token and the auth headers the route sent, so a protocol can attach them
|
|
55
|
+
* to output URLs that require the same credentials to download (Veo). `materialize` downloads an output through the
|
|
56
|
+
* route's executor, for URLs that expire too soon to hand back (BFL).
|
|
57
|
+
*/
|
|
58
|
+
export interface PollContext<Token> {
|
|
59
|
+
readonly token: Token;
|
|
60
|
+
readonly auth: Record<string, string>;
|
|
61
|
+
readonly materialize: (asset: Media.Asset) => Effect.Effect<Media.Asset, AIError>;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Submit, then poll. `start` posts the body to the route endpoint; `status`, `result`, and `cancel` are follow-up
|
|
65
|
+
* calls addressed by the token. Paths are relative to the route base URL unless the provider hands back absolute
|
|
66
|
+
* URLs (fal `status_url`), in which case they are used verbatim. `result` is always its own GET: providers that
|
|
67
|
+
* return the output inside the status body (Veo, xAI, Runway) point `result.path` at the status path and decode the
|
|
68
|
+
* same document, so `Generation.await` and `Video.resume(...).await()` behave identically everywhere.
|
|
69
|
+
*/
|
|
70
|
+
export interface Queued<Request, Response, Token> {
|
|
71
|
+
readonly kind: "queued";
|
|
72
|
+
readonly id: string;
|
|
73
|
+
readonly name: string;
|
|
74
|
+
/** Common request fields this protocol cannot lower; the route rejects them before `start.body.from` runs. */
|
|
75
|
+
readonly unsupported?: ReadonlyArray<keyof Request & string>;
|
|
76
|
+
/** Serializable handle. `Generation.token` carries the encoded form so it can be persisted and resumed elsewhere. */
|
|
77
|
+
readonly token: Schema.Codec<Token, unknown>;
|
|
78
|
+
readonly start: {
|
|
79
|
+
readonly prepare?: Prepare<Request>;
|
|
80
|
+
readonly body: {
|
|
81
|
+
readonly from: (request: Request) => Effect.Effect<Body, AIError>;
|
|
82
|
+
};
|
|
83
|
+
readonly decode: (response: HttpClientResponse.HttpClientResponse, context: DecodeContext<Request>) => Effect.Effect<Started<Token>, AIError>;
|
|
84
|
+
};
|
|
85
|
+
readonly status: {
|
|
86
|
+
readonly path: (token: Token) => string;
|
|
87
|
+
readonly decode: (response: HttpClientResponse.HttpClientResponse, context: PollContext<Token>) => Effect.Effect<Snapshot, AIError>;
|
|
88
|
+
};
|
|
89
|
+
readonly result: {
|
|
90
|
+
readonly path: (token: Token) => string;
|
|
91
|
+
readonly decode: (response: HttpClientResponse.HttpClientResponse, context: PollContext<Token>) => Effect.Effect<Response, AIError>;
|
|
92
|
+
};
|
|
93
|
+
readonly cancel?: {
|
|
94
|
+
readonly method: AuthInput["method"];
|
|
95
|
+
readonly path: (token: Token) => string;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export declare const queued: <Request, Response, Token>(input: Omit<Queued<Request, Response, Token>, "kind">) => Queued<Request, Response, Token>;
|
|
99
|
+
export type Mode = "generate" | "stream";
|
|
100
|
+
export type Addressed<Request> = Request & {
|
|
101
|
+
readonly mode: Mode;
|
|
102
|
+
};
|
|
103
|
+
export interface ResponseContext<Request> extends DecodeContext<Addressed<Request>> {
|
|
104
|
+
readonly http: HttpContext;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* One request whose body is parsed incrementally, like LLM protocols: `frames` → `step`* → `finish`. `generate` and
|
|
108
|
+
* `stream` share this state machine; `request.mode` lets a protocol pick a different body, path, or framing.
|
|
109
|
+
*/
|
|
110
|
+
export interface Streamed<Request, Event, Frame, State> {
|
|
111
|
+
readonly kind: "stream";
|
|
112
|
+
readonly id: string;
|
|
113
|
+
readonly name: string;
|
|
114
|
+
/** Common request fields this protocol cannot lower; the route rejects them before `body.from` runs. */
|
|
115
|
+
readonly unsupported?: ReadonlyArray<keyof Request & string>;
|
|
116
|
+
readonly body: {
|
|
117
|
+
readonly from: (request: Addressed<Request>) => Effect.Effect<Body, AIError>;
|
|
118
|
+
};
|
|
119
|
+
readonly frames: (bytes: Stream.Stream<Uint8Array, AIError>, context: DecodeContext<Addressed<Request>>) => Stream.Stream<Frame, AIError>;
|
|
120
|
+
readonly initial: () => State;
|
|
121
|
+
readonly step: (state: State, frame: Frame) => Effect.Effect<readonly [State, ReadonlyArray<Event>], AIError>;
|
|
122
|
+
/** Emit exactly one terminal event, or fail when the provider stopped before completing. */
|
|
123
|
+
readonly finish: (state: State, context: ResponseContext<Request>) => Effect.Effect<ReadonlyArray<Event>, AIError>;
|
|
124
|
+
}
|
|
125
|
+
export declare const stream: <Request, Event, Frame, State>(input: Omit<Streamed<Request, Event, Frame, State>, "kind">) => Streamed<Request, Event, Frame, State>;
|
|
126
|
+
/**
|
|
127
|
+
* Read a text body while retaining the original payload and HTTP context on every downstream error. `invalid` is a
|
|
128
|
+
* malformed provider document; `ended` is a generation that reached a terminal status without output (`failed` is
|
|
129
|
+
* provider-side, `cancelled`/`expired` mean the result will never exist); `contentPolicy` is a moderated result.
|
|
130
|
+
*/
|
|
33
131
|
export declare const text: (route: string, name: string, response: HttpClientResponse.HttpClientResponse) => Effect.Effect<{
|
|
34
132
|
body: string;
|
|
35
133
|
http: HttpContext;
|
|
36
134
|
invalid: (message: string, cause?: unknown) => AIError;
|
|
135
|
+
ended: (status: Exclude<Status, "queued" | "running" | "completed">, message: string) => AIError;
|
|
136
|
+
contentPolicy: (message: string) => AIError;
|
|
37
137
|
}, AIError, never>;
|
|
138
|
+
export type Output = Effect.Success<ReturnType<typeof text>>;
|
|
38
139
|
/** Read and Schema-decode a JSON body. Decode failures keep the raw body as `reason.body`. */
|
|
39
140
|
export declare const decodeJson: <A>(route: string, name: string, schema: Schema.Codec<A, unknown>) => (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<{
|
|
40
141
|
value: A;
|
|
41
142
|
body: string;
|
|
42
143
|
http: HttpContext;
|
|
43
144
|
invalid: (message: string, cause?: unknown) => AIError;
|
|
145
|
+
ended: (status: Exclude<Status, "queued" | "running" | "completed">, message: string) => AIError;
|
|
146
|
+
contentPolicy: (message: string) => AIError;
|
|
44
147
|
}, AIError, never>;
|
|
148
|
+
/** Decode a submission response into the token and first snapshot. */
|
|
149
|
+
export declare const decodeStarted: <A, Token>(route: string, name: string, schema: Schema.Codec<A, unknown>, started: (value: A) => Started<Token>) => (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<Started<Token>, AIError, never>;
|
|
150
|
+
/** Map a provider status string through the protocol's table; unknown values are an invalid provider document. */
|
|
151
|
+
export declare const status: <Table extends Record<string, Status>>(table: Table, raw: string, output: Output) => Effect.Effect<Status, AIError>;
|
|
152
|
+
export declare const frameError: (route: string, message: string, body?: string, cause?: unknown) => AIError;
|
|
153
|
+
export declare const incomplete: (route: string) => AIError;
|
|
154
|
+
/** Schema-decode one JSON stream frame. Decode failures keep the frame as `reason.body`. */
|
|
155
|
+
export declare const decodeFrame: <A>(route: string, name: string, schema: Schema.Codec<A, unknown>) => (frame: string) => Effect.Effect<A, AIError, never>;
|
|
156
|
+
/** A `url` asset whose provider-declared retention window starts now. */
|
|
157
|
+
export declare const expiringUrl: (url: string, retention: Duration.Duration, options?: Parameters<typeof Media.url>[1]) => Effect.Effect<Media.Asset, never, never>;
|
|
45
158
|
export * as MediaProtocol from "./media-protocol.js";
|
|
@@ -1,17 +1,40 @@
|
|
|
1
|
-
import { Effect, Schema } from "effect";
|
|
1
|
+
import { Clock, Duration, Effect, Schema } from "effect";
|
|
2
2
|
import { HttpClientResponse } from "effect/unstable/http";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import { Media } from "../media.js";
|
|
4
|
+
import { AIError, ContentPolicyError, HttpContext, InvalidProviderOutputError, InvalidRequestError, ProviderInternalError, } from "../schema/index.js";
|
|
5
|
+
export const json = (value, query) => ({
|
|
6
|
+
type: "json",
|
|
7
|
+
value,
|
|
8
|
+
query,
|
|
9
|
+
});
|
|
5
10
|
export const multipart = (value) => ({ type: "multipart", value });
|
|
11
|
+
export const binary = (value, contentType, query) => ({
|
|
12
|
+
type: "binary",
|
|
13
|
+
value,
|
|
14
|
+
contentType,
|
|
15
|
+
query,
|
|
16
|
+
});
|
|
6
17
|
export const inline = (input) => ({
|
|
7
18
|
kind: "inline",
|
|
8
19
|
...input,
|
|
9
20
|
});
|
|
21
|
+
export const queued = (input) => ({
|
|
22
|
+
kind: "queued",
|
|
23
|
+
...input,
|
|
24
|
+
});
|
|
25
|
+
export const stream = (input) => ({
|
|
26
|
+
kind: "stream",
|
|
27
|
+
...input,
|
|
28
|
+
});
|
|
10
29
|
// ---------------------------------------------------------------------------
|
|
11
30
|
// Response helpers
|
|
12
31
|
// ---------------------------------------------------------------------------
|
|
13
32
|
const context = (response) => new HttpContext({ url: response.request.url, status: response.status, headers: response.headers });
|
|
14
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Read a text body while retaining the original payload and HTTP context on every downstream error. `invalid` is a
|
|
35
|
+
* malformed provider document; `ended` is a generation that reached a terminal status without output (`failed` is
|
|
36
|
+
* provider-side, `cancelled`/`expired` mean the result will never exist); `contentPolicy` is a moderated result.
|
|
37
|
+
*/
|
|
15
38
|
export const text = Effect.fn("MediaProtocol.text")(function* (route, name, response) {
|
|
16
39
|
const http = context(response);
|
|
17
40
|
const body = yield* response.text.pipe(Effect.mapError((cause) => new AIError({
|
|
@@ -26,6 +49,12 @@ export const text = Effect.fn("MediaProtocol.text")(function* (route, name, resp
|
|
|
26
49
|
body,
|
|
27
50
|
http,
|
|
28
51
|
invalid: (message, cause) => new AIError({ reason: new InvalidProviderOutputError({ route, message, body, http, cause }) }),
|
|
52
|
+
ended: (status, message) => new AIError({
|
|
53
|
+
reason: status === "failed"
|
|
54
|
+
? new ProviderInternalError({ message, body, http })
|
|
55
|
+
: new InvalidRequestError({ message, body, http }),
|
|
56
|
+
}),
|
|
57
|
+
contentPolicy: (message) => new AIError({ reason: new ContentPolicyError({ message, body, http }) }),
|
|
29
58
|
};
|
|
30
59
|
});
|
|
31
60
|
/** Read and Schema-decode a JSON body. Decode failures keep the raw body as `reason.body`. */
|
|
@@ -37,4 +66,31 @@ export const decodeJson = (route, name, schema) => {
|
|
|
37
66
|
return { ...output, value };
|
|
38
67
|
});
|
|
39
68
|
};
|
|
69
|
+
/** Decode a submission response into the token and first snapshot. */
|
|
70
|
+
export const decodeStarted = (route, name, schema, started) => {
|
|
71
|
+
const decode = decodeJson(route, name, schema);
|
|
72
|
+
return (response) => decode(response).pipe(Effect.map((output) => started(output.value)));
|
|
73
|
+
};
|
|
74
|
+
/** Map a provider status string through the protocol's table; unknown values are an invalid provider document. */
|
|
75
|
+
export const status = (table, raw, output) => {
|
|
76
|
+
const normalized = table[raw];
|
|
77
|
+
if (normalized === undefined)
|
|
78
|
+
return Effect.fail(output.invalid(`Unknown generation status "${raw}"`));
|
|
79
|
+
return Effect.succeed(normalized);
|
|
80
|
+
};
|
|
81
|
+
export const frameError = (route, message, body, cause) => new AIError({ reason: new InvalidProviderOutputError({ route, message, body, cause }) });
|
|
82
|
+
export const incomplete = (route) => new AIError({
|
|
83
|
+
reason: new InvalidProviderOutputError({
|
|
84
|
+
route,
|
|
85
|
+
message: "The provider response ended unexpectedly.",
|
|
86
|
+
classification: "incomplete-stream",
|
|
87
|
+
}),
|
|
88
|
+
});
|
|
89
|
+
/** Schema-decode one JSON stream frame. Decode failures keep the frame as `reason.body`. */
|
|
90
|
+
export const decodeFrame = (route, name, schema) => {
|
|
91
|
+
const decode = Schema.decodeUnknownEffect(Schema.fromJsonString(schema));
|
|
92
|
+
return (frame) => decode(frame).pipe(Effect.mapError((cause) => frameError(route, `${name} sent an invalid stream event`, frame, cause)));
|
|
93
|
+
};
|
|
94
|
+
/** A `url` asset whose provider-declared retention window starts now. */
|
|
95
|
+
export const expiringUrl = (url, retention, options) => Clock.currentTimeMillis.pipe(Effect.map((now) => Media.url(url, { ...options, expiresAt: now + Duration.toMillis(retention) })));
|
|
40
96
|
export * as MediaProtocol from "./media-protocol.js";
|
package/dist/route/media.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
1
|
+
import { Effect, Stream } from "effect";
|
|
2
2
|
import { Auth } from "./auth.js";
|
|
3
3
|
import { Endpoint } from "./endpoint.js";
|
|
4
|
-
import type
|
|
4
|
+
import { type Interface } from "./executor-service.js";
|
|
5
5
|
import { MediaProtocol } from "./media-protocol.js";
|
|
6
|
+
import { Generation, type AwaitOptions, type Observation } from "../generation.js";
|
|
6
7
|
import { AIError, HttpOptions, ProviderID } from "../schema/index.js";
|
|
7
8
|
export type Execute = Interface["execute"];
|
|
8
9
|
/** The minimum a media request must carry for the route to build a transport request. */
|
|
@@ -22,25 +23,75 @@ export interface ModelInput {
|
|
|
22
23
|
readonly headers?: Record<string, string>;
|
|
23
24
|
readonly http?: HttpOptions;
|
|
24
25
|
}
|
|
26
|
+
/** One request, one response. */
|
|
25
27
|
export interface Route<Request extends MediaRequest, Response> {
|
|
28
|
+
readonly kind: "inline";
|
|
26
29
|
readonly id: string;
|
|
27
30
|
readonly provider: ProviderID;
|
|
28
31
|
readonly protocol: string;
|
|
29
32
|
readonly generate: (request: Request, execute: Execute) => Effect.Effect<Response, AIError>;
|
|
30
33
|
}
|
|
31
|
-
|
|
34
|
+
/** Submit, then poll through the returned `Generation`. */
|
|
35
|
+
export interface QueuedRoute<Request extends MediaRequest, Response> {
|
|
36
|
+
readonly kind: "queued";
|
|
37
|
+
readonly id: string;
|
|
38
|
+
readonly provider: ProviderID;
|
|
39
|
+
readonly protocol: string;
|
|
40
|
+
readonly start: (request: Request, execute: Execute) => Effect.Effect<Generation<Response>, AIError>;
|
|
41
|
+
/** Rebuild a handle from a persisted `Generation.token`; fails typed when the token is not this route's. */
|
|
42
|
+
readonly resume: (model: MediaRequest["model"], token: unknown, execute: Execute) => Effect.Effect<Generation<Response>, AIError>;
|
|
43
|
+
}
|
|
44
|
+
/** One request whose response parses into events; `generate` runs the same stream and collects it. */
|
|
45
|
+
export interface StreamRoute<Request extends MediaRequest, Event, Response> {
|
|
46
|
+
readonly kind: "stream";
|
|
47
|
+
readonly id: string;
|
|
48
|
+
readonly provider: ProviderID;
|
|
49
|
+
readonly protocol: string;
|
|
50
|
+
readonly stream: (request: Request, execute: Execute) => Stream.Stream<Event, AIError>;
|
|
51
|
+
readonly generate: (request: Request, execute: Execute) => Effect.Effect<Response, AIError>;
|
|
52
|
+
}
|
|
53
|
+
export type AnyRoute<Request extends MediaRequest, Event, Response> = Route<Request, Response> | StreamRoute<Request, Event, Response> | QueuedRoute<Request, Response>;
|
|
54
|
+
export interface Composition<Request extends MediaRequest> {
|
|
32
55
|
readonly id: string;
|
|
33
56
|
readonly provider: string | ProviderID;
|
|
34
|
-
readonly protocol: MediaProtocol.Inline<Request, Response>;
|
|
35
57
|
readonly endpoint: Endpoint.Definition<MediaProtocol.Body, Request>;
|
|
36
58
|
readonly auth: Auth.Definition;
|
|
37
59
|
/** Deployment headers applied before transport authentication. */
|
|
38
60
|
readonly headers?: Record<string, string>;
|
|
39
61
|
}
|
|
62
|
+
export interface InlineInput<Request extends MediaRequest, Response> extends Composition<Request> {
|
|
63
|
+
readonly protocol: MediaProtocol.Inline<Request, Response>;
|
|
64
|
+
}
|
|
65
|
+
export interface QueuedInput<Request extends MediaRequest, Response, Token> extends Composition<Request> {
|
|
66
|
+
readonly protocol: MediaProtocol.Queued<Request, Response, Token>;
|
|
67
|
+
}
|
|
68
|
+
export interface StreamInput<Request extends MediaRequest, Event, Response, Frame, State> extends Composition<MediaProtocol.Addressed<Request>> {
|
|
69
|
+
readonly protocol: MediaProtocol.Streamed<Request, Event, Frame, State>;
|
|
70
|
+
readonly collect: (events: ReadonlyArray<Event>) => Effect.Effect<Response, AIError>;
|
|
71
|
+
}
|
|
40
72
|
/**
|
|
41
73
|
* Compose an inline media protocol with an endpoint and auth into a runnable route. The route owns the transport
|
|
42
|
-
* plumbing every media protocol would otherwise duplicate: option merging, surrogate sanitizing, unsupported-field
|
|
43
|
-
* rendering, auth headers, JSON
|
|
74
|
+
* plumbing every media protocol would otherwise duplicate: option merging, surrogate sanitizing, unsupported-field
|
|
75
|
+
* rejection, URL and query rendering, auth headers, JSON, multipart, or binary encoding, and handing responses back
|
|
76
|
+
* to the protocol.
|
|
77
|
+
*/
|
|
78
|
+
export declare const inline: <Request extends MediaRequest, Response>(input: InlineInput<Request, Response>) => Route<Request, Response>;
|
|
79
|
+
/**
|
|
80
|
+
* Compose a queued media protocol the same way, adding `start`/`resume` handles whose polls reuse the route's auth,
|
|
81
|
+
* deployment headers, and (for `start`) the request's `http` overlay. The token is decoded once at the boundary and
|
|
82
|
+
* closed over by the resulting `Generation.Route`.
|
|
44
83
|
*/
|
|
45
|
-
export declare const
|
|
84
|
+
export declare const queued: <Request extends MediaRequest, Response, Token>(input: QueuedInput<Request, Response, Token>) => QueuedRoute<Request, Response>;
|
|
85
|
+
/** Compose a streaming media protocol; `generate` runs the same stream in `generate` mode and folds it with `collect`. */
|
|
86
|
+
export declare const stream: <Request extends MediaRequest, Event, Response, Frame, State>(input: StreamInput<Request, Event, Response, Frame, State>) => StreamRoute<Request, Event, Response>;
|
|
87
|
+
export declare const dispatch: <Event, Response>(input: {
|
|
88
|
+
readonly modality: string;
|
|
89
|
+
readonly execute: Execute;
|
|
90
|
+
readonly responseEvents: (response: Response) => ReadonlyArray<Event>;
|
|
91
|
+
}) => {
|
|
92
|
+
start: <Request extends MediaRequest>(route: AnyRoute<Request, Event, Response>, request: Request) => Effect.Effect<Generation<Response>, AIError, never>;
|
|
93
|
+
resume: <Request extends MediaRequest>(route: AnyRoute<Request, Event, Response>, model: MediaRequest["model"], token: unknown) => Effect.Effect<Generation<Response>, AIError, never>;
|
|
94
|
+
generate: <Request extends MediaRequest>(route: AnyRoute<Request, Event, Response>, request: Request, options?: AwaitOptions) => Effect.Effect<Response, AIError, never>;
|
|
95
|
+
stream: <Request extends MediaRequest>(route: AnyRoute<Request, Event, Response>, request: Request, options?: AwaitOptions) => Stream.Stream<Event | Observation, AIError>;
|
|
96
|
+
};
|
|
46
97
|
export * as MediaRoute from "./media.js";
|