@opencode/ai 2.0.16 → 2.0.17
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 +187 -129
- package/dist/ai-client.d.ts +8 -0
- package/dist/ai-client.js +12 -0
- package/dist/experimental/evaluation-client.d.ts +3 -3
- package/dist/experimental/evaluation-client.js +1 -1
- package/dist/experimental/evaluation.js +1 -1
- package/dist/generation.d.ts +6 -10
- package/dist/generation.js +17 -20
- package/dist/image-client.d.ts +59 -24
- package/dist/image-client.js +16 -40
- package/dist/image.d.ts +15 -27
- package/dist/image.js +4 -15
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/llm.d.ts +7 -5
- package/dist/llm.js +10 -4
- package/dist/media-client.d.ts +30 -0
- package/dist/media-client.js +51 -0
- package/dist/media-model.d.ts +9 -10
- package/dist/media-model.js +10 -12
- package/dist/media.js +2 -2
- package/dist/promise.d.ts +68 -30
- package/dist/promise.js +52 -31
- package/dist/protocols/alibaba-chat.js +4 -1
- package/dist/protocols/alibaba-messages.d.ts +1 -1
- package/dist/protocols/alibaba-messages.js +6 -4
- package/dist/protocols/anthropic-messages.d.ts +34 -34
- package/dist/protocols/anthropic-messages.js +14 -8
- package/dist/protocols/assemblyai-transcription.d.ts +2 -1
- package/dist/protocols/assemblyai-transcription.js +14 -16
- package/dist/protocols/bedrock-converse.d.ts +7 -0
- package/dist/protocols/bedrock-converse.js +37 -11
- package/dist/protocols/bfl-images.d.ts +7 -1
- package/dist/protocols/bfl-images.js +36 -36
- package/dist/protocols/cartesia-speech.d.ts +18 -2
- package/dist/protocols/cartesia-speech.js +10 -16
- package/dist/protocols/deepgram-speech.d.ts +19 -3
- package/dist/protocols/deepgram-speech.js +11 -12
- package/dist/protocols/deepgram-transcription.d.ts +2 -1
- package/dist/protocols/deepgram-transcription.js +9 -13
- package/dist/protocols/elevenlabs-speech.d.ts +19 -3
- package/dist/protocols/elevenlabs-speech.js +9 -13
- package/dist/protocols/fal-images.d.ts +2 -1
- package/dist/protocols/fal-images.js +30 -40
- package/dist/protocols/fal-video.d.ts +2 -2
- package/dist/protocols/fal-video.js +9 -24
- package/dist/protocols/gemini.d.ts +13 -13
- package/dist/protocols/gemini.js +16 -7
- package/dist/protocols/google-images.d.ts +3 -3
- package/dist/protocols/google-images.js +11 -21
- package/dist/protocols/google-speech.d.ts +16 -0
- package/dist/protocols/google-speech.js +20 -16
- package/dist/protocols/google-transcription.d.ts +2 -1
- package/dist/protocols/google-transcription.js +8 -20
- package/dist/protocols/google-video.d.ts +2 -2
- package/dist/protocols/google-video.js +14 -30
- package/dist/protocols/meta-images.d.ts +3 -4
- package/dist/protocols/meta-images.js +12 -21
- package/dist/protocols/meta-messages.d.ts +5 -5
- package/dist/protocols/meta-responses.js +6 -4
- package/dist/protocols/open-responses.d.ts +4 -3
- package/dist/protocols/open-responses.js +4 -8
- package/dist/protocols/openai-chat.js +3 -4
- package/dist/protocols/openai-images.d.ts +7 -5
- package/dist/protocols/openai-images.js +65 -66
- package/dist/protocols/openai-responses.d.ts +10 -10
- package/dist/protocols/openai-responses.js +31 -30
- package/dist/protocols/openai-speech.d.ts +21 -1
- package/dist/protocols/openai-speech.js +11 -12
- package/dist/protocols/openai-transcription.d.ts +4 -0
- package/dist/protocols/openai-transcription.js +44 -32
- package/dist/protocols/replicate-images.js +11 -17
- package/dist/protocols/runway-video.d.ts +3 -3
- package/dist/protocols/runway-video.js +11 -17
- package/dist/protocols/shared.d.ts +11 -17
- package/dist/protocols/shared.js +11 -40
- package/dist/protocols/stability-images.d.ts +2 -1
- package/dist/protocols/stability-images.js +24 -36
- package/dist/protocols/utils/fal-queue.d.ts +1 -3
- package/dist/protocols/utils/fal-queue.js +4 -6
- package/dist/protocols/utils/media-input.d.ts +15 -1
- package/dist/protocols/utils/media-input.js +22 -0
- package/dist/protocols/utils/responses-checkpoint.js +3 -7
- package/dist/protocols/utils/responses-compaction.d.ts +3 -1
- package/dist/protocols/utils/responses-compaction.js +16 -3
- package/dist/protocols/utils/speech-stream.d.ts +3 -3
- package/dist/protocols/utils/speech-stream.js +1 -4
- package/dist/protocols/utils/tool-schema.d.ts +2 -2
- package/dist/protocols/utils/tool-schema.js +29 -9
- package/dist/protocols/xai-images.d.ts +4 -4
- package/dist/protocols/xai-images.js +14 -29
- package/dist/protocols/xai-responses.js +1 -1
- package/dist/protocols/xai-video.js +12 -18
- package/dist/protocols/zai-images.d.ts +2 -2
- package/dist/protocols/zai-images.js +11 -14
- package/dist/protocols/zai-messages.d.ts +1 -1
- package/dist/provider-error.js +7 -1
- package/dist/providers/alibaba.d.ts +1 -1
- package/dist/providers/amazon-bedrock.d.ts +2 -0
- package/dist/providers/amazon-bedrock.js +1 -0
- package/dist/providers/anthropic-compatible.d.ts +5 -5
- package/dist/providers/anthropic.d.ts +5 -5
- package/dist/providers/assemblyai.d.ts +1 -1
- package/dist/providers/assemblyai.js +5 -10
- package/dist/providers/azure.d.ts +4 -4
- package/dist/providers/azure.js +2 -2
- package/dist/providers/black-forest-labs.d.ts +1 -1
- package/dist/providers/black-forest-labs.js +5 -10
- package/dist/providers/cartesia.d.ts +1 -1
- package/dist/providers/cartesia.js +5 -10
- package/dist/providers/cloudflare-ai-gateway.d.ts +14 -14
- package/dist/providers/deepgram.d.ts +1 -1
- package/dist/providers/deepgram.js +6 -12
- package/dist/providers/elevenlabs.d.ts +1 -1
- package/dist/providers/elevenlabs.js +5 -10
- package/dist/providers/fal.d.ts +1 -1
- package/dist/providers/fal.js +5 -12
- package/dist/providers/google-vertex-messages.d.ts +5 -5
- package/dist/providers/google-vertex.d.ts +3 -3
- package/dist/providers/google.d.ts +3 -3
- package/dist/providers/google.js +7 -12
- package/dist/providers/meta.d.ts +8 -8
- package/dist/providers/meta.js +4 -8
- package/dist/providers/minimax.d.ts +5 -5
- package/dist/providers/moonshot.d.ts +5 -5
- package/dist/providers/openai-options.d.ts +3 -9
- package/dist/providers/openai-options.js +4 -7
- package/dist/providers/openai.d.ts +9 -10
- package/dist/providers/openai.js +11 -12
- package/dist/providers/opencode-zen.js +1 -1
- package/dist/providers/openrouter.d.ts +6 -7
- package/dist/providers/openrouter.js +10 -5
- package/dist/providers/replicate.d.ts +1 -1
- package/dist/providers/replicate.js +5 -10
- package/dist/providers/runway.d.ts +1 -1
- package/dist/providers/runway.js +5 -10
- package/dist/providers/stability.d.ts +1 -1
- package/dist/providers/stability.js +6 -11
- package/dist/providers/typesafe-ai.js +1 -1
- package/dist/providers/vercel-ai-gateway.js +1 -1
- package/dist/providers/xai.js +5 -10
- package/dist/providers/zai-coding-plan.d.ts +1 -1
- package/dist/providers/zai.js +4 -8
- package/dist/route/auth.d.ts +1 -1
- package/dist/route/auth.js +14 -13
- package/dist/route/client.d.ts +9 -7
- package/dist/route/client.js +6 -8
- package/dist/route/endpoint.d.ts +1 -0
- package/dist/route/endpoint.js +2 -2
- package/dist/route/executor-service.d.ts +4 -2
- package/dist/route/executor-service.js +2 -1
- package/dist/route/executor.d.ts +3 -1
- package/dist/route/executor.js +7 -0
- package/dist/route/framing.d.ts +10 -1
- package/dist/route/framing.js +45 -5
- package/dist/route/index.d.ts +1 -1
- package/dist/route/media-protocol.d.ts +49 -35
- package/dist/route/media-protocol.js +77 -65
- package/dist/route/media.d.ts +10 -16
- package/dist/route/media.js +46 -62
- package/dist/route/protocol.d.ts +3 -1
- package/dist/schema/options.d.ts +6 -3
- package/dist/schema/options.js +7 -3
- package/dist/speech-client.d.ts +62 -17
- package/dist/speech-client.js +17 -21
- package/dist/speech.d.ts +178 -21
- package/dist/speech.js +13 -8
- package/dist/testing.d.ts +2 -2
- package/dist/transcription-client.d.ts +78 -24
- package/dist/transcription-client.js +9 -40
- package/dist/transcription.d.ts +15 -27
- package/dist/transcription.js +4 -10
- package/dist/utils/json.d.ts +4 -0
- package/dist/utils/json.js +4 -0
- package/dist/utils/media-type.d.ts +2 -1
- package/dist/utils/media-type.js +3 -1
- package/dist/video-client.d.ts +55 -24
- package/dist/video-client.js +16 -36
- package/dist/video.d.ts +18 -26
- package/dist/video.js +15 -16
- package/package.json +3 -3
- package/dist/protocols/utils/meta-image.d.ts +0 -2
- package/dist/protocols/utils/meta-image.js +0 -13
- package/dist/protocols/utils/openai-image.d.ts +0 -5
- package/dist/protocols/utils/openai-image.js +0 -18
|
@@ -3,7 +3,7 @@ import { HttpClientResponse } from "effect/unstable/http";
|
|
|
3
3
|
import type { Snapshot, Status } from "../generation.js";
|
|
4
4
|
import { Media } from "../media.js";
|
|
5
5
|
import type { AuthInput } from "./auth.js";
|
|
6
|
-
import { AIError, HttpContext } from "../schema/index.js";
|
|
6
|
+
import { AIError, HttpContext, ProviderID } from "../schema/index.js";
|
|
7
7
|
/** Array values become repeated parameters (`keyterm=a&keyterm=b`). */
|
|
8
8
|
export type Query = Readonly<Record<string, string | ReadonlyArray<string>>>;
|
|
9
9
|
/** `query` is appended to the endpoint URL before the route and caller `http.query` overlays. */
|
|
@@ -34,7 +34,7 @@ export interface DecodeContext<Request> {
|
|
|
34
34
|
export interface Inline<Request, Response> {
|
|
35
35
|
readonly kind: "inline";
|
|
36
36
|
readonly id: string;
|
|
37
|
-
readonly
|
|
37
|
+
readonly provider: ProviderID;
|
|
38
38
|
/** Common request fields this protocol cannot lower; the route rejects them before `body.from` runs. */
|
|
39
39
|
readonly unsupported?: ReadonlyArray<keyof Request & string>;
|
|
40
40
|
readonly body: {
|
|
@@ -44,7 +44,7 @@ export interface Inline<Request, Response> {
|
|
|
44
44
|
readonly decode: (response: HttpClientResponse.HttpClientResponse, context: DecodeContext<Request>) => Effect.Effect<Response, AIError>;
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
|
-
export declare const inline: <Request, Response>(input: Omit<Inline<Request, Response>, "kind">) => Inline<Request, Response>;
|
|
47
|
+
export declare const inline: <Request, Response>(route: Identity, input: Omit<Inline<Request, Response>, "kind" | "id" | "provider">) => Inline<Request, Response>;
|
|
48
48
|
/** What `start` learned from the submission response: the route-owned handle plus the first observation. */
|
|
49
49
|
export interface Started<Token> {
|
|
50
50
|
readonly token: Token;
|
|
@@ -70,7 +70,7 @@ export interface PollContext<Token> {
|
|
|
70
70
|
export interface Queued<Request, Response, Token> {
|
|
71
71
|
readonly kind: "queued";
|
|
72
72
|
readonly id: string;
|
|
73
|
-
readonly
|
|
73
|
+
readonly provider: ProviderID;
|
|
74
74
|
/** Common request fields this protocol cannot lower; the route rejects them before `start.body.from` runs. */
|
|
75
75
|
readonly unsupported?: ReadonlyArray<keyof Request & string>;
|
|
76
76
|
/** Serializable handle. `Generation.token` carries the encoded form so it can be persisted and resumed elsewhere. */
|
|
@@ -93,9 +93,14 @@ export interface Queued<Request, Response, Token> {
|
|
|
93
93
|
readonly cancel?: {
|
|
94
94
|
readonly method: AuthInput["method"];
|
|
95
95
|
readonly path: (token: Token) => string;
|
|
96
|
+
/**
|
|
97
|
+
* Fetch a fresh status first and skip the call for terminal generations, for providers whose cancel endpoint
|
|
98
|
+
* destroys finished work (Runway's `DELETE /v1/tasks/{id}` deletes completed tasks and their outputs).
|
|
99
|
+
*/
|
|
100
|
+
readonly activeOnly?: boolean;
|
|
96
101
|
};
|
|
97
102
|
}
|
|
98
|
-
export declare const queued: <Request, Response, Token>(input: Omit<Queued<Request, Response, Token>, "kind">) => Queued<Request, Response, Token>;
|
|
103
|
+
export declare const queued: <Request, Response, Token>(route: Identity, input: Omit<Queued<Request, Response, Token>, "kind" | "id" | "provider">) => Queued<Request, Response, Token>;
|
|
99
104
|
export type Mode = "generate" | "stream";
|
|
100
105
|
export type Addressed<Request> = Request & {
|
|
101
106
|
readonly mode: Mode;
|
|
@@ -110,7 +115,7 @@ export interface ResponseContext<Request> extends DecodeContext<Addressed<Reques
|
|
|
110
115
|
export interface Streamed<Request, Event, Frame, State> {
|
|
111
116
|
readonly kind: "stream";
|
|
112
117
|
readonly id: string;
|
|
113
|
-
readonly
|
|
118
|
+
readonly provider: ProviderID;
|
|
114
119
|
/** Common request fields this protocol cannot lower; the route rejects them before `body.from` runs. */
|
|
115
120
|
readonly unsupported?: ReadonlyArray<keyof Request & string>;
|
|
116
121
|
readonly body: {
|
|
@@ -122,37 +127,46 @@ export interface Streamed<Request, Event, Frame, State> {
|
|
|
122
127
|
/** Emit exactly one terminal event, or fail when the provider stopped before completing. */
|
|
123
128
|
readonly finish: (state: State, context: ResponseContext<Request>) => Effect.Effect<ReadonlyArray<Event>, AIError>;
|
|
124
129
|
}
|
|
125
|
-
export declare const stream: <Request, Event, Frame, State>(input: Omit<Streamed<Request, Event, Frame, State>, "kind">) => Streamed<Request, Event, Frame, State>;
|
|
126
|
-
/**
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
130
|
+
export declare const stream: <Request, Event, Frame, State>(route: Identity, input: Omit<Streamed<Request, Event, Frame, State>, "kind" | "id" | "provider">) => Streamed<Request, Event, Frame, State>;
|
|
131
|
+
/** One protocol's route id, display name, and provider, with the decoders and errors that carry them. */
|
|
132
|
+
export declare const identity: (input: {
|
|
133
|
+
readonly id: string;
|
|
134
|
+
readonly name: string;
|
|
135
|
+
readonly provider: string;
|
|
136
|
+
}) => {
|
|
137
|
+
id: string;
|
|
138
|
+
name: string;
|
|
139
|
+
provider: string & import("effect/Brand").Brand<"AI.ProviderID">;
|
|
140
|
+
text: (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<{
|
|
141
|
+
body: string;
|
|
142
|
+
http: HttpContext;
|
|
143
|
+
invalid: (message: string, cause?: unknown) => AIError;
|
|
144
|
+
ended: (status: Exclude<Status, "queued" | "running" | "completed">, message: string) => AIError;
|
|
145
|
+
pending: (id: string) => AIError;
|
|
146
|
+
contentPolicy: (message: string) => AIError;
|
|
147
|
+
}, AIError, never>;
|
|
148
|
+
decodeJson: <A>(schema: Schema.Codec<A, unknown>) => (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<{
|
|
149
|
+
value: A;
|
|
150
|
+
body: string;
|
|
151
|
+
http: HttpContext;
|
|
152
|
+
invalid: (message: string, cause?: unknown) => AIError;
|
|
153
|
+
ended: (status: Exclude<Status, "queued" | "running" | "completed">, message: string) => AIError;
|
|
154
|
+
pending: (id: string) => AIError;
|
|
155
|
+
contentPolicy: (message: string) => AIError;
|
|
156
|
+
}, AIError, never>;
|
|
157
|
+
/** Decode a submission response into the token and first snapshot. */
|
|
158
|
+
decodeStarted: <A, Token>(schema: Schema.Codec<A, unknown>, started: (value: A) => Started<Token>) => (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<Started<Token>, AIError, never>;
|
|
159
|
+
/** Schema-decode one JSON stream frame. Decode failures keep the frame as `reason.body`. */
|
|
160
|
+
decodeFrame: <A>(schema: Schema.Codec<A, unknown>) => (frame: string) => Effect.Effect<A, AIError, never>;
|
|
161
|
+
/** A stream-time failure; the frame stays on `reason.body`. */
|
|
162
|
+
frameError: (message: string, body?: string, cause?: unknown) => AIError;
|
|
163
|
+
incomplete: () => AIError;
|
|
164
|
+
unsupported: (operation: string, message: string) => AIError;
|
|
165
|
+
};
|
|
166
|
+
export type Identity = ReturnType<typeof identity>;
|
|
167
|
+
export type Output = Effect.Success<ReturnType<Identity["text"]>>;
|
|
150
168
|
/** Map a provider status string through the protocol's table; unknown values are an invalid provider document. */
|
|
151
169
|
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
170
|
/** A `url` asset whose provider-declared retention window starts now. */
|
|
157
171
|
export declare const expiringUrl: (url: string, retention: Duration.Duration, options?: Parameters<typeof Media.url>[1]) => Effect.Effect<Media.Asset, never, never>;
|
|
158
172
|
export * as MediaProtocol from "./media-protocol.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Clock, Duration, Effect, Schema } from "effect";
|
|
2
2
|
import { HttpClientResponse } from "effect/unstable/http";
|
|
3
3
|
import { Media } from "../media.js";
|
|
4
|
-
import { AIError, ContentPolicyError, HttpContext, InvalidProviderOutputError, InvalidRequestError, ProviderInternalError, } from "../schema/index.js";
|
|
4
|
+
import { AIError, ContentPolicyError, HttpContext, InvalidProviderOutputError, InvalidRequestError, ProviderID, ProviderInternalError, UnsupportedOperationError, } from "../schema/index.js";
|
|
5
5
|
export const json = (value, query) => ({
|
|
6
6
|
type: "json",
|
|
7
7
|
value,
|
|
@@ -14,82 +14,94 @@ export const binary = (value, contentType, query) => ({
|
|
|
14
14
|
contentType,
|
|
15
15
|
query,
|
|
16
16
|
});
|
|
17
|
-
export const inline = (input) => ({
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
});
|
|
21
|
-
export const queued = (input) => ({
|
|
22
|
-
kind: "queued",
|
|
23
|
-
...input,
|
|
24
|
-
});
|
|
25
|
-
export const stream = (input) => ({
|
|
26
|
-
kind: "stream",
|
|
27
|
-
...input,
|
|
28
|
-
});
|
|
17
|
+
export const inline = (route, input) => ({ kind: "inline", id: route.id, provider: route.provider, ...input });
|
|
18
|
+
export const queued = (route, input) => ({ kind: "queued", id: route.id, provider: route.provider, ...input });
|
|
19
|
+
export const stream = (route, input) => ({ kind: "stream", id: route.id, provider: route.provider, ...input });
|
|
29
20
|
// ---------------------------------------------------------------------------
|
|
30
21
|
// Response helpers
|
|
31
22
|
// ---------------------------------------------------------------------------
|
|
32
23
|
const context = (response) => new HttpContext({ url: response.request.url, status: response.status, headers: response.headers });
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
24
|
+
/** One protocol's route id, display name, and provider, with the decoders and errors that carry them. */
|
|
25
|
+
export const identity = (input) => {
|
|
26
|
+
const provider = ProviderID.make(input.provider);
|
|
27
|
+
const frameError = (message, body, cause) => new AIError({ reason: new InvalidProviderOutputError({ route: input.id, message, body, cause }) });
|
|
28
|
+
/**
|
|
29
|
+
* Read a text body while retaining the original payload and HTTP context on every downstream error. `invalid` is a
|
|
30
|
+
* malformed provider document; `ended` is a generation that reached a terminal status without output (`failed` is
|
|
31
|
+
* provider-side, `cancelled`/`expired` mean the result will never exist); `pending` is a `result()` read before the
|
|
32
|
+
* generation finished, which is caller misuse; `contentPolicy` is a moderated result.
|
|
33
|
+
*/
|
|
34
|
+
const text = Effect.fn("MediaProtocol.text")(function* (response) {
|
|
35
|
+
const http = context(response);
|
|
36
|
+
const body = yield* response.text.pipe(Effect.mapError((cause) => new AIError({
|
|
37
|
+
reason: new InvalidProviderOutputError({
|
|
38
|
+
route: input.id,
|
|
39
|
+
message: `Failed to read the ${input.name} response`,
|
|
40
|
+
http,
|
|
41
|
+
cause,
|
|
42
|
+
}),
|
|
43
|
+
})));
|
|
44
|
+
return {
|
|
45
|
+
body,
|
|
44
46
|
http,
|
|
45
|
-
cause,
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
invalid: (message, cause) => new AIError({ reason: new InvalidProviderOutputError({ route: input.id, message, body, http, cause }) }),
|
|
48
|
+
ended: (status, message) => new AIError({
|
|
49
|
+
reason: status === "failed"
|
|
50
|
+
? new ProviderInternalError({ message, body, http })
|
|
51
|
+
: new InvalidRequestError({ message, body, http }),
|
|
52
|
+
}),
|
|
53
|
+
pending: (id) => new AIError({
|
|
54
|
+
reason: new InvalidRequestError({
|
|
55
|
+
message: `${input.name} generation ${id} has not finished; await it before reading the result`,
|
|
56
|
+
body,
|
|
57
|
+
http,
|
|
58
|
+
}),
|
|
59
|
+
}),
|
|
60
|
+
contentPolicy: (message) => new AIError({ reason: new ContentPolicyError({ message, body, http }) }),
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
/** Read and Schema-decode a JSON body. Decode failures keep the raw body as `reason.body`. */
|
|
64
|
+
const decodeJson = (schema) => {
|
|
65
|
+
const decode = Schema.decodeUnknownEffect(Schema.fromJsonString(schema));
|
|
66
|
+
return Effect.fn("MediaProtocol.decodeJson")(function* (response) {
|
|
67
|
+
const output = yield* text(response);
|
|
68
|
+
const value = yield* decode(output.body).pipe(Effect.mapError((cause) => output.invalid(`${input.name} returned an invalid response`, cause)));
|
|
69
|
+
return { ...output, value };
|
|
70
|
+
});
|
|
71
|
+
};
|
|
48
72
|
return {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
73
|
+
id: input.id,
|
|
74
|
+
name: input.name,
|
|
75
|
+
provider,
|
|
76
|
+
text,
|
|
77
|
+
decodeJson,
|
|
78
|
+
/** Decode a submission response into the token and first snapshot. */
|
|
79
|
+
decodeStarted: (schema, started) => {
|
|
80
|
+
const decode = decodeJson(schema);
|
|
81
|
+
return (response) => decode(response).pipe(Effect.map((output) => started(output.value)));
|
|
82
|
+
},
|
|
83
|
+
/** Schema-decode one JSON stream frame. Decode failures keep the frame as `reason.body`. */
|
|
84
|
+
decodeFrame: (schema) => {
|
|
85
|
+
const decode = Schema.decodeUnknownEffect(Schema.fromJsonString(schema));
|
|
86
|
+
return (frame) => decode(frame).pipe(Effect.mapError((cause) => frameError(`${input.name} sent an invalid stream event`, frame, cause)));
|
|
87
|
+
},
|
|
88
|
+
/** A stream-time failure; the frame stays on `reason.body`. */
|
|
89
|
+
frameError,
|
|
90
|
+
incomplete: () => new AIError({
|
|
91
|
+
reason: new InvalidProviderOutputError({
|
|
92
|
+
route: input.id,
|
|
93
|
+
message: "The provider response ended unexpectedly.",
|
|
94
|
+
classification: "incomplete-stream",
|
|
95
|
+
}),
|
|
56
96
|
}),
|
|
57
|
-
|
|
97
|
+
unsupported: (operation, message) => new AIError({ reason: new UnsupportedOperationError({ operation, provider, route: input.id, message }) }),
|
|
58
98
|
};
|
|
59
|
-
});
|
|
60
|
-
/** Read and Schema-decode a JSON body. Decode failures keep the raw body as `reason.body`. */
|
|
61
|
-
export const decodeJson = (route, name, schema) => {
|
|
62
|
-
const decode = Schema.decodeUnknownEffect(Schema.fromJsonString(schema));
|
|
63
|
-
return Effect.fn("MediaProtocol.decodeJson")(function* (response) {
|
|
64
|
-
const output = yield* text(route, name, response);
|
|
65
|
-
const value = yield* decode(output.body).pipe(Effect.mapError((cause) => output.invalid(`${name} returned an invalid response`, cause)));
|
|
66
|
-
return { ...output, value };
|
|
67
|
-
});
|
|
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
99
|
};
|
|
74
100
|
/** Map a provider status string through the protocol's table; unknown values are an invalid provider document. */
|
|
75
101
|
export const status = (table, raw, output) => {
|
|
76
|
-
|
|
77
|
-
if (normalized === undefined)
|
|
102
|
+
if (!Object.hasOwn(table, raw))
|
|
78
103
|
return Effect.fail(output.invalid(`Unknown generation status "${raw}"`));
|
|
79
|
-
return Effect.succeed(
|
|
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)));
|
|
104
|
+
return Effect.succeed(table[raw]);
|
|
93
105
|
};
|
|
94
106
|
/** A `url` asset whose provider-declared retention window starts now. */
|
|
95
107
|
export const expiringUrl = (url, retention, options) => Clock.currentTimeMillis.pipe(Effect.map((now) => Media.url(url, { ...options, expiresAt: now + Duration.toMillis(retention) })));
|
package/dist/route/media.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Auth } from "./auth.js";
|
|
|
3
3
|
import { Endpoint } from "./endpoint.js";
|
|
4
4
|
import { type Interface } from "./executor-service.js";
|
|
5
5
|
import { MediaProtocol } from "./media-protocol.js";
|
|
6
|
-
import { Generation
|
|
6
|
+
import { Generation } from "../generation.js";
|
|
7
7
|
import { AIError, HttpOptions, ProviderID } from "../schema/index.js";
|
|
8
8
|
export type Execute = Interface["execute"];
|
|
9
9
|
/** The minimum a media request must carry for the route to build a transport request. */
|
|
@@ -23,8 +23,14 @@ export interface ModelInput {
|
|
|
23
23
|
readonly headers?: Record<string, string>;
|
|
24
24
|
readonly http?: HttpOptions;
|
|
25
25
|
}
|
|
26
|
+
/** A provider facade's `configure(...)` input as the `ModelInput` every media selector shares, minus the model id. */
|
|
27
|
+
export declare const deployment: (input: {
|
|
28
|
+
readonly baseURL?: string;
|
|
29
|
+
readonly headers?: Record<string, string>;
|
|
30
|
+
readonly http?: HttpOptions.Input;
|
|
31
|
+
}, auth: Auth.Definition) => Omit<ModelInput, "id">;
|
|
26
32
|
/** One request, one response. */
|
|
27
|
-
export interface
|
|
33
|
+
export interface InlineRoute<Request extends MediaRequest, Response> {
|
|
28
34
|
readonly kind: "inline";
|
|
29
35
|
readonly id: string;
|
|
30
36
|
readonly provider: ProviderID;
|
|
@@ -50,10 +56,8 @@ export interface StreamRoute<Request extends MediaRequest, Event, Response> {
|
|
|
50
56
|
readonly stream: (request: Request, execute: Execute) => Stream.Stream<Event, AIError>;
|
|
51
57
|
readonly generate: (request: Request, execute: Execute) => Effect.Effect<Response, AIError>;
|
|
52
58
|
}
|
|
53
|
-
export type AnyRoute<Request extends MediaRequest, Event, Response> =
|
|
59
|
+
export type AnyRoute<Request extends MediaRequest, Event, Response> = InlineRoute<Request, Response> | StreamRoute<Request, Event, Response> | QueuedRoute<Request, Response>;
|
|
54
60
|
export interface Composition<Request extends MediaRequest> {
|
|
55
|
-
readonly id: string;
|
|
56
|
-
readonly provider: string | ProviderID;
|
|
57
61
|
readonly endpoint: Endpoint.Definition<MediaProtocol.Body, Request>;
|
|
58
62
|
readonly auth: Auth.Definition;
|
|
59
63
|
/** Deployment headers applied before transport authentication. */
|
|
@@ -75,7 +79,7 @@ export interface StreamInput<Request extends MediaRequest, Event, Response, Fram
|
|
|
75
79
|
* rejection, URL and query rendering, auth headers, JSON, multipart, or binary encoding, and handing responses back
|
|
76
80
|
* to the protocol.
|
|
77
81
|
*/
|
|
78
|
-
export declare const inline: <Request extends MediaRequest, Response>(input: InlineInput<Request, Response>) =>
|
|
82
|
+
export declare const inline: <Request extends MediaRequest, Response>(input: InlineInput<Request, Response>) => InlineRoute<Request, Response>;
|
|
79
83
|
/**
|
|
80
84
|
* Compose a queued media protocol the same way, adding `start`/`resume` handles whose polls reuse the route's auth,
|
|
81
85
|
* deployment headers, and (for `start`) the request's `http` overlay. The token is decoded once at the boundary and
|
|
@@ -84,14 +88,4 @@ export declare const inline: <Request extends MediaRequest, Response>(input: Inl
|
|
|
84
88
|
export declare const queued: <Request extends MediaRequest, Response, Token>(input: QueuedInput<Request, Response, Token>) => QueuedRoute<Request, Response>;
|
|
85
89
|
/** Compose a streaming media protocol; `generate` runs the same stream in `generate` mode and folds it with `collect`. */
|
|
86
90
|
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
|
-
};
|
|
97
91
|
export * as MediaRoute from "./media.js";
|
package/dist/route/media.js
CHANGED
|
@@ -2,13 +2,20 @@ import { Effect, Schema, Stream } from "effect";
|
|
|
2
2
|
import { Headers, HttpClientRequest } from "effect/unstable/http";
|
|
3
3
|
import { Auth } from "./auth.js";
|
|
4
4
|
import { Endpoint } from "./endpoint.js";
|
|
5
|
-
import {
|
|
5
|
+
import { RequestExecutorService } from "./executor-service.js";
|
|
6
6
|
import { RequestExecutor } from "./executor.js";
|
|
7
7
|
import { MediaProtocol } from "./media-protocol.js";
|
|
8
|
-
import { Generation,
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
8
|
+
import { Generation, isTerminal } from "../generation.js";
|
|
9
|
+
import { AIError, AIErrorReason, HttpOptions, InvalidRequestError, ProviderID, UnsupportedOperationError, mergeHttpOptions, } from "../schema/index.js";
|
|
10
|
+
import { encodeJson } from "../utils/json.js";
|
|
11
11
|
import { sanitizeSurrogates } from "../utils/sanitize.js";
|
|
12
|
+
/** A provider facade's `configure(...)` input as the `ModelInput` every media selector shares, minus the model id. */
|
|
13
|
+
export const deployment = (input, auth) => ({
|
|
14
|
+
auth,
|
|
15
|
+
baseURL: input.baseURL,
|
|
16
|
+
headers: input.headers,
|
|
17
|
+
http: HttpOptions.make(input.http),
|
|
18
|
+
});
|
|
12
19
|
/**
|
|
13
20
|
* Compose an inline media protocol with an endpoint and auth into a runnable route. The route owns the transport
|
|
14
21
|
* plumbing every media protocol would otherwise duplicate: option merging, surrogate sanitizing, unsupported-field
|
|
@@ -19,8 +26,8 @@ export const inline = (input) => {
|
|
|
19
26
|
const transport = makeTransport(input);
|
|
20
27
|
return {
|
|
21
28
|
kind: "inline",
|
|
22
|
-
id: input.id,
|
|
23
|
-
provider:
|
|
29
|
+
id: input.protocol.id,
|
|
30
|
+
provider: input.protocol.provider,
|
|
24
31
|
protocol: input.protocol.id,
|
|
25
32
|
generate: Effect.fn(`MediaRoute.generate`)(function* (request, execute) {
|
|
26
33
|
const submitted = yield* transport.submit(request, { unsupported: input.protocol.unsupported, from: input.protocol.body.from }, execute);
|
|
@@ -44,15 +51,18 @@ export const queued = (input) => {
|
|
|
44
51
|
const poll = (operation) => transport
|
|
45
52
|
.call("GET", operation.path(token), http, execute)
|
|
46
53
|
.pipe(Effect.flatMap((sent) => operation.decode(sent.response, { token, auth: sent.auth, materialize })));
|
|
54
|
+
const status = poll(protocol.status);
|
|
47
55
|
const cancel = protocol.cancel;
|
|
48
|
-
const
|
|
49
|
-
|
|
56
|
+
const send = cancel === undefined
|
|
57
|
+
? undefined
|
|
58
|
+
: transport.call(cancel.method, cancel.path(token), http, execute).pipe(Effect.asVoid);
|
|
59
|
+
return {
|
|
60
|
+
status,
|
|
50
61
|
result: poll(protocol.result),
|
|
51
|
-
cancel:
|
|
52
|
-
?
|
|
53
|
-
:
|
|
62
|
+
cancel: send !== undefined && cancel?.activeOnly
|
|
63
|
+
? status.pipe(Effect.flatMap((snapshot) => (isTerminal(snapshot.status) ? Effect.void : send)))
|
|
64
|
+
: send,
|
|
54
65
|
};
|
|
55
|
-
return route;
|
|
56
66
|
};
|
|
57
67
|
const start = Effect.fn("MediaRoute.start")(function* (request, execute) {
|
|
58
68
|
const submitted = yield* transport.submit(request, { unsupported: protocol.unsupported, prepare: protocol.start.prepare, from: protocol.start.body.from }, execute);
|
|
@@ -63,14 +73,14 @@ export const queued = (input) => {
|
|
|
63
73
|
const resume = Effect.fn("MediaRoute.resume")(function* (model, raw, execute) {
|
|
64
74
|
const token = yield* decodeToken(raw).pipe(Effect.mapError((cause) => new AIError({
|
|
65
75
|
reason: new InvalidRequestError({
|
|
66
|
-
message: `${
|
|
76
|
+
message: `${protocol.id} cannot resume a generation from this token`,
|
|
67
77
|
cause,
|
|
68
78
|
}),
|
|
69
79
|
})));
|
|
70
80
|
const route = generationRoute(token, transport.http(model), execute);
|
|
71
81
|
return new Generation(route, encodeToken(token), yield* route.status);
|
|
72
82
|
});
|
|
73
|
-
return { kind: "queued", id:
|
|
83
|
+
return { kind: "queued", id: protocol.id, provider: protocol.provider, protocol: protocol.id, start, resume };
|
|
74
84
|
};
|
|
75
85
|
/** Compose a streaming media protocol; `generate` runs the same stream in `generate` mode and folds it with `collect`. */
|
|
76
86
|
export const stream = (input) => {
|
|
@@ -99,55 +109,21 @@ export const stream = (input) => {
|
|
|
99
109
|
}));
|
|
100
110
|
return {
|
|
101
111
|
kind: "stream",
|
|
102
|
-
id:
|
|
103
|
-
provider:
|
|
112
|
+
id: protocol.id,
|
|
113
|
+
provider: protocol.provider,
|
|
104
114
|
protocol: protocol.id,
|
|
105
115
|
stream: (request, execute) => events(request, execute, "stream"),
|
|
106
116
|
generate: (request, execute) => events(request, execute, "generate").pipe(Stream.runCollect, Effect.flatMap(input.collect)),
|
|
107
117
|
};
|
|
108
118
|
};
|
|
109
|
-
export const dispatch = (input) => {
|
|
110
|
-
const notQueued = (route, operation) => ProviderShared.unsupportedOperation({
|
|
111
|
-
operation: `${input.modality}.${operation}`,
|
|
112
|
-
provider: route.provider,
|
|
113
|
-
route: route.id,
|
|
114
|
-
message: `${route.provider}/${route.id} is not a queued route; use generate or stream`,
|
|
115
|
-
});
|
|
116
|
-
const start = (route, request) => {
|
|
117
|
-
if (route.kind !== "queued")
|
|
118
|
-
return Effect.fail(notQueued(route, "start"));
|
|
119
|
-
return route.start(request, input.execute);
|
|
120
|
-
};
|
|
121
|
-
return {
|
|
122
|
-
start,
|
|
123
|
-
resume: (route, model, token) => {
|
|
124
|
-
if (route.kind !== "queued")
|
|
125
|
-
return Effect.fail(notQueued(route, "resume"));
|
|
126
|
-
return route.resume(model, token, input.execute);
|
|
127
|
-
},
|
|
128
|
-
generate: (route, request, options) => {
|
|
129
|
-
if (route.kind !== "queued")
|
|
130
|
-
return route.generate(request, input.execute);
|
|
131
|
-
return start(route, request).pipe(Effect.flatMap((generation) => generation.await(options)));
|
|
132
|
-
},
|
|
133
|
-
stream: (route, request, options) => {
|
|
134
|
-
if (route.kind === "stream")
|
|
135
|
-
return route.stream(request, input.execute);
|
|
136
|
-
if (route.kind === "queued")
|
|
137
|
-
return Stream.unwrap(start(route, request).pipe(Effect.map((generation) => resultEvents(generation, input.responseEvents, options))));
|
|
138
|
-
return Stream.fromIterableEffect(Effect.map(route.generate(request, input.execute), input.responseEvents));
|
|
139
|
-
},
|
|
140
|
-
};
|
|
141
|
-
};
|
|
142
119
|
// ---------------------------------------------------------------------------
|
|
143
120
|
// Transport plumbing shared by every kind
|
|
144
121
|
// ---------------------------------------------------------------------------
|
|
145
122
|
const makeTransport = (input) => {
|
|
146
|
-
const provider = ProviderID.make(input.provider);
|
|
147
123
|
const routeHttp = input.headers === undefined ? undefined : new HttpOptions({ headers: input.headers });
|
|
148
124
|
const authorize = Auth.toEffect(input.auth);
|
|
149
|
-
const baseURL = (path) => new URL(`${
|
|
150
|
-
/** `auth` is only what `Auth` added, never deployment headers. */
|
|
125
|
+
const baseURL = (path) => new URL(`${Endpoint.trimBaseUrl(input.endpoint.baseURL ?? "")}${path}`);
|
|
126
|
+
/** `auth` is only what `Auth` added or changed, never untouched deployment headers. */
|
|
151
127
|
const send = Effect.fn("MediaRoute.send")(function* (call, execute) {
|
|
152
128
|
const encoded = encode(call.body, call.headers);
|
|
153
129
|
const url = call.url.toString();
|
|
@@ -159,15 +135,17 @@ const makeTransport = (input) => {
|
|
|
159
135
|
headers: encoded.headers,
|
|
160
136
|
});
|
|
161
137
|
const response = yield* execute(encoded.apply(HttpClientRequest.make(call.method)(url).pipe(HttpClientRequest.setHeaders(headers))));
|
|
162
|
-
return {
|
|
138
|
+
return {
|
|
139
|
+
response,
|
|
140
|
+
auth: Object.fromEntries(Object.entries(headers).filter(([key, value]) => encoded.headers[key] !== value)),
|
|
141
|
+
};
|
|
163
142
|
});
|
|
164
143
|
return {
|
|
165
|
-
provider,
|
|
166
144
|
/** Route and model overlays; `start` additionally merges the request's own `http`. */
|
|
167
145
|
http: (model) => mergeHttpOptions(routeHttp, model.http),
|
|
168
146
|
/** POST the protocol body to the route endpoint. */
|
|
169
147
|
submit: Effect.fn("MediaRoute.submit")(function* (request, protocol, execute) {
|
|
170
|
-
yield* rejectUnsupported(input.id, provider, request, protocol.unsupported);
|
|
148
|
+
yield* rejectUnsupported(input.protocol.id, input.protocol.provider, request, protocol.unsupported);
|
|
171
149
|
const http = mergeHttpOptions(routeHttp, request.model.http, request.http);
|
|
172
150
|
const headers = Headers.fromInput(http?.headers);
|
|
173
151
|
const prepared = protocol.prepare === undefined
|
|
@@ -202,7 +180,7 @@ const encode = (body, headers) => {
|
|
|
202
180
|
if (body === undefined)
|
|
203
181
|
return { text: "", headers, apply: (request) => request };
|
|
204
182
|
if (body.type === "json") {
|
|
205
|
-
const text =
|
|
183
|
+
const text = encodeJson(body.value);
|
|
206
184
|
return { text, headers, apply: HttpClientRequest.bodyText(text, "application/json") };
|
|
207
185
|
}
|
|
208
186
|
if (body.type === "binary")
|
|
@@ -218,7 +196,11 @@ const encode = (body, headers) => {
|
|
|
218
196
|
apply: HttpClientRequest.bodyFormData(body.value),
|
|
219
197
|
};
|
|
220
198
|
};
|
|
221
|
-
/**
|
|
199
|
+
/**
|
|
200
|
+
* Common fields are never silently dropped: a present field the protocol declared unsupported fails typed. `false`
|
|
201
|
+
* counts as present because some booleans mean something when false (video `audio`); protocols reject opt-in
|
|
202
|
+
* booleans such as speech `timestamps` with `=== true` in `body.from` instead of listing them.
|
|
203
|
+
*/
|
|
222
204
|
const rejectUnsupported = (route, provider, request, unsupported) => {
|
|
223
205
|
const present = (unsupported ?? []).filter((field) => {
|
|
224
206
|
const value = request[field];
|
|
@@ -226,11 +208,13 @@ const rejectUnsupported = (route, provider, request, unsupported) => {
|
|
|
226
208
|
});
|
|
227
209
|
if (present.length === 0)
|
|
228
210
|
return Effect.void;
|
|
229
|
-
return Effect.fail(
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
211
|
+
return Effect.fail(new AIError({
|
|
212
|
+
reason: new UnsupportedOperationError({
|
|
213
|
+
operation: `media.${present[0]}`,
|
|
214
|
+
provider,
|
|
215
|
+
route,
|
|
216
|
+
message: `${provider}/${route} does not support ${present.join(", ")}`,
|
|
217
|
+
}),
|
|
234
218
|
}));
|
|
235
219
|
};
|
|
236
220
|
export * as MediaRoute from "./media.js";
|
package/dist/route/protocol.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Schema, type Effect } from "effect";
|
|
2
|
-
import type { AIError, LLMEvent, LLMRequest, ProtocolID } from "../schema/index.js";
|
|
2
|
+
import type { AIError, LanguageModelSanitizerCompatibility, LLMEvent, LLMRequest, ProtocolID } from "../schema/index.js";
|
|
3
3
|
/**
|
|
4
4
|
* The semantic API contract of one model server family.
|
|
5
5
|
*
|
|
@@ -42,6 +42,8 @@ export interface Protocol<Body, Frame, Event, State> {
|
|
|
42
42
|
readonly stream: ProtocolStream<Frame, Event, State>;
|
|
43
43
|
/** Whether `body.from` lowers `Message.effort(...)` markers; wrappers around another `body.from` must forward it. */
|
|
44
44
|
readonly supportsEffortUpdates?: (request: LLMRequest) => boolean;
|
|
45
|
+
/** Tool schema sanitizer for every model on this protocol unless the model's compatibility sets one; wrappers around another `body.from` must forward it. */
|
|
46
|
+
readonly sanitizer?: LanguageModelSanitizerCompatibility;
|
|
45
47
|
}
|
|
46
48
|
export interface ProtocolBody<Body> {
|
|
47
49
|
/** Schema for the validated provider-native body sent as the JSON request. */
|
package/dist/schema/options.d.ts
CHANGED
|
@@ -18,8 +18,9 @@ export declare class HttpOptions extends HttpOptions_base {
|
|
|
18
18
|
}
|
|
19
19
|
export declare namespace HttpOptions {
|
|
20
20
|
type Input = HttpOptions | ConstructorParameters<typeof HttpOptions>[0];
|
|
21
|
-
/** Normalize HTTP option input into the canonical `HttpOptions` class
|
|
22
|
-
|
|
21
|
+
/** Normalize HTTP option input into the canonical `HttpOptions` class; `undefined` stays `undefined`. */
|
|
22
|
+
function make(input: Input): HttpOptions;
|
|
23
|
+
function make(input: Input | undefined): HttpOptions | undefined;
|
|
23
24
|
}
|
|
24
25
|
export declare const mergeHttpOptions: (...items: ReadonlyArray<HttpOptions | undefined>) => HttpOptions | undefined;
|
|
25
26
|
declare const GenerationOptions_base: Schema.Class<GenerationOptions, Schema.Struct<{
|
|
@@ -67,8 +68,10 @@ export declare namespace LanguageModelDefaults {
|
|
|
67
68
|
/** Normalize selected-model request defaults without applying precedence. */
|
|
68
69
|
const make: (input: Input) => LanguageModelDefaults;
|
|
69
70
|
}
|
|
71
|
+
/** Provider-defined string enum: known values for autocomplete, any string accepted. */
|
|
72
|
+
export type OpenString<Known extends string> = Known | (string & {});
|
|
70
73
|
export declare const ReasoningEfforts: readonly ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
71
|
-
export type ReasoningEffort = (typeof ReasoningEfforts)[number]
|
|
74
|
+
export type ReasoningEffort = OpenString<(typeof ReasoningEfforts)[number]>;
|
|
72
75
|
export declare const ReasoningEffort: Schema.declare<ReasoningEffort, ReasoningEffort>;
|
|
73
76
|
/** Tool schema sanitizer for a model family. `none` opts out of the protocol and model-name defaults. */
|
|
74
77
|
export declare const LanguageModelSanitizerCompatibility: Schema.Literals<readonly ["gemini", "moonshot", "none"]>;
|
package/dist/schema/options.js
CHANGED
|
@@ -39,8 +39,12 @@ export class HttpOptions extends Schema.Class("AI.HttpOptions")({
|
|
|
39
39
|
}) {
|
|
40
40
|
}
|
|
41
41
|
(function (HttpOptions) {
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
function make(input) {
|
|
43
|
+
if (input === undefined || input instanceof HttpOptions)
|
|
44
|
+
return input;
|
|
45
|
+
return new HttpOptions(input);
|
|
46
|
+
}
|
|
47
|
+
HttpOptions.make = make;
|
|
44
48
|
})(HttpOptions || (HttpOptions = {}));
|
|
45
49
|
export const mergeHttpOptions = (...items) => {
|
|
46
50
|
const body = mergeJsonRecords(...items.map((item) => item?.body));
|
|
@@ -93,7 +97,7 @@ export class LanguageModelDefaults extends Schema.Class("LLM.LanguageModelDefaul
|
|
|
93
97
|
return new LanguageModelDefaults({
|
|
94
98
|
generation: input.generation === undefined ? undefined : GenerationOptions.make(input.generation),
|
|
95
99
|
providerOptions: input.providerOptions,
|
|
96
|
-
http:
|
|
100
|
+
http: HttpOptions.make(input.http),
|
|
97
101
|
});
|
|
98
102
|
};
|
|
99
103
|
})(LanguageModelDefaults || (LanguageModelDefaults = {}));
|