@opencode/ai 0.0.0-dev-20120 → 0.0.0-dev-20123
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/dist/experimental/evaluation.js +1 -1
- package/dist/generation.d.ts +1 -7
- package/dist/generation.js +1 -11
- package/dist/image.d.ts +2 -2
- package/dist/image.js +2 -2
- package/dist/llm.js +1 -1
- package/dist/media-model.d.ts +0 -2
- package/dist/media-model.js +0 -2
- package/dist/protocols/alibaba-messages.d.ts +1 -1
- package/dist/protocols/anthropic-messages.d.ts +34 -34
- package/dist/protocols/assemblyai-transcription.d.ts +2 -1
- package/dist/protocols/assemblyai-transcription.js +10 -14
- package/dist/protocols/bfl-images.js +15 -31
- package/dist/protocols/cartesia-speech.d.ts +2 -2
- package/dist/protocols/cartesia-speech.js +10 -16
- package/dist/protocols/deepgram-speech.d.ts +3 -3
- package/dist/protocols/deepgram-speech.js +7 -11
- package/dist/protocols/deepgram-transcription.d.ts +2 -1
- package/dist/protocols/deepgram-transcription.js +9 -13
- package/dist/protocols/elevenlabs-speech.d.ts +3 -3
- package/dist/protocols/elevenlabs-speech.js +9 -13
- package/dist/protocols/fal-images.d.ts +2 -1
- package/dist/protocols/fal-images.js +16 -29
- 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/google-images.d.ts +3 -3
- package/dist/protocols/google-images.js +11 -21
- package/dist/protocols/google-speech.js +7 -13
- 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 +5 -2
- package/dist/protocols/mistral-chat.js +6 -3
- package/dist/protocols/openai-images.d.ts +4 -5
- package/dist/protocols/openai-images.js +24 -55
- package/dist/protocols/openai-responses.js +8 -2
- package/dist/protocols/openai-speech.js +7 -11
- package/dist/protocols/openai-transcription.js +21 -29
- package/dist/protocols/replicate-images.js +11 -17
- package/dist/protocols/runway-video.d.ts +3 -3
- package/dist/protocols/runway-video.js +10 -16
- package/dist/protocols/shared.d.ts +5 -17
- package/dist/protocols/shared.js +3 -39
- 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/speech-stream.d.ts +3 -3
- package/dist/protocols/utils/speech-stream.js +1 -4
- package/dist/protocols/utils/tool-schema.js +19 -6
- package/dist/protocols/xai-images.d.ts +4 -4
- package/dist/protocols/xai-images.js +12 -28
- package/dist/protocols/xai-video.js +12 -18
- package/dist/protocols/zai-images.d.ts +2 -2
- package/dist/protocols/zai-images.js +8 -11
- package/dist/protocols/zai-messages.d.ts +1 -1
- package/dist/providers/alibaba.d.ts +1 -1
- 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.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 +10 -10
- 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.d.ts +7 -8
- package/dist/providers/openai.js +10 -11
- package/dist/providers/opencode-zen.js +1 -1
- package/dist/providers/openrouter.d.ts +6 -7
- package/dist/providers/openrouter.js +1 -1
- 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/client.d.ts +0 -1
- package/dist/route/client.js +1 -6
- package/dist/route/endpoint.d.ts +1 -0
- package/dist/route/endpoint.js +2 -2
- package/dist/route/framing.d.ts +10 -1
- package/dist/route/framing.js +45 -5
- package/dist/route/media-protocol.d.ts +42 -35
- package/dist/route/media-protocol.js +67 -62
- package/dist/route/media.d.ts +6 -2
- package/dist/route/media.js +40 -29
- package/dist/schema/options.d.ts +6 -3
- package/dist/schema/options.js +7 -3
- package/dist/speech.d.ts +2 -2
- package/dist/speech.js +2 -2
- package/dist/transcription.js +2 -2
- package/dist/utils/json.d.ts +4 -0
- package/dist/utils/json.js +4 -0
- package/dist/video.d.ts +2 -2
- package/dist/video.js +2 -2
- 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
package/dist/route/framing.js
CHANGED
|
@@ -1,16 +1,56 @@
|
|
|
1
|
-
import { Stream } from "effect";
|
|
2
|
-
import
|
|
1
|
+
import { Effect, Stream } from "effect";
|
|
2
|
+
import { makeParser } from "effect/unstable/encoding/Sse";
|
|
3
|
+
import { AIError, InvalidProviderOutputError } from "../schema/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* `framing` step for Server-Sent Events. Decodes UTF-8, runs the SSE channel
|
|
6
|
+
* decoder, optionally filters named events, and drops empty events and known
|
|
7
|
+
* keepalives that proxies send as data. `[DONE]` is dropped by default or
|
|
8
|
+
* retained for protocols that use it as their stream boundary. Retry control events are ignored without
|
|
9
|
+
* interrupting the stream. Decoder failures become provider output errors so
|
|
10
|
+
* the public error channel stays `AIError`.
|
|
11
|
+
*/
|
|
12
|
+
export const sseFraming = (bytes, events, includeDone = false) => bytes.pipe(Stream.decodeText(), Stream.mapAccumEffect(() => {
|
|
13
|
+
const output = [];
|
|
14
|
+
return {
|
|
15
|
+
output,
|
|
16
|
+
parser: makeParser((event) => {
|
|
17
|
+
if (event._tag === "Event")
|
|
18
|
+
output.push(event);
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
}, (state, chunk) => Effect.gen(function* () {
|
|
22
|
+
const error = state.parser.feed(chunk);
|
|
23
|
+
if (error)
|
|
24
|
+
return yield* new AIError({
|
|
25
|
+
reason: new InvalidProviderOutputError({
|
|
26
|
+
route: "sse",
|
|
27
|
+
message: error.message,
|
|
28
|
+
body: chunk,
|
|
29
|
+
cause: error,
|
|
30
|
+
}),
|
|
31
|
+
});
|
|
32
|
+
return [state, state.output.splice(0)];
|
|
33
|
+
})), Stream.filter((event) => (events === undefined || events.has(event.event)) &&
|
|
34
|
+
event.data.length > 0 &&
|
|
35
|
+
// Some OpenAI-compatible proxies serialize an empty flush as a bare
|
|
36
|
+
// `data: null`, between events or after `[DONE]`. No protocol has a
|
|
37
|
+
// null event, so it carries nothing and must not abort the stream.
|
|
38
|
+
event.data !== "null" &&
|
|
39
|
+
// Vertex AI partner models (e.g. `xai/grok-4.6`) send their SSE
|
|
40
|
+
// keepalive comment as `data: : keepalive` while reasoning.
|
|
41
|
+
event.data !== ": keepalive" &&
|
|
42
|
+
(event.data !== "[DONE]" || includeDone || (events !== undefined && event.event !== "message"))), Stream.map((event) => event.data));
|
|
3
43
|
/** Server-Sent Events framing. Used by every JSON-streaming HTTP provider. */
|
|
4
|
-
export const sse = { id: "sse", frame:
|
|
44
|
+
export const sse = { id: "sse", frame: sseFraming };
|
|
5
45
|
/** Server-Sent Events framing that retains the conventional `[DONE]` sentinel. */
|
|
6
46
|
export const sseWithDone = {
|
|
7
47
|
id: "sse",
|
|
8
|
-
frame: (bytes) =>
|
|
48
|
+
frame: (bytes) => sseFraming(bytes, undefined, true),
|
|
9
49
|
};
|
|
10
50
|
/** SSE framing restricted to protocol-recognized event names. */
|
|
11
51
|
export const sseEvents = (events) => ({
|
|
12
52
|
id: "sse",
|
|
13
|
-
frame: (bytes) =>
|
|
53
|
+
frame: (bytes) => sseFraming(bytes, events),
|
|
14
54
|
});
|
|
15
55
|
export const lines = {
|
|
16
56
|
id: "lines",
|
|
@@ -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. */
|
|
@@ -95,7 +95,7 @@ export interface Queued<Request, Response, Token> {
|
|
|
95
95
|
readonly path: (token: Token) => string;
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
|
-
export declare const queued: <Request, Response, Token>(input: Omit<Queued<Request, Response, Token>, "kind">) => Queued<Request, Response, Token>;
|
|
98
|
+
export declare const queued: <Request, Response, Token>(route: Identity, input: Omit<Queued<Request, Response, Token>, "kind" | "id" | "provider">) => Queued<Request, Response, Token>;
|
|
99
99
|
export type Mode = "generate" | "stream";
|
|
100
100
|
export type Addressed<Request> = Request & {
|
|
101
101
|
readonly mode: Mode;
|
|
@@ -110,7 +110,7 @@ export interface ResponseContext<Request> extends DecodeContext<Addressed<Reques
|
|
|
110
110
|
export interface Streamed<Request, Event, Frame, State> {
|
|
111
111
|
readonly kind: "stream";
|
|
112
112
|
readonly id: string;
|
|
113
|
-
readonly
|
|
113
|
+
readonly provider: ProviderID;
|
|
114
114
|
/** Common request fields this protocol cannot lower; the route rejects them before `body.from` runs. */
|
|
115
115
|
readonly unsupported?: ReadonlyArray<keyof Request & string>;
|
|
116
116
|
readonly body: {
|
|
@@ -122,37 +122,44 @@ export interface Streamed<Request, Event, Frame, State> {
|
|
|
122
122
|
/** Emit exactly one terminal event, or fail when the provider stopped before completing. */
|
|
123
123
|
readonly finish: (state: State, context: ResponseContext<Request>) => Effect.Effect<ReadonlyArray<Event>, AIError>;
|
|
124
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
125
|
+
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>;
|
|
126
|
+
/** One protocol's route id, display name, and provider, with the decoders and errors that carry them. */
|
|
127
|
+
export declare const identity: (input: {
|
|
128
|
+
readonly id: string;
|
|
129
|
+
readonly name: string;
|
|
130
|
+
readonly provider: string;
|
|
131
|
+
}) => {
|
|
132
|
+
id: string;
|
|
133
|
+
name: string;
|
|
134
|
+
provider: string & import("effect/Brand").Brand<"AI.ProviderID">;
|
|
135
|
+
text: (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<{
|
|
136
|
+
body: string;
|
|
137
|
+
http: HttpContext;
|
|
138
|
+
invalid: (message: string, cause?: unknown) => AIError;
|
|
139
|
+
ended: (status: Exclude<Status, "queued" | "running" | "completed">, message: string) => AIError;
|
|
140
|
+
contentPolicy: (message: string) => AIError;
|
|
141
|
+
}, AIError, never>;
|
|
142
|
+
decodeJson: <A>(schema: Schema.Codec<A, unknown>) => (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<{
|
|
143
|
+
value: A;
|
|
144
|
+
body: string;
|
|
145
|
+
http: HttpContext;
|
|
146
|
+
invalid: (message: string, cause?: unknown) => AIError;
|
|
147
|
+
ended: (status: Exclude<Status, "queued" | "running" | "completed">, message: string) => AIError;
|
|
148
|
+
contentPolicy: (message: string) => AIError;
|
|
149
|
+
}, AIError, never>;
|
|
150
|
+
/** Decode a submission response into the token and first snapshot. */
|
|
151
|
+
decodeStarted: <A, Token>(schema: Schema.Codec<A, unknown>, started: (value: A) => Started<Token>) => (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<Started<Token>, AIError, never>;
|
|
152
|
+
/** Schema-decode one JSON stream frame. Decode failures keep the frame as `reason.body`. */
|
|
153
|
+
decodeFrame: <A>(schema: Schema.Codec<A, unknown>) => (frame: string) => Effect.Effect<A, AIError, never>;
|
|
154
|
+
/** A stream-time failure; the frame stays on `reason.body`. */
|
|
155
|
+
frameError: (message: string, body?: string, cause?: unknown) => AIError;
|
|
156
|
+
incomplete: () => AIError;
|
|
157
|
+
unsupported: (operation: string, message: string) => AIError;
|
|
158
|
+
};
|
|
159
|
+
export type Identity = ReturnType<typeof identity>;
|
|
160
|
+
export type Output = Effect.Success<ReturnType<Identity["text"]>>;
|
|
150
161
|
/** Map a provider status string through the protocol's table; unknown values are an invalid provider document. */
|
|
151
162
|
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
163
|
/** A `url` asset whose provider-declared retention window starts now. */
|
|
157
164
|
export declare const expiringUrl: (url: string, retention: Duration.Duration, options?: Parameters<typeof Media.url>[1]) => Effect.Effect<Media.Asset, never, never>;
|
|
158
165
|
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,62 +14,80 @@ 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); `contentPolicy` is a moderated result.
|
|
32
|
+
*/
|
|
33
|
+
const text = Effect.fn("MediaProtocol.text")(function* (response) {
|
|
34
|
+
const http = context(response);
|
|
35
|
+
const body = yield* response.text.pipe(Effect.mapError((cause) => new AIError({
|
|
36
|
+
reason: new InvalidProviderOutputError({
|
|
37
|
+
route: input.id,
|
|
38
|
+
message: `Failed to read the ${input.name} response`,
|
|
39
|
+
http,
|
|
40
|
+
cause,
|
|
41
|
+
}),
|
|
42
|
+
})));
|
|
43
|
+
return {
|
|
44
|
+
body,
|
|
44
45
|
http,
|
|
45
|
-
cause,
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
invalid: (message, cause) => new AIError({ reason: new InvalidProviderOutputError({ route: input.id, message, body, http, cause }) }),
|
|
47
|
+
ended: (status, message) => new AIError({
|
|
48
|
+
reason: status === "failed"
|
|
49
|
+
? new ProviderInternalError({ message, body, http })
|
|
50
|
+
: new InvalidRequestError({ message, body, http }),
|
|
51
|
+
}),
|
|
52
|
+
contentPolicy: (message) => new AIError({ reason: new ContentPolicyError({ message, body, http }) }),
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
/** Read and Schema-decode a JSON body. Decode failures keep the raw body as `reason.body`. */
|
|
56
|
+
const decodeJson = (schema) => {
|
|
57
|
+
const decode = Schema.decodeUnknownEffect(Schema.fromJsonString(schema));
|
|
58
|
+
return Effect.fn("MediaProtocol.decodeJson")(function* (response) {
|
|
59
|
+
const output = yield* text(response);
|
|
60
|
+
const value = yield* decode(output.body).pipe(Effect.mapError((cause) => output.invalid(`${input.name} returned an invalid response`, cause)));
|
|
61
|
+
return { ...output, value };
|
|
62
|
+
});
|
|
63
|
+
};
|
|
48
64
|
return {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
id: input.id,
|
|
66
|
+
name: input.name,
|
|
67
|
+
provider,
|
|
68
|
+
text,
|
|
69
|
+
decodeJson,
|
|
70
|
+
/** Decode a submission response into the token and first snapshot. */
|
|
71
|
+
decodeStarted: (schema, started) => {
|
|
72
|
+
const decode = decodeJson(schema);
|
|
73
|
+
return (response) => decode(response).pipe(Effect.map((output) => started(output.value)));
|
|
74
|
+
},
|
|
75
|
+
/** Schema-decode one JSON stream frame. Decode failures keep the frame as `reason.body`. */
|
|
76
|
+
decodeFrame: (schema) => {
|
|
77
|
+
const decode = Schema.decodeUnknownEffect(Schema.fromJsonString(schema));
|
|
78
|
+
return (frame) => decode(frame).pipe(Effect.mapError((cause) => frameError(`${input.name} sent an invalid stream event`, frame, cause)));
|
|
79
|
+
},
|
|
80
|
+
/** A stream-time failure; the frame stays on `reason.body`. */
|
|
81
|
+
frameError,
|
|
82
|
+
incomplete: () => new AIError({
|
|
83
|
+
reason: new InvalidProviderOutputError({
|
|
84
|
+
route: input.id,
|
|
85
|
+
message: "The provider response ended unexpectedly.",
|
|
86
|
+
classification: "incomplete-stream",
|
|
87
|
+
}),
|
|
56
88
|
}),
|
|
57
|
-
|
|
89
|
+
unsupported: (operation, message) => new AIError({ reason: new UnsupportedOperationError({ operation, provider, route: input.id, message }) }),
|
|
58
90
|
};
|
|
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
91
|
};
|
|
74
92
|
/** Map a provider status string through the protocol's table; unknown values are an invalid provider document. */
|
|
75
93
|
export const status = (table, raw, output) => {
|
|
@@ -78,19 +96,6 @@ export const status = (table, raw, output) => {
|
|
|
78
96
|
return Effect.fail(output.invalid(`Unknown generation status "${raw}"`));
|
|
79
97
|
return Effect.succeed(normalized);
|
|
80
98
|
};
|
|
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
99
|
/** A `url` asset whose provider-declared retention window starts now. */
|
|
95
100
|
export const expiringUrl = (url, retention, options) => Clock.currentTimeMillis.pipe(Effect.map((now) => Media.url(url, { ...options, expiresAt: now + Duration.toMillis(retention) })));
|
|
96
101
|
export * as MediaProtocol from "./media-protocol.js";
|
package/dist/route/media.d.ts
CHANGED
|
@@ -23,6 +23,12 @@ 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
33
|
export interface Route<Request extends MediaRequest, Response> {
|
|
28
34
|
readonly kind: "inline";
|
|
@@ -52,8 +58,6 @@ export interface StreamRoute<Request extends MediaRequest, Event, Response> {
|
|
|
52
58
|
}
|
|
53
59
|
export type AnyRoute<Request extends MediaRequest, Event, Response> = Route<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. */
|
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, resultEvents
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
8
|
+
import { Generation, resultEvents } 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);
|
|
@@ -45,14 +52,13 @@ export const queued = (input) => {
|
|
|
45
52
|
.call("GET", operation.path(token), http, execute)
|
|
46
53
|
.pipe(Effect.flatMap((sent) => operation.decode(sent.response, { token, auth: sent.auth, materialize })));
|
|
47
54
|
const cancel = protocol.cancel;
|
|
48
|
-
|
|
55
|
+
return {
|
|
49
56
|
status: poll(protocol.status),
|
|
50
57
|
result: poll(protocol.result),
|
|
51
58
|
cancel: cancel === undefined
|
|
52
59
|
? undefined
|
|
53
60
|
: transport.call(cancel.method, cancel.path(token), http, execute).pipe(Effect.asVoid),
|
|
54
61
|
};
|
|
55
|
-
return route;
|
|
56
62
|
};
|
|
57
63
|
const start = Effect.fn("MediaRoute.start")(function* (request, execute) {
|
|
58
64
|
const submitted = yield* transport.submit(request, { unsupported: protocol.unsupported, prepare: protocol.start.prepare, from: protocol.start.body.from }, execute);
|
|
@@ -63,14 +69,14 @@ export const queued = (input) => {
|
|
|
63
69
|
const resume = Effect.fn("MediaRoute.resume")(function* (model, raw, execute) {
|
|
64
70
|
const token = yield* decodeToken(raw).pipe(Effect.mapError((cause) => new AIError({
|
|
65
71
|
reason: new InvalidRequestError({
|
|
66
|
-
message: `${
|
|
72
|
+
message: `${protocol.id} cannot resume a generation from this token`,
|
|
67
73
|
cause,
|
|
68
74
|
}),
|
|
69
75
|
})));
|
|
70
76
|
const route = generationRoute(token, transport.http(model), execute);
|
|
71
77
|
return new Generation(route, encodeToken(token), yield* route.status);
|
|
72
78
|
});
|
|
73
|
-
return { kind: "queued", id:
|
|
79
|
+
return { kind: "queued", id: protocol.id, provider: protocol.provider, protocol: protocol.id, start, resume };
|
|
74
80
|
};
|
|
75
81
|
/** Compose a streaming media protocol; `generate` runs the same stream in `generate` mode and folds it with `collect`. */
|
|
76
82
|
export const stream = (input) => {
|
|
@@ -99,19 +105,21 @@ export const stream = (input) => {
|
|
|
99
105
|
}));
|
|
100
106
|
return {
|
|
101
107
|
kind: "stream",
|
|
102
|
-
id:
|
|
103
|
-
provider:
|
|
108
|
+
id: protocol.id,
|
|
109
|
+
provider: protocol.provider,
|
|
104
110
|
protocol: protocol.id,
|
|
105
111
|
stream: (request, execute) => events(request, execute, "stream"),
|
|
106
112
|
generate: (request, execute) => events(request, execute, "generate").pipe(Stream.runCollect, Effect.flatMap(input.collect)),
|
|
107
113
|
};
|
|
108
114
|
};
|
|
109
115
|
export const dispatch = (input) => {
|
|
110
|
-
const notQueued = (route, operation) =>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
const notQueued = (route, operation) => new AIError({
|
|
117
|
+
reason: new UnsupportedOperationError({
|
|
118
|
+
operation: `${input.modality}.${operation}`,
|
|
119
|
+
provider: route.provider,
|
|
120
|
+
route: route.id,
|
|
121
|
+
message: `${route.provider}/${route.id} is not a queued route; use generate or stream`,
|
|
122
|
+
}),
|
|
115
123
|
});
|
|
116
124
|
const start = (route, request) => {
|
|
117
125
|
if (route.kind !== "queued")
|
|
@@ -143,11 +151,10 @@ export const dispatch = (input) => {
|
|
|
143
151
|
// Transport plumbing shared by every kind
|
|
144
152
|
// ---------------------------------------------------------------------------
|
|
145
153
|
const makeTransport = (input) => {
|
|
146
|
-
const provider = ProviderID.make(input.provider);
|
|
147
154
|
const routeHttp = input.headers === undefined ? undefined : new HttpOptions({ headers: input.headers });
|
|
148
155
|
const authorize = Auth.toEffect(input.auth);
|
|
149
|
-
const baseURL = (path) => new URL(`${
|
|
150
|
-
/** `auth` is only what `Auth` added, never deployment headers. */
|
|
156
|
+
const baseURL = (path) => new URL(`${Endpoint.trimBaseUrl(input.endpoint.baseURL ?? "")}${path}`);
|
|
157
|
+
/** `auth` is only what `Auth` added or changed, never untouched deployment headers. */
|
|
151
158
|
const send = Effect.fn("MediaRoute.send")(function* (call, execute) {
|
|
152
159
|
const encoded = encode(call.body, call.headers);
|
|
153
160
|
const url = call.url.toString();
|
|
@@ -159,15 +166,17 @@ const makeTransport = (input) => {
|
|
|
159
166
|
headers: encoded.headers,
|
|
160
167
|
});
|
|
161
168
|
const response = yield* execute(encoded.apply(HttpClientRequest.make(call.method)(url).pipe(HttpClientRequest.setHeaders(headers))));
|
|
162
|
-
return {
|
|
169
|
+
return {
|
|
170
|
+
response,
|
|
171
|
+
auth: Object.fromEntries(Object.entries(headers).filter(([key, value]) => encoded.headers[key] !== value)),
|
|
172
|
+
};
|
|
163
173
|
});
|
|
164
174
|
return {
|
|
165
|
-
provider,
|
|
166
175
|
/** Route and model overlays; `start` additionally merges the request's own `http`. */
|
|
167
176
|
http: (model) => mergeHttpOptions(routeHttp, model.http),
|
|
168
177
|
/** POST the protocol body to the route endpoint. */
|
|
169
178
|
submit: Effect.fn("MediaRoute.submit")(function* (request, protocol, execute) {
|
|
170
|
-
yield* rejectUnsupported(input.id, provider, request, protocol.unsupported);
|
|
179
|
+
yield* rejectUnsupported(input.protocol.id, input.protocol.provider, request, protocol.unsupported);
|
|
171
180
|
const http = mergeHttpOptions(routeHttp, request.model.http, request.http);
|
|
172
181
|
const headers = Headers.fromInput(http?.headers);
|
|
173
182
|
const prepared = protocol.prepare === undefined
|
|
@@ -202,7 +211,7 @@ const encode = (body, headers) => {
|
|
|
202
211
|
if (body === undefined)
|
|
203
212
|
return { text: "", headers, apply: (request) => request };
|
|
204
213
|
if (body.type === "json") {
|
|
205
|
-
const text =
|
|
214
|
+
const text = encodeJson(body.value);
|
|
206
215
|
return { text, headers, apply: HttpClientRequest.bodyText(text, "application/json") };
|
|
207
216
|
}
|
|
208
217
|
if (body.type === "binary")
|
|
@@ -226,11 +235,13 @@ const rejectUnsupported = (route, provider, request, unsupported) => {
|
|
|
226
235
|
});
|
|
227
236
|
if (present.length === 0)
|
|
228
237
|
return Effect.void;
|
|
229
|
-
return Effect.fail(
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
238
|
+
return Effect.fail(new AIError({
|
|
239
|
+
reason: new UnsupportedOperationError({
|
|
240
|
+
operation: `media.${present[0]}`,
|
|
241
|
+
provider,
|
|
242
|
+
route,
|
|
243
|
+
message: `${provider}/${route} does not support ${present.join(", ")}`,
|
|
244
|
+
}),
|
|
234
245
|
}));
|
|
235
246
|
};
|
|
236
247
|
export * as MediaRoute from "./media.js";
|
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 = {}));
|
package/dist/speech.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Media } from "./media.js";
|
|
|
3
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 } from "./schema/index.js";
|
|
6
|
+
import { AIError, HttpOptions, type OpenString } from "./schema/index.js";
|
|
7
7
|
import { Service } from "./speech-client.js";
|
|
8
8
|
export type SpeechOptions = Record<string, unknown>;
|
|
9
9
|
export type SpeechRoute<Options extends SpeechOptions = SpeechOptions> = MediaRoute.StreamRoute<SpeechRequestFor<Options>, SpeechEvent, SpeechResponse>;
|
|
@@ -22,7 +22,7 @@ export declare const SpeechVoice: Schema.Union<readonly [Schema.String, Schema.S
|
|
|
22
22
|
readonly id: Schema.String;
|
|
23
23
|
}>]>;
|
|
24
24
|
export type SpeechVoice = Schema.Schema.Type<typeof SpeechVoice>;
|
|
25
|
-
export type SpeechFormat = "mp3" | "wav" | "pcm" | "opus" | "aac" | "flac"
|
|
25
|
+
export type SpeechFormat = OpenString<"mp3" | "wav" | "pcm" | "opus" | "aac" | "flac">;
|
|
26
26
|
/** Granularity is provider-native: characters on ElevenLabs, words on Cartesia. */
|
|
27
27
|
export declare const SpeechTimestamp: Schema.Struct<{
|
|
28
28
|
readonly text: Schema.String;
|
package/dist/speech.js
CHANGED
|
@@ -12,7 +12,7 @@ export class SpeechModel extends MediaModel {
|
|
|
12
12
|
static fromRoute(route, input) {
|
|
13
13
|
return new SpeechModel({
|
|
14
14
|
id: input.id,
|
|
15
|
-
provider: route.provider,
|
|
15
|
+
provider: route.protocol.provider,
|
|
16
16
|
http: input.http,
|
|
17
17
|
route: composeRoute((composition) => MediaRoute.stream({ ...composition, collect: collectResponse }), route, input),
|
|
18
18
|
});
|
|
@@ -102,7 +102,7 @@ export function request(input) {
|
|
|
102
102
|
return input;
|
|
103
103
|
return new SpeechRequest({
|
|
104
104
|
...input,
|
|
105
|
-
http:
|
|
105
|
+
http: HttpOptions.make(input.http),
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
108
|
const requestEffect = (input) => tryRequest(() => request(input));
|
package/dist/transcription.js
CHANGED
|
@@ -12,7 +12,7 @@ export class TranscriptionModel extends MediaModel {
|
|
|
12
12
|
static fromRoute(route, input) {
|
|
13
13
|
return new TranscriptionModel({
|
|
14
14
|
id: input.id,
|
|
15
|
-
provider: route.provider,
|
|
15
|
+
provider: route.protocol.provider,
|
|
16
16
|
http: input.http,
|
|
17
17
|
route: composeAnyRoute(route, input, collectResponse),
|
|
18
18
|
});
|
|
@@ -110,7 +110,7 @@ export function request(input) {
|
|
|
110
110
|
return input;
|
|
111
111
|
return new TranscriptionRequest({
|
|
112
112
|
...input,
|
|
113
|
-
http:
|
|
113
|
+
http: HttpOptions.make(input.http),
|
|
114
114
|
});
|
|
115
115
|
}
|
|
116
116
|
const requestEffect = (input) => tryRequest(() => request(input));
|