@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
|
@@ -2,13 +2,11 @@ import { Effect, Schema } from "effect";
|
|
|
2
2
|
import { ImageModel, ImageResponse } from "../image.js";
|
|
3
3
|
import { MediaProtocol } from "../route/media-protocol.js";
|
|
4
4
|
import { MediaRoute } from "../route/media.js";
|
|
5
|
-
import {
|
|
5
|
+
import { mergeJsonRecords } from "../schema/index.js";
|
|
6
6
|
import { ProviderShared } from "./shared.js";
|
|
7
7
|
import { MediaInput } from "./utils/media-input.js";
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const NAME = "Stability AI";
|
|
11
|
-
const PROVIDER = ProviderID.make("stability");
|
|
8
|
+
const route = MediaProtocol.identity({ id: "stability-images", name: "Stability AI", provider: "stability" });
|
|
9
|
+
const upscaleRoute = MediaProtocol.identity({ id: "stability-upscale", name: "Stability AI", provider: "stability" });
|
|
12
10
|
export const DEFAULT_BASE_URL = "https://api.stability.ai";
|
|
13
11
|
const RESULTS_PATH = "/v2beta/results";
|
|
14
12
|
const UPSCALE_MODEL = "creative";
|
|
@@ -32,26 +30,24 @@ const Started = Schema.Struct({ id: Schema.String });
|
|
|
32
30
|
/** `sd3.5-*` ids share the `sd3` endpoint and select the model with a form field. */
|
|
33
31
|
const endpoint = (model) => (model.startsWith("sd3") ? "sd3" : model);
|
|
34
32
|
const RESERVED_FORM_FIELDS = new Set(["image", "prompt", "mode", "model"]);
|
|
35
|
-
const
|
|
36
|
-
const form = Effect.fn("StabilityImages.form")(function* (route, fields, native, source) {
|
|
33
|
+
const form = Effect.fn("StabilityImages.form")(function* (identity, fields, native, source) {
|
|
37
34
|
if ((source?.length ?? 0) > 1)
|
|
38
|
-
return yield* unsupported(
|
|
35
|
+
return yield* identity.unsupported("media.images", `${identity.name} takes one source image`);
|
|
39
36
|
const body = new FormData();
|
|
40
|
-
|
|
41
|
-
Object.entries(mergeJsonRecords(fields, Object.fromEntries(overlay)) ?? {}).forEach(([key, value]) => body.append(key, typeof value === "string" ? value : ProviderShared.encodeJson(value)));
|
|
37
|
+
MediaInput.appendFields(body, fields, { overlay: native, reserved: RESERVED_FORM_FIELDS });
|
|
42
38
|
const image = source?.[0];
|
|
43
39
|
if (image !== undefined)
|
|
44
|
-
body.append("image", MediaInput.blob(yield* MediaInput.inlineBytes(
|
|
40
|
+
body.append("image", MediaInput.blob(yield* MediaInput.inlineBytes(identity.id, image), image.mediaType), "image");
|
|
45
41
|
return MediaProtocol.multipart(body);
|
|
46
42
|
});
|
|
47
43
|
const fromRequest = Effect.fn("StabilityImages.fromRequest")(function* (request) {
|
|
48
44
|
if (request.n !== undefined && request.n > 1)
|
|
49
|
-
return yield* unsupported(
|
|
45
|
+
return yield* route.unsupported("media.n", `${route.name} generates one image per request; call it once per image`);
|
|
50
46
|
const target = endpoint(request.model.id);
|
|
51
47
|
const edit = (request.images?.length ?? 0) > 0;
|
|
52
48
|
if (edit && target === "core")
|
|
53
|
-
return yield* unsupported(
|
|
54
|
-
return yield* form(
|
|
49
|
+
return yield* route.unsupported("media.images", `${route.name} core is text-to-image only; use ultra or sd3.5-*`);
|
|
50
|
+
return yield* form(route, {
|
|
55
51
|
prompt: request.prompt,
|
|
56
52
|
aspect_ratio: request.aspectRatio,
|
|
57
53
|
seed: request.seed,
|
|
@@ -62,33 +58,33 @@ const fromRequest = Effect.fn("StabilityImages.fromRequest")(function* (request)
|
|
|
62
58
|
});
|
|
63
59
|
const fromUpscaleRequest = Effect.fn("StabilityImages.fromUpscaleRequest")(function* (request) {
|
|
64
60
|
if ((request.images?.length ?? 0) === 0)
|
|
65
|
-
return yield* ProviderShared.invalidRequest(`${
|
|
66
|
-
return yield* form(
|
|
61
|
+
return yield* ProviderShared.invalidRequest(`${upscaleRoute.name} upscale requires the source image in images`);
|
|
62
|
+
return yield* form(upscaleRoute, { prompt: request.prompt, seed: request.seed, output_format: request.format }, mergeJsonRecords(request.providerOptions, request.http?.body), request.images);
|
|
67
63
|
});
|
|
68
64
|
// ---------------------------------------------------------------------------
|
|
69
65
|
// 6. Response decoding
|
|
70
66
|
// ---------------------------------------------------------------------------
|
|
71
|
-
const decodeImageDocument = (
|
|
72
|
-
const decode =
|
|
67
|
+
const decodeImageDocument = (identity) => {
|
|
68
|
+
const decode = identity.decodeJson(ImageDocument);
|
|
73
69
|
return Effect.fn("StabilityImages.decodeImage")(function* (response) {
|
|
74
70
|
const output = yield* decode(response);
|
|
75
71
|
const document = output.value;
|
|
76
72
|
const data = document.image ?? document.result;
|
|
77
73
|
if (data === undefined)
|
|
78
|
-
return yield* output.invalid(`${
|
|
79
|
-
const image = yield* MediaInput.decodedAsset(output.invalid, `${
|
|
74
|
+
return yield* output.invalid(`${identity.name} returned no image`);
|
|
75
|
+
const image = yield* MediaInput.decodedAsset(output.invalid, `${identity.name} result`, data, undefined);
|
|
80
76
|
return new ImageResponse({
|
|
81
77
|
images: [image],
|
|
82
78
|
notices: document.finish_reason === "CONTENT_FILTERED"
|
|
83
|
-
? [{ type: "moderated", message: `${
|
|
79
|
+
? [{ type: "moderated", message: `${identity.name} blurred the image for violating its content policy` }]
|
|
84
80
|
: undefined,
|
|
85
81
|
providerMetadata: { stability: { seed: document.seed, finishReason: document.finish_reason } },
|
|
86
82
|
});
|
|
87
83
|
});
|
|
88
84
|
};
|
|
89
|
-
const decodeResponse = decodeImageDocument(
|
|
90
|
-
const decodeUpscaleImage = decodeImageDocument(
|
|
91
|
-
const decodeStart =
|
|
85
|
+
const decodeResponse = decodeImageDocument(route);
|
|
86
|
+
const decodeUpscaleImage = decodeImageDocument(upscaleRoute);
|
|
87
|
+
const decodeStart = upscaleRoute.decodeStarted(Started, (value) => ({
|
|
92
88
|
token: { id: value.id },
|
|
93
89
|
snapshot: { id: value.id, status: "queued" },
|
|
94
90
|
}));
|
|
@@ -99,24 +95,20 @@ const decodeStatus = (response, context) => Effect.succeed({
|
|
|
99
95
|
});
|
|
100
96
|
const decodeUpscaleResult = Effect.fn("StabilityImages.decodeUpscaleResult")(function* (response, context) {
|
|
101
97
|
if (response.status === 202) {
|
|
102
|
-
const output = yield*
|
|
103
|
-
return yield* output.
|
|
98
|
+
const output = yield* upscaleRoute.text(response);
|
|
99
|
+
return yield* output.pending(context.token.id);
|
|
104
100
|
}
|
|
105
101
|
return yield* decodeUpscaleImage(response);
|
|
106
102
|
});
|
|
107
103
|
// ---------------------------------------------------------------------------
|
|
108
104
|
// 7. Protocol and route
|
|
109
105
|
// ---------------------------------------------------------------------------
|
|
110
|
-
export const protocol = MediaProtocol.inline({
|
|
111
|
-
id: ADAPTER,
|
|
112
|
-
name: NAME,
|
|
106
|
+
export const protocol = MediaProtocol.inline(route, {
|
|
113
107
|
unsupported: ["size", "mask"],
|
|
114
108
|
body: { from: fromRequest },
|
|
115
109
|
response: { decode: decodeResponse },
|
|
116
110
|
});
|
|
117
|
-
export const upscaleProtocol = MediaProtocol.queued({
|
|
118
|
-
id: UPSCALE_ADAPTER,
|
|
119
|
-
name: NAME,
|
|
111
|
+
export const upscaleProtocol = MediaProtocol.queued(upscaleRoute, {
|
|
120
112
|
token: Token,
|
|
121
113
|
unsupported: ["n", "size", "aspectRatio", "mask"],
|
|
122
114
|
start: { body: { from: fromUpscaleRequest }, decode: decodeStart },
|
|
@@ -124,16 +116,12 @@ export const upscaleProtocol = MediaProtocol.queued({
|
|
|
124
116
|
result: { path: (token) => `${RESULTS_PATH}/${token.id}`, decode: decodeUpscaleResult },
|
|
125
117
|
});
|
|
126
118
|
export const model = (input) => ImageModel.fromRoute({
|
|
127
|
-
id: ADAPTER,
|
|
128
|
-
provider: PROVIDER,
|
|
129
119
|
protocol,
|
|
130
120
|
baseURL: DEFAULT_BASE_URL,
|
|
131
121
|
headers: HEADERS,
|
|
132
122
|
path: ({ request }) => `/v2beta/stable-image/generate/${endpoint(request.model.id)}`,
|
|
133
123
|
}, input);
|
|
134
124
|
export const upscaleModel = (input) => ImageModel.fromRoute({
|
|
135
|
-
id: UPSCALE_ADAPTER,
|
|
136
|
-
provider: PROVIDER,
|
|
137
125
|
protocol: upscaleProtocol,
|
|
138
126
|
baseURL: DEFAULT_BASE_URL,
|
|
139
127
|
headers: HEADERS,
|
|
@@ -13,9 +13,7 @@ export declare const Token: Schema.Struct<{
|
|
|
13
13
|
}>;
|
|
14
14
|
export type Token = Schema.Schema.Type<typeof Token>;
|
|
15
15
|
export declare const mediaUrl: (asset: Media.Asset, name: string) => Effect.Effect<string, AIError, never>;
|
|
16
|
-
export declare const protocol: <Request, Response>(input: {
|
|
17
|
-
readonly id: string;
|
|
18
|
-
readonly name: string;
|
|
16
|
+
export declare const protocol: <Request, Response>(route: MediaProtocol.Identity, input: {
|
|
19
17
|
readonly unsupported?: ReadonlyArray<keyof Request & string>;
|
|
20
18
|
readonly from: (request: Request) => Effect.Effect<MediaProtocol.Body, AIError>;
|
|
21
19
|
readonly decodeResult: (response: HttpClientResponse.HttpClientResponse, context: MediaProtocol.PollContext<Token>) => Effect.Effect<Response, AIError>;
|
|
@@ -28,16 +28,14 @@ const STATUS = {
|
|
|
28
28
|
};
|
|
29
29
|
// fal accepts public URLs and data URIs; there is no provider file handle to forward.
|
|
30
30
|
export const mediaUrl = (asset, name) => ProviderShared.mediaReference(asset, undefined, name).pipe(Effect.map((reference) => reference.value));
|
|
31
|
-
export const protocol = (input) => {
|
|
32
|
-
const decodeQueueStatus =
|
|
33
|
-
return MediaProtocol.queued({
|
|
34
|
-
id: input.id,
|
|
35
|
-
name: input.name,
|
|
31
|
+
export const protocol = (route, input) => {
|
|
32
|
+
const decodeQueueStatus = route.decodeJson(QueueStatus);
|
|
33
|
+
return MediaProtocol.queued(route, {
|
|
36
34
|
token: Token,
|
|
37
35
|
unsupported: input.unsupported,
|
|
38
36
|
start: {
|
|
39
37
|
body: { from: input.from },
|
|
40
|
-
decode:
|
|
38
|
+
decode: route.decodeStarted(StartResponse, (value) => ({
|
|
41
39
|
token: {
|
|
42
40
|
requestID: value.request_id,
|
|
43
41
|
statusURL: value.status_url,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { Media } from "../../media.js";
|
|
3
3
|
import type { MediaProtocol } from "../../route/media-protocol.js";
|
|
4
|
-
import type
|
|
4
|
+
import { type AIError, type ProviderID } from "../../schema/index.js";
|
|
5
5
|
/** Owned bytes for multipart uploads; decodes `base64` sources and rejects remote sources. */
|
|
6
6
|
export declare const inlineBytes: (route: string, asset: Media.Asset) => Effect.Effect<Uint8Array, AIError>;
|
|
7
7
|
/** Copied because `BlobPart` requires a plain `ArrayBuffer`. */
|
|
@@ -15,4 +15,18 @@ export declare const dimensions: (size: string) => {
|
|
|
15
15
|
export declare const refID: (asset: Media.Asset, provider: ProviderID) => string | undefined;
|
|
16
16
|
/** Decode a provider's base64 output once into an owned `bytes` asset, sniffing the type when it is not declared. */
|
|
17
17
|
export declare const decodedAsset: (invalid: (message: string, cause?: unknown) => AIError, label: string, data: string, mediaType: string | undefined, options?: Media.AssetOptions) => Effect.Effect<Media.Asset, AIError, never>;
|
|
18
|
+
/** One image of an OpenAI-shaped `data` array, which carries either `b64_json` or a `url`. */
|
|
19
|
+
export declare const imageOutput: (invalid: (message: string, cause?: unknown) => AIError, label: string, item: {
|
|
20
|
+
readonly b64_json?: string | null;
|
|
21
|
+
readonly url?: string | null;
|
|
22
|
+
}, mediaType: string | undefined, options?: Media.AssetOptions) => Effect.Effect<Media.Asset, AIError, never>;
|
|
23
|
+
/**
|
|
24
|
+
* Append multipart text fields: strings as-is, other values as JSON, or arrays as repeated `key[]` parts with
|
|
25
|
+
* `repeatArrays`. `overlay` keys in `reserved` are dropped so `http.body` cannot replace route-owned fields.
|
|
26
|
+
*/
|
|
27
|
+
export declare const appendFields: (form: FormData, fields: Record<string, unknown>, options: {
|
|
28
|
+
readonly overlay?: Record<string, unknown>;
|
|
29
|
+
readonly reserved: ReadonlySet<string>;
|
|
30
|
+
readonly repeatArrays?: true;
|
|
31
|
+
}) => void;
|
|
18
32
|
export * as MediaInput from "./media-input.js";
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Effect, Encoding } from "effect";
|
|
2
2
|
import { Media } from "../../media.js";
|
|
3
|
+
import { mergeJsonRecords } from "../../schema/index.js";
|
|
4
|
+
import { encodeJson } from "../../utils/json.js";
|
|
3
5
|
import { ProviderShared } from "../shared.js";
|
|
4
6
|
/** Owned bytes for multipart uploads; decodes `base64` sources and rejects remote sources. */
|
|
5
7
|
export const inlineBytes = (route, asset) => {
|
|
@@ -32,4 +34,24 @@ export const dimensions = (size) => {
|
|
|
32
34
|
export const refID = (asset, provider) => asset.source.type === "ref" && asset.source.provider === provider ? asset.source.id : undefined;
|
|
33
35
|
/** Decode a provider's base64 output once into an owned `bytes` asset, sniffing the type when it is not declared. */
|
|
34
36
|
export const decodedAsset = (invalid, label, data, mediaType, options) => Effect.fromResult(Encoding.decodeBase64(data)).pipe(Effect.mapError((cause) => invalid(`${label} contains invalid base64 data`, cause)), Effect.map((bytes) => Media.bytes(bytes, mediaType, options)));
|
|
37
|
+
/** One image of an OpenAI-shaped `data` array, which carries either `b64_json` or a `url`. */
|
|
38
|
+
export const imageOutput = (invalid, label, item, mediaType, options) => {
|
|
39
|
+
if (item.b64_json)
|
|
40
|
+
return decodedAsset(invalid, label, item.b64_json, mediaType, options);
|
|
41
|
+
if (item.url)
|
|
42
|
+
return Effect.succeed(Media.url(item.url, { ...options, mediaType }));
|
|
43
|
+
return Effect.fail(invalid(`${label} has neither image data nor a URL`));
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Append multipart text fields: strings as-is, other values as JSON, or arrays as repeated `key[]` parts with
|
|
47
|
+
* `repeatArrays`. `overlay` keys in `reserved` are dropped so `http.body` cannot replace route-owned fields.
|
|
48
|
+
*/
|
|
49
|
+
export const appendFields = (form, fields, options) => {
|
|
50
|
+
const overlay = Object.entries(options.overlay ?? {}).filter(([key]) => !options.reserved.has(key));
|
|
51
|
+
Object.entries(mergeJsonRecords(fields, Object.fromEntries(overlay)) ?? {}).forEach(([key, value]) => {
|
|
52
|
+
if (Array.isArray(value) && options.repeatArrays)
|
|
53
|
+
return value.forEach((item) => form.append(`${key}[]`, String(item)));
|
|
54
|
+
form.append(key, typeof value === "string" ? value : encodeJson(value));
|
|
55
|
+
});
|
|
56
|
+
};
|
|
35
57
|
export * as MediaInput from "./media-input.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Effect, Schema, Stream } from "effect";
|
|
2
2
|
import { Route } from "../../route/client.js";
|
|
3
3
|
import { Protocol } from "../../route/protocol.js";
|
|
4
|
-
import { CompactionCheckpointResponse,
|
|
4
|
+
import { CompactionCheckpointResponse, LLMEvent, LLMRequest } from "../../schema/index.js";
|
|
5
5
|
import { OpenResponses } from "../open-responses.js";
|
|
6
6
|
import { ProviderShared } from "../shared.js";
|
|
7
7
|
const onOutputItem = Effect.fn("ResponsesCheckpoint.onOutputItem")(function* (state, input) {
|
|
@@ -94,12 +94,8 @@ export const make = (body) => Effect.fn("ResponsesCheckpoint.execute")(function*
|
|
|
94
94
|
transport: source.transport,
|
|
95
95
|
});
|
|
96
96
|
const native = yield* body.from(request);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
http: request.http === undefined ? undefined : new HttpOptions({ ...request.http, body: undefined }),
|
|
100
|
-
});
|
|
101
|
-
const prepared = yield* route.prepareTransport(native, preparedRequest, options);
|
|
102
|
-
yield* route.streamPrepared(prepared, preparedRequest, { http: executor }, options).pipe(Stream.runDrain);
|
|
97
|
+
const prepared = yield* route.prepareTransport(native, request, options);
|
|
98
|
+
yield* route.streamPrepared(prepared, request, { http: executor }, options).pipe(Stream.runDrain);
|
|
103
99
|
if (!result)
|
|
104
100
|
return yield* ProviderShared.eventError(source.id, "Compaction response ended without a checkpoint");
|
|
105
101
|
return result;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { AIError, LLMRequest } from "../../schema/index.js";
|
|
1
3
|
import type { CompactOperation } from "../../route/client.js";
|
|
2
4
|
import { OpenResponses } from "../open-responses.js";
|
|
3
|
-
export declare const make: (adapter: OpenResponses.ProviderAdapter) => CompactOperation;
|
|
5
|
+
export declare const make: (adapter: OpenResponses.ProviderAdapter, lowerTools: (request: LLMRequest) => Effect.Effect<ReadonlyArray<Record<string, unknown>>, AIError>) => CompactOperation;
|
|
4
6
|
export * as ResponsesCompaction from "./responses-compaction.js";
|
|
@@ -7,12 +7,18 @@ import { HttpTransport } from "../../route/transport/index.js";
|
|
|
7
7
|
import { OpenResponses } from "../open-responses.js";
|
|
8
8
|
import { JsonObject, optionalNull, ProviderShared } from "../shared.js";
|
|
9
9
|
import { Media } from "../../media.js";
|
|
10
|
+
// /compact has a smaller wire contract than /responses; keep the request controls it accepts.
|
|
10
11
|
const Body = Schema.Struct({
|
|
11
12
|
model: Schema.String,
|
|
12
13
|
input: Schema.Array(Schema.Unknown),
|
|
13
14
|
instructions: optionalNull(Schema.String),
|
|
14
15
|
previous_response_id: optionalNull(Schema.String),
|
|
15
16
|
service_tier: optionalNull(Schema.String),
|
|
17
|
+
reasoning: Schema.optional(JsonObject),
|
|
18
|
+
text: Schema.optional(JsonObject),
|
|
19
|
+
include: OpenResponses.coreFields.include,
|
|
20
|
+
parallel_tool_calls: OpenResponses.coreFields.parallel_tool_calls,
|
|
21
|
+
tools: Schema.optional(Schema.Array(JsonObject)),
|
|
16
22
|
prompt_cache_key: optionalNull(Schema.String),
|
|
17
23
|
prompt_cache_retention: optionalNull(Schema.String),
|
|
18
24
|
prompt_cache_options: optionalNull(Schema.Struct({ mode: Schema.optional(Schema.String), ttl: Schema.optional(Schema.String) })),
|
|
@@ -54,14 +60,21 @@ const Response = Schema.Struct({
|
|
|
54
60
|
])),
|
|
55
61
|
usage: Schema.optional(Schema.StructWithRest(OpenResponses.OpenResponsesUsage, [JsonObject])),
|
|
56
62
|
});
|
|
57
|
-
export const make = (adapter) => Effect.fn("ResponsesCompaction.execute")(function* (request, executor, options) {
|
|
63
|
+
export const make = (adapter, lowerTools) => Effect.fn("ResponsesCompaction.execute")(function* (request, executor, options) {
|
|
58
64
|
const route = request.model.route;
|
|
59
65
|
// The standalone compaction endpoint rejects histories containing configuration updates.
|
|
60
66
|
const native = yield* OpenResponses.lowerConversation(stripEffortUpdates(request), adapter);
|
|
67
|
+
const generation = OpenResponses.lowerGeneration(request);
|
|
68
|
+
const tools = request.tools.length === 0 ? undefined : yield* lowerTools(request);
|
|
61
69
|
const body = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Body))(mergeJsonRecords({
|
|
62
70
|
...native,
|
|
63
|
-
service_tier:
|
|
64
|
-
|
|
71
|
+
service_tier: generation.service_tier,
|
|
72
|
+
reasoning: generation.reasoning,
|
|
73
|
+
text: generation.text,
|
|
74
|
+
include: generation.include,
|
|
75
|
+
parallel_tool_calls: generation.parallel_tool_calls,
|
|
76
|
+
tools,
|
|
77
|
+
prompt_cache_key: generation.prompt_cache_key,
|
|
65
78
|
}, request.http?.body));
|
|
66
79
|
const url = Endpoint.render(route.endpoint, { request, body: native });
|
|
67
80
|
url.pathname = `${url.pathname.replace(/\/$/, "")}/compact`;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { Media } from "../../media.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { MediaProtocol } from "../../route/media-protocol.js";
|
|
4
|
+
import type { AIError, MediaUsage, ProviderMetadata } from "../../schema/index.js";
|
|
4
5
|
import { type SpeechEvent, type SpeechVoice } from "../../speech.js";
|
|
5
6
|
export interface Audio {
|
|
6
7
|
/** Appended in place: the route creates fresh state for each response through `initial`. */
|
|
@@ -36,9 +37,8 @@ export declare const pcm: (encoding: PcmEncoding, sampleRate: number | undefined
|
|
|
36
37
|
};
|
|
37
38
|
};
|
|
38
39
|
export declare const sampleRate: (mediaType: string | undefined) => number | undefined;
|
|
39
|
-
export declare const unsupportedFormat: (provider: ProviderID, route: string, message: string) => AIError;
|
|
40
40
|
/** A declared `mediaType` wins over sniffing: headerless PCM can start with bytes that look like an MPEG frame sync. */
|
|
41
|
-
export declare const finish: (route:
|
|
41
|
+
export declare const finish: (route: MediaProtocol.Identity, state: Audio, output: {
|
|
42
42
|
readonly mediaType: string | undefined;
|
|
43
43
|
readonly info?: Media.Info;
|
|
44
44
|
readonly usage?: MediaUsage;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { Media } from "../../media.js";
|
|
3
|
-
import { MediaProtocol } from "../../route/media-protocol.js";
|
|
4
3
|
import { SpeechAudioDeltaEvent, SpeechFinishEvent, SpeechTimestampsEvent, } from "../../speech.js";
|
|
5
4
|
import { concatBytes } from "../../utils/bytes.js";
|
|
6
|
-
import { ProviderShared } from "../shared.js";
|
|
7
5
|
/** Empty chunks (keep-alive records) emit nothing. */
|
|
8
6
|
export const delta = (state, chunk) => {
|
|
9
7
|
if (chunk.length === 0)
|
|
@@ -45,11 +43,10 @@ export const sampleRate = (mediaType) => {
|
|
|
45
43
|
const rate = /rate=(\d+)/i.exec(mediaType ?? "")?.[1];
|
|
46
44
|
return rate === undefined ? undefined : Number(rate);
|
|
47
45
|
};
|
|
48
|
-
export const unsupportedFormat = (provider, route, message) => ProviderShared.unsupportedOperation({ operation: "media.format", provider, route, message });
|
|
49
46
|
/** A declared `mediaType` wins over sniffing: headerless PCM can start with bytes that look like an MPEG frame sync. */
|
|
50
47
|
export const finish = (route, state, output) => {
|
|
51
48
|
if (state.chunks.length === 0)
|
|
52
|
-
return Effect.fail(
|
|
49
|
+
return Effect.fail(route.frameError(`The provider returned no audio${output.detail === undefined ? "" : ` (${output.detail})`}`));
|
|
53
50
|
return Effect.succeed([
|
|
54
51
|
SpeechFinishEvent.make({
|
|
55
52
|
audio: Media.bytes(concatBytes(state.chunks), output.mediaType, { info: output.info }),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type JsonSchema, type LanguageModel, type LLMRequest } from "../../schema/index.js";
|
|
2
2
|
export declare const ToolSchemaProjection: {
|
|
3
3
|
readonly gemini: (schema: JsonSchema) => JsonSchema;
|
|
4
|
-
readonly modelCompatibility: (schema: JsonSchema, model: LanguageModel, protocolDefault?: LanguageModelSanitizerCompatibility) => JsonSchema;
|
|
5
4
|
readonly moonshot: (schema: JsonSchema) => JsonSchema;
|
|
6
5
|
readonly openAI: (schema: JsonSchema) => JsonSchema;
|
|
7
6
|
readonly responses: (schema: JsonSchema) => JsonSchema;
|
|
7
|
+
readonly tools: (entries: LLMRequest["tools"], model: LanguageModel) => LLMRequest["tools"];
|
|
8
8
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ToolDefinition } from "../../schema/index.js";
|
|
1
2
|
import { isRecord } from "../../utils/record.js";
|
|
2
3
|
import { GeminiJsonSchema } from "./gemini-json-schema.js";
|
|
3
4
|
const tupleItemsSchema = (items) => {
|
|
@@ -54,24 +55,43 @@ const MODEL_NAMES = [
|
|
|
54
55
|
[/gemini/i, "gemini"],
|
|
55
56
|
[/kimi/i, "moonshot"],
|
|
56
57
|
];
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
const
|
|
61
|
-
|
|
58
|
+
// Tool arguments are always a JSON object, and most providers reject a tool schema whose root does not
|
|
59
|
+
// declare `type: "object"`, such as `{}` or a bare `properties` map. Effect encodes an empty struct as
|
|
60
|
+
// `anyOf` object or array; every object matches its bare object branch, so that `anyOf` is dropped.
|
|
61
|
+
const objectRoot = (schema) => {
|
|
62
|
+
if (schema.type !== undefined)
|
|
63
|
+
return schema;
|
|
64
|
+
if (Array.isArray(schema.anyOf) &&
|
|
65
|
+
schema.anyOf.some((branch) => isRecord(branch) && branch.type === "object" && Object.keys(branch).length === 1))
|
|
66
|
+
return { type: "object", ...Object.fromEntries(Object.entries(schema).filter(([key]) => key !== "anyOf")) };
|
|
67
|
+
return { type: "object", ...schema };
|
|
68
|
+
};
|
|
69
|
+
// Every tool schema gets an object root. Then an explicit `sanitizer` wins, and `none` opts out.
|
|
70
|
+
// Otherwise the protocol's own default applies (the Gemini API always uses Gemini's rules), then the
|
|
71
|
+
// model name selects the family's rules so models reached through gateways and OpenAI-compatible
|
|
72
|
+
// endpoints get the same handling.
|
|
73
|
+
const modelCompatibility = (schema, model) => {
|
|
74
|
+
const root = objectRoot(schema);
|
|
75
|
+
switch (model.compatibility?.sanitizer ??
|
|
76
|
+
model.route.sanitizer ??
|
|
77
|
+
MODEL_NAMES.find(([name]) => name.test(model.id))?.[1]) {
|
|
62
78
|
case "gemini":
|
|
63
|
-
return gemini(
|
|
79
|
+
return gemini(root);
|
|
64
80
|
case "moonshot":
|
|
65
|
-
return moonshot(
|
|
81
|
+
return moonshot(root);
|
|
66
82
|
case "none":
|
|
67
83
|
case undefined:
|
|
68
|
-
return
|
|
84
|
+
return root;
|
|
69
85
|
}
|
|
70
86
|
};
|
|
87
|
+
// Applied once to every request before any protocol builds its body, including tools in namespaces.
|
|
88
|
+
const tools = (entries, model) => entries.map((tool) => tool.type === "tool"
|
|
89
|
+
? new ToolDefinition({ ...tool, inputSchema: modelCompatibility(tool.inputSchema, model) })
|
|
90
|
+
: { ...tool, tools: tools(tool.tools, model) });
|
|
71
91
|
export const ToolSchemaProjection = {
|
|
72
92
|
gemini,
|
|
73
|
-
modelCompatibility,
|
|
74
93
|
moonshot,
|
|
75
94
|
openAI,
|
|
76
95
|
responses,
|
|
96
|
+
tools,
|
|
77
97
|
};
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { ImageModel, ImageResponse, type ImageRequestFor } from "../image.js";
|
|
2
2
|
import { MediaProtocol } from "../route/media-protocol.js";
|
|
3
3
|
import { MediaRoute } from "../route/media.js";
|
|
4
|
+
import { type OpenString } from "../schema/index.js";
|
|
4
5
|
export declare const DEFAULT_BASE_URL = "https://api.x.ai/v1";
|
|
5
6
|
export declare const PATH = "/images/generations";
|
|
6
7
|
export declare const EDIT_PATH = "/images/edits";
|
|
7
|
-
export type XAIImageString<Known extends string> = Known | (string & {});
|
|
8
8
|
/** Provider-native options. Common fields (`n`, `aspectRatio`, `images`) live on the request. */
|
|
9
9
|
export type XAIImageOptions = {
|
|
10
|
-
readonly resolution?:
|
|
11
|
-
readonly responseFormat?:
|
|
12
|
-
readonly response_format?:
|
|
10
|
+
readonly resolution?: OpenString<"1k" | "2k">;
|
|
11
|
+
readonly responseFormat?: OpenString<"url" | "b64_json">;
|
|
12
|
+
readonly response_format?: OpenString<"url" | "b64_json">;
|
|
13
13
|
} & Record<string, unknown>;
|
|
14
14
|
export type Request = ImageRequestFor<XAIImageOptions>;
|
|
15
15
|
export declare const protocol: MediaProtocol.Inline<Request, ImageResponse>;
|
|
@@ -3,12 +3,10 @@ import { ImageModel, ImageResponse } from "../image.js";
|
|
|
3
3
|
import { Media } from "../media.js";
|
|
4
4
|
import { MediaProtocol } from "../route/media-protocol.js";
|
|
5
5
|
import { MediaRoute } from "../route/media.js";
|
|
6
|
-
import {
|
|
6
|
+
import { mergeJsonRecords } from "../schema/index.js";
|
|
7
7
|
import { ProviderShared, optionalNull } from "./shared.js";
|
|
8
8
|
import { MediaInput } from "./utils/media-input.js";
|
|
9
|
-
const
|
|
10
|
-
const NAME = "xAI Images";
|
|
11
|
-
const PROVIDER = ProviderID.make("xai");
|
|
9
|
+
const route = MediaProtocol.identity({ id: "xai-images", name: "xAI Images", provider: "xai" });
|
|
12
10
|
export const DEFAULT_BASE_URL = "https://api.x.ai/v1";
|
|
13
11
|
export const PATH = "/images/generations";
|
|
14
12
|
export const EDIT_PATH = "/images/edits";
|
|
@@ -34,7 +32,7 @@ const nativeOptions = (options) => {
|
|
|
34
32
|
return { response_format: responseFormat, ...native };
|
|
35
33
|
};
|
|
36
34
|
const isEdit = (request) => (request.images?.length ?? 0) > 0;
|
|
37
|
-
const reference = (asset) => ProviderShared.mediaReference(asset,
|
|
35
|
+
const reference = (asset) => ProviderShared.mediaReference(asset, route.provider, route.name).pipe(Effect.map((item) => item.type === "ref" ? { file_id: item.value } : { url: item.value, type: "image_url" }));
|
|
38
36
|
const fromRequest = Effect.fn("XAIImages.fromRequest")(function* (request) {
|
|
39
37
|
const references = yield* Effect.forEach(request.images ?? [], reference);
|
|
40
38
|
return MediaProtocol.json(mergeJsonRecords({
|
|
@@ -50,25 +48,20 @@ const fromRequest = Effect.fn("XAIImages.fromRequest")(function* (request) {
|
|
|
50
48
|
// ---------------------------------------------------------------------------
|
|
51
49
|
// 6. Response decoding
|
|
52
50
|
// ---------------------------------------------------------------------------
|
|
51
|
+
const decodeDocument = route.decodeJson(XAIImageResponse);
|
|
53
52
|
const decodeResponse = Effect.fn("XAIImages.decodeResponse")(function* (response) {
|
|
54
|
-
const output = yield*
|
|
53
|
+
const output = yield* decodeDocument(response);
|
|
55
54
|
const decoded = output.value;
|
|
56
|
-
const images = yield* Effect.forEach(decoded.data, (item, index) => {
|
|
57
|
-
|
|
55
|
+
const images = yield* Effect.forEach(decoded.data, (item, index) => MediaInput.imageOutput(output.invalid, `${route.name} result ${index}`, item, item.mime_type ?? undefined, {
|
|
56
|
+
providerMetadata: item.revised_prompt === undefined || item.revised_prompt === null
|
|
58
57
|
? undefined
|
|
59
|
-
: { xai: { revisedPrompt: item.revised_prompt } }
|
|
60
|
-
|
|
61
|
-
return MediaInput.decodedAsset(output.invalid, `${NAME} result ${index}`, item.b64_json, item.mime_type ?? undefined, {
|
|
62
|
-
providerMetadata,
|
|
63
|
-
});
|
|
64
|
-
if (item.url)
|
|
65
|
-
return Effect.succeed(Media.url(item.url, { mediaType: item.mime_type ?? undefined, providerMetadata }));
|
|
66
|
-
return Effect.fail(output.invalid(`${NAME} result ${index} has neither image data nor a URL`));
|
|
67
|
-
});
|
|
58
|
+
: { xai: { revisedPrompt: item.revised_prompt } },
|
|
59
|
+
}));
|
|
68
60
|
if (images.length === 0)
|
|
69
|
-
return yield* output.invalid(`${
|
|
61
|
+
return yield* output.invalid(`${route.name} returned no images`);
|
|
70
62
|
const usage = ProviderShared.isRecord(decoded.usage) ? decoded.usage : undefined;
|
|
71
|
-
// xAI reports
|
|
63
|
+
// xAI reports a USD cost (`cost_in_usd_ticks`) rather than tokens, seconds, or credits; the raw record stays in
|
|
64
|
+
// provider metadata.
|
|
72
65
|
return new ImageResponse({
|
|
73
66
|
images,
|
|
74
67
|
providerMetadata: usage === undefined ? undefined : { xai: { usage } },
|
|
@@ -77,20 +70,12 @@ const decodeResponse = Effect.fn("XAIImages.decodeResponse")(function* (response
|
|
|
77
70
|
// ---------------------------------------------------------------------------
|
|
78
71
|
// 7. Protocol and route
|
|
79
72
|
// ---------------------------------------------------------------------------
|
|
80
|
-
export const protocol = MediaProtocol.inline({
|
|
81
|
-
id: ADAPTER,
|
|
82
|
-
name: NAME,
|
|
73
|
+
export const protocol = MediaProtocol.inline(route, {
|
|
83
74
|
unsupported: ["mask", "size", "seed", "format"],
|
|
84
75
|
body: { from: fromRequest },
|
|
85
76
|
response: { decode: decodeResponse },
|
|
86
77
|
});
|
|
87
|
-
export const model = (input) => ImageModel.fromRoute({
|
|
88
|
-
id: ADAPTER,
|
|
89
|
-
provider: PROVIDER,
|
|
90
|
-
protocol,
|
|
91
|
-
baseURL: DEFAULT_BASE_URL,
|
|
92
|
-
path: ({ request }) => (isEdit(request) ? EDIT_PATH : PATH),
|
|
93
|
-
}, input);
|
|
78
|
+
export const model = (input) => ImageModel.fromRoute({ protocol, baseURL: DEFAULT_BASE_URL, path: ({ request }) => (isEdit(request) ? EDIT_PATH : PATH) }, input);
|
|
94
79
|
export const XAIImages = {
|
|
95
80
|
protocol,
|
|
96
81
|
model,
|
|
@@ -77,5 +77,5 @@ export const protocol = Protocol.make({
|
|
|
77
77
|
terminal: OpenResponses.terminal,
|
|
78
78
|
},
|
|
79
79
|
});
|
|
80
|
-
export const compact = ResponsesCompaction.make(adapter);
|
|
80
|
+
export const compact = ResponsesCompaction.make(adapter, (request) => OpenResponses.lowerTools(ProviderShared.flattenTools(request.tools), adapter));
|
|
81
81
|
export * as XAIResponses from "./xai-responses.js";
|