@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
package/dist/route/media.js
CHANGED
|
@@ -1,49 +1,221 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
1
|
+
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 { Service as RequestExecutorService } from "./executor-service.js";
|
|
6
|
+
import { RequestExecutor } from "./executor.js";
|
|
5
7
|
import { MediaProtocol } from "./media-protocol.js";
|
|
8
|
+
import { Generation, resultEvents, } from "../generation.js";
|
|
6
9
|
import { ProviderShared } from "../protocols/shared.js";
|
|
7
|
-
import { AIError, HttpOptions, ProviderID, mergeHttpOptions } from "../schema/index.js";
|
|
10
|
+
import { AIError, AIErrorReason, HttpOptions, InvalidRequestError, ProviderID, mergeHttpOptions, } from "../schema/index.js";
|
|
8
11
|
import { sanitizeSurrogates } from "../utils/sanitize.js";
|
|
9
12
|
/**
|
|
10
13
|
* Compose an inline media protocol with an endpoint and auth into a runnable route. The route owns the transport
|
|
11
|
-
* plumbing every media protocol would otherwise duplicate: option merging, surrogate sanitizing, unsupported-field
|
|
12
|
-
* rendering, auth headers, JSON
|
|
14
|
+
* plumbing every media protocol would otherwise duplicate: option merging, surrogate sanitizing, unsupported-field
|
|
15
|
+
* rejection, URL and query rendering, auth headers, JSON, multipart, or binary encoding, and handing responses back
|
|
16
|
+
* to the protocol.
|
|
13
17
|
*/
|
|
14
|
-
export const
|
|
18
|
+
export const inline = (input) => {
|
|
19
|
+
const transport = makeTransport(input);
|
|
20
|
+
return {
|
|
21
|
+
kind: "inline",
|
|
22
|
+
id: input.id,
|
|
23
|
+
provider: transport.provider,
|
|
24
|
+
protocol: input.protocol.id,
|
|
25
|
+
generate: Effect.fn(`MediaRoute.generate`)(function* (request, execute) {
|
|
26
|
+
const submitted = yield* transport.submit(request, { unsupported: input.protocol.unsupported, from: input.protocol.body.from }, execute);
|
|
27
|
+
return yield* input.protocol.response.decode(submitted.response, submitted.context);
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Compose a queued media protocol the same way, adding `start`/`resume` handles whose polls reuse the route's auth,
|
|
33
|
+
* deployment headers, and (for `start`) the request's `http` overlay. The token is decoded once at the boundary and
|
|
34
|
+
* closed over by the resulting `Generation.Route`.
|
|
35
|
+
*/
|
|
36
|
+
export const queued = (input) => {
|
|
37
|
+
const transport = makeTransport(input);
|
|
38
|
+
const protocol = input.protocol;
|
|
39
|
+
const decodeToken = Schema.decodeUnknownEffect(protocol.token);
|
|
40
|
+
// A protocol producing a token its own codec rejects is a programmer defect, not a provider error.
|
|
41
|
+
const encodeToken = Schema.encodeSync(protocol.token);
|
|
42
|
+
const generationRoute = (token, http, execute) => {
|
|
43
|
+
const materialize = (asset) => asset.materialize().pipe(Effect.provideService(RequestExecutorService, { execute }));
|
|
44
|
+
const poll = (operation) => transport
|
|
45
|
+
.call("GET", operation.path(token), http, execute)
|
|
46
|
+
.pipe(Effect.flatMap((sent) => operation.decode(sent.response, { token, auth: sent.auth, materialize })));
|
|
47
|
+
const cancel = protocol.cancel;
|
|
48
|
+
const route = {
|
|
49
|
+
status: poll(protocol.status),
|
|
50
|
+
result: poll(protocol.result),
|
|
51
|
+
cancel: cancel === undefined
|
|
52
|
+
? undefined
|
|
53
|
+
: transport.call(cancel.method, cancel.path(token), http, execute).pipe(Effect.asVoid),
|
|
54
|
+
};
|
|
55
|
+
return route;
|
|
56
|
+
};
|
|
57
|
+
const start = Effect.fn("MediaRoute.start")(function* (request, execute) {
|
|
58
|
+
const submitted = yield* transport.submit(request, { unsupported: protocol.unsupported, prepare: protocol.start.prepare, from: protocol.start.body.from }, execute);
|
|
59
|
+
const started = yield* protocol.start.decode(submitted.response, submitted.context);
|
|
60
|
+
const route = generationRoute(started.token, submitted.context.request.http, execute);
|
|
61
|
+
return new Generation(route, encodeToken(started.token), started.snapshot);
|
|
62
|
+
});
|
|
63
|
+
const resume = Effect.fn("MediaRoute.resume")(function* (model, raw, execute) {
|
|
64
|
+
const token = yield* decodeToken(raw).pipe(Effect.mapError((cause) => new AIError({
|
|
65
|
+
reason: new InvalidRequestError({
|
|
66
|
+
message: `${input.id} cannot resume a generation from this token`,
|
|
67
|
+
cause,
|
|
68
|
+
}),
|
|
69
|
+
})));
|
|
70
|
+
const route = generationRoute(token, transport.http(model), execute);
|
|
71
|
+
return new Generation(route, encodeToken(token), yield* route.status);
|
|
72
|
+
});
|
|
73
|
+
return { kind: "queued", id: input.id, provider: transport.provider, protocol: protocol.id, start, resume };
|
|
74
|
+
};
|
|
75
|
+
/** Compose a streaming media protocol; `generate` runs the same stream in `generate` mode and folds it with `collect`. */
|
|
76
|
+
export const stream = (input) => {
|
|
77
|
+
const transport = makeTransport(input);
|
|
78
|
+
const protocol = input.protocol;
|
|
79
|
+
const events = (request, execute, mode) => Stream.unwrap(Effect.gen(function* () {
|
|
80
|
+
const submitted = yield* transport.submit({ ...request, mode }, { unsupported: protocol.unsupported, from: protocol.body.from }, execute);
|
|
81
|
+
const http = RequestExecutor.responseHttp(submitted.response);
|
|
82
|
+
return Stream.suspend(() => {
|
|
83
|
+
// Parser state is local to one response, exactly like `Route.make`'s LLM stream loop.
|
|
84
|
+
let state = protocol.initial();
|
|
85
|
+
return protocol.frames(RequestExecutor.responseStream(submitted.response), submitted.context).pipe(Stream.mapEffect((frame) => protocol.step(state, frame).pipe(Effect.map(([next, output]) => {
|
|
86
|
+
state = next;
|
|
87
|
+
return output;
|
|
88
|
+
}))), Stream.flattenIterable, Stream.concat(Stream.suspend(() => Stream.fromIterableEffect(protocol.finish(state, { ...submitted.context, http })))), Stream.mapError((error) => error.reason.http !== undefined
|
|
89
|
+
? error
|
|
90
|
+
: new AIError({
|
|
91
|
+
reason: AIErrorReason.make({
|
|
92
|
+
...error.reason,
|
|
93
|
+
message: error.reason.message,
|
|
94
|
+
cause: error.reason.cause,
|
|
95
|
+
http,
|
|
96
|
+
}),
|
|
97
|
+
})));
|
|
98
|
+
});
|
|
99
|
+
}));
|
|
100
|
+
return {
|
|
101
|
+
kind: "stream",
|
|
102
|
+
id: input.id,
|
|
103
|
+
provider: transport.provider,
|
|
104
|
+
protocol: protocol.id,
|
|
105
|
+
stream: (request, execute) => events(request, execute, "stream"),
|
|
106
|
+
generate: (request, execute) => events(request, execute, "generate").pipe(Stream.runCollect, Effect.flatMap(input.collect)),
|
|
107
|
+
};
|
|
108
|
+
};
|
|
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
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// Transport plumbing shared by every kind
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
const makeTransport = (input) => {
|
|
15
146
|
const provider = ProviderID.make(input.provider);
|
|
16
147
|
const routeHttp = input.headers === undefined ? undefined : new HttpOptions({ headers: input.headers });
|
|
17
148
|
const authorize = Auth.toEffect(input.auth);
|
|
149
|
+
const baseURL = (path) => new URL(`${ProviderShared.trimBaseUrl(input.endpoint.baseURL ?? "")}${path}`);
|
|
150
|
+
/** `auth` is only what `Auth` added, never deployment headers. */
|
|
151
|
+
const send = Effect.fn("MediaRoute.send")(function* (call, execute) {
|
|
152
|
+
const encoded = encode(call.body, call.headers);
|
|
153
|
+
const url = call.url.toString();
|
|
154
|
+
const headers = yield* authorize({
|
|
155
|
+
request: call.request,
|
|
156
|
+
method: call.method,
|
|
157
|
+
url,
|
|
158
|
+
body: encoded.text,
|
|
159
|
+
headers: encoded.headers,
|
|
160
|
+
});
|
|
161
|
+
const response = yield* execute(encoded.apply(HttpClientRequest.make(call.method)(url).pipe(HttpClientRequest.setHeaders(headers))));
|
|
162
|
+
return { response, auth: Object.fromEntries(Object.entries(headers).filter(([key]) => !(key in call.headers))) };
|
|
163
|
+
});
|
|
18
164
|
return {
|
|
19
|
-
id: input.id,
|
|
20
165
|
provider,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
166
|
+
/** Route and model overlays; `start` additionally merges the request's own `http`. */
|
|
167
|
+
http: (model) => mergeHttpOptions(routeHttp, model.http),
|
|
168
|
+
/** POST the protocol body to the route endpoint. */
|
|
169
|
+
submit: Effect.fn("MediaRoute.submit")(function* (request, protocol, execute) {
|
|
170
|
+
yield* rejectUnsupported(input.id, provider, request, protocol.unsupported);
|
|
24
171
|
const http = mergeHttpOptions(routeHttp, request.model.http, request.http);
|
|
172
|
+
const headers = Headers.fromInput(http?.headers);
|
|
173
|
+
const prepared = protocol.prepare === undefined
|
|
174
|
+
? request
|
|
175
|
+
: yield* protocol.prepare(request, (path, body) => send({ method: "POST", url: baseURL(path), headers, request, body }, execute).pipe(Effect.map((sent) => sent.response)));
|
|
25
176
|
// Sanitize after merging so model-level overlays are covered; the model value is restored, not sanitized.
|
|
26
|
-
const resolved = { ...sanitizeSurrogates({ ...
|
|
27
|
-
const body = yield*
|
|
28
|
-
const url = Endpoint.render(input.endpoint, { request: resolved, body });
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const encoded = body.type === "json" ? ProviderShared.encodeJson(body.value) : "[multipart/form-data]";
|
|
32
|
-
const baseHeaders = Headers.fromInput(http?.headers);
|
|
33
|
-
const headers = yield* authorize({
|
|
34
|
-
request: resolved,
|
|
35
|
-
method: "POST",
|
|
36
|
-
url: url.toString(),
|
|
37
|
-
body: encoded,
|
|
38
|
-
// The HTTP client sets the multipart boundary; a caller-supplied content-type would corrupt it.
|
|
39
|
-
headers: body.type === "multipart" ? Headers.remove(baseHeaders, "content-type") : baseHeaders,
|
|
40
|
-
});
|
|
41
|
-
const transport = HttpClientRequest.post(url.toString()).pipe(HttpClientRequest.setHeaders(headers), body.type === "json"
|
|
42
|
-
? HttpClientRequest.bodyText(encoded, "application/json")
|
|
43
|
-
: HttpClientRequest.bodyFormData(body.value));
|
|
44
|
-
const response = yield* execute(transport);
|
|
45
|
-
return yield* input.protocol.response.decode(response, { request: resolved, body });
|
|
177
|
+
const resolved = { ...sanitizeSurrogates({ ...prepared, http }), model: request.model };
|
|
178
|
+
const body = yield* protocol.from(resolved);
|
|
179
|
+
const url = withQuery(withQuery(Endpoint.render(input.endpoint, { request: resolved, body }), body.type === "multipart" ? undefined : body.query), http?.query);
|
|
180
|
+
const sent = yield* send({ method: "POST", url, headers, request: resolved, body }, execute);
|
|
181
|
+
return { response: sent.response, context: { request: resolved, body } };
|
|
46
182
|
}),
|
|
183
|
+
/** Bodiless follow-up call (status, result, cancel) with the same auth and headers as `submit`. */
|
|
184
|
+
call: (method, path, http, execute) => {
|
|
185
|
+
// Provider-issued absolute URLs (fal `status_url`) are used as-is; everything else resolves against the base.
|
|
186
|
+
const url = withQuery(/^https?:\/\//.test(path) ? new URL(path) : baseURL(path), http?.query);
|
|
187
|
+
for (const [key, value] of Object.entries(input.endpoint.query ?? {}))
|
|
188
|
+
url.searchParams.set(key, value);
|
|
189
|
+
return send({ method, url, headers: Headers.fromInput(http?.headers), request: { http } }, execute);
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
const withQuery = (url, query) => {
|
|
194
|
+
for (const [key, value] of Object.entries(query ?? {})) {
|
|
195
|
+
url.searchParams.delete(key);
|
|
196
|
+
for (const item of typeof value === "string" ? [value] : value)
|
|
197
|
+
url.searchParams.append(key, item);
|
|
198
|
+
}
|
|
199
|
+
return url;
|
|
200
|
+
};
|
|
201
|
+
const encode = (body, headers) => {
|
|
202
|
+
if (body === undefined)
|
|
203
|
+
return { text: "", headers, apply: (request) => request };
|
|
204
|
+
if (body.type === "json") {
|
|
205
|
+
const text = ProviderShared.encodeJson(body.value);
|
|
206
|
+
return { text, headers, apply: HttpClientRequest.bodyText(text, "application/json") };
|
|
207
|
+
}
|
|
208
|
+
if (body.type === "binary")
|
|
209
|
+
return {
|
|
210
|
+
text: `[${body.contentType}]`,
|
|
211
|
+
headers,
|
|
212
|
+
apply: HttpClientRequest.bodyUint8Array(body.value, body.contentType),
|
|
213
|
+
};
|
|
214
|
+
return {
|
|
215
|
+
text: "[multipart/form-data]",
|
|
216
|
+
// The HTTP client sets the multipart boundary; a caller-supplied content-type would corrupt it.
|
|
217
|
+
headers: Headers.remove(headers, "content-type"),
|
|
218
|
+
apply: HttpClientRequest.bodyFormData(body.value),
|
|
47
219
|
};
|
|
48
220
|
};
|
|
49
221
|
/** Common fields are never silently dropped: a present field the protocol declared unsupported fails typed. */
|
package/dist/schema/events.d.ts
CHANGED
|
@@ -365,7 +365,6 @@ export declare const MediaEvent: Schema.Struct<{
|
|
|
365
365
|
readonly url: Schema.String;
|
|
366
366
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
367
367
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
368
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
369
368
|
}>, Schema.Struct<{
|
|
370
369
|
readonly type: Schema.Literal<"ref">;
|
|
371
370
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
|
@@ -578,7 +577,6 @@ declare const llmEventTagged: Schema.toTaggedUnion<"type", readonly [Schema.refi
|
|
|
578
577
|
readonly url: Schema.String;
|
|
579
578
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
580
579
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
581
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
582
580
|
}>, Schema.Struct<{
|
|
583
581
|
readonly type: Schema.Literal<"ref">;
|
|
584
582
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
|
@@ -820,7 +818,6 @@ export declare const LLMEvent: Schema.Union<readonly [Schema.refine<{
|
|
|
820
818
|
readonly url: Schema.String;
|
|
821
819
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
822
820
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
823
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
824
821
|
}>, Schema.Struct<{
|
|
825
822
|
readonly type: Schema.Literal<"ref">;
|
|
826
823
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
|
@@ -1064,7 +1061,6 @@ export declare const LLMEvent: Schema.Union<readonly [Schema.refine<{
|
|
|
1064
1061
|
readonly url: Schema.String;
|
|
1065
1062
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
1066
1063
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
1067
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
1068
1064
|
}>, Schema.Struct<{
|
|
1069
1065
|
readonly type: Schema.Literal<"ref">;
|
|
1070
1066
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
|
@@ -4805,7 +4801,6 @@ declare const LLMResponse_base: Schema.Class<LLMResponse, Schema.Struct<{
|
|
|
4805
4801
|
readonly url: Schema.String;
|
|
4806
4802
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
4807
4803
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
4808
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
4809
4804
|
}>, Schema.Struct<{
|
|
4810
4805
|
readonly type: Schema.Literal<"ref">;
|
|
4811
4806
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
|
@@ -5049,7 +5044,6 @@ declare const LLMResponse_base: Schema.Class<LLMResponse, Schema.Struct<{
|
|
|
5049
5044
|
readonly url: Schema.String;
|
|
5050
5045
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
5051
5046
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
5052
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
5053
5047
|
}>, Schema.Struct<{
|
|
5054
5048
|
readonly type: Schema.Literal<"ref">;
|
|
5055
5049
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
|
@@ -44,7 +44,6 @@ export declare const MediaPart: Schema.Struct<{
|
|
|
44
44
|
readonly url: Schema.String;
|
|
45
45
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
46
46
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
47
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
48
47
|
}>, Schema.Struct<{
|
|
49
48
|
readonly type: Schema.Literal<"ref">;
|
|
50
49
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
|
@@ -305,7 +304,6 @@ export declare const ContentPart: Schema.toTaggedUnion<"type", readonly [Schema.
|
|
|
305
304
|
readonly url: Schema.String;
|
|
306
305
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
307
306
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
308
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
309
307
|
}>, Schema.Struct<{
|
|
310
308
|
readonly type: Schema.Literal<"ref">;
|
|
311
309
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
|
@@ -450,7 +448,6 @@ declare const Message_base: Schema.Class<Message, Schema.Struct<{
|
|
|
450
448
|
readonly url: Schema.String;
|
|
451
449
|
readonly mediaType: Schema.optional<Schema.String>;
|
|
452
450
|
readonly expiresAt: Schema.optional<Schema.Number>;
|
|
453
|
-
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
454
451
|
}>, Schema.Struct<{
|
|
455
452
|
readonly type: Schema.Literal<"ref">;
|
|
456
453
|
readonly provider: Schema.brand<Schema.String, "AI.ProviderID">;
|
package/dist/schema/options.d.ts
CHANGED
|
@@ -70,12 +70,13 @@ export declare namespace LanguageModelDefaults {
|
|
|
70
70
|
export declare const ReasoningEfforts: readonly ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
71
71
|
export type ReasoningEffort = (typeof ReasoningEfforts)[number] | (string & {});
|
|
72
72
|
export declare const ReasoningEffort: Schema.declare<ReasoningEffort, ReasoningEffort>;
|
|
73
|
-
|
|
74
|
-
export
|
|
73
|
+
/** Tool schema sanitizer for a model family. `none` opts out of the protocol and model-name defaults. */
|
|
74
|
+
export declare const LanguageModelSanitizerCompatibility: Schema.Literals<readonly ["gemini", "moonshot", "none"]>;
|
|
75
|
+
export type LanguageModelSanitizerCompatibility = Schema.Schema.Type<typeof LanguageModelSanitizerCompatibility>;
|
|
75
76
|
export declare const LanguageModelMaxTokensFieldCompatibility: Schema.Literals<readonly ["max_completion_tokens", "max_tokens"]>;
|
|
76
77
|
export type LanguageModelMaxTokensFieldCompatibility = Schema.Schema.Type<typeof LanguageModelMaxTokensFieldCompatibility>;
|
|
77
78
|
declare const LanguageModelCompatibility_base: Schema.Class<LanguageModelCompatibility, Schema.Struct<{
|
|
78
|
-
readonly
|
|
79
|
+
readonly sanitizer: Schema.optional<Schema.Literals<readonly ["gemini", "moonshot", "none"]>>;
|
|
79
80
|
readonly reasoningField: Schema.optional<Schema.String>;
|
|
80
81
|
/** Require every assistant message to include its reasoning field, even when empty. */
|
|
81
82
|
readonly requireReasoning: Schema.optional<Schema.Boolean>;
|
package/dist/schema/options.js
CHANGED
|
@@ -99,10 +99,11 @@ export class LanguageModelDefaults extends Schema.Class("LLM.LanguageModelDefaul
|
|
|
99
99
|
})(LanguageModelDefaults || (LanguageModelDefaults = {}));
|
|
100
100
|
export const ReasoningEfforts = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
101
101
|
export const ReasoningEffort = Schema.declare((value) => typeof value === "string", { title: "ReasoningEffort" });
|
|
102
|
-
|
|
102
|
+
/** Tool schema sanitizer for a model family. `none` opts out of the protocol and model-name defaults. */
|
|
103
|
+
export const LanguageModelSanitizerCompatibility = Schema.Literals(["gemini", "moonshot", "none"]);
|
|
103
104
|
export const LanguageModelMaxTokensFieldCompatibility = Schema.Literals(["max_completion_tokens", "max_tokens"]);
|
|
104
105
|
export class LanguageModelCompatibility extends Schema.Class("LLM.LanguageModelCompatibility")({
|
|
105
|
-
|
|
106
|
+
sanitizer: Schema.optional(LanguageModelSanitizerCompatibility),
|
|
106
107
|
reasoningField: Schema.optional(Schema.String),
|
|
107
108
|
/** Require every assistant message to include its reasoning field, even when empty. */
|
|
108
109
|
requireReasoning: Schema.optional(Schema.Boolean),
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Stream } from "effect";
|
|
2
|
+
import { RequestExecutor } from "./route/executor.js";
|
|
3
|
+
import type { AIError } from "./schema/index.js";
|
|
4
|
+
import type { SpeechEvent, SpeechOptions, SpeechRequestFor, SpeechResponse } from "./speech.js";
|
|
5
|
+
export interface Interface {
|
|
6
|
+
readonly generate: <Options extends SpeechOptions>(request: SpeechRequestFor<Options>) => Effect.Effect<SpeechResponse, AIError>;
|
|
7
|
+
readonly stream: <Options extends SpeechOptions>(request: SpeechRequestFor<Options>) => Stream.Stream<SpeechEvent, AIError>;
|
|
8
|
+
}
|
|
9
|
+
declare const Service_base: Context.ServiceClass<Service, "@opencode/SpeechClient", Interface>;
|
|
10
|
+
export declare class Service extends Service_base {
|
|
11
|
+
}
|
|
12
|
+
export declare const generate: <Options extends SpeechOptions>(request: SpeechRequestFor<Options>) => Effect.Effect<SpeechResponse, AIError, Service>;
|
|
13
|
+
export declare const stream: <Options extends SpeechOptions>(request: SpeechRequestFor<Options>) => Stream.Stream<SpeechEvent, AIError, Service>;
|
|
14
|
+
export declare const layer: Layer.Layer<Service, never, RequestExecutor.Service>;
|
|
15
|
+
export declare const SpeechClient: {
|
|
16
|
+
readonly Service: typeof Service;
|
|
17
|
+
readonly layer: Layer.Layer<Service, never, RequestExecutor.Service>;
|
|
18
|
+
readonly generate: <Options extends SpeechOptions>(request: SpeechRequestFor<Options>) => Effect.Effect<SpeechResponse, AIError, Service>;
|
|
19
|
+
readonly stream: <Options extends SpeechOptions>(request: SpeechRequestFor<Options>) => Stream.Stream<SpeechEvent, AIError, Service>;
|
|
20
|
+
};
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Stream } from "effect";
|
|
2
|
+
import { RequestExecutor } from "./route/executor.js";
|
|
3
|
+
export class Service extends Context.Service()("@opencode/SpeechClient") {
|
|
4
|
+
}
|
|
5
|
+
export const generate = (request) => Effect.gen(function* () {
|
|
6
|
+
const client = yield* Service;
|
|
7
|
+
return yield* client.generate(request);
|
|
8
|
+
});
|
|
9
|
+
export const stream = (request) => Stream.unwrap(Effect.gen(function* () {
|
|
10
|
+
const client = yield* Service;
|
|
11
|
+
return client.stream(request);
|
|
12
|
+
}));
|
|
13
|
+
export const layer = Layer.effect(Service, Effect.gen(function* () {
|
|
14
|
+
const executor = yield* RequestExecutor.Service;
|
|
15
|
+
return Service.of({
|
|
16
|
+
generate: (request) => request.model.route.generate(request, executor.execute),
|
|
17
|
+
stream: (request) => request.model.route.stream(request, executor.execute),
|
|
18
|
+
});
|
|
19
|
+
}));
|
|
20
|
+
export const SpeechClient = {
|
|
21
|
+
Service,
|
|
22
|
+
layer,
|
|
23
|
+
generate,
|
|
24
|
+
stream,
|
|
25
|
+
};
|