@ai-ecoverse/pi-audio 1.21.0 → 1.23.0
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 +4 -2
- package/dist/cloudflare/catalog.js +165 -0
- package/dist/cloudflare/catalog.js.map +7 -0
- package/dist/cloudflare/index.js +5 -0
- package/dist/cloudflare/index.js.map +7 -0
- package/dist/cloudflare/listen.js +133 -0
- package/dist/cloudflare/listen.js.map +7 -0
- package/dist/cloudflare/speak.js +224 -0
- package/dist/cloudflare/speak.js.map +7 -0
- package/dist/cloudflare/transcribe.js +155 -0
- package/dist/cloudflare/transcribe.js.map +7 -0
- package/dist/cloudflare/wire.js +166 -0
- package/dist/cloudflare/wire.js.map +7 -0
- package/dist/custom-llm.js +32 -0
- package/dist/custom-llm.js.map +7 -0
- package/dist/elevenlabs-agents/index.js +2 -0
- package/dist/elevenlabs-agents/index.js.map +2 -2
- package/dist/elevenlabs-agents/provision.js +2 -8
- package/dist/elevenlabs-agents/provision.js.map +2 -2
- package/dist/fish-agents/index.js +2 -0
- package/dist/fish-agents/index.js.map +2 -2
- package/dist/fish-agents/provision.js +2 -14
- package/dist/fish-agents/provision.js.map +2 -2
- package/dist/types/cloudflare/catalog.d.ts +1 -0
- package/dist/types/cloudflare/index.d.ts +1 -0
- package/dist/types/cloudflare/listen.d.ts +4 -0
- package/dist/types/cloudflare/speak.d.ts +4 -0
- package/dist/types/cloudflare/transcribe.d.ts +4 -0
- package/dist/types/cloudflare/wire.d.ts +27 -0
- package/dist/types/custom-llm.d.ts +4 -0
- package/dist/types/elevenlabs-agents/index.d.ts +1 -0
- package/dist/types/fish-agents/index.d.ts +1 -0
- package/package.json +5 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { encodeAudioBase64 } from "../convert.js";
|
|
2
|
+
import { VoiceRequestError } from "../errors.js";
|
|
3
|
+
import {
|
|
4
|
+
applyPayload,
|
|
5
|
+
asVoiceError,
|
|
6
|
+
isRecord,
|
|
7
|
+
numberField,
|
|
8
|
+
readAudio,
|
|
9
|
+
requestSignal,
|
|
10
|
+
stringField
|
|
11
|
+
} from "../openai/wire.js";
|
|
12
|
+
import { priceAudio } from "../usage.js";
|
|
13
|
+
import { errorText, resultOf, runUrl, session } from "./wire.js";
|
|
14
|
+
function novaEncoding(mimeType) {
|
|
15
|
+
const mime = mimeType.toLowerCase();
|
|
16
|
+
if (mime.includes("mulaw") || mime === "audio/basic") return "mulaw";
|
|
17
|
+
if (mime.includes("flac")) return "flac";
|
|
18
|
+
if (mime.includes("opus")) return "opus";
|
|
19
|
+
if (mime.includes("pcm") || mime.includes("l16") || mime.includes("wav")) return "linear16";
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
function requestBody(modelId, audio, mimeType, language) {
|
|
23
|
+
if (modelId.includes("whisper")) {
|
|
24
|
+
const body2 = { audio };
|
|
25
|
+
if (language !== void 0) body2.language = language;
|
|
26
|
+
return body2;
|
|
27
|
+
}
|
|
28
|
+
const body = { audio: { body: audio, contentType: mimeType } };
|
|
29
|
+
const encoding = novaEncoding(mimeType);
|
|
30
|
+
if (encoding !== void 0) body.encoding = encoding;
|
|
31
|
+
if (language !== void 0) body.language = language;
|
|
32
|
+
return body;
|
|
33
|
+
}
|
|
34
|
+
function speakerOf(record) {
|
|
35
|
+
const value = record.speaker;
|
|
36
|
+
if (typeof value === "string" && value !== "") return value;
|
|
37
|
+
if (typeof value === "number") return String(value);
|
|
38
|
+
return void 0;
|
|
39
|
+
}
|
|
40
|
+
function segmentText(record) {
|
|
41
|
+
return stringField(record, "text") ?? stringField(record, "word") ?? "";
|
|
42
|
+
}
|
|
43
|
+
function segmentsFrom(value) {
|
|
44
|
+
if (!Array.isArray(value)) return [];
|
|
45
|
+
const segments = [];
|
|
46
|
+
for (const item of value) {
|
|
47
|
+
if (!isRecord(item) || Array.isArray(item)) continue;
|
|
48
|
+
const text = segmentText(item);
|
|
49
|
+
if (text === "") continue;
|
|
50
|
+
const segment = {
|
|
51
|
+
text,
|
|
52
|
+
start: numberField(item, "start") ?? 0,
|
|
53
|
+
end: numberField(item, "end") ?? 0
|
|
54
|
+
};
|
|
55
|
+
const speaker = speakerOf(item);
|
|
56
|
+
if (speaker !== void 0) segment.speaker = speaker;
|
|
57
|
+
segments.push(segment);
|
|
58
|
+
}
|
|
59
|
+
return segments;
|
|
60
|
+
}
|
|
61
|
+
function alternative(body) {
|
|
62
|
+
const results = body.results;
|
|
63
|
+
if (!isRecord(results) || Array.isArray(results)) return void 0;
|
|
64
|
+
const channels = results.channels;
|
|
65
|
+
if (!Array.isArray(channels)) return void 0;
|
|
66
|
+
const first = channels[0];
|
|
67
|
+
if (!isRecord(first) || Array.isArray(first)) return void 0;
|
|
68
|
+
const alternatives = first.alternatives;
|
|
69
|
+
if (!Array.isArray(alternatives)) return void 0;
|
|
70
|
+
const alt = alternatives[0];
|
|
71
|
+
if (!isRecord(alt) || Array.isArray(alt)) return void 0;
|
|
72
|
+
return alt;
|
|
73
|
+
}
|
|
74
|
+
function durationSeconds(body) {
|
|
75
|
+
const direct = numberField(body, "duration");
|
|
76
|
+
if (direct !== void 0) return direct;
|
|
77
|
+
const metadata = body.metadata;
|
|
78
|
+
if (!isRecord(metadata) || Array.isArray(metadata)) return 0;
|
|
79
|
+
const nested = numberField(metadata, "duration");
|
|
80
|
+
if (nested === void 0) return 0;
|
|
81
|
+
return nested;
|
|
82
|
+
}
|
|
83
|
+
function optionLanguage(options) {
|
|
84
|
+
const record = options;
|
|
85
|
+
if (record === void 0) return void 0;
|
|
86
|
+
if (typeof record.language !== "string") return void 0;
|
|
87
|
+
return record.language;
|
|
88
|
+
}
|
|
89
|
+
function transcriptFrom(body, model) {
|
|
90
|
+
const result = resultOf(body);
|
|
91
|
+
if (!isRecord(result) || Array.isArray(result))
|
|
92
|
+
throw new Error("Transcription response had no text");
|
|
93
|
+
const alt = alternative(result);
|
|
94
|
+
const nova = alt === void 0 ? void 0 : stringField(alt, "transcript");
|
|
95
|
+
const text = nova ?? stringField(result, "text");
|
|
96
|
+
if (text === void 0) throw new Error("Transcription response had no text");
|
|
97
|
+
const words = alt === void 0 ? void 0 : alt.words;
|
|
98
|
+
const segments = segmentsFrom(words ?? result.segments);
|
|
99
|
+
const seconds = durationSeconds(result);
|
|
100
|
+
const meters = {};
|
|
101
|
+
if (seconds > 0) meters.inputSeconds = seconds;
|
|
102
|
+
const transcript = { text, usage: priceAudio(model.audioCost, meters) };
|
|
103
|
+
const language = stringField(result, "language");
|
|
104
|
+
if (language !== void 0) transcript.language = language;
|
|
105
|
+
if (segments.length > 0) transcript.segments = segments;
|
|
106
|
+
return transcript;
|
|
107
|
+
}
|
|
108
|
+
async function postTranscription(model, body, options) {
|
|
109
|
+
const route = session(model, options);
|
|
110
|
+
const headers = route.headers;
|
|
111
|
+
headers["Content-Type"] = "application/json";
|
|
112
|
+
const fetchImpl = options?.fetch ?? globalThis.fetch;
|
|
113
|
+
const response = await fetchImpl(runUrl(model.baseUrl, model.id, route.account), {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers,
|
|
116
|
+
body: JSON.stringify(body),
|
|
117
|
+
signal: requestSignal(options)
|
|
118
|
+
});
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
const text2 = await response.text();
|
|
121
|
+
throw new Error(errorText(text2, response.status));
|
|
122
|
+
}
|
|
123
|
+
const text = await response.text();
|
|
124
|
+
if (text === "") throw new Error("Transcription response had no text");
|
|
125
|
+
try {
|
|
126
|
+
return JSON.parse(text);
|
|
127
|
+
} catch {
|
|
128
|
+
return text;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async function transcribeCloudflare(model, input, options) {
|
|
132
|
+
try {
|
|
133
|
+
if (model.id.includes("/flux")) throw new Error("Cloudflare flux is websocket only");
|
|
134
|
+
if (options?.signal?.aborted) throw new DOMException("aborted", "AbortError");
|
|
135
|
+
const file = await readAudio(input, options?.fetch ?? globalThis.fetch, requestSignal(options));
|
|
136
|
+
const payload = requestBody(
|
|
137
|
+
model.id,
|
|
138
|
+
encodeAudioBase64(file.data),
|
|
139
|
+
file.mimeType,
|
|
140
|
+
optionLanguage(options)
|
|
141
|
+
);
|
|
142
|
+
const body = await applyPayload(payload, model, options);
|
|
143
|
+
if (!isRecord(body) || Array.isArray(body))
|
|
144
|
+
throw new Error("Transcription payload was not JSON");
|
|
145
|
+
const parsed = await postTranscription(model, body, options);
|
|
146
|
+
return transcriptFrom(parsed, model);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
const voice = asVoiceError(error);
|
|
149
|
+
throw new VoiceRequestError(voice.reason, voice.error);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export {
|
|
153
|
+
transcribeCloudflare
|
|
154
|
+
};
|
|
155
|
+
//# sourceMappingURL=transcribe.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/cloudflare/transcribe.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ProviderRequestOptions } from '@earendil-works/pi-ai';\nimport { encodeAudioBase64 } from '../convert.ts';\nimport { VoiceRequestError } from '../errors.ts';\nimport type { TranscriptionModel } from '../models.ts';\nimport {\n applyPayload,\n asVoiceError,\n isRecord,\n numberField,\n readAudio,\n requestSignal,\n stringField,\n} from '../openai/wire.ts';\nimport type { AudioInput, Transcript, TranscriptSegment } from '../transcription.ts';\nimport { type AudioCounters, priceAudio } from '../usage.ts';\nimport { errorText, resultOf, runUrl, session } from './wire.ts';\n\nfunction novaEncoding(mimeType: string): string | undefined {\n const mime = mimeType.toLowerCase();\n if (mime.includes('mulaw') || mime === 'audio/basic') return 'mulaw';\n if (mime.includes('flac')) return 'flac';\n if (mime.includes('opus')) return 'opus';\n if (mime.includes('pcm') || mime.includes('l16') || mime.includes('wav')) return 'linear16';\n return undefined;\n}\n\nfunction requestBody(\n modelId: string,\n audio: string,\n mimeType: string,\n language: string | undefined\n): Record<string, unknown> {\n if (modelId.includes('whisper')) {\n const body: Record<string, unknown> = { audio };\n if (language !== undefined) body.language = language;\n return body;\n }\n const body: Record<string, unknown> = { audio: { body: audio, contentType: mimeType } };\n const encoding = novaEncoding(mimeType);\n if (encoding !== undefined) body.encoding = encoding;\n if (language !== undefined) body.language = language;\n return body;\n}\n\nfunction speakerOf(record: Record<string, unknown>): string | undefined {\n const value = record.speaker;\n if (typeof value === 'string' && value !== '') return value;\n if (typeof value === 'number') return String(value);\n return undefined;\n}\n\nfunction segmentText(record: Record<string, unknown>): string {\n return stringField(record, 'text') ?? stringField(record, 'word') ?? '';\n}\n\nfunction segmentsFrom(value: unknown): TranscriptSegment[] {\n if (!Array.isArray(value)) return [];\n const segments: TranscriptSegment[] = [];\n for (const item of value) {\n if (!isRecord(item) || Array.isArray(item)) continue;\n const text = segmentText(item);\n if (text === '') continue;\n const segment: TranscriptSegment = {\n text,\n start: numberField(item, 'start') ?? 0,\n end: numberField(item, 'end') ?? 0,\n };\n const speaker = speakerOf(item);\n if (speaker !== undefined) segment.speaker = speaker;\n segments.push(segment);\n }\n return segments;\n}\n\nfunction alternative(body: Record<string, unknown>): Record<string, unknown> | undefined {\n const results = body.results;\n if (!isRecord(results) || Array.isArray(results)) return undefined;\n const channels = results.channels;\n if (!Array.isArray(channels)) return undefined;\n const first = channels[0];\n if (!isRecord(first) || Array.isArray(first)) return undefined;\n const alternatives = first.alternatives;\n if (!Array.isArray(alternatives)) return undefined;\n const alt = alternatives[0];\n if (!isRecord(alt) || Array.isArray(alt)) return undefined;\n return alt;\n}\n\nfunction durationSeconds(body: Record<string, unknown>): number {\n const direct = numberField(body, 'duration');\n if (direct !== undefined) return direct;\n const metadata = body.metadata;\n if (!isRecord(metadata) || Array.isArray(metadata)) return 0;\n const nested = numberField(metadata, 'duration');\n if (nested === undefined) return 0;\n return nested;\n}\n\nfunction optionLanguage(options: ProviderRequestOptions | undefined): string | undefined {\n const record = options as { language?: unknown } | undefined;\n if (record === undefined) return undefined;\n if (typeof record.language !== 'string') return undefined;\n return record.language;\n}\n\nfunction transcriptFrom(body: unknown, model: TranscriptionModel<string>): Transcript {\n const result = resultOf(body);\n if (!isRecord(result) || Array.isArray(result))\n throw new Error('Transcription response had no text');\n const alt = alternative(result);\n const nova = alt === undefined ? undefined : stringField(alt, 'transcript');\n const text = nova ?? stringField(result, 'text');\n if (text === undefined) throw new Error('Transcription response had no text');\n const words = alt === undefined ? undefined : alt.words;\n const segments = segmentsFrom(words ?? result.segments);\n const seconds = durationSeconds(result);\n const meters: AudioCounters = {};\n if (seconds > 0) meters.inputSeconds = seconds;\n const transcript: Transcript = { text, usage: priceAudio(model.audioCost, meters) };\n const language = stringField(result, 'language');\n if (language !== undefined) transcript.language = language;\n if (segments.length > 0) transcript.segments = segments;\n return transcript;\n}\n\nasync function postTranscription(\n model: TranscriptionModel<string>,\n body: unknown,\n options: ProviderRequestOptions | undefined\n): Promise<unknown> {\n const route = session(model, options);\n const headers = route.headers;\n headers['Content-Type'] = 'application/json';\n const fetchImpl = options?.fetch ?? globalThis.fetch;\n const response = await fetchImpl(runUrl(model.baseUrl, model.id, route.account), {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: requestSignal(options),\n });\n if (!response.ok) {\n const text = await response.text();\n throw new Error(errorText(text, response.status));\n }\n const text = await response.text();\n if (text === '') throw new Error('Transcription response had no text');\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nexport async function transcribeCloudflare(\n model: TranscriptionModel<string>,\n input: AudioInput,\n options?: ProviderRequestOptions\n): Promise<Transcript> {\n try {\n if (model.id.includes('/flux')) throw new Error('Cloudflare flux is websocket only');\n if (options?.signal?.aborted) throw new DOMException('aborted', 'AbortError');\n const file = await readAudio(input, options?.fetch ?? globalThis.fetch, requestSignal(options));\n const payload = requestBody(\n model.id,\n encodeAudioBase64(file.data),\n file.mimeType,\n optionLanguage(options)\n );\n const body = await applyPayload(payload, model, options);\n if (!isRecord(body) || Array.isArray(body))\n throw new Error('Transcription payload was not JSON');\n const parsed = await postTranscription(model, body, options);\n return transcriptFrom(parsed, model);\n } catch (error) {\n const voice = asVoiceError(error);\n throw new VoiceRequestError(voice.reason, voice.error);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["body", "text"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { isRecord, requestHeaders, stringField } from "../openai/wire.js";
|
|
2
|
+
const placeholder = "{account_id}";
|
|
3
|
+
const speechEncodings = {
|
|
4
|
+
pcm16: "linear16",
|
|
5
|
+
mp3: "mp3",
|
|
6
|
+
flac: "flac",
|
|
7
|
+
mulaw: "mulaw",
|
|
8
|
+
alaw: "alaw",
|
|
9
|
+
opus: "opus"
|
|
10
|
+
};
|
|
11
|
+
function headerValue(headers, name) {
|
|
12
|
+
const expected = name.toLowerCase();
|
|
13
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
14
|
+
if (key.toLowerCase() === expected && value.trim() !== "") return value;
|
|
15
|
+
}
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
function takeHeader(headers, name) {
|
|
19
|
+
const expected = name.toLowerCase();
|
|
20
|
+
let found;
|
|
21
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
22
|
+
if (key.toLowerCase() !== expected) continue;
|
|
23
|
+
if (found === void 0 && value.trim() !== "") found = value.trim();
|
|
24
|
+
delete headers[key];
|
|
25
|
+
}
|
|
26
|
+
return found;
|
|
27
|
+
}
|
|
28
|
+
function accountFromUrl(baseUrl) {
|
|
29
|
+
const match = /\/accounts\/([^/]+)\/ai\/run/.exec(baseUrl);
|
|
30
|
+
const found = match?.[1];
|
|
31
|
+
if (found === void 0 || found === placeholder) return void 0;
|
|
32
|
+
return found;
|
|
33
|
+
}
|
|
34
|
+
function gatewayId(headers) {
|
|
35
|
+
const value = headerValue(headers, "cf-aig-gateway-id");
|
|
36
|
+
if (value === void 0) return "default";
|
|
37
|
+
return value.trim();
|
|
38
|
+
}
|
|
39
|
+
function stampGatewayAuth(headers) {
|
|
40
|
+
const gateway = headerValue(headers, "cf-aig-authorization");
|
|
41
|
+
const authorization = headerValue(headers, "authorization");
|
|
42
|
+
if (gateway === void 0 && authorization !== void 0 && authorization.toLowerCase().startsWith("bearer ")) {
|
|
43
|
+
headers["cf-aig-authorization"] = authorization.slice(authorization.indexOf(" ") + 1).trim();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (gateway !== void 0 && authorization === void 0)
|
|
47
|
+
headers.Authorization = `Bearer ${gateway}`;
|
|
48
|
+
}
|
|
49
|
+
function session(model, options) {
|
|
50
|
+
const headers = requestHeaders(model, options);
|
|
51
|
+
const fromHeader = takeHeader(headers, "cf-account-id");
|
|
52
|
+
const account = fromHeader ?? accountFromUrl(model.baseUrl);
|
|
53
|
+
if (account === void 0) throw new Error("No Cloudflare account id");
|
|
54
|
+
const gateway = gatewayId(headers);
|
|
55
|
+
stampGatewayAuth(headers);
|
|
56
|
+
return { headers, account, gateway };
|
|
57
|
+
}
|
|
58
|
+
function runUrl(baseUrl, modelId, account) {
|
|
59
|
+
const base = baseUrl.replaceAll(placeholder, account).replace(/\/$/, "");
|
|
60
|
+
return `${base}/${modelId}`;
|
|
61
|
+
}
|
|
62
|
+
function workersAiSocket(account, gateway, modelId, query) {
|
|
63
|
+
const extra = query === "" ? "" : `&${query}`;
|
|
64
|
+
return `wss://gateway.ai.cloudflare.com/v1/${account}/${gateway}/workers-ai?model=${encodeURIComponent(modelId)}${extra}`;
|
|
65
|
+
}
|
|
66
|
+
function auraEncoding(format) {
|
|
67
|
+
const name = speechEncodings[format.encoding];
|
|
68
|
+
if (!name) throw new Error(`Unsupported speech format ${format.encoding}`);
|
|
69
|
+
return name;
|
|
70
|
+
}
|
|
71
|
+
function listenQuery(modelId, format) {
|
|
72
|
+
if (format.encoding !== "pcm16" && format.encoding !== "mulaw") {
|
|
73
|
+
throw new Error(`Unsupported audio format ${format.encoding}`);
|
|
74
|
+
}
|
|
75
|
+
if (modelId.includes("/flux") && format.encoding !== "pcm16") {
|
|
76
|
+
throw new Error(`Unsupported audio format ${format.encoding}`);
|
|
77
|
+
}
|
|
78
|
+
const encoding = format.encoding === "mulaw" ? "mulaw" : "linear16";
|
|
79
|
+
const shared = `encoding=${encoding}&sample_rate=${format.sampleRate}`;
|
|
80
|
+
if (modelId.includes("/flux")) return shared;
|
|
81
|
+
return `${shared}&interim_results=true&vad_events=true`;
|
|
82
|
+
}
|
|
83
|
+
function sameFormat(left, right) {
|
|
84
|
+
return left.encoding === right.encoding && left.sampleRate === right.sampleRate && left.channels === right.channels;
|
|
85
|
+
}
|
|
86
|
+
function connectSocket(url, headers) {
|
|
87
|
+
const Socket = WebSocket;
|
|
88
|
+
return new Socket(url, { headers });
|
|
89
|
+
}
|
|
90
|
+
function awaitOpen(socket, label, fail) {
|
|
91
|
+
return new Promise((resolve) => {
|
|
92
|
+
let opened = false;
|
|
93
|
+
socket.addEventListener("open", () => {
|
|
94
|
+
opened = true;
|
|
95
|
+
resolve();
|
|
96
|
+
});
|
|
97
|
+
socket.addEventListener("error", () => {
|
|
98
|
+
fail(`${label} socket failed`);
|
|
99
|
+
if (!opened) resolve();
|
|
100
|
+
});
|
|
101
|
+
socket.addEventListener("close", () => {
|
|
102
|
+
if (!opened) {
|
|
103
|
+
fail(`${label} socket closed`);
|
|
104
|
+
resolve();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function audioFrame(data) {
|
|
110
|
+
const copy = new Uint8Array(data.byteLength);
|
|
111
|
+
copy.set(data);
|
|
112
|
+
return copy.buffer;
|
|
113
|
+
}
|
|
114
|
+
async function messageBytes(data) {
|
|
115
|
+
if (data instanceof ArrayBuffer) return new Uint8Array(data);
|
|
116
|
+
if (data instanceof Uint8Array) return data;
|
|
117
|
+
if (typeof Blob !== "undefined" && data instanceof Blob)
|
|
118
|
+
return new Uint8Array(await data.arrayBuffer());
|
|
119
|
+
return void 0;
|
|
120
|
+
}
|
|
121
|
+
function failureMessage(body, fallback) {
|
|
122
|
+
const errors = body.errors;
|
|
123
|
+
if (!Array.isArray(errors)) return fallback;
|
|
124
|
+
const first = errors[0];
|
|
125
|
+
if (!isRecord(first) || Array.isArray(first)) return fallback;
|
|
126
|
+
const message = stringField(first, "message");
|
|
127
|
+
if (message === void 0 || message === "") return fallback;
|
|
128
|
+
return message;
|
|
129
|
+
}
|
|
130
|
+
function resultOf(body) {
|
|
131
|
+
if (!isRecord(body) || Array.isArray(body)) return body;
|
|
132
|
+
if (body.success === false) throw new Error(failureMessage(body, "Cloudflare request failed"));
|
|
133
|
+
if (!Object.hasOwn(body, "result")) return body;
|
|
134
|
+
return body.result;
|
|
135
|
+
}
|
|
136
|
+
function errorText(text, status) {
|
|
137
|
+
if (text === "") return `HTTP ${status}`;
|
|
138
|
+
let parsed;
|
|
139
|
+
try {
|
|
140
|
+
parsed = JSON.parse(text);
|
|
141
|
+
} catch {
|
|
142
|
+
return text;
|
|
143
|
+
}
|
|
144
|
+
if (!isRecord(parsed) || Array.isArray(parsed)) return text;
|
|
145
|
+
if (Array.isArray(parsed.errors)) return failureMessage(parsed, text);
|
|
146
|
+
const message = stringField(parsed, "message");
|
|
147
|
+
if (message !== void 0) return message;
|
|
148
|
+
return text;
|
|
149
|
+
}
|
|
150
|
+
export {
|
|
151
|
+
audioFrame,
|
|
152
|
+
auraEncoding,
|
|
153
|
+
awaitOpen,
|
|
154
|
+
connectSocket,
|
|
155
|
+
errorText,
|
|
156
|
+
failureMessage,
|
|
157
|
+
gatewayId,
|
|
158
|
+
listenQuery,
|
|
159
|
+
messageBytes,
|
|
160
|
+
resultOf,
|
|
161
|
+
runUrl,
|
|
162
|
+
sameFormat,
|
|
163
|
+
session,
|
|
164
|
+
workersAiSocket
|
|
165
|
+
};
|
|
166
|
+
//# sourceMappingURL=wire.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/cloudflare/wire.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ProviderRequestOptions } from '@earendil-works/pi-ai';\nimport type { AudioFormat } from '../audio.ts';\nimport { isRecord, requestHeaders, stringField } from '../openai/wire.ts';\n\nconst placeholder = '{account_id}';\n\nconst speechEncodings: Record<string, string> = {\n pcm16: 'linear16',\n mp3: 'mp3',\n flac: 'flac',\n mulaw: 'mulaw',\n alaw: 'alaw',\n opus: 'opus',\n};\n\ninterface RouteModel {\n provider: string;\n baseUrl: string;\n headers?: Record<string, string>;\n}\n\nexport interface CloudflareRoute {\n headers: Record<string, string>;\n account: string;\n gateway: string;\n}\n\nfunction headerValue(headers: Record<string, string>, name: string): string | undefined {\n const expected = name.toLowerCase();\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === expected && value.trim() !== '') return value;\n }\n return undefined;\n}\n\nfunction takeHeader(headers: Record<string, string>, name: string): string | undefined {\n const expected = name.toLowerCase();\n let found: string | undefined;\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() !== expected) continue;\n if (found === undefined && value.trim() !== '') found = value.trim();\n delete headers[key];\n }\n return found;\n}\n\nfunction accountFromUrl(baseUrl: string): string | undefined {\n const match = /\\/accounts\\/([^/]+)\\/ai\\/run/.exec(baseUrl);\n const found = match?.[1];\n if (found === undefined || found === placeholder) return undefined;\n return found;\n}\n\nexport function gatewayId(headers: Record<string, string>): string {\n const value = headerValue(headers, 'cf-aig-gateway-id');\n if (value === undefined) return 'default';\n return value.trim();\n}\n\nfunction stampGatewayAuth(headers: Record<string, string>): void {\n const gateway = headerValue(headers, 'cf-aig-authorization');\n const authorization = headerValue(headers, 'authorization');\n if (\n gateway === undefined &&\n authorization !== undefined &&\n authorization.toLowerCase().startsWith('bearer ')\n ) {\n headers['cf-aig-authorization'] = authorization.slice(authorization.indexOf(' ') + 1).trim();\n return;\n }\n if (gateway !== undefined && authorization === undefined)\n headers.Authorization = `Bearer ${gateway}`;\n}\n\nexport function session(\n model: RouteModel,\n options: ProviderRequestOptions | undefined\n): CloudflareRoute {\n const headers = requestHeaders(model, options);\n const fromHeader = takeHeader(headers, 'cf-account-id');\n const account = fromHeader ?? accountFromUrl(model.baseUrl);\n if (account === undefined) throw new Error('No Cloudflare account id');\n const gateway = gatewayId(headers);\n stampGatewayAuth(headers);\n return { headers, account, gateway };\n}\n\nexport function runUrl(baseUrl: string, modelId: string, account: string): string {\n const base = baseUrl.replaceAll(placeholder, account).replace(/\\/$/, '');\n return `${base}/${modelId}`;\n}\n\nexport function workersAiSocket(\n account: string,\n gateway: string,\n modelId: string,\n query: string\n): string {\n const extra = query === '' ? '' : `&${query}`;\n return `wss://gateway.ai.cloudflare.com/v1/${account}/${gateway}/workers-ai?model=${encodeURIComponent(modelId)}${extra}`;\n}\n\nexport function auraEncoding(format: AudioFormat): string {\n const name = speechEncodings[format.encoding];\n if (!name) throw new Error(`Unsupported speech format ${format.encoding}`);\n return name;\n}\n\nexport function listenQuery(modelId: string, format: AudioFormat): string {\n if (format.encoding !== 'pcm16' && format.encoding !== 'mulaw') {\n throw new Error(`Unsupported audio format ${format.encoding}`);\n }\n if (modelId.includes('/flux') && format.encoding !== 'pcm16') {\n throw new Error(`Unsupported audio format ${format.encoding}`);\n }\n const encoding = format.encoding === 'mulaw' ? 'mulaw' : 'linear16';\n const shared = `encoding=${encoding}&sample_rate=${format.sampleRate}`;\n if (modelId.includes('/flux')) return shared;\n return `${shared}&interim_results=true&vad_events=true`;\n}\n\nexport function sameFormat(left: AudioFormat, right: AudioFormat): boolean {\n return (\n left.encoding === right.encoding &&\n left.sampleRate === right.sampleRate &&\n left.channels === right.channels\n );\n}\n\nexport function connectSocket(url: string, headers: Record<string, string>): WebSocket {\n const Socket = WebSocket as unknown as new (\n address: string,\n init: { headers: Record<string, string> }\n ) => WebSocket;\n return new Socket(url, { headers });\n}\n\nexport function awaitOpen(\n socket: WebSocket,\n label: string,\n fail: (message: string) => void\n): Promise<void> {\n return new Promise((resolve) => {\n let opened = false;\n socket.addEventListener('open', () => {\n opened = true;\n resolve();\n });\n socket.addEventListener('error', () => {\n fail(`${label} socket failed`);\n if (!opened) resolve();\n });\n socket.addEventListener('close', () => {\n if (!opened) {\n fail(`${label} socket closed`);\n resolve();\n }\n });\n });\n}\n\nexport function audioFrame(data: Uint8Array): ArrayBuffer {\n const copy = new Uint8Array(data.byteLength);\n copy.set(data);\n return copy.buffer;\n}\n\nexport async function messageBytes(data: unknown): Promise<Uint8Array | undefined> {\n if (data instanceof ArrayBuffer) return new Uint8Array(data);\n if (data instanceof Uint8Array) return data;\n if (typeof Blob !== 'undefined' && data instanceof Blob)\n return new Uint8Array(await data.arrayBuffer());\n return undefined;\n}\n\nexport function failureMessage(body: Record<string, unknown>, fallback: string): string {\n const errors = body.errors;\n if (!Array.isArray(errors)) return fallback;\n const first = errors[0];\n if (!isRecord(first) || Array.isArray(first)) return fallback;\n const message = stringField(first, 'message');\n if (message === undefined || message === '') return fallback;\n return message;\n}\n\nexport function resultOf(body: unknown): unknown {\n if (!isRecord(body) || Array.isArray(body)) return body;\n if (body.success === false) throw new Error(failureMessage(body, 'Cloudflare request failed'));\n if (!Object.hasOwn(body, 'result')) return body;\n return body.result;\n}\n\nexport function errorText(text: string, status: number): string {\n if (text === '') return `HTTP ${status}`;\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return text;\n }\n if (!isRecord(parsed) || Array.isArray(parsed)) return text;\n if (Array.isArray(parsed.errors)) return failureMessage(parsed, text);\n const message = stringField(parsed, 'message');\n if (message !== undefined) return message;\n return text;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { VoiceRequestError } from "./errors.js";
|
|
2
|
+
function bearer(headers) {
|
|
3
|
+
if (headers === void 0) return void 0;
|
|
4
|
+
const value = headers.Authorization ?? headers.authorization;
|
|
5
|
+
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
6
|
+
if (value.startsWith("Bearer ")) return value.slice("Bearer ".length);
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
function elevenLabsCustomLlm(llm) {
|
|
10
|
+
const custom = {
|
|
11
|
+
url: llm.baseUrl,
|
|
12
|
+
model_id: llm.id,
|
|
13
|
+
api_type: llm.api === "openai-responses" ? "responses" : "chat_completions"
|
|
14
|
+
};
|
|
15
|
+
const headers = llm.headers;
|
|
16
|
+
if (headers !== void 0 && Object.keys(headers).length > 0) custom.request_headers = headers;
|
|
17
|
+
return { llm: "custom-llm", custom_llm: custom };
|
|
18
|
+
}
|
|
19
|
+
function fishCustomLlm(llm) {
|
|
20
|
+
if (llm.api === "openai-responses") {
|
|
21
|
+
throw new VoiceRequestError("error", "fish custom llm accepts openai-completions only");
|
|
22
|
+
}
|
|
23
|
+
const custom = { base_url: llm.baseUrl, model: llm.id };
|
|
24
|
+
const key = bearer(llm.headers);
|
|
25
|
+
if (key !== void 0) custom.api_key = key;
|
|
26
|
+
return { custom };
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
elevenLabsCustomLlm,
|
|
30
|
+
fishCustomLlm
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=custom-llm.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/custom-llm.ts"],
|
|
4
|
+
"sourcesContent": ["import type { JsonObject } from '@earendil-works/pi-ai';\nimport { VoiceRequestError } from './errors.ts';\nimport type { CustomLlm } from './hosted-agent.ts';\n\nfunction bearer(headers: CustomLlm['headers']): string | undefined {\n if (headers === undefined) return undefined;\n const value = headers.Authorization ?? headers.authorization;\n if (typeof value !== 'string' || value.length === 0) return undefined;\n if (value.startsWith('Bearer ')) return value.slice('Bearer '.length);\n return value;\n}\n\nexport function elevenLabsCustomLlm(llm: CustomLlm): JsonObject {\n const custom: JsonObject = {\n url: llm.baseUrl,\n model_id: llm.id,\n api_type: llm.api === 'openai-responses' ? 'responses' : 'chat_completions',\n };\n const headers = llm.headers;\n if (headers !== undefined && Object.keys(headers).length > 0) custom.request_headers = headers;\n return { llm: 'custom-llm', custom_llm: custom };\n}\n\nexport function fishCustomLlm(llm: CustomLlm): JsonObject {\n if (llm.api === 'openai-responses') {\n throw new VoiceRequestError('error', 'fish custom llm accepts openai-completions only');\n }\n const custom: JsonObject = { base_url: llm.baseUrl, model: llm.id };\n const key = bearer(llm.headers);\n if (key !== undefined) custom.api_key = key;\n return { custom };\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,yBAAyB;AAGlC,SAAS,OAAO,SAAmD;AACjE,MAAI,YAAY,OAAW,QAAO;AAClC,QAAM,QAAQ,QAAQ,iBAAiB,QAAQ;AAC/C,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,EAAG,QAAO;AAC5D,MAAI,MAAM,WAAW,SAAS,EAAG,QAAO,MAAM,MAAM,UAAU,MAAM;AACpE,SAAO;AACT;AAEO,SAAS,oBAAoB,KAA4B;AAC9D,QAAM,SAAqB;AAAA,IACzB,KAAK,IAAI;AAAA,IACT,UAAU,IAAI;AAAA,IACd,UAAU,IAAI,QAAQ,qBAAqB,cAAc;AAAA,EAC3D;AACA,QAAM,UAAU,IAAI;AACpB,MAAI,YAAY,UAAa,OAAO,KAAK,OAAO,EAAE,SAAS,EAAG,QAAO,kBAAkB;AACvF,SAAO,EAAE,KAAK,cAAc,YAAY,OAAO;AACjD;AAEO,SAAS,cAAc,KAA4B;AACxD,MAAI,IAAI,QAAQ,oBAAoB;AAClC,UAAM,IAAI,kBAAkB,SAAS,iDAAiD;AAAA,EACxF;AACA,QAAM,SAAqB,EAAE,UAAU,IAAI,SAAS,OAAO,IAAI,GAAG;AAClE,QAAM,MAAM,OAAO,IAAI,OAAO;AAC9B,MAAI,QAAQ,OAAW,QAAO,UAAU;AACxC,SAAO,EAAE,OAAO;AAClB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -44,11 +44,13 @@ function registerElevenLabsAgents(options = {}) {
|
|
|
44
44
|
unregisterApi();
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
|
+
import { elevenLabsCustomLlm } from "../custom-llm.js";
|
|
47
48
|
import { hashHostedSpec, memoryHostedCache } from "../hosted-agent.js";
|
|
48
49
|
import { agentBody, httpsOrigin, provisionAgent } from "./provision.js";
|
|
49
50
|
export {
|
|
50
51
|
agentBody,
|
|
51
52
|
elevenLabsAgent,
|
|
53
|
+
elevenLabsCustomLlm,
|
|
52
54
|
hashHostedSpec,
|
|
53
55
|
httpsOrigin,
|
|
54
56
|
memoryHostedCache,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/elevenlabs-agents/index.ts"],
|
|
4
|
-
"sourcesContent": ["import type { RealtimeModel } from '../models.ts';\nimport { registerVoiceModel } from '../models.ts';\nimport { registerRealtimeApiProvider } from '../registry.ts';\nimport type { ElevenLabsOptions } from './session.ts';\nimport { connectElevenLabs } from './session.ts';\n\nconst voices = [\n { id: '21m00Tcm4TlvDq8ikWAM', name: 'Rachel' },\n { id: 'EXAVITQu4vr4xnSDxMaL', name: 'Sarah' },\n { id: 'pNInz6obpgDQGcFmaJgB', name: 'Adam' },\n];\n\nfunction pcm16(sampleRate: number) {\n return { encoding: 'pcm16' as const, sampleRate, channels: 1 as const };\n}\n\nexport const elevenLabsAgent: RealtimeModel<'elevenlabs-agents'> = {\n id: 'elevenlabs-agent',\n name: 'ElevenLabs Agent',\n api: 'elevenlabs-agents',\n provider: 'elevenlabs',\n baseUrl: 'wss://api.elevenlabs.io/v1/convai/conversation',\n type: 'realtime',\n transports: ['websocket'],\n toolCalls: 'blocking',\n inputFormat: pcm16(16000),\n outputFormat: pcm16(16000),\n maxSessionMs: 600_000,\n resumable: false,\n ephemeralAuth: true,\n voices,\n cascade: { stt: 'scribe_realtime', llm: 'gemini-2.5-flash', tts: 'eleven_flash_v2_5' },\n provisioning: 'agent',\n delivery: { interrupt: 'approximate', when_idle: 'approximate', silent: 'native' },\n truncation: 'server',\n liveToolUpdate: false,\n toolTimeoutMs: { min: 1000, max: 120_000 },\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n audioCost: { perInputMinute: 0.08 },\n};\n\nexport function registerElevenLabsAgents(options: ElevenLabsOptions = {}): () => void {\n const unregisterModel = registerVoiceModel(elevenLabsAgent);\n const unregisterApi = registerRealtimeApiProvider({\n api: 'elevenlabs-agents',\n connect: (model, context, connectOptions) =>\n connectElevenLabs(model, context, connectOptions, options),\n });\n return () => {\n unregisterModel();\n unregisterApi();\n };\n}\n\nexport type { CustomLlm, HostedAgent, HostedAgentSpec, HostedCache } from '../hosted-agent.ts';\nexport { hashHostedSpec, memoryHostedCache } from '../hosted-agent.ts';\nexport type { ProvisionOptions } from './provision.ts';\nexport { agentBody, httpsOrigin, provisionAgent } from './provision.ts';\nexport type { ElevenLabsOptions, VoiceWireFactory } from './session.ts';\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,0BAA0B;AACnC,SAAS,mCAAmC;AAE5C,SAAS,yBAAyB;AAElC,MAAM,SAAS;AAAA,EACb,EAAE,IAAI,wBAAwB,MAAM,SAAS;AAAA,EAC7C,EAAE,IAAI,wBAAwB,MAAM,QAAQ;AAAA,EAC5C,EAAE,IAAI,wBAAwB,MAAM,OAAO;AAC7C;AAEA,SAAS,MAAM,YAAoB;AACjC,SAAO,EAAE,UAAU,SAAkB,YAAY,UAAU,EAAW;AACxE;AAEO,MAAM,kBAAsD;AAAA,EACjE,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,KAAK;AAAA,EACL,UAAU;AAAA,EACV,SAAS;AAAA,EACT,MAAM;AAAA,EACN,YAAY,CAAC,WAAW;AAAA,EACxB,WAAW;AAAA,EACX,aAAa,MAAM,IAAK;AAAA,EACxB,cAAc,MAAM,IAAK;AAAA,EACzB,cAAc;AAAA,EACd,WAAW;AAAA,EACX,eAAe;AAAA,EACf;AAAA,EACA,SAAS,EAAE,KAAK,mBAAmB,KAAK,oBAAoB,KAAK,oBAAoB;AAAA,EACrF,cAAc;AAAA,EACd,UAAU,EAAE,WAAW,eAAe,WAAW,eAAe,QAAQ,SAAS;AAAA,EACjF,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,eAAe,EAAE,KAAK,KAAM,KAAK,KAAQ;AAAA,EACzC,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,EAAE;AAAA,EACzD,WAAW,EAAE,gBAAgB,KAAK;AACpC;AAEO,SAAS,yBAAyB,UAA6B,CAAC,GAAe;AACpF,QAAM,kBAAkB,mBAAmB,eAAe;AAC1D,QAAM,gBAAgB,4BAA4B;AAAA,IAChD,KAAK;AAAA,IACL,SAAS,CAAC,OAAO,SAAS,mBACxB,kBAAkB,OAAO,SAAS,gBAAgB,OAAO;AAAA,EAC7D,CAAC;AACD,SAAO,MAAM;AACX,oBAAgB;AAChB,kBAAc;AAAA,EAChB;AACF;
|
|
4
|
+
"sourcesContent": ["import type { RealtimeModel } from '../models.ts';\nimport { registerVoiceModel } from '../models.ts';\nimport { registerRealtimeApiProvider } from '../registry.ts';\nimport type { ElevenLabsOptions } from './session.ts';\nimport { connectElevenLabs } from './session.ts';\n\nconst voices = [\n { id: '21m00Tcm4TlvDq8ikWAM', name: 'Rachel' },\n { id: 'EXAVITQu4vr4xnSDxMaL', name: 'Sarah' },\n { id: 'pNInz6obpgDQGcFmaJgB', name: 'Adam' },\n];\n\nfunction pcm16(sampleRate: number) {\n return { encoding: 'pcm16' as const, sampleRate, channels: 1 as const };\n}\n\nexport const elevenLabsAgent: RealtimeModel<'elevenlabs-agents'> = {\n id: 'elevenlabs-agent',\n name: 'ElevenLabs Agent',\n api: 'elevenlabs-agents',\n provider: 'elevenlabs',\n baseUrl: 'wss://api.elevenlabs.io/v1/convai/conversation',\n type: 'realtime',\n transports: ['websocket'],\n toolCalls: 'blocking',\n inputFormat: pcm16(16000),\n outputFormat: pcm16(16000),\n maxSessionMs: 600_000,\n resumable: false,\n ephemeralAuth: true,\n voices,\n cascade: { stt: 'scribe_realtime', llm: 'gemini-2.5-flash', tts: 'eleven_flash_v2_5' },\n provisioning: 'agent',\n delivery: { interrupt: 'approximate', when_idle: 'approximate', silent: 'native' },\n truncation: 'server',\n liveToolUpdate: false,\n toolTimeoutMs: { min: 1000, max: 120_000 },\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n audioCost: { perInputMinute: 0.08 },\n};\n\nexport function registerElevenLabsAgents(options: ElevenLabsOptions = {}): () => void {\n const unregisterModel = registerVoiceModel(elevenLabsAgent);\n const unregisterApi = registerRealtimeApiProvider({\n api: 'elevenlabs-agents',\n connect: (model, context, connectOptions) =>\n connectElevenLabs(model, context, connectOptions, options),\n });\n return () => {\n unregisterModel();\n unregisterApi();\n };\n}\n\nexport { elevenLabsCustomLlm } from '../custom-llm.ts';\nexport type { CustomLlm, HostedAgent, HostedAgentSpec, HostedCache } from '../hosted-agent.ts';\nexport { hashHostedSpec, memoryHostedCache } from '../hosted-agent.ts';\nexport type { ProvisionOptions } from './provision.ts';\nexport { agentBody, httpsOrigin, provisionAgent } from './provision.ts';\nexport type { ElevenLabsOptions, VoiceWireFactory } from './session.ts';\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,0BAA0B;AACnC,SAAS,mCAAmC;AAE5C,SAAS,yBAAyB;AAElC,MAAM,SAAS;AAAA,EACb,EAAE,IAAI,wBAAwB,MAAM,SAAS;AAAA,EAC7C,EAAE,IAAI,wBAAwB,MAAM,QAAQ;AAAA,EAC5C,EAAE,IAAI,wBAAwB,MAAM,OAAO;AAC7C;AAEA,SAAS,MAAM,YAAoB;AACjC,SAAO,EAAE,UAAU,SAAkB,YAAY,UAAU,EAAW;AACxE;AAEO,MAAM,kBAAsD;AAAA,EACjE,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,KAAK;AAAA,EACL,UAAU;AAAA,EACV,SAAS;AAAA,EACT,MAAM;AAAA,EACN,YAAY,CAAC,WAAW;AAAA,EACxB,WAAW;AAAA,EACX,aAAa,MAAM,IAAK;AAAA,EACxB,cAAc,MAAM,IAAK;AAAA,EACzB,cAAc;AAAA,EACd,WAAW;AAAA,EACX,eAAe;AAAA,EACf;AAAA,EACA,SAAS,EAAE,KAAK,mBAAmB,KAAK,oBAAoB,KAAK,oBAAoB;AAAA,EACrF,cAAc;AAAA,EACd,UAAU,EAAE,WAAW,eAAe,WAAW,eAAe,QAAQ,SAAS;AAAA,EACjF,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,eAAe,EAAE,KAAK,KAAM,KAAK,KAAQ;AAAA,EACzC,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,EAAE;AAAA,EACzD,WAAW,EAAE,gBAAgB,KAAK;AACpC;AAEO,SAAS,yBAAyB,UAA6B,CAAC,GAAe;AACpF,QAAM,kBAAkB,mBAAmB,eAAe;AAC1D,QAAM,gBAAgB,4BAA4B;AAAA,IAChD,KAAK;AAAA,IACL,SAAS,CAAC,OAAO,SAAS,mBACxB,kBAAkB,OAAO,SAAS,gBAAgB,OAAO;AAAA,EAC7D,CAAC;AACD,SAAO,MAAM;AACX,oBAAgB;AAChB,kBAAc;AAAA,EAChB;AACF;AAEA,SAAS,2BAA2B;AAEpC,SAAS,gBAAgB,yBAAyB;AAElD,SAAS,WAAW,aAAa,sBAAsB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { elevenLabsCustomLlm } from "../custom-llm.js";
|
|
1
2
|
import { VoiceRequestError } from "../errors.js";
|
|
2
3
|
import {
|
|
3
4
|
hashHostedSpec,
|
|
@@ -66,14 +67,7 @@ function clientTool(tool, timeoutMs) {
|
|
|
66
67
|
};
|
|
67
68
|
}
|
|
68
69
|
function customLlmFields(llm) {
|
|
69
|
-
|
|
70
|
-
url: llm.baseUrl,
|
|
71
|
-
model_id: llm.id,
|
|
72
|
-
api_type: llm.api === "openai-responses" ? "responses" : "chat_completions"
|
|
73
|
-
};
|
|
74
|
-
const headers = llm.headers;
|
|
75
|
-
if (headers !== void 0 && Object.keys(headers).length > 0) custom.request_headers = headers;
|
|
76
|
-
return { llm: "custom-llm", custom_llm: custom };
|
|
70
|
+
return elevenLabsCustomLlm(llm);
|
|
77
71
|
}
|
|
78
72
|
function promptFields(spec) {
|
|
79
73
|
const llm = typeof spec.llm === "string" ? { llm: spec.llm } : customLlmFields(spec.llm);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/elevenlabs-agents/provision.ts"],
|
|
4
|
-
"sourcesContent": ["import type { JsonObject, ProviderRequestOptions, Tool } from '@earendil-works/pi-ai';\nimport { VoiceRequestError } from '../errors.ts';\nimport {\n type CustomLlm,\n type HostedAgent,\n type HostedAgentSpec,\n type HostedCache,\n hashHostedSpec,\n hostedCacheKey,\n memoryHostedCache,\n} from '../hosted-agent.ts';\nimport type { RealtimeModel } from '../models.ts';\n\nexport interface ProvisionOptions extends ProviderRequestOptions {\n agentId?: string;\n now?: () => number;\n cache?: HostedCache;\n}\n\nconst defaultCache = memoryHostedCache();\n\nconst clientEvents = [\n 'conversation_initiation_metadata',\n 'ping',\n 'audio',\n 'interruption',\n 'user_transcript',\n 'agent_response',\n 'agent_response_correction',\n 'client_tool_call',\n 'vad_score',\n 'agent_chat_response_part',\n 'agent_response_complete',\n];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction asVoice(error: unknown): VoiceRequestError {\n if (error instanceof VoiceRequestError) return error;\n if (error instanceof Error && error.name === 'AbortError') {\n return new VoiceRequestError('aborted', 'session aborted');\n }\n if (error instanceof Error) return new VoiceRequestError('error', error.message);\n return new VoiceRequestError('error', String(error));\n}\n\nexport function httpsOrigin(baseUrl: string): string {\n const url = new URL(baseUrl);\n let protocol = url.protocol;\n if (protocol === 'wss:') protocol = 'https:';\n if (protocol === 'ws:') protocol = 'http:';\n return `${protocol}//${url.host}`;\n}\n\nfunction mergedHeaders(\n options: ProvisionOptions,\n defaults: Record<string, string>\n): Record<string, string> {\n const headers = { ...defaults };\n const extra = options.headers;\n if (extra === undefined) return headers;\n for (const [key, value] of Object.entries(extra)) {\n if (value === null) delete headers[key];\n else headers[key] = value;\n }\n return headers;\n}\n\nfunction apiKey(options: ProvisionOptions): string {\n const key = options.apiKey;\n if (typeof key === 'string' && key.length > 0) return key;\n throw new VoiceRequestError('error', 'missing realtime credential');\n}\n\nfunction timeoutSeconds(timeoutMs: number | undefined): number {\n if (timeoutMs === undefined) return 20;\n return Math.min(120, Math.max(1, Math.round(timeoutMs / 1000)));\n}\n\nfunction clientTool(tool: Tool, timeoutMs: number | undefined): JsonObject {\n return {\n type: 'client',\n name: tool.name,\n description: tool.description,\n expects_response: true,\n response_timeout_secs: timeoutSeconds(timeoutMs),\n parameters: tool.parameters as unknown as JsonObject,\n };\n}\n\nfunction customLlmFields(llm: CustomLlm): JsonObject {\n
|
|
5
|
-
"mappings": "AACA,SAAS,yBAAyB;AAClC;AAAA,EAKE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AASP,MAAM,eAAe,kBAAkB;AAEvC,MAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,QAAQ,OAAmC;AAClD,MAAI,iBAAiB,kBAAmB,QAAO;AAC/C,MAAI,iBAAiB,SAAS,MAAM,SAAS,cAAc;AACzD,WAAO,IAAI,kBAAkB,WAAW,iBAAiB;AAAA,EAC3D;AACA,MAAI,iBAAiB,MAAO,QAAO,IAAI,kBAAkB,SAAS,MAAM,OAAO;AAC/E,SAAO,IAAI,kBAAkB,SAAS,OAAO,KAAK,CAAC;AACrD;AAEO,SAAS,YAAY,SAAyB;AACnD,QAAM,MAAM,IAAI,IAAI,OAAO;AAC3B,MAAI,WAAW,IAAI;AACnB,MAAI,aAAa,OAAQ,YAAW;AACpC,MAAI,aAAa,MAAO,YAAW;AACnC,SAAO,GAAG,QAAQ,KAAK,IAAI,IAAI;AACjC;AAEA,SAAS,cACP,SACA,UACwB;AACxB,QAAM,UAAU,EAAE,GAAG,SAAS;AAC9B,QAAM,QAAQ,QAAQ;AACtB,MAAI,UAAU,OAAW,QAAO;AAChC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,UAAU,KAAM,QAAO,QAAQ,GAAG;AAAA,QACjC,SAAQ,GAAG,IAAI;AAAA,EACtB;AACA,SAAO;AACT;AAEA,SAAS,OAAO,SAAmC;AACjD,QAAM,MAAM,QAAQ;AACpB,MAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,EAAG,QAAO;AACtD,QAAM,IAAI,kBAAkB,SAAS,6BAA6B;AACpE;AAEA,SAAS,eAAe,WAAuC;AAC7D,MAAI,cAAc,OAAW,QAAO;AACpC,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,GAAI,CAAC,CAAC;AAChE;AAEA,SAAS,WAAW,MAAY,WAA2C;AACzE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM,KAAK;AAAA,IACX,aAAa,KAAK;AAAA,IAClB,kBAAkB;AAAA,IAClB,uBAAuB,eAAe,SAAS;AAAA,IAC/C,YAAY,KAAK;AAAA,EACnB;AACF;AAEA,SAAS,gBAAgB,KAA4B;AACnD,
|
|
4
|
+
"sourcesContent": ["import type { JsonObject, ProviderRequestOptions, Tool } from '@earendil-works/pi-ai';\nimport { elevenLabsCustomLlm } from '../custom-llm.ts';\nimport { VoiceRequestError } from '../errors.ts';\nimport {\n type CustomLlm,\n type HostedAgent,\n type HostedAgentSpec,\n type HostedCache,\n hashHostedSpec,\n hostedCacheKey,\n memoryHostedCache,\n} from '../hosted-agent.ts';\nimport type { RealtimeModel } from '../models.ts';\n\nexport interface ProvisionOptions extends ProviderRequestOptions {\n agentId?: string;\n now?: () => number;\n cache?: HostedCache;\n}\n\nconst defaultCache = memoryHostedCache();\n\nconst clientEvents = [\n 'conversation_initiation_metadata',\n 'ping',\n 'audio',\n 'interruption',\n 'user_transcript',\n 'agent_response',\n 'agent_response_correction',\n 'client_tool_call',\n 'vad_score',\n 'agent_chat_response_part',\n 'agent_response_complete',\n];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction asVoice(error: unknown): VoiceRequestError {\n if (error instanceof VoiceRequestError) return error;\n if (error instanceof Error && error.name === 'AbortError') {\n return new VoiceRequestError('aborted', 'session aborted');\n }\n if (error instanceof Error) return new VoiceRequestError('error', error.message);\n return new VoiceRequestError('error', String(error));\n}\n\nexport function httpsOrigin(baseUrl: string): string {\n const url = new URL(baseUrl);\n let protocol = url.protocol;\n if (protocol === 'wss:') protocol = 'https:';\n if (protocol === 'ws:') protocol = 'http:';\n return `${protocol}//${url.host}`;\n}\n\nfunction mergedHeaders(\n options: ProvisionOptions,\n defaults: Record<string, string>\n): Record<string, string> {\n const headers = { ...defaults };\n const extra = options.headers;\n if (extra === undefined) return headers;\n for (const [key, value] of Object.entries(extra)) {\n if (value === null) delete headers[key];\n else headers[key] = value;\n }\n return headers;\n}\n\nfunction apiKey(options: ProvisionOptions): string {\n const key = options.apiKey;\n if (typeof key === 'string' && key.length > 0) return key;\n throw new VoiceRequestError('error', 'missing realtime credential');\n}\n\nfunction timeoutSeconds(timeoutMs: number | undefined): number {\n if (timeoutMs === undefined) return 20;\n return Math.min(120, Math.max(1, Math.round(timeoutMs / 1000)));\n}\n\nfunction clientTool(tool: Tool, timeoutMs: number | undefined): JsonObject {\n return {\n type: 'client',\n name: tool.name,\n description: tool.description,\n expects_response: true,\n response_timeout_secs: timeoutSeconds(timeoutMs),\n parameters: tool.parameters as unknown as JsonObject,\n };\n}\n\nfunction customLlmFields(llm: CustomLlm): JsonObject {\n return elevenLabsCustomLlm(llm);\n}\n\nfunction promptFields(spec: HostedAgentSpec): JsonObject {\n const llm = typeof spec.llm === 'string' ? { llm: spec.llm } : customLlmFields(spec.llm);\n return {\n prompt: spec.instructions,\n tools: spec.tools.map((tool) => clientTool(tool, spec.clientToolTimeoutMs)),\n ...llm,\n };\n}\n\nexport function agentBody(spec: HostedAgentSpec): JsonObject {\n const agent: JsonObject = { prompt: promptFields(spec) };\n if (spec.firstMessage !== undefined) agent.first_message = spec.firstMessage;\n return {\n name: spec.name,\n conversation_config: {\n agent,\n tts: { voice_id: spec.voice },\n conversation: { client_events: clientEvents },\n },\n platform_settings: {\n overrides: {\n conversation_config_override: {\n agent: { prompt: { prompt: true }, first_message: true },\n tts: { voice_id: true },\n },\n custom_llm_extra_body: true,\n },\n },\n };\n}\n\nasync function requestJson(\n fetchImpl: typeof fetch,\n url: string,\n init: RequestInit\n): Promise<Record<string, unknown>> {\n let response: Response;\n try {\n response = await fetchImpl(url, init);\n } catch (error) {\n throw asVoice(error);\n }\n if (!response.ok) {\n throw new VoiceRequestError('error', `agent request failed (${response.status})`);\n }\n let json: unknown;\n try {\n json = await response.json();\n } catch (error) {\n throw asVoice(error);\n }\n if (!isRecord(json)) throw new VoiceRequestError('error', 'agent response was not an object');\n return json;\n}\n\nfunction agentIdFrom(json: Record<string, unknown>, fallback: string | undefined): string {\n const id = json.agent_id;\n if (typeof id === 'string' && id.length > 0) return id;\n if (fallback !== undefined) return fallback;\n throw new VoiceRequestError('error', 'agent response had no agent_id');\n}\n\nexport async function provisionAgent(\n model: RealtimeModel<string>,\n spec: HostedAgentSpec,\n options: ProvisionOptions = {}\n): Promise<HostedAgent> {\n if (options.signal?.aborted === true) throw new VoiceRequestError('aborted', 'session aborted');\n const cache = options.cache ?? defaultCache;\n const now = options.now ?? Date.now;\n const key = hostedCacheKey(model.provider, spec.name);\n const hash = hashHostedSpec(spec);\n const cached = cache.get(key);\n if (cached !== undefined && cached.specHash === hash) return cached;\n const existing = cached?.agentId ?? options.agentId;\n const origin = httpsOrigin(model.baseUrl);\n const url =\n existing === undefined\n ? `${origin}/v1/convai/agents/create`\n : `${origin}/v1/convai/agents/${encodeURIComponent(existing)}`;\n const headers = mergedHeaders(options, {\n 'Content-Type': 'application/json',\n 'xi-api-key': apiKey(options),\n });\n const fetchImpl = options.fetch ?? globalThis.fetch;\n const json = await requestJson(fetchImpl, url, {\n method: existing === undefined ? 'POST' : 'PATCH',\n headers,\n body: JSON.stringify(agentBody(spec)),\n signal: options.signal,\n });\n const agent: HostedAgent = {\n agentId: agentIdFrom(json, existing),\n specHash: hash,\n publishedAt: now(),\n };\n cache.set(key, agent);\n return agent;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -40,12 +40,14 @@ function registerFishAgents(options = {}) {
|
|
|
40
40
|
unregisterApi();
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
+
import { fishCustomLlm } from "../custom-llm.js";
|
|
43
44
|
import { hashHostedSpec, memoryHostedCache } from "../hosted-agent.js";
|
|
44
45
|
import { agentConfig, httpsOrigin, provisionAgent } from "./provision.js";
|
|
45
46
|
import { importFishSdk, openFishRoom } from "./room.js";
|
|
46
47
|
export {
|
|
47
48
|
agentConfig,
|
|
48
49
|
fishAgent,
|
|
50
|
+
fishCustomLlm,
|
|
49
51
|
hashHostedSpec,
|
|
50
52
|
httpsOrigin,
|
|
51
53
|
importFishSdk,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/fish-agents/index.ts"],
|
|
4
|
-
"sourcesContent": ["import type { RealtimeModel } from '../models.ts';\nimport { registerVoiceModel } from '../models.ts';\nimport { registerRealtimeApiProvider } from '../registry.ts';\nimport type { FishOptions } from './session.ts';\nimport { connectFish } from './session.ts';\n\nconst voices = [{ id: '802e3bc2b27e49c2995d23ef70e6ac89', name: 'Example' }];\n\nfunction pcm16(sampleRate: number) {\n return { encoding: 'pcm16' as const, sampleRate, channels: 1 as const };\n}\n\nexport const fishAgent: RealtimeModel<'fish-agents'> = {\n id: 'fish-agent',\n name: 'Fish Agent',\n api: 'fish-agents',\n provider: 'fish',\n baseUrl: 'https://api.fish.audio',\n type: 'realtime',\n transports: ['webrtc'],\n toolCalls: 'blocking',\n inputFormat: pcm16(24000),\n outputFormat: pcm16(24000),\n maxSessionMs: 1_800_000,\n resumable: false,\n ephemeralAuth: true,\n voices,\n cascade: { stt: 'deepgram:nova-3', llm: 'fish', tts: 's2.1-pro' },\n provisioning: 'agent',\n delivery: { interrupt: 'native', when_idle: 'approximate', silent: 'approximate' },\n truncation: 'server',\n liveToolUpdate: false,\n toolTimeoutMs: { min: 1000, max: 120_000 },\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n audioCost: { perInputMinute: 0 },\n};\n\nexport function registerFishAgents(options: FishOptions = {}): () => void {\n const unregisterModel = registerVoiceModel(fishAgent);\n const unregisterApi = registerRealtimeApiProvider({\n api: 'fish-agents',\n connect: (model, context, connectOptions) =>\n connectFish(model, context, connectOptions, options),\n });\n return () => {\n unregisterModel();\n unregisterApi();\n };\n}\n\nexport type { CustomLlm, HostedAgent, HostedAgentSpec, HostedCache } from '../hosted-agent.ts';\nexport { hashHostedSpec, memoryHostedCache } from '../hosted-agent.ts';\nexport type { ProvisionOptions } from './provision.ts';\nexport { agentConfig, httpsOrigin, provisionAgent } from './provision.ts';\nexport type { FishRoom, FishRoomFactory, FishSdk } from './room.ts';\nexport { importFishSdk, openFishRoom } from './room.ts';\nexport type { FishOptions } from './session.ts';\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,0BAA0B;AACnC,SAAS,mCAAmC;AAE5C,SAAS,mBAAmB;AAE5B,MAAM,SAAS,CAAC,EAAE,IAAI,oCAAoC,MAAM,UAAU,CAAC;AAE3E,SAAS,MAAM,YAAoB;AACjC,SAAO,EAAE,UAAU,SAAkB,YAAY,UAAU,EAAW;AACxE;AAEO,MAAM,YAA0C;AAAA,EACrD,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,KAAK;AAAA,EACL,UAAU;AAAA,EACV,SAAS;AAAA,EACT,MAAM;AAAA,EACN,YAAY,CAAC,QAAQ;AAAA,EACrB,WAAW;AAAA,EACX,aAAa,MAAM,IAAK;AAAA,EACxB,cAAc,MAAM,IAAK;AAAA,EACzB,cAAc;AAAA,EACd,WAAW;AAAA,EACX,eAAe;AAAA,EACf;AAAA,EACA,SAAS,EAAE,KAAK,mBAAmB,KAAK,QAAQ,KAAK,WAAW;AAAA,EAChE,cAAc;AAAA,EACd,UAAU,EAAE,WAAW,UAAU,WAAW,eAAe,QAAQ,cAAc;AAAA,EACjF,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,eAAe,EAAE,KAAK,KAAM,KAAK,KAAQ;AAAA,EACzC,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,EAAE;AAAA,EACzD,WAAW,EAAE,gBAAgB,EAAE;AACjC;AAEO,SAAS,mBAAmB,UAAuB,CAAC,GAAe;AACxE,QAAM,kBAAkB,mBAAmB,SAAS;AACpD,QAAM,gBAAgB,4BAA4B;AAAA,IAChD,KAAK;AAAA,IACL,SAAS,CAAC,OAAO,SAAS,mBACxB,YAAY,OAAO,SAAS,gBAAgB,OAAO;AAAA,EACvD,CAAC;AACD,SAAO,MAAM;AACX,oBAAgB;AAChB,kBAAc;AAAA,EAChB;AACF;
|
|
4
|
+
"sourcesContent": ["import type { RealtimeModel } from '../models.ts';\nimport { registerVoiceModel } from '../models.ts';\nimport { registerRealtimeApiProvider } from '../registry.ts';\nimport type { FishOptions } from './session.ts';\nimport { connectFish } from './session.ts';\n\nconst voices = [{ id: '802e3bc2b27e49c2995d23ef70e6ac89', name: 'Example' }];\n\nfunction pcm16(sampleRate: number) {\n return { encoding: 'pcm16' as const, sampleRate, channels: 1 as const };\n}\n\nexport const fishAgent: RealtimeModel<'fish-agents'> = {\n id: 'fish-agent',\n name: 'Fish Agent',\n api: 'fish-agents',\n provider: 'fish',\n baseUrl: 'https://api.fish.audio',\n type: 'realtime',\n transports: ['webrtc'],\n toolCalls: 'blocking',\n inputFormat: pcm16(24000),\n outputFormat: pcm16(24000),\n maxSessionMs: 1_800_000,\n resumable: false,\n ephemeralAuth: true,\n voices,\n cascade: { stt: 'deepgram:nova-3', llm: 'fish', tts: 's2.1-pro' },\n provisioning: 'agent',\n delivery: { interrupt: 'native', when_idle: 'approximate', silent: 'approximate' },\n truncation: 'server',\n liveToolUpdate: false,\n toolTimeoutMs: { min: 1000, max: 120_000 },\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n audioCost: { perInputMinute: 0 },\n};\n\nexport function registerFishAgents(options: FishOptions = {}): () => void {\n const unregisterModel = registerVoiceModel(fishAgent);\n const unregisterApi = registerRealtimeApiProvider({\n api: 'fish-agents',\n connect: (model, context, connectOptions) =>\n connectFish(model, context, connectOptions, options),\n });\n return () => {\n unregisterModel();\n unregisterApi();\n };\n}\n\nexport { fishCustomLlm } from '../custom-llm.ts';\nexport type { CustomLlm, HostedAgent, HostedAgentSpec, HostedCache } from '../hosted-agent.ts';\nexport { hashHostedSpec, memoryHostedCache } from '../hosted-agent.ts';\nexport type { ProvisionOptions } from './provision.ts';\nexport { agentConfig, httpsOrigin, provisionAgent } from './provision.ts';\nexport type { FishRoom, FishRoomFactory, FishSdk } from './room.ts';\nexport { importFishSdk, openFishRoom } from './room.ts';\nexport type { FishOptions } from './session.ts';\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,0BAA0B;AACnC,SAAS,mCAAmC;AAE5C,SAAS,mBAAmB;AAE5B,MAAM,SAAS,CAAC,EAAE,IAAI,oCAAoC,MAAM,UAAU,CAAC;AAE3E,SAAS,MAAM,YAAoB;AACjC,SAAO,EAAE,UAAU,SAAkB,YAAY,UAAU,EAAW;AACxE;AAEO,MAAM,YAA0C;AAAA,EACrD,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,KAAK;AAAA,EACL,UAAU;AAAA,EACV,SAAS;AAAA,EACT,MAAM;AAAA,EACN,YAAY,CAAC,QAAQ;AAAA,EACrB,WAAW;AAAA,EACX,aAAa,MAAM,IAAK;AAAA,EACxB,cAAc,MAAM,IAAK;AAAA,EACzB,cAAc;AAAA,EACd,WAAW;AAAA,EACX,eAAe;AAAA,EACf;AAAA,EACA,SAAS,EAAE,KAAK,mBAAmB,KAAK,QAAQ,KAAK,WAAW;AAAA,EAChE,cAAc;AAAA,EACd,UAAU,EAAE,WAAW,UAAU,WAAW,eAAe,QAAQ,cAAc;AAAA,EACjF,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,eAAe,EAAE,KAAK,KAAM,KAAK,KAAQ;AAAA,EACzC,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,EAAE;AAAA,EACzD,WAAW,EAAE,gBAAgB,EAAE;AACjC;AAEO,SAAS,mBAAmB,UAAuB,CAAC,GAAe;AACxE,QAAM,kBAAkB,mBAAmB,SAAS;AACpD,QAAM,gBAAgB,4BAA4B;AAAA,IAChD,KAAK;AAAA,IACL,SAAS,CAAC,OAAO,SAAS,mBACxB,YAAY,OAAO,SAAS,gBAAgB,OAAO;AAAA,EACvD,CAAC;AACD,SAAO,MAAM;AACX,oBAAgB;AAChB,kBAAc;AAAA,EAChB;AACF;AAEA,SAAS,qBAAqB;AAE9B,SAAS,gBAAgB,yBAAyB;AAElD,SAAS,aAAa,aAAa,sBAAsB;AAEzD,SAAS,eAAe,oBAAoB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fishCustomLlm } from "../custom-llm.js";
|
|
1
2
|
import { VoiceRequestError } from "../errors.js";
|
|
2
3
|
import {
|
|
3
4
|
hashHostedSpec,
|
|
@@ -66,22 +67,9 @@ function clientTool(tool, timeoutMs) {
|
|
|
66
67
|
timeout_seconds: timeoutSeconds(timeoutMs)
|
|
67
68
|
};
|
|
68
69
|
}
|
|
69
|
-
function bearer(headers) {
|
|
70
|
-
if (headers === void 0) return void 0;
|
|
71
|
-
const value = headers.Authorization ?? headers.authorization;
|
|
72
|
-
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
73
|
-
if (value.startsWith("Bearer ")) return value.slice("Bearer ".length);
|
|
74
|
-
return value;
|
|
75
|
-
}
|
|
76
70
|
function llmFields(llm) {
|
|
77
71
|
if (typeof llm === "string") return { model: llm };
|
|
78
|
-
|
|
79
|
-
throw new VoiceRequestError("error", "fish custom llm accepts openai-completions only");
|
|
80
|
-
}
|
|
81
|
-
const custom = { base_url: llm.baseUrl, model: llm.id };
|
|
82
|
-
const key = bearer(llm.headers);
|
|
83
|
-
if (key !== void 0) custom.api_key = key;
|
|
84
|
-
return { custom };
|
|
72
|
+
return fishCustomLlm(llm);
|
|
85
73
|
}
|
|
86
74
|
function agentConfig(spec, toolIds) {
|
|
87
75
|
const prompt = { system_prompt: spec.instructions };
|