@ai-ecoverse/pi-audio 1.9.0 → 1.11.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 +6 -0
- package/dist/durable/index.js +45 -0
- package/dist/durable/index.js.map +7 -0
- package/dist/gemini-live/index.js +71 -0
- package/dist/gemini-live/index.js.map +7 -0
- package/dist/gemini-live/session.js +599 -0
- package/dist/gemini-live/session.js.map +7 -0
- package/dist/types/durable/index.d.ts +8 -0
- package/dist/types/gemini-live/index.d.ts +7 -0
- package/dist/types/gemini-live/session.d.ts +12 -0
- package/dist/types/xai/catalog.d.ts +1 -0
- package/dist/types/xai/index.d.ts +1 -0
- package/dist/types/xai/listen.d.ts +4 -0
- package/dist/types/xai/speak.d.ts +4 -0
- package/dist/types/xai/transcribe.d.ts +4 -0
- package/dist/types/xai/wire.d.ts +20 -0
- package/dist/xai/catalog.js +133 -0
- package/dist/xai/catalog.js.map +7 -0
- package/dist/xai/index.js +2 -0
- package/dist/xai/index.js.map +2 -2
- package/dist/xai/listen.js +211 -0
- package/dist/xai/listen.js.map +7 -0
- package/dist/xai/speak.js +208 -0
- package/dist/xai/speak.js.map +7 -0
- package/dist/xai/transcribe.js +113 -0
- package/dist/xai/transcribe.js.map +7 -0
- package/dist/xai/wire.js +171 -0
- package/dist/xai/wire.js.map +7 -0
- package/package.json +17 -2
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { chunkSeconds } from "../audio.js";
|
|
2
|
+
import { decodeAudioBase64 } from "../convert.js";
|
|
3
|
+
import { VoiceRequestError } from "../errors.js";
|
|
4
|
+
import {
|
|
5
|
+
applyPayload,
|
|
6
|
+
asVoiceError,
|
|
7
|
+
endpoint,
|
|
8
|
+
isRecord,
|
|
9
|
+
requestHeaders,
|
|
10
|
+
stringField,
|
|
11
|
+
voiceFetch
|
|
12
|
+
} from "../openai/wire.js";
|
|
13
|
+
import { SpeechEventStream } from "../speech.js";
|
|
14
|
+
import { priceAudio } from "../usage.js";
|
|
15
|
+
import { awaitOpen, codecName, connectSocket, messageText, sameFormat, socketUrl } from "./wire.js";
|
|
16
|
+
function languageOf(language) {
|
|
17
|
+
if (language === void 0 || language.trim() === "") return "auto";
|
|
18
|
+
return language;
|
|
19
|
+
}
|
|
20
|
+
function resolveFormat(model, requested) {
|
|
21
|
+
const fallback = model.outputFormats[0];
|
|
22
|
+
if (!fallback) throw new Error("No output format");
|
|
23
|
+
if (!requested) return fallback;
|
|
24
|
+
const match = model.outputFormats.find((format) => sameFormat(format, requested));
|
|
25
|
+
if (!match) throw new Error(`Unsupported speech format ${requested.encoding}`);
|
|
26
|
+
return match;
|
|
27
|
+
}
|
|
28
|
+
function outputFormat(format) {
|
|
29
|
+
const body = {
|
|
30
|
+
codec: codecName(format.encoding),
|
|
31
|
+
sample_rate: format.sampleRate
|
|
32
|
+
};
|
|
33
|
+
if (format.encoding === "mp3") body.bit_rate = 128e3;
|
|
34
|
+
return body;
|
|
35
|
+
}
|
|
36
|
+
function ttsUrl(baseUrl, voice, language, format) {
|
|
37
|
+
const url = socketUrl(baseUrl, "tts");
|
|
38
|
+
const params = new URLSearchParams();
|
|
39
|
+
params.set("language", language);
|
|
40
|
+
params.set("voice", voice);
|
|
41
|
+
params.set("codec", codecName(format.encoding));
|
|
42
|
+
params.set("sample_rate", String(format.sampleRate));
|
|
43
|
+
if (format.encoding === "mp3") params.set("bit_rate", "128000");
|
|
44
|
+
url.search = params.toString();
|
|
45
|
+
return url.toString();
|
|
46
|
+
}
|
|
47
|
+
function speakXAI(model, input, options) {
|
|
48
|
+
const stream = new SpeechEventStream();
|
|
49
|
+
void pumpSpeech(model, input, options, stream);
|
|
50
|
+
return stream;
|
|
51
|
+
}
|
|
52
|
+
async function pumpSpeech(model, input, options, stream) {
|
|
53
|
+
try {
|
|
54
|
+
if (options?.signal?.aborted) throw new DOMException("aborted", "AbortError");
|
|
55
|
+
const format = resolveFormat(model, input.format);
|
|
56
|
+
const language = languageOf(input.language);
|
|
57
|
+
if (typeof input.text === "string") {
|
|
58
|
+
await postSpeech(model, input.text, input.voice, language, format, options, stream);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
await streamSpeech(model, input.text, input.voice, language, format, options, stream);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
const voice = asVoiceError(error);
|
|
64
|
+
stream.push({ type: "error", reason: voice.reason, error: voice.error });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async function postSpeech(model, text, voice, language, format, options, stream) {
|
|
68
|
+
const payload = {
|
|
69
|
+
text,
|
|
70
|
+
voice_id: voice,
|
|
71
|
+
language,
|
|
72
|
+
output_format: outputFormat(format)
|
|
73
|
+
};
|
|
74
|
+
const body = await applyPayload(payload, model, options);
|
|
75
|
+
if (!isRecord(body)) throw new Error("Speech payload was not JSON");
|
|
76
|
+
const response = await voiceFetch(
|
|
77
|
+
model,
|
|
78
|
+
options,
|
|
79
|
+
endpoint(model.baseUrl, "tts"),
|
|
80
|
+
JSON.stringify(body),
|
|
81
|
+
"application/json"
|
|
82
|
+
);
|
|
83
|
+
const data = new Uint8Array(await response.arrayBuffer());
|
|
84
|
+
const seconds = chunkSeconds({ data, format, timestamp: 0 });
|
|
85
|
+
const meters = { characters: text.length };
|
|
86
|
+
if (seconds > 0) meters.outputSeconds = seconds;
|
|
87
|
+
stream.push({ type: "start", format });
|
|
88
|
+
stream.push({
|
|
89
|
+
type: "audio",
|
|
90
|
+
chunk: { data, format, timestamp: 0 },
|
|
91
|
+
span: { itemId: model.id, start: 0, end: text.length }
|
|
92
|
+
});
|
|
93
|
+
stream.push({ type: "done", usage: priceAudio(model.audioCost, meters) });
|
|
94
|
+
}
|
|
95
|
+
async function streamSpeech(model, text, voice, language, format, options, stream) {
|
|
96
|
+
const state = {
|
|
97
|
+
opened: false,
|
|
98
|
+
started: false,
|
|
99
|
+
done: false,
|
|
100
|
+
failed: false,
|
|
101
|
+
outputSeconds: 0,
|
|
102
|
+
characters: 0
|
|
103
|
+
};
|
|
104
|
+
let settled = false;
|
|
105
|
+
const push = (event) => {
|
|
106
|
+
if (settled) return;
|
|
107
|
+
if (event.type === "done" || event.type === "error") settled = true;
|
|
108
|
+
stream.push(event);
|
|
109
|
+
};
|
|
110
|
+
const doneWait = Promise.withResolvers();
|
|
111
|
+
const wake = () => {
|
|
112
|
+
doneWait.resolve();
|
|
113
|
+
};
|
|
114
|
+
const headers = requestHeaders(model, options);
|
|
115
|
+
const socket = connectSocket(ttsUrl(model.baseUrl, voice, language, format), headers);
|
|
116
|
+
let incoming = Promise.resolve();
|
|
117
|
+
socket.addEventListener("message", (event) => {
|
|
118
|
+
incoming = incoming.then(() => onMessage(event.data, model.id, format, state, push, wake));
|
|
119
|
+
});
|
|
120
|
+
socket.addEventListener("close", () => {
|
|
121
|
+
if (state.opened && !state.done && !state.failed) {
|
|
122
|
+
state.failed = true;
|
|
123
|
+
push({ type: "error", reason: "error", error: "xAI speech socket closed" });
|
|
124
|
+
}
|
|
125
|
+
wake();
|
|
126
|
+
});
|
|
127
|
+
try {
|
|
128
|
+
await awaitOpen(socket, "xAI speech", (message) => {
|
|
129
|
+
state.failed = true;
|
|
130
|
+
push({ type: "error", reason: "error", error: message });
|
|
131
|
+
wake();
|
|
132
|
+
});
|
|
133
|
+
if (state.failed) return;
|
|
134
|
+
state.opened = true;
|
|
135
|
+
state.characters = await writeText(socket, text, options?.signal);
|
|
136
|
+
if (state.failed) return;
|
|
137
|
+
socket.send(JSON.stringify({ type: "text.done" }));
|
|
138
|
+
if (!state.done) await doneWait.promise;
|
|
139
|
+
if (state.failed) return;
|
|
140
|
+
const meters = { characters: state.characters };
|
|
141
|
+
if (state.outputSeconds > 0) meters.outputSeconds = state.outputSeconds;
|
|
142
|
+
push({ type: "done", usage: priceAudio(model.audioCost, meters) });
|
|
143
|
+
} catch (error) {
|
|
144
|
+
const voice2 = asVoiceError(error);
|
|
145
|
+
state.failed = true;
|
|
146
|
+
push({ type: "error", reason: voice2.reason, error: voice2.error });
|
|
147
|
+
} finally {
|
|
148
|
+
socket.close();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function writeText(socket, text, signal) {
|
|
152
|
+
let combined = "";
|
|
153
|
+
for await (const part of text) {
|
|
154
|
+
if (signal?.aborted) throw new VoiceRequestError("aborted", "aborted");
|
|
155
|
+
combined += part;
|
|
156
|
+
if (part !== "") socket.send(JSON.stringify({ type: "text.delta", delta: part }));
|
|
157
|
+
}
|
|
158
|
+
return combined.length;
|
|
159
|
+
}
|
|
160
|
+
async function onMessage(data, itemId, format, state, push, wake) {
|
|
161
|
+
try {
|
|
162
|
+
const parsed = JSON.parse(await messageText(data));
|
|
163
|
+
if (!isRecord(parsed)) throw new Error("xAI speech event was not JSON");
|
|
164
|
+
applySpeech(parsed, itemId, format, state, push);
|
|
165
|
+
if (state.done || state.failed) wake();
|
|
166
|
+
} catch (error) {
|
|
167
|
+
const voice = asVoiceError(error);
|
|
168
|
+
state.failed = true;
|
|
169
|
+
push({ type: "error", reason: voice.reason, error: voice.error });
|
|
170
|
+
wake();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function applySpeech(event, itemId, format, state, push) {
|
|
174
|
+
const type = stringField(event, "type");
|
|
175
|
+
if (type === "audio.delta") {
|
|
176
|
+
const delta = stringField(event, "delta");
|
|
177
|
+
const payload = delta === void 0 ? "" : delta;
|
|
178
|
+
const data = decodeAudioBase64(payload);
|
|
179
|
+
if (!state.started) {
|
|
180
|
+
state.started = true;
|
|
181
|
+
push({ type: "start", format });
|
|
182
|
+
}
|
|
183
|
+
state.outputSeconds += chunkSeconds({ data, format, timestamp: 0 });
|
|
184
|
+
push({
|
|
185
|
+
type: "audio",
|
|
186
|
+
chunk: { data, format, timestamp: 0 },
|
|
187
|
+
span: { itemId, start: 0, end: state.characters }
|
|
188
|
+
});
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (type === "audio.done") {
|
|
192
|
+
state.done = true;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (type === "error") {
|
|
196
|
+
const message = stringField(event, "message");
|
|
197
|
+
state.failed = true;
|
|
198
|
+
push({
|
|
199
|
+
type: "error",
|
|
200
|
+
reason: "error",
|
|
201
|
+
error: message === void 0 ? "xAI speech failed" : message
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
export {
|
|
206
|
+
speakXAI
|
|
207
|
+
};
|
|
208
|
+
//# sourceMappingURL=speak.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/xai/speak.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ProviderRequestOptions } from '@earendil-works/pi-ai';\nimport type { AudioFormat } from '../audio.ts';\nimport { chunkSeconds } from '../audio.ts';\nimport { decodeAudioBase64 } from '../convert.ts';\nimport { VoiceRequestError } from '../errors.ts';\nimport type { SpeechModel } from '../models.ts';\nimport {\n applyPayload,\n asVoiceError,\n endpoint,\n isRecord,\n requestHeaders,\n stringField,\n voiceFetch,\n} from '../openai/wire.ts';\nimport { type SpeechEvent, SpeechEventStream, type SpeechInput } from '../speech.ts';\nimport { type AudioCounters, priceAudio } from '../usage.ts';\nimport { awaitOpen, codecName, connectSocket, messageText, sameFormat, socketUrl } from './wire.ts';\n\ninterface SpeechState {\n opened: boolean;\n started: boolean;\n done: boolean;\n failed: boolean;\n outputSeconds: number;\n characters: number;\n}\n\nfunction languageOf(language: string | undefined): string {\n if (language === undefined || language.trim() === '') return 'auto';\n return language;\n}\n\nfunction resolveFormat(\n model: SpeechModel<string>,\n requested: AudioFormat | undefined\n): AudioFormat {\n const fallback = model.outputFormats[0];\n if (!fallback) throw new Error('No output format');\n if (!requested) return fallback;\n const match = model.outputFormats.find((format) => sameFormat(format, requested));\n if (!match) throw new Error(`Unsupported speech format ${requested.encoding}`);\n return match;\n}\n\nfunction outputFormat(format: AudioFormat): Record<string, number | string> {\n const body: Record<string, number | string> = {\n codec: codecName(format.encoding),\n sample_rate: format.sampleRate,\n };\n if (format.encoding === 'mp3') body.bit_rate = 128000;\n return body;\n}\n\nfunction ttsUrl(baseUrl: string, voice: string, language: string, format: AudioFormat): string {\n const url = socketUrl(baseUrl, 'tts');\n const params = new URLSearchParams();\n params.set('language', language);\n params.set('voice', voice);\n params.set('codec', codecName(format.encoding));\n params.set('sample_rate', String(format.sampleRate));\n if (format.encoding === 'mp3') params.set('bit_rate', '128000');\n url.search = params.toString();\n return url.toString();\n}\n\nexport function speakXAI(\n model: SpeechModel<string>,\n input: SpeechInput,\n options?: ProviderRequestOptions\n): SpeechEventStream {\n const stream = new SpeechEventStream();\n void pumpSpeech(model, input, options, stream);\n return stream;\n}\n\nasync function pumpSpeech(\n model: SpeechModel<string>,\n input: SpeechInput,\n options: ProviderRequestOptions | undefined,\n stream: SpeechEventStream\n) {\n try {\n if (options?.signal?.aborted) throw new DOMException('aborted', 'AbortError');\n const format = resolveFormat(model, input.format);\n const language = languageOf(input.language);\n if (typeof input.text === 'string') {\n await postSpeech(model, input.text, input.voice, language, format, options, stream);\n return;\n }\n await streamSpeech(model, input.text, input.voice, language, format, options, stream);\n } catch (error) {\n const voice = asVoiceError(error);\n stream.push({ type: 'error', reason: voice.reason, error: voice.error });\n }\n}\n\nasync function postSpeech(\n model: SpeechModel<string>,\n text: string,\n voice: string,\n language: string,\n format: AudioFormat,\n options: ProviderRequestOptions | undefined,\n stream: SpeechEventStream\n) {\n const payload = {\n text,\n voice_id: voice,\n language,\n output_format: outputFormat(format),\n };\n const body = await applyPayload(payload, model, options);\n if (!isRecord(body)) throw new Error('Speech payload was not JSON');\n const response = await voiceFetch(\n model,\n options,\n endpoint(model.baseUrl, 'tts'),\n JSON.stringify(body),\n 'application/json'\n );\n const data = new Uint8Array(await response.arrayBuffer());\n const seconds = chunkSeconds({ data, format, timestamp: 0 });\n const meters: AudioCounters = { characters: text.length };\n if (seconds > 0) meters.outputSeconds = seconds;\n stream.push({ type: 'start', format });\n stream.push({\n type: 'audio',\n chunk: { data, format, timestamp: 0 },\n span: { itemId: model.id, start: 0, end: text.length },\n });\n stream.push({ type: 'done', usage: priceAudio(model.audioCost, meters) });\n}\n\nasync function streamSpeech(\n model: SpeechModel<string>,\n text: AsyncIterable<string>,\n voice: string,\n language: string,\n format: AudioFormat,\n options: ProviderRequestOptions | undefined,\n stream: SpeechEventStream\n) {\n const state: SpeechState = {\n opened: false,\n started: false,\n done: false,\n failed: false,\n outputSeconds: 0,\n characters: 0,\n };\n let settled = false;\n const push = (event: SpeechEvent) => {\n if (settled) return;\n if (event.type === 'done' || event.type === 'error') settled = true;\n stream.push(event);\n };\n const doneWait = Promise.withResolvers<void>();\n const wake = () => {\n doneWait.resolve();\n };\n const headers = requestHeaders(model, options);\n const socket = connectSocket(ttsUrl(model.baseUrl, voice, language, format), headers);\n let incoming = Promise.resolve();\n socket.addEventListener('message', (event) => {\n incoming = incoming.then(() => onMessage(event.data, model.id, format, state, push, wake));\n });\n socket.addEventListener('close', () => {\n if (state.opened && !state.done && !state.failed) {\n state.failed = true;\n push({ type: 'error', reason: 'error', error: 'xAI speech socket closed' });\n }\n wake();\n });\n try {\n await awaitOpen(socket, 'xAI speech', (message) => {\n state.failed = true;\n push({ type: 'error', reason: 'error', error: message });\n wake();\n });\n if (state.failed) return;\n state.opened = true;\n state.characters = await writeText(socket, text, options?.signal);\n if (state.failed) return;\n socket.send(JSON.stringify({ type: 'text.done' }));\n if (!state.done) await doneWait.promise;\n if (state.failed) return;\n const meters: AudioCounters = { characters: state.characters };\n if (state.outputSeconds > 0) meters.outputSeconds = state.outputSeconds;\n push({ type: 'done', usage: priceAudio(model.audioCost, meters) });\n } catch (error) {\n const voice = asVoiceError(error);\n state.failed = true;\n push({ type: 'error', reason: voice.reason, error: voice.error });\n } finally {\n socket.close();\n }\n}\n\nasync function writeText(\n socket: WebSocket,\n text: AsyncIterable<string>,\n signal: AbortSignal | undefined\n): Promise<number> {\n let combined = '';\n for await (const part of text) {\n if (signal?.aborted) throw new VoiceRequestError('aborted', 'aborted');\n combined += part;\n if (part !== '') socket.send(JSON.stringify({ type: 'text.delta', delta: part }));\n }\n return combined.length;\n}\n\nasync function onMessage(\n data: unknown,\n itemId: string,\n format: AudioFormat,\n state: SpeechState,\n push: (event: SpeechEvent) => void,\n wake: () => void\n) {\n try {\n const parsed = JSON.parse(await messageText(data)) as unknown;\n if (!isRecord(parsed)) throw new Error('xAI speech event was not JSON');\n applySpeech(parsed, itemId, format, state, push);\n if (state.done || state.failed) wake();\n } catch (error) {\n const voice = asVoiceError(error);\n state.failed = true;\n push({ type: 'error', reason: voice.reason, error: voice.error });\n wake();\n }\n}\n\nfunction applySpeech(\n event: Record<string, unknown>,\n itemId: string,\n format: AudioFormat,\n state: SpeechState,\n push: (event: SpeechEvent) => void\n) {\n const type = stringField(event, 'type');\n if (type === 'audio.delta') {\n const delta = stringField(event, 'delta');\n const payload = delta === undefined ? '' : delta;\n const data = decodeAudioBase64(payload);\n if (!state.started) {\n state.started = true;\n push({ type: 'start', format });\n }\n state.outputSeconds += chunkSeconds({ data, format, timestamp: 0 });\n push({\n type: 'audio',\n chunk: { data, format, timestamp: 0 },\n span: { itemId, start: 0, end: state.characters },\n });\n return;\n }\n if (type === 'audio.done') {\n state.done = true;\n return;\n }\n if (type === 'error') {\n const message = stringField(event, 'message');\n state.failed = true;\n push({\n type: 'error',\n reason: 'error',\n error: message === undefined ? 'xAI speech failed' : message,\n });\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["voice"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { VoiceRequestError } from "../errors.js";
|
|
2
|
+
import {
|
|
3
|
+
applyPayload,
|
|
4
|
+
asVoiceError,
|
|
5
|
+
endpoint,
|
|
6
|
+
isRecord,
|
|
7
|
+
numberField,
|
|
8
|
+
readAudio,
|
|
9
|
+
readJson,
|
|
10
|
+
requestSignal,
|
|
11
|
+
stringField,
|
|
12
|
+
voiceFetch
|
|
13
|
+
} from "../openai/wire.js";
|
|
14
|
+
import { priceAudio } from "../usage.js";
|
|
15
|
+
import { audioFilename, encodeForm, formType, rawFields } from "./wire.js";
|
|
16
|
+
function durationOf(record) {
|
|
17
|
+
const duration = numberField(record, "duration");
|
|
18
|
+
if (duration === void 0) return 0;
|
|
19
|
+
return duration;
|
|
20
|
+
}
|
|
21
|
+
function textOf(record, key) {
|
|
22
|
+
const text = stringField(record, key);
|
|
23
|
+
if (text === void 0) return "";
|
|
24
|
+
return text;
|
|
25
|
+
}
|
|
26
|
+
function secondsOf(record, key) {
|
|
27
|
+
const value = numberField(record, key);
|
|
28
|
+
if (value === void 0) return 0;
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
function speakerOf(record) {
|
|
32
|
+
const value = record.speaker;
|
|
33
|
+
if (typeof value === "number") return String(value);
|
|
34
|
+
if (typeof value === "string") return value;
|
|
35
|
+
return void 0;
|
|
36
|
+
}
|
|
37
|
+
function wordFrom(word) {
|
|
38
|
+
const speaker = speakerOf(word);
|
|
39
|
+
const segment = {
|
|
40
|
+
text: textOf(word, "text"),
|
|
41
|
+
start: secondsOf(word, "start"),
|
|
42
|
+
end: secondsOf(word, "end")
|
|
43
|
+
};
|
|
44
|
+
if (speaker !== void 0) segment.speaker = speaker;
|
|
45
|
+
return segment;
|
|
46
|
+
}
|
|
47
|
+
function transcriptFrom(body, model) {
|
|
48
|
+
if (!isRecord(body)) throw new Error("Transcription response had no text");
|
|
49
|
+
const text = stringField(body, "text");
|
|
50
|
+
if (text === void 0) throw new Error("Transcription response had no text");
|
|
51
|
+
const language = stringField(body, "language");
|
|
52
|
+
const words = Array.isArray(body.words) ? body.words.filter(isRecord).map(wordFrom) : void 0;
|
|
53
|
+
return {
|
|
54
|
+
text,
|
|
55
|
+
...language !== void 0 ? { language } : {},
|
|
56
|
+
...words !== void 0 ? { segments: words } : {},
|
|
57
|
+
usage: priceAudio(model.audioCost, { inputSeconds: durationOf(body) })
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function fileOf(body) {
|
|
61
|
+
const file = body.file;
|
|
62
|
+
if (!isRecord(file) || !(file.data instanceof Uint8Array) || typeof file.mimeType !== "string") {
|
|
63
|
+
throw new Error("Transcription payload was not a file");
|
|
64
|
+
}
|
|
65
|
+
const named = typeof file.filename === "string" ? file.filename : audioFilename(file.mimeType);
|
|
66
|
+
return { data: file.data, mimeType: file.mimeType, filename: named };
|
|
67
|
+
}
|
|
68
|
+
function fieldStrings(body) {
|
|
69
|
+
const fields = {};
|
|
70
|
+
for (const [key, value] of Object.entries(body)) {
|
|
71
|
+
if (key === "file") continue;
|
|
72
|
+
if (typeof value !== "string") continue;
|
|
73
|
+
fields[key] = value;
|
|
74
|
+
}
|
|
75
|
+
return fields;
|
|
76
|
+
}
|
|
77
|
+
async function transcribeXAI(model, input, options) {
|
|
78
|
+
try {
|
|
79
|
+
if (options?.signal?.aborted) throw new DOMException("aborted", "AbortError");
|
|
80
|
+
const fetchImpl = options?.fetch ?? globalThis.fetch;
|
|
81
|
+
const loaded = await readAudio(input, fetchImpl, requestSignal(options));
|
|
82
|
+
const filename = audioFilename(loaded.mimeType);
|
|
83
|
+
const payload = {
|
|
84
|
+
model: model.id,
|
|
85
|
+
file: { data: loaded.data, mimeType: loaded.mimeType, filename }
|
|
86
|
+
};
|
|
87
|
+
if (model.diarization) payload.diarize = "true";
|
|
88
|
+
const raw = rawFields(loaded.mimeType, model);
|
|
89
|
+
if (raw) {
|
|
90
|
+
payload.audio_format = raw.audio_format;
|
|
91
|
+
payload.sample_rate = raw.sample_rate;
|
|
92
|
+
}
|
|
93
|
+
const body = await applyPayload(payload, model, options);
|
|
94
|
+
if (!isRecord(body)) throw new Error("Transcription payload was not a file");
|
|
95
|
+
const file = fileOf(body);
|
|
96
|
+
const encoded = encodeForm(file, fieldStrings(body));
|
|
97
|
+
const response = await voiceFetch(
|
|
98
|
+
model,
|
|
99
|
+
options,
|
|
100
|
+
endpoint(model.baseUrl, "stt"),
|
|
101
|
+
encoded,
|
|
102
|
+
formType()
|
|
103
|
+
);
|
|
104
|
+
return transcriptFrom(await readJson(response), model);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
const voice = asVoiceError(error);
|
|
107
|
+
throw new VoiceRequestError(voice.reason, voice.error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export {
|
|
111
|
+
transcribeXAI
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=transcribe.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/xai/transcribe.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ProviderRequestOptions } from '@earendil-works/pi-ai';\nimport { VoiceRequestError } from '../errors.ts';\nimport type { TranscriptionModel } from '../models.ts';\nimport {\n applyPayload,\n asVoiceError,\n endpoint,\n isRecord,\n numberField,\n readAudio,\n readJson,\n requestSignal,\n stringField,\n voiceFetch,\n} from '../openai/wire.ts';\nimport type { AudioInput, Transcript, TranscriptSegment } from '../transcription.ts';\nimport { priceAudio } from '../usage.ts';\nimport { audioFilename, encodeForm, formType, rawFields, type XaiFile } from './wire.ts';\n\nfunction durationOf(record: Record<string, unknown>): number {\n const duration = numberField(record, 'duration');\n if (duration === undefined) return 0;\n return duration;\n}\n\nfunction textOf(record: Record<string, unknown>, key: string): string {\n const text = stringField(record, key);\n if (text === undefined) return '';\n return text;\n}\n\nfunction secondsOf(record: Record<string, unknown>, key: string): number {\n const value = numberField(record, key);\n if (value === undefined) return 0;\n return value;\n}\n\nfunction speakerOf(record: Record<string, unknown>): string | undefined {\n const value = record.speaker;\n if (typeof value === 'number') return String(value);\n if (typeof value === 'string') return value;\n return undefined;\n}\n\nfunction wordFrom(word: Record<string, unknown>): TranscriptSegment {\n const speaker = speakerOf(word);\n const segment: TranscriptSegment = {\n text: textOf(word, 'text'),\n start: secondsOf(word, 'start'),\n end: secondsOf(word, 'end'),\n };\n if (speaker !== undefined) segment.speaker = speaker;\n return segment;\n}\n\nfunction transcriptFrom(body: unknown, model: TranscriptionModel<string>): Transcript {\n if (!isRecord(body)) throw new Error('Transcription response had no text');\n const text = stringField(body, 'text');\n if (text === undefined) throw new Error('Transcription response had no text');\n const language = stringField(body, 'language');\n const words = Array.isArray(body.words) ? body.words.filter(isRecord).map(wordFrom) : undefined;\n return {\n text,\n ...(language !== undefined ? { language } : {}),\n ...(words !== undefined ? { segments: words } : {}),\n usage: priceAudio(model.audioCost, { inputSeconds: durationOf(body) }),\n };\n}\n\nfunction fileOf(body: Record<string, unknown>): XaiFile {\n const file = body.file;\n if (!isRecord(file) || !(file.data instanceof Uint8Array) || typeof file.mimeType !== 'string') {\n throw new Error('Transcription payload was not a file');\n }\n const named = typeof file.filename === 'string' ? file.filename : audioFilename(file.mimeType);\n return { data: file.data, mimeType: file.mimeType, filename: named };\n}\n\nfunction fieldStrings(body: Record<string, unknown>): Record<string, string> {\n const fields: Record<string, string> = {};\n for (const [key, value] of Object.entries(body)) {\n if (key === 'file') continue;\n if (typeof value !== 'string') continue;\n fields[key] = value;\n }\n return fields;\n}\n\nexport async function transcribeXAI(\n model: TranscriptionModel<string>,\n input: AudioInput,\n options?: ProviderRequestOptions\n): Promise<Transcript> {\n try {\n if (options?.signal?.aborted) throw new DOMException('aborted', 'AbortError');\n const fetchImpl = options?.fetch ?? globalThis.fetch;\n const loaded = await readAudio(input, fetchImpl, requestSignal(options));\n const filename = audioFilename(loaded.mimeType);\n const payload: Record<string, unknown> = {\n model: model.id,\n file: { data: loaded.data, mimeType: loaded.mimeType, filename },\n };\n if (model.diarization) payload.diarize = 'true';\n const raw = rawFields(loaded.mimeType, model);\n if (raw) {\n payload.audio_format = raw.audio_format;\n payload.sample_rate = raw.sample_rate;\n }\n const body = await applyPayload(payload, model, options);\n if (!isRecord(body)) throw new Error('Transcription payload was not a file');\n const file = fileOf(body);\n const encoded = encodeForm(file, fieldStrings(body));\n const response = await voiceFetch(\n model,\n options,\n endpoint(model.baseUrl, 'stt'),\n encoded,\n formType()\n );\n return transcriptFrom(await readJson(response), 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": []
|
|
7
|
+
}
|
package/dist/xai/wire.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
const boundary = "pi-audio-xai-boundary";
|
|
2
|
+
const filenames = {
|
|
3
|
+
"audio/wav": "audio.wav",
|
|
4
|
+
"audio/wave": "audio.wav",
|
|
5
|
+
"audio/x-wav": "audio.wav",
|
|
6
|
+
"audio/mpeg": "audio.mp3",
|
|
7
|
+
"audio/mp3": "audio.mp3",
|
|
8
|
+
"audio/flac": "audio.flac",
|
|
9
|
+
"audio/ogg": "audio.ogg",
|
|
10
|
+
"audio/opus": "audio.opus",
|
|
11
|
+
"audio/pcm": "audio.pcm",
|
|
12
|
+
"audio/l16": "audio.pcm",
|
|
13
|
+
"audio/mulaw": "audio.mulaw",
|
|
14
|
+
"audio/basic": "audio.mulaw",
|
|
15
|
+
"audio/alaw": "audio.alaw",
|
|
16
|
+
"audio/mp4": "audio.mp4",
|
|
17
|
+
"audio/m4a": "audio.m4a",
|
|
18
|
+
"audio/aac": "audio.aac",
|
|
19
|
+
"audio/webm": "audio.webm"
|
|
20
|
+
};
|
|
21
|
+
function mediaType(mimeType) {
|
|
22
|
+
const type = mimeType.split(";")[0]?.trim().toLowerCase();
|
|
23
|
+
if (!type) throw new Error(`Unsupported audio type ${mimeType}`);
|
|
24
|
+
return type;
|
|
25
|
+
}
|
|
26
|
+
function audioFilename(mimeType) {
|
|
27
|
+
const name = filenames[mediaType(mimeType)];
|
|
28
|
+
if (!name) throw new Error(`Unsupported audio type ${mimeType}`);
|
|
29
|
+
return name;
|
|
30
|
+
}
|
|
31
|
+
function declaredRate(model, encoding) {
|
|
32
|
+
const match = model.inputFormats.find((format) => format.encoding === encoding);
|
|
33
|
+
if (!match) return "16000";
|
|
34
|
+
return String(match.sampleRate);
|
|
35
|
+
}
|
|
36
|
+
function parameter(mimeType, name) {
|
|
37
|
+
for (const part of mimeType.split(";").slice(1)) {
|
|
38
|
+
const pieces = part.split("=");
|
|
39
|
+
const key = pieces[0]?.trim().toLowerCase();
|
|
40
|
+
const value = pieces[1]?.trim();
|
|
41
|
+
if (key === name && value) return value;
|
|
42
|
+
}
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
function rawFields(mimeType, model) {
|
|
46
|
+
const type = mediaType(mimeType);
|
|
47
|
+
const rate = parameter(mimeType, "rate");
|
|
48
|
+
if (type === "audio/pcm" || type === "audio/l16") {
|
|
49
|
+
const sampleRate = rate === void 0 ? declaredRate(model, "pcm16") : rate;
|
|
50
|
+
return { audio_format: "pcm", sample_rate: sampleRate };
|
|
51
|
+
}
|
|
52
|
+
if (type === "audio/mulaw" || type === "audio/basic") {
|
|
53
|
+
const sampleRate = rate === void 0 ? declaredRate(model, "mulaw") : rate;
|
|
54
|
+
return { audio_format: "mulaw", sample_rate: sampleRate };
|
|
55
|
+
}
|
|
56
|
+
if (type === "audio/alaw") {
|
|
57
|
+
const sampleRate = rate === void 0 ? declaredRate(model, "alaw") : rate;
|
|
58
|
+
return { audio_format: "alaw", sample_rate: sampleRate };
|
|
59
|
+
}
|
|
60
|
+
return void 0;
|
|
61
|
+
}
|
|
62
|
+
function encodeForm(file, fields) {
|
|
63
|
+
const chunks = [];
|
|
64
|
+
const encoder = new TextEncoder();
|
|
65
|
+
const write = (value) => {
|
|
66
|
+
chunks.push(encoder.encode(value));
|
|
67
|
+
};
|
|
68
|
+
for (const [name, value] of Object.entries(fields)) {
|
|
69
|
+
write(`--${boundary}\r
|
|
70
|
+
Content-Disposition: form-data; name="${name}"\r
|
|
71
|
+
\r
|
|
72
|
+
${value}\r
|
|
73
|
+
`);
|
|
74
|
+
}
|
|
75
|
+
write(
|
|
76
|
+
`--${boundary}\r
|
|
77
|
+
Content-Disposition: form-data; name="file"; filename="${file.filename}"\r
|
|
78
|
+
Content-Type: ${file.mimeType}\r
|
|
79
|
+
\r
|
|
80
|
+
`
|
|
81
|
+
);
|
|
82
|
+
chunks.push(file.data);
|
|
83
|
+
write(`\r
|
|
84
|
+
--${boundary}--\r
|
|
85
|
+
`);
|
|
86
|
+
const body = new Uint8Array(chunks.reduce((sum, chunk) => sum + chunk.length, 0));
|
|
87
|
+
let offset = 0;
|
|
88
|
+
for (const chunk of chunks) {
|
|
89
|
+
body.set(chunk, offset);
|
|
90
|
+
offset += chunk.length;
|
|
91
|
+
}
|
|
92
|
+
return body.buffer;
|
|
93
|
+
}
|
|
94
|
+
function formType() {
|
|
95
|
+
return `multipart/form-data; boundary=${boundary}`;
|
|
96
|
+
}
|
|
97
|
+
function socketUrl(baseUrl, path) {
|
|
98
|
+
const url = new URL(baseUrl);
|
|
99
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
100
|
+
url.pathname = `${url.pathname.replace(/\/$/, "")}/${path}`;
|
|
101
|
+
url.search = "";
|
|
102
|
+
return url;
|
|
103
|
+
}
|
|
104
|
+
function codecName(encoding) {
|
|
105
|
+
if (encoding === "pcm16") return "pcm";
|
|
106
|
+
if (encoding === "mp3") return "mp3";
|
|
107
|
+
if (encoding === "wav") return "wav";
|
|
108
|
+
if (encoding === "mulaw") return "mulaw";
|
|
109
|
+
if (encoding === "alaw") return "alaw";
|
|
110
|
+
throw new Error(`xAI speech cannot return ${encoding}`);
|
|
111
|
+
}
|
|
112
|
+
function encodingName(encoding) {
|
|
113
|
+
if (encoding === "pcm16") return "pcm";
|
|
114
|
+
if (encoding === "mulaw") return "mulaw";
|
|
115
|
+
if (encoding === "alaw") return "alaw";
|
|
116
|
+
throw new Error(`Unsupported audio format ${encoding}`);
|
|
117
|
+
}
|
|
118
|
+
function connectSocket(url, headers) {
|
|
119
|
+
const Socket = WebSocket;
|
|
120
|
+
return new Socket(url, { headers });
|
|
121
|
+
}
|
|
122
|
+
function awaitOpen(socket, label, fail) {
|
|
123
|
+
return new Promise((resolve) => {
|
|
124
|
+
let opened = false;
|
|
125
|
+
socket.addEventListener("open", () => {
|
|
126
|
+
opened = true;
|
|
127
|
+
resolve();
|
|
128
|
+
});
|
|
129
|
+
socket.addEventListener("error", () => {
|
|
130
|
+
fail(`${label} socket failed`);
|
|
131
|
+
if (!opened) resolve();
|
|
132
|
+
});
|
|
133
|
+
socket.addEventListener("close", () => {
|
|
134
|
+
if (!opened) {
|
|
135
|
+
fail(`${label} socket closed`);
|
|
136
|
+
resolve();
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async function messageText(data) {
|
|
142
|
+
if (typeof data === "string") return data;
|
|
143
|
+
if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);
|
|
144
|
+
if (data instanceof Uint8Array) return new TextDecoder().decode(data);
|
|
145
|
+
if (typeof Blob !== "undefined" && data instanceof Blob) return data.text();
|
|
146
|
+
throw new Error("Unsupported socket message");
|
|
147
|
+
}
|
|
148
|
+
function sameFormat(left, right) {
|
|
149
|
+
return left.encoding === right.encoding && left.sampleRate === right.sampleRate && left.channels === right.channels;
|
|
150
|
+
}
|
|
151
|
+
function copiedBytes(data) {
|
|
152
|
+
const copy = new ArrayBuffer(data.byteLength);
|
|
153
|
+
new Uint8Array(copy).set(data);
|
|
154
|
+
return copy;
|
|
155
|
+
}
|
|
156
|
+
export {
|
|
157
|
+
audioFilename,
|
|
158
|
+
awaitOpen,
|
|
159
|
+
codecName,
|
|
160
|
+
connectSocket,
|
|
161
|
+
copiedBytes,
|
|
162
|
+
encodeForm,
|
|
163
|
+
encodingName,
|
|
164
|
+
formType,
|
|
165
|
+
mediaType,
|
|
166
|
+
messageText,
|
|
167
|
+
rawFields,
|
|
168
|
+
sameFormat,
|
|
169
|
+
socketUrl
|
|
170
|
+
};
|
|
171
|
+
//# sourceMappingURL=wire.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/xai/wire.ts"],
|
|
4
|
+
"sourcesContent": ["import type { AudioFormat } from '../audio.ts';\nimport type { TranscriptionModel } from '../models.ts';\n\nconst boundary = 'pi-audio-xai-boundary';\n\nconst filenames: Record<string, string> = {\n 'audio/wav': 'audio.wav',\n 'audio/wave': 'audio.wav',\n 'audio/x-wav': 'audio.wav',\n 'audio/mpeg': 'audio.mp3',\n 'audio/mp3': 'audio.mp3',\n 'audio/flac': 'audio.flac',\n 'audio/ogg': 'audio.ogg',\n 'audio/opus': 'audio.opus',\n 'audio/pcm': 'audio.pcm',\n 'audio/l16': 'audio.pcm',\n 'audio/mulaw': 'audio.mulaw',\n 'audio/basic': 'audio.mulaw',\n 'audio/alaw': 'audio.alaw',\n 'audio/mp4': 'audio.mp4',\n 'audio/m4a': 'audio.m4a',\n 'audio/aac': 'audio.aac',\n 'audio/webm': 'audio.webm',\n};\n\nexport interface XaiFile {\n data: Uint8Array;\n mimeType: string;\n filename: string;\n}\n\nexport function mediaType(mimeType: string): string {\n const type = mimeType.split(';')[0]?.trim().toLowerCase();\n if (!type) throw new Error(`Unsupported audio type ${mimeType}`);\n return type;\n}\n\nexport function audioFilename(mimeType: string): string {\n const name = filenames[mediaType(mimeType)];\n if (!name) throw new Error(`Unsupported audio type ${mimeType}`);\n return name;\n}\n\nfunction declaredRate(\n model: TranscriptionModel<string>,\n encoding: AudioFormat['encoding']\n): string {\n const match = model.inputFormats.find((format) => format.encoding === encoding);\n if (!match) return '16000';\n return String(match.sampleRate);\n}\n\nfunction parameter(mimeType: string, name: string): string | undefined {\n for (const part of mimeType.split(';').slice(1)) {\n const pieces = part.split('=');\n const key = pieces[0]?.trim().toLowerCase();\n const value = pieces[1]?.trim();\n if (key === name && value) return value;\n }\n return undefined;\n}\n\nexport function rawFields(\n mimeType: string,\n model: TranscriptionModel<string>\n): Record<string, string> | undefined {\n const type = mediaType(mimeType);\n const rate = parameter(mimeType, 'rate');\n if (type === 'audio/pcm' || type === 'audio/l16') {\n const sampleRate = rate === undefined ? declaredRate(model, 'pcm16') : rate;\n return { audio_format: 'pcm', sample_rate: sampleRate };\n }\n if (type === 'audio/mulaw' || type === 'audio/basic') {\n const sampleRate = rate === undefined ? declaredRate(model, 'mulaw') : rate;\n return { audio_format: 'mulaw', sample_rate: sampleRate };\n }\n if (type === 'audio/alaw') {\n const sampleRate = rate === undefined ? declaredRate(model, 'alaw') : rate;\n return { audio_format: 'alaw', sample_rate: sampleRate };\n }\n return undefined;\n}\n\nexport function encodeForm(file: XaiFile, fields: Record<string, string>): ArrayBuffer {\n const chunks: Uint8Array[] = [];\n const encoder = new TextEncoder();\n const write = (value: string) => {\n chunks.push(encoder.encode(value));\n };\n for (const [name, value] of Object.entries(fields)) {\n write(`--${boundary}\\r\\nContent-Disposition: form-data; name=\"${name}\"\\r\\n\\r\\n${value}\\r\\n`);\n }\n write(\n `--${boundary}\\r\\nContent-Disposition: form-data; name=\"file\"; filename=\"${file.filename}\"\\r\\nContent-Type: ${file.mimeType}\\r\\n\\r\\n`\n );\n chunks.push(file.data);\n write(`\\r\\n--${boundary}--\\r\\n`);\n const body = new Uint8Array(chunks.reduce((sum, chunk) => sum + chunk.length, 0));\n let offset = 0;\n for (const chunk of chunks) {\n body.set(chunk, offset);\n offset += chunk.length;\n }\n return body.buffer;\n}\n\nexport function formType(): string {\n return `multipart/form-data; boundary=${boundary}`;\n}\n\nexport function socketUrl(baseUrl: string, path: string): URL {\n const url = new URL(baseUrl);\n url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';\n url.pathname = `${url.pathname.replace(/\\/$/, '')}/${path}`;\n url.search = '';\n return url;\n}\n\nexport function codecName(encoding: AudioFormat['encoding']): string {\n if (encoding === 'pcm16') return 'pcm';\n if (encoding === 'mp3') return 'mp3';\n if (encoding === 'wav') return 'wav';\n if (encoding === 'mulaw') return 'mulaw';\n if (encoding === 'alaw') return 'alaw';\n throw new Error(`xAI speech cannot return ${encoding}`);\n}\n\nexport function encodingName(encoding: AudioFormat['encoding']): string {\n if (encoding === 'pcm16') return 'pcm';\n if (encoding === 'mulaw') return 'mulaw';\n if (encoding === 'alaw') return 'alaw';\n throw new Error(`Unsupported audio format ${encoding}`);\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 async function messageText(data: unknown): Promise<string> {\n if (typeof data === 'string') return data;\n if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);\n if (data instanceof Uint8Array) return new TextDecoder().decode(data);\n if (typeof Blob !== 'undefined' && data instanceof Blob) return data.text();\n throw new Error('Unsupported socket message');\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 copiedBytes(data: Uint8Array): ArrayBuffer {\n const copy = new ArrayBuffer(data.byteLength);\n new Uint8Array(copy).set(data);\n return copy;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/pi-audio",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "Voice bindings for pi: speech-to-text, text-to-speech and realtime speech for pi-coding-agent plugins and slicc-agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -45,6 +45,14 @@
|
|
|
45
45
|
"./xai": {
|
|
46
46
|
"types": "./dist/types/xai/index.d.ts",
|
|
47
47
|
"default": "./dist/xai/index.js"
|
|
48
|
+
},
|
|
49
|
+
"./durable": {
|
|
50
|
+
"types": "./dist/types/durable/index.d.ts",
|
|
51
|
+
"default": "./dist/durable/index.js"
|
|
52
|
+
},
|
|
53
|
+
"./gemini-live": {
|
|
54
|
+
"types": "./dist/types/gemini-live/index.d.ts",
|
|
55
|
+
"default": "./dist/gemini-live/index.js"
|
|
48
56
|
}
|
|
49
57
|
},
|
|
50
58
|
"files": [
|
|
@@ -68,6 +76,7 @@
|
|
|
68
76
|
"@ai-ecoverse/slicc-shared-web": "1.21.1",
|
|
69
77
|
"@biomejs/biome": "2.5.15",
|
|
70
78
|
"@earendil-works/pi-ai": "1.1.0",
|
|
79
|
+
"@earendil-works/pi-durable": "1.1.0",
|
|
71
80
|
"@types/node": "24.19.1",
|
|
72
81
|
"esbuild": "0.28.2",
|
|
73
82
|
"lefthook": "2.2.1",
|
|
@@ -76,6 +85,12 @@
|
|
|
76
85
|
"typescript": "7.0.2"
|
|
77
86
|
},
|
|
78
87
|
"peerDependencies": {
|
|
79
|
-
"@earendil-works/pi-ai": "1.1.0"
|
|
88
|
+
"@earendil-works/pi-ai": "1.1.0",
|
|
89
|
+
"@earendil-works/pi-durable": "^1.1.0"
|
|
90
|
+
},
|
|
91
|
+
"peerDependenciesMeta": {
|
|
92
|
+
"@earendil-works/pi-durable": {
|
|
93
|
+
"optional": true
|
|
94
|
+
}
|
|
80
95
|
}
|
|
81
96
|
}
|