@opencode/ai 2.0.14 → 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 +399 -56
- package/dist/experimental/evaluation-client.d.ts +1 -1
- package/dist/experimental/evaluation-client.js +39 -3
- package/dist/experimental/evaluation.d.ts +4 -4
- package/dist/experimental/evaluation.js +2 -2
- package/dist/experimental/system-one.d.ts +3 -3
- package/dist/experimental/system-one.js +40 -51
- package/dist/generation.d.ts +83 -0
- package/dist/generation.js +113 -0
- package/dist/image-client.d.ts +16 -7
- package/dist/image-client.js +29 -13
- package/dist/image.d.ts +1410 -81
- package/dist/image.js +97 -67
- package/dist/index.d.ts +17 -2
- package/dist/index.js +12 -1
- package/dist/llm.d.ts +9 -1
- package/dist/media-model.d.ts +44 -0
- package/dist/media-model.js +49 -0
- package/dist/media.d.ts +213 -0
- package/dist/media.js +227 -0
- package/dist/promise.d.ts +974 -0
- package/dist/promise.js +81 -0
- package/dist/protocols/alibaba-chat.d.ts +12 -0
- package/dist/protocols/alibaba-responses.d.ts +2 -2
- package/dist/protocols/anthropic-messages.js +8 -19
- package/dist/protocols/assemblyai-transcription.d.ts +40 -0
- package/dist/protocols/assemblyai-transcription.js +138 -0
- package/dist/protocols/bedrock-converse.d.ts +4 -4
- package/dist/protocols/bedrock-converse.js +6 -17
- 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 +30 -9
- package/dist/protocols/gemini.js +45 -35
- package/dist/protocols/google-images.d.ts +9 -21
- package/dist/protocols/google-images.js +158 -133
- 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.d.ts +7 -12
- package/dist/protocols/meta-images.js +85 -66
- package/dist/protocols/meta-responses.d.ts +4 -4
- package/dist/protocols/meta-responses.js +1 -1
- package/dist/protocols/mistral-chat.js +7 -6
- package/dist/protocols/open-responses.d.ts +17 -9
- package/dist/protocols/open-responses.js +24 -14
- package/dist/protocols/openai-chat.d.ts +118 -1
- package/dist/protocols/openai-chat.js +125 -44
- 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 +128 -18
- package/dist/protocols/openai-images.js +177 -154
- 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 +27 -17
- package/dist/protocols/shared.js +52 -35
- package/dist/protocols/stability-images.d.ts +38 -0
- package/dist/protocols/stability-images.js +148 -0
- package/dist/protocols/utils/bedrock-media.d.ts +2 -3
- package/dist/protocols/utils/bedrock-media.js +4 -4
- 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 +18 -0
- package/dist/protocols/utils/media-input.js +35 -0
- package/dist/protocols/utils/responses-compaction.js +6 -5
- 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/utils/tool-stream.d.ts +27 -3
- package/dist/protocols/xai-images.d.ts +9 -15
- package/dist/protocols/xai-images.js +75 -84
- 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/protocols/zai-images.d.ts +9 -13
- package/dist/protocols/zai-images.js +59 -57
- 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/amazon-bedrock.d.ts +2 -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/cerebras.js +6 -1
- 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/deepinfra.js +6 -1
- 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 +10 -3
- package/dist/providers/google.d.ts +25 -3
- package/dist/providers/google.js +11 -2
- package/dist/providers/groq.d.ts +24 -0
- package/dist/providers/index.d.ts +10 -0
- package/dist/providers/index.js +10 -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 +67 -0
- package/dist/providers/openrouter.js +13 -1
- 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/vercel-ai-gateway.d.ts +41 -0
- package/dist/providers/vercel-ai-gateway.js +85 -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/client.d.ts +9 -1
- package/dist/route/endpoint.d.ts +10 -10
- package/dist/route/executor-service.d.ts +12 -0
- package/dist/route/executor-service.js +3 -0
- package/dist/route/executor.d.ts +4 -9
- package/dist/route/executor.js +3 -3
- package/dist/route/framing.d.ts +5 -1
- package/dist/route/framing.js +9 -0
- package/dist/route/index.d.ts +2 -0
- package/dist/route/index.js +2 -0
- package/dist/route/media-protocol.d.ts +158 -0
- package/dist/route/media-protocol.js +96 -0
- package/dist/route/media.d.ts +97 -0
- package/dist/route/media.js +236 -0
- package/dist/schema/errors.d.ts +13 -3
- package/dist/schema/errors.js +7 -0
- package/dist/schema/events.d.ts +557 -40
- package/dist/schema/events.js +35 -2
- package/dist/schema/messages.d.ts +95 -8
- package/dist/schema/messages.js +8 -6
- package/dist/schema/options.d.ts +6 -3
- package/dist/schema/options.js +6 -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/testing.d.ts +72 -8
- 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 +7 -0
- package/dist/utils/media-type.js +70 -0
- package/dist/utils/sanitize.js +3 -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 +7 -3
- package/dist/protocols/utils/gemini-tool-schema.d.ts +0 -2
- package/dist/protocols/utils/gemini-tool-schema.js +0 -103
- package/dist/protocols/utils/image-input.d.ts +0 -21
- package/dist/protocols/utils/image-input.js +0 -20
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { isRecord } from "../../utils/record.js";
|
|
2
|
+
// Gemini's `parametersJsonSchema` accepts standard JSON Schema, but rejects a few shapes that
|
|
3
|
+
// published tool schemas commonly contain. Rewrite only those and send everything else unchanged.
|
|
4
|
+
const SCHEMA_MAPS = new Set([
|
|
5
|
+
"properties",
|
|
6
|
+
"patternProperties",
|
|
7
|
+
"$defs",
|
|
8
|
+
"definitions",
|
|
9
|
+
"dependentSchemas",
|
|
10
|
+
"dependencies",
|
|
11
|
+
]);
|
|
12
|
+
const VALUES = new Set(["const", "default", "enum", "examples", "dependentRequired"]);
|
|
13
|
+
const mapValues = (record, map) => Object.fromEntries(Object.entries(record).map(([key, value]) => [key, map(value, key)]));
|
|
14
|
+
const normalizeNode = (schema) => {
|
|
15
|
+
if (Array.isArray(schema))
|
|
16
|
+
return schema.map(normalizeNode);
|
|
17
|
+
if (!isRecord(schema))
|
|
18
|
+
return schema;
|
|
19
|
+
const properties = isRecord(schema.properties) ? schema.properties : undefined;
|
|
20
|
+
return Object.fromEntries(Object.entries(schema).flatMap(([key, value]) => {
|
|
21
|
+
if (VALUES.has(key))
|
|
22
|
+
return [[key, value]];
|
|
23
|
+
if (SCHEMA_MAPS.has(key) && isRecord(value))
|
|
24
|
+
return [[key, mapValues(value, normalizeNode)]];
|
|
25
|
+
// `required` may only name declared properties.
|
|
26
|
+
if (key === "required" && properties && Array.isArray(value))
|
|
27
|
+
return [[key, value.filter((name) => typeof name === "string" && Object.hasOwn(properties, name))]];
|
|
28
|
+
// Draft-04 boolean exclusive bounds become the numeric form.
|
|
29
|
+
if (key === "exclusiveMinimum" && typeof value === "boolean")
|
|
30
|
+
return value && typeof schema.minimum === "number" ? [[key, schema.minimum]] : [];
|
|
31
|
+
if (key === "exclusiveMaximum" && typeof value === "boolean")
|
|
32
|
+
return value && typeof schema.maximum === "number" ? [[key, schema.maximum]] : [];
|
|
33
|
+
if (key === "minimum" && schema.exclusiveMinimum === true)
|
|
34
|
+
return [];
|
|
35
|
+
if (key === "maximum" && schema.exclusiveMaximum === true)
|
|
36
|
+
return [];
|
|
37
|
+
// Draft-07 tuples (`items` array plus `additionalItems`) are `prefixItems` plus `items` in 2020-12.
|
|
38
|
+
if (key === "items" && Array.isArray(value))
|
|
39
|
+
return [["prefixItems", value.map(normalizeNode)]];
|
|
40
|
+
if (key === "additionalItems" && Array.isArray(schema.items))
|
|
41
|
+
return [["items", normalizeNode(value)]];
|
|
42
|
+
return [[key, normalizeNode(value)]];
|
|
43
|
+
}));
|
|
44
|
+
};
|
|
45
|
+
// Gemini accepts a recursive `$ref` only when the loop passes through an optional property or
|
|
46
|
+
// potentially empty array `items`. Replace other self-references with an unconstrained schema.
|
|
47
|
+
const cutLoops = (schema, target, safe) => {
|
|
48
|
+
if (Array.isArray(schema))
|
|
49
|
+
return schema.map((item) => cutLoops(item, target, safe));
|
|
50
|
+
if (!isRecord(schema))
|
|
51
|
+
return schema;
|
|
52
|
+
if (schema.$ref === target && !safe)
|
|
53
|
+
return {};
|
|
54
|
+
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
55
|
+
return mapValues(schema, (value, key) => {
|
|
56
|
+
if (VALUES.has(key))
|
|
57
|
+
return value;
|
|
58
|
+
if (key === "items")
|
|
59
|
+
return cutLoops(value, target, safe || !(Number(schema.minItems) > 0));
|
|
60
|
+
if (key === "properties" && isRecord(value))
|
|
61
|
+
return mapValues(value, (child, name) => cutLoops(child, target, safe || !required.includes(name)));
|
|
62
|
+
if (SCHEMA_MAPS.has(key) && isRecord(value))
|
|
63
|
+
return mapValues(value, (child) => cutLoops(child, target, safe));
|
|
64
|
+
return cutLoops(value, target, safe);
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
export const normalize = (schema) => {
|
|
68
|
+
const normalized = normalizeNode(schema);
|
|
69
|
+
if (!isRecord(normalized))
|
|
70
|
+
return {};
|
|
71
|
+
const result = cutLoops(mapValues(normalized, (value, key) => (key === "$defs" || key === "definitions") && isRecord(value)
|
|
72
|
+
? mapValues(value, (def, name) => cutLoops(def, `#/${key}/${name}`, false))
|
|
73
|
+
: value), "#", false);
|
|
74
|
+
return isRecord(result) ? result : {};
|
|
75
|
+
};
|
|
76
|
+
export * as GeminiJsonSchema from "./gemini-json-schema.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Media } from "../../media.js";
|
|
3
|
+
import type { MediaProtocol } from "../../route/media-protocol.js";
|
|
4
|
+
import type { AIError, ProviderID } from "../../schema/index.js";
|
|
5
|
+
/** Owned bytes for multipart uploads; decodes `base64` sources and rejects remote sources. */
|
|
6
|
+
export declare const inlineBytes: (route: string, asset: Media.Asset) => Effect.Effect<Uint8Array, AIError>;
|
|
7
|
+
/** Copied because `BlobPart` requires a plain `ArrayBuffer`. */
|
|
8
|
+
export declare const blob: (data: Uint8Array, mediaType: string) => Blob;
|
|
9
|
+
export declare const query: (route: string, values: Record<string, unknown>) => Effect.Effect<MediaProtocol.Query, AIError>;
|
|
10
|
+
export declare const dimensions: (size: string) => {
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
};
|
|
14
|
+
/** Provider file handle when the ref belongs to this provider; refs from other providers are never forwarded. */
|
|
15
|
+
export declare const refID: (asset: Media.Asset, provider: ProviderID) => string | undefined;
|
|
16
|
+
/** Decode a provider's base64 output once into an owned `bytes` asset, sniffing the type when it is not declared. */
|
|
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
|
+
export * as MediaInput from "./media-input.js";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Effect, Encoding } from "effect";
|
|
2
|
+
import { Media } from "../../media.js";
|
|
3
|
+
import { ProviderShared } from "../shared.js";
|
|
4
|
+
/** Owned bytes for multipart uploads; decodes `base64` sources and rejects remote sources. */
|
|
5
|
+
export const inlineBytes = (route, asset) => {
|
|
6
|
+
if (asset.source.type === "bytes")
|
|
7
|
+
return Effect.succeed(asset.source.data);
|
|
8
|
+
const inline = asset.inline();
|
|
9
|
+
if (!inline)
|
|
10
|
+
return Effect.fail(ProviderShared.inlineRequired(route, asset));
|
|
11
|
+
return Effect.fromResult(Encoding.decodeBase64(inline.base64)).pipe(Effect.mapError((cause) => ProviderShared.invalidRequest(`${route} media contains invalid base64 data`, cause)));
|
|
12
|
+
};
|
|
13
|
+
/** Copied because `BlobPart` requires a plain `ArrayBuffer`. */
|
|
14
|
+
export const blob = (data, mediaType) => {
|
|
15
|
+
const buffer = new ArrayBuffer(data.byteLength);
|
|
16
|
+
new Uint8Array(buffer).set(data);
|
|
17
|
+
return new Blob([buffer], { type: mediaType });
|
|
18
|
+
};
|
|
19
|
+
const isScalar = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
20
|
+
export const query = (route, values) => {
|
|
21
|
+
const entries = Object.entries(values).filter(([, value]) => value !== undefined);
|
|
22
|
+
const invalid = entries.find(([, value]) => !isScalar(value) && !(Array.isArray(value) && value.every(isScalar)));
|
|
23
|
+
if (invalid !== undefined)
|
|
24
|
+
return Effect.fail(ProviderShared.invalidRequest(`${route} cannot send "${invalid[0]}" as a query parameter`));
|
|
25
|
+
return Effect.succeed(Object.fromEntries(entries.map(([key, value]) => [key, Array.isArray(value) ? value.map(String) : String(value)])));
|
|
26
|
+
};
|
|
27
|
+
export const dimensions = (size) => {
|
|
28
|
+
const [width, height] = size.split("x").map(Number);
|
|
29
|
+
return { width, height };
|
|
30
|
+
};
|
|
31
|
+
/** Provider file handle when the ref belongs to this provider; refs from other providers are never forwarded. */
|
|
32
|
+
export const refID = (asset, provider) => asset.source.type === "ref" && asset.source.provider === provider ? asset.source.id : undefined;
|
|
33
|
+
/** Decode a provider's base64 output once into an owned `bytes` asset, sniffing the type when it is not declared. */
|
|
34
|
+
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)));
|
|
35
|
+
export * as MediaInput from "./media-input.js";
|
|
@@ -6,6 +6,7 @@ import { RequestExecutor } from "../../route/executor.js";
|
|
|
6
6
|
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
|
+
import { Media } from "../../media.js";
|
|
9
10
|
const Body = Schema.Struct({
|
|
10
11
|
model: Schema.String,
|
|
11
12
|
input: Schema.Array(Schema.Unknown),
|
|
@@ -113,19 +114,19 @@ function toMessage(item, model) {
|
|
|
113
114
|
if (part.type === "input_image")
|
|
114
115
|
return {
|
|
115
116
|
type: "media",
|
|
116
|
-
|
|
117
|
-
mediaType: /^data:([^;,]+)/.exec(part.image_url)?.[1] ?? "image/*",
|
|
117
|
+
media: replayMedia(part.image_url, "image/*"),
|
|
118
118
|
providerMetadata: part.detail === undefined ? undefined : { [key]: { detail: part.detail } },
|
|
119
119
|
};
|
|
120
|
-
const data = part.file_url === undefined ? part.file_data : part.file_url;
|
|
121
120
|
return {
|
|
122
121
|
type: "media",
|
|
123
|
-
|
|
122
|
+
media: replayMedia(part.file_url === undefined ? part.file_data : part.file_url, "application/octet-stream"),
|
|
124
123
|
filename: part.filename,
|
|
125
|
-
mediaType: /^data:([^;,]+)/.exec(data)?.[1] ?? "application/octet-stream",
|
|
126
124
|
providerMetadata: part.detail === undefined ? undefined : { [key]: { detail: part.detail } },
|
|
127
125
|
};
|
|
128
126
|
}),
|
|
129
127
|
});
|
|
130
128
|
}
|
|
129
|
+
/** Replayed compaction items carry either a data URL or a remote URL; the data URL's own type wins when present. */
|
|
130
|
+
const replayMedia = (value, fallbackType) => Media.parseDataUrl(value) ??
|
|
131
|
+
(/^https?:\/\//.test(value) ? Media.url(value, { mediaType: fallbackType }) : Media.base64(value, fallbackType));
|
|
131
132
|
export * as ResponsesCompaction from "./responses-compaction.js";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Media } from "../../media.js";
|
|
3
|
+
import type { AIError, MediaUsage, ProviderID, ProviderMetadata } from "../../schema/index.js";
|
|
4
|
+
import { type SpeechEvent, type SpeechVoice } from "../../speech.js";
|
|
5
|
+
export interface Audio {
|
|
6
|
+
/** Appended in place: the route creates fresh state for each response through `initial`. */
|
|
7
|
+
readonly chunks: Array<Uint8Array>;
|
|
8
|
+
}
|
|
9
|
+
export type StepResult<State> = readonly [State, ReadonlyArray<SpeechEvent>];
|
|
10
|
+
/** Empty chunks (keep-alive records) emit nothing. */
|
|
11
|
+
export declare const delta: <State extends Audio>(state: State, chunk: Uint8Array) => StepResult<State>;
|
|
12
|
+
export declare const step: <State extends Audio>(onRecord: (state: State, frame: string) => Effect.Effect<StepResult<State>, AIError>) => (state: State, frame: string | Uint8Array) => Effect.Effect<StepResult<State>, AIError, never>;
|
|
13
|
+
export declare const timestamps: (texts: ReadonlyArray<string>, starts: ReadonlyArray<number>, ends: ReadonlyArray<number>) => ReadonlyArray<SpeechEvent>;
|
|
14
|
+
export declare const voiceID: (voice: SpeechVoice | undefined) => string | undefined;
|
|
15
|
+
export declare const container: (format: string, sampleRate?: number) => {
|
|
16
|
+
mediaType: string;
|
|
17
|
+
info: {
|
|
18
|
+
format: string;
|
|
19
|
+
sampleRate: number | undefined;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
declare const PCM_MEDIA_TYPES: {
|
|
23
|
+
readonly pcm_s16le: "audio/pcm";
|
|
24
|
+
readonly pcm_f32le: "audio/pcm";
|
|
25
|
+
readonly pcm_mulaw: "audio/mulaw";
|
|
26
|
+
readonly pcm_alaw: "audio/alaw";
|
|
27
|
+
};
|
|
28
|
+
export type PcmEncoding = keyof typeof PCM_MEDIA_TYPES;
|
|
29
|
+
export declare const pcm: (encoding: PcmEncoding, sampleRate: number | undefined, mediaType?: string) => {
|
|
30
|
+
mediaType: string;
|
|
31
|
+
info: {
|
|
32
|
+
format: string;
|
|
33
|
+
encoding: "pcm_s16le" | "pcm_f32le" | "pcm_mulaw" | "pcm_alaw";
|
|
34
|
+
sampleRate: number | undefined;
|
|
35
|
+
channels: number;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
export declare const sampleRate: (mediaType: string | undefined) => number | undefined;
|
|
39
|
+
export declare const unsupportedFormat: (provider: ProviderID, route: string, message: string) => AIError;
|
|
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: string, state: Audio, output: {
|
|
42
|
+
readonly mediaType: string | undefined;
|
|
43
|
+
readonly info?: Media.Info;
|
|
44
|
+
readonly usage?: MediaUsage;
|
|
45
|
+
readonly providerMetadata?: ProviderMetadata;
|
|
46
|
+
readonly detail?: string;
|
|
47
|
+
}) => Effect.Effect<ReadonlyArray<SpeechEvent>, AIError>;
|
|
48
|
+
export declare const headerUsage: (type: "characters" | "credits", value: string | undefined) => MediaUsage | undefined;
|
|
49
|
+
export * as SpeechStream from "./speech-stream.js";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Media } from "../../media.js";
|
|
3
|
+
import { MediaProtocol } from "../../route/media-protocol.js";
|
|
4
|
+
import { SpeechAudioDeltaEvent, SpeechFinishEvent, SpeechTimestampsEvent, } from "../../speech.js";
|
|
5
|
+
import { concatBytes } from "../../utils/bytes.js";
|
|
6
|
+
import { ProviderShared } from "../shared.js";
|
|
7
|
+
/** Empty chunks (keep-alive records) emit nothing. */
|
|
8
|
+
export const delta = (state, chunk) => {
|
|
9
|
+
if (chunk.length === 0)
|
|
10
|
+
return [state, []];
|
|
11
|
+
state.chunks.push(chunk);
|
|
12
|
+
return [state, [SpeechAudioDeltaEvent.make({ chunk })]];
|
|
13
|
+
};
|
|
14
|
+
export const step = (onRecord) => (state, frame) => typeof frame === "string" ? onRecord(state, frame) : Effect.succeed(delta(state, frame));
|
|
15
|
+
export const timestamps = (texts, starts, ends) => texts.length === 0
|
|
16
|
+
? []
|
|
17
|
+
: [
|
|
18
|
+
SpeechTimestampsEvent.make({
|
|
19
|
+
items: texts.map((text, index) => ({ text, startSeconds: starts[index] ?? 0, endSeconds: ends[index] ?? 0 })),
|
|
20
|
+
}),
|
|
21
|
+
];
|
|
22
|
+
export const voiceID = (voice) => (typeof voice === "object" ? voice.id : voice);
|
|
23
|
+
const CONTAINER_MEDIA_TYPES = {
|
|
24
|
+
mp3: "audio/mpeg",
|
|
25
|
+
wav: "audio/wav",
|
|
26
|
+
opus: "audio/ogg",
|
|
27
|
+
aac: "audio/aac",
|
|
28
|
+
flac: "audio/flac",
|
|
29
|
+
};
|
|
30
|
+
export const container = (format, sampleRate) => ({
|
|
31
|
+
mediaType: CONTAINER_MEDIA_TYPES[format],
|
|
32
|
+
info: { format, sampleRate },
|
|
33
|
+
});
|
|
34
|
+
const PCM_MEDIA_TYPES = {
|
|
35
|
+
pcm_s16le: "audio/pcm",
|
|
36
|
+
pcm_f32le: "audio/pcm",
|
|
37
|
+
pcm_mulaw: "audio/mulaw",
|
|
38
|
+
pcm_alaw: "audio/alaw",
|
|
39
|
+
};
|
|
40
|
+
export const pcm = (encoding, sampleRate, mediaType) => ({
|
|
41
|
+
mediaType: mediaType ?? PCM_MEDIA_TYPES[encoding],
|
|
42
|
+
info: { format: "pcm", encoding, sampleRate, channels: 1 },
|
|
43
|
+
});
|
|
44
|
+
export const sampleRate = (mediaType) => {
|
|
45
|
+
const rate = /rate=(\d+)/i.exec(mediaType ?? "")?.[1];
|
|
46
|
+
return rate === undefined ? undefined : Number(rate);
|
|
47
|
+
};
|
|
48
|
+
export const unsupportedFormat = (provider, route, message) => ProviderShared.unsupportedOperation({ operation: "media.format", provider, route, message });
|
|
49
|
+
/** A declared `mediaType` wins over sniffing: headerless PCM can start with bytes that look like an MPEG frame sync. */
|
|
50
|
+
export const finish = (route, state, output) => {
|
|
51
|
+
if (state.chunks.length === 0)
|
|
52
|
+
return Effect.fail(MediaProtocol.frameError(route, `The provider returned no audio${output.detail === undefined ? "" : ` (${output.detail})`}`));
|
|
53
|
+
return Effect.succeed([
|
|
54
|
+
SpeechFinishEvent.make({
|
|
55
|
+
audio: Media.bytes(concatBytes(state.chunks), output.mediaType, { info: output.info }),
|
|
56
|
+
usage: output.usage,
|
|
57
|
+
providerMetadata: output.providerMetadata,
|
|
58
|
+
}),
|
|
59
|
+
]);
|
|
60
|
+
};
|
|
61
|
+
export const headerUsage = (type, value) => {
|
|
62
|
+
const amount = Number(value);
|
|
63
|
+
if (!Number.isFinite(amount))
|
|
64
|
+
return undefined;
|
|
65
|
+
return type === "credits" ? { type, credits: amount } : { type, characters: amount };
|
|
66
|
+
};
|
|
67
|
+
export * as SpeechStream from "./speech-stream.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { JsonSchema,
|
|
1
|
+
import type { JsonSchema, LanguageModel, LanguageModelSanitizerCompatibility } from "../../schema/index.js";
|
|
2
2
|
export declare const ToolSchemaProjection: {
|
|
3
3
|
readonly gemini: (schema: JsonSchema) => JsonSchema;
|
|
4
|
-
readonly modelCompatibility: (schema: JsonSchema,
|
|
4
|
+
readonly modelCompatibility: (schema: JsonSchema, model: LanguageModel, protocolDefault?: LanguageModelSanitizerCompatibility) => JsonSchema;
|
|
5
5
|
readonly moonshot: (schema: JsonSchema) => JsonSchema;
|
|
6
6
|
readonly openAI: (schema: JsonSchema) => JsonSchema;
|
|
7
7
|
readonly responses: (schema: JsonSchema) => JsonSchema;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isRecord } from "../../utils/record.js";
|
|
2
|
-
import {
|
|
2
|
+
import { GeminiJsonSchema } from "./gemini-json-schema.js";
|
|
3
3
|
const tupleItemsSchema = (items) => {
|
|
4
4
|
const projected = items.map(moonshotNode);
|
|
5
5
|
if (projected.length === 0)
|
|
@@ -8,6 +8,17 @@ const tupleItemsSchema = (items) => {
|
|
|
8
8
|
return projected[0];
|
|
9
9
|
return { anyOf: projected };
|
|
10
10
|
};
|
|
11
|
+
// Moonshot rejects an `enum` without a sibling `type`. Its `type` may be one type, or one type plus "null".
|
|
12
|
+
const enumType = (values) => {
|
|
13
|
+
const types = [
|
|
14
|
+
...new Set(values.map((value) => (value === null ? "null" : Array.isArray(value) ? "array" : typeof value))),
|
|
15
|
+
];
|
|
16
|
+
if (types.length === 1)
|
|
17
|
+
return types[0];
|
|
18
|
+
if (types.length === 2 && types.includes("null"))
|
|
19
|
+
return [...types.filter((type) => type !== "null"), "null"];
|
|
20
|
+
return undefined;
|
|
21
|
+
};
|
|
11
22
|
const moonshotNode = (schema) => {
|
|
12
23
|
if (Array.isArray(schema))
|
|
13
24
|
return schema.map(moonshotNode);
|
|
@@ -15,18 +26,22 @@ const moonshotNode = (schema) => {
|
|
|
15
26
|
return schema;
|
|
16
27
|
if (typeof schema.$ref === "string")
|
|
17
28
|
return { $ref: schema.$ref };
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if ("items"
|
|
29
|
+
const type = schema.type === undefined && Array.isArray(schema.enum) ? enumType(schema.enum) : undefined;
|
|
30
|
+
return Object.fromEntries([
|
|
31
|
+
...(type === undefined ? [] : [["type", type]]),
|
|
32
|
+
...Object.entries(schema).flatMap(([key, value]) => {
|
|
33
|
+
if (key === "items" && Array.isArray(value))
|
|
34
|
+
return [[key, tupleItemsSchema(value)]];
|
|
35
|
+
if (key === "prefixItems") {
|
|
36
|
+
if ("items" in schema)
|
|
37
|
+
return [];
|
|
38
|
+
return [["items", tupleItemsSchema(Array.isArray(value) ? value : [])]];
|
|
39
|
+
}
|
|
40
|
+
if (key === "unevaluatedItems")
|
|
23
41
|
return [];
|
|
24
|
-
return [[
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return [];
|
|
28
|
-
return [[key, moonshotNode(value)]];
|
|
29
|
-
}));
|
|
42
|
+
return [[key, moonshotNode(value)]];
|
|
43
|
+
}),
|
|
44
|
+
]);
|
|
30
45
|
};
|
|
31
46
|
const moonshot = (schema) => {
|
|
32
47
|
const projected = moonshotNode(schema);
|
|
@@ -34,15 +49,23 @@ const moonshot = (schema) => {
|
|
|
34
49
|
};
|
|
35
50
|
const openAI = (schema) => schema;
|
|
36
51
|
const responses = openAI;
|
|
37
|
-
const gemini =
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
52
|
+
const gemini = GeminiJsonSchema.normalize;
|
|
53
|
+
const MODEL_NAMES = [
|
|
54
|
+
[/gemini/i, "gemini"],
|
|
55
|
+
[/kimi/i, "moonshot"],
|
|
56
|
+
];
|
|
57
|
+
// An explicit `sanitizer` wins, and `none` opts out. Otherwise the protocol's own default
|
|
58
|
+
// applies (the Gemini API always uses Gemini's rules), then the model name selects the family's rules
|
|
59
|
+
// so models reached through gateways and OpenAI-compatible endpoints get the same handling.
|
|
60
|
+
const modelCompatibility = (schema, model, protocolDefault) => {
|
|
61
|
+
switch (model.compatibility?.sanitizer ?? protocolDefault ?? MODEL_NAMES.find(([name]) => name.test(model.id))?.[1]) {
|
|
42
62
|
case "gemini":
|
|
43
63
|
return gemini(schema);
|
|
44
64
|
case "moonshot":
|
|
45
65
|
return moonshot(schema);
|
|
66
|
+
case "none":
|
|
67
|
+
case undefined:
|
|
68
|
+
return schema;
|
|
46
69
|
}
|
|
47
70
|
};
|
|
48
71
|
export const ToolSchemaProjection = {
|
|
@@ -244,6 +244,14 @@ export declare const finish: <K extends StreamKey>(route: string, tools: State<K
|
|
|
244
244
|
};
|
|
245
245
|
} | undefined;
|
|
246
246
|
readonly namespace?: string | undefined;
|
|
247
|
+
} | {
|
|
248
|
+
readonly type: "media";
|
|
249
|
+
readonly media: import("../../media.js").Asset;
|
|
250
|
+
readonly providerMetadata?: {
|
|
251
|
+
readonly [x: string]: {
|
|
252
|
+
readonly [x: string]: unknown;
|
|
253
|
+
};
|
|
254
|
+
} | undefined;
|
|
247
255
|
} | {
|
|
248
256
|
readonly type: "step-finish";
|
|
249
257
|
readonly reason: {
|
|
@@ -272,12 +280,12 @@ export declare const finish: <K extends StreamKey>(route: string, tools: State<K
|
|
|
272
280
|
} | {
|
|
273
281
|
readonly type: "provider-error";
|
|
274
282
|
readonly message: string;
|
|
283
|
+
readonly classification?: "context-overflow" | "payload-too-large" | undefined;
|
|
275
284
|
readonly providerMetadata?: {
|
|
276
285
|
readonly [x: string]: {
|
|
277
286
|
readonly [x: string]: unknown;
|
|
278
287
|
};
|
|
279
288
|
} | undefined;
|
|
280
|
-
readonly classification?: "context-overflow" | "payload-too-large" | undefined;
|
|
281
289
|
})[];
|
|
282
290
|
}, AIError, never>;
|
|
283
291
|
/**
|
|
@@ -459,6 +467,14 @@ export declare const finishWithInput: <K extends StreamKey>(route: string, tools
|
|
|
459
467
|
};
|
|
460
468
|
} | undefined;
|
|
461
469
|
readonly namespace?: string | undefined;
|
|
470
|
+
} | {
|
|
471
|
+
readonly type: "media";
|
|
472
|
+
readonly media: import("../../media.js").Asset;
|
|
473
|
+
readonly providerMetadata?: {
|
|
474
|
+
readonly [x: string]: {
|
|
475
|
+
readonly [x: string]: unknown;
|
|
476
|
+
};
|
|
477
|
+
} | undefined;
|
|
462
478
|
} | {
|
|
463
479
|
readonly type: "step-finish";
|
|
464
480
|
readonly reason: {
|
|
@@ -487,12 +503,12 @@ export declare const finishWithInput: <K extends StreamKey>(route: string, tools
|
|
|
487
503
|
} | {
|
|
488
504
|
readonly type: "provider-error";
|
|
489
505
|
readonly message: string;
|
|
506
|
+
readonly classification?: "context-overflow" | "payload-too-large" | undefined;
|
|
490
507
|
readonly providerMetadata?: {
|
|
491
508
|
readonly [x: string]: {
|
|
492
509
|
readonly [x: string]: unknown;
|
|
493
510
|
};
|
|
494
511
|
} | undefined;
|
|
495
|
-
readonly classification?: "context-overflow" | "payload-too-large" | undefined;
|
|
496
512
|
})[];
|
|
497
513
|
}, AIError, never>;
|
|
498
514
|
/**
|
|
@@ -671,6 +687,14 @@ export declare const finishAll: <K extends StreamKey>(route: string, tools: Stat
|
|
|
671
687
|
};
|
|
672
688
|
} | undefined;
|
|
673
689
|
readonly namespace?: string | undefined;
|
|
690
|
+
} | {
|
|
691
|
+
readonly type: "media";
|
|
692
|
+
readonly media: import("../../media.js").Asset;
|
|
693
|
+
readonly providerMetadata?: {
|
|
694
|
+
readonly [x: string]: {
|
|
695
|
+
readonly [x: string]: unknown;
|
|
696
|
+
};
|
|
697
|
+
} | undefined;
|
|
674
698
|
} | {
|
|
675
699
|
readonly type: "step-finish";
|
|
676
700
|
readonly reason: {
|
|
@@ -699,12 +723,12 @@ export declare const finishAll: <K extends StreamKey>(route: string, tools: Stat
|
|
|
699
723
|
} | {
|
|
700
724
|
readonly type: "provider-error";
|
|
701
725
|
readonly message: string;
|
|
726
|
+
readonly classification?: "context-overflow" | "payload-too-large" | undefined;
|
|
702
727
|
readonly providerMetadata?: {
|
|
703
728
|
readonly [x: string]: {
|
|
704
729
|
readonly [x: string]: unknown;
|
|
705
730
|
};
|
|
706
731
|
} | undefined;
|
|
707
|
-
readonly classification?: "context-overflow" | "payload-too-large" | undefined;
|
|
708
732
|
})[];
|
|
709
733
|
}, AIError, never>;
|
|
710
734
|
export * as ToolStream from "./tool-stream.js";
|
|
@@ -1,26 +1,20 @@
|
|
|
1
|
-
import { ImageModel } from "../image.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { ImageModel, ImageResponse, type ImageRequestFor } from "../image.js";
|
|
2
|
+
import { MediaProtocol } from "../route/media-protocol.js";
|
|
3
|
+
import { MediaRoute } from "../route/media.js";
|
|
4
4
|
export declare const DEFAULT_BASE_URL = "https://api.x.ai/v1";
|
|
5
5
|
export declare const PATH = "/images/generations";
|
|
6
6
|
export declare const EDIT_PATH = "/images/edits";
|
|
7
7
|
export type XAIImageString<Known extends string> = Known | (string & {});
|
|
8
|
+
/** Provider-native options. Common fields (`n`, `aspectRatio`, `images`) live on the request. */
|
|
8
9
|
export type XAIImageOptions = {
|
|
9
|
-
readonly n?: number;
|
|
10
|
-
readonly aspectRatio?: XAIImageString<"1:1" | "3:4" | "4:3" | "9:16" | "16:9" | "2:3" | "3:2" | "9:19.5" | "19.5:9" | "9:20" | "20:9" | "1:2" | "2:1" | "auto">;
|
|
11
|
-
readonly aspect_ratio?: XAIImageString<"1:1" | "3:4" | "4:3" | "9:16" | "16:9" | "2:3" | "3:2" | "9:19.5" | "19.5:9" | "9:20" | "20:9" | "1:2" | "2:1" | "auto">;
|
|
12
10
|
readonly resolution?: XAIImageString<"1k" | "2k">;
|
|
13
11
|
readonly responseFormat?: XAIImageString<"url" | "b64_json">;
|
|
14
12
|
readonly response_format?: XAIImageString<"url" | "b64_json">;
|
|
15
13
|
} & Record<string, unknown>;
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
readonly baseURL?: string;
|
|
20
|
-
readonly headers?: Record<string, string>;
|
|
21
|
-
readonly http?: HttpOptions;
|
|
22
|
-
}
|
|
23
|
-
export declare const model: (input: ModelInput) => ImageModel<XAIImageOptions>;
|
|
14
|
+
export type Request = ImageRequestFor<XAIImageOptions>;
|
|
15
|
+
export declare const protocol: MediaProtocol.Inline<Request, ImageResponse>;
|
|
16
|
+
export declare const model: (input: MediaRoute.ModelInput) => ImageModel<XAIImageOptions>;
|
|
24
17
|
export declare const XAIImages: {
|
|
25
|
-
readonly
|
|
18
|
+
readonly protocol: MediaProtocol.Inline<Request, ImageResponse>;
|
|
19
|
+
readonly model: (input: MediaRoute.ModelInput) => ImageModel<XAIImageOptions>;
|
|
26
20
|
};
|