@ai-ecoverse/pi-audio 1.11.0 → 1.13.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 -0
- package/dist/elevenlabs/catalog.js +136 -0
- package/dist/elevenlabs/catalog.js.map +7 -0
- package/dist/elevenlabs/index.js +5 -0
- package/dist/elevenlabs/index.js.map +7 -0
- package/dist/elevenlabs/listen.js +268 -0
- package/dist/elevenlabs/listen.js.map +7 -0
- package/dist/elevenlabs/speak.js +251 -0
- package/dist/elevenlabs/speak.js.map +7 -0
- package/dist/elevenlabs/transcribe.js +132 -0
- package/dist/elevenlabs/transcribe.js.map +7 -0
- package/dist/elevenlabs/wire.js +215 -0
- package/dist/elevenlabs/wire.js.map +7 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +2 -2
- package/dist/tokens.js +200 -0
- package/dist/tokens.js.map +7 -0
- package/dist/types/elevenlabs/catalog.d.ts +1 -0
- package/dist/types/elevenlabs/index.d.ts +1 -0
- package/dist/types/elevenlabs/listen.d.ts +4 -0
- package/dist/types/elevenlabs/speak.d.ts +4 -0
- package/dist/types/elevenlabs/transcribe.d.ts +4 -0
- package/dist/types/elevenlabs/wire.d.ts +26 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/tokens.d.ts +11 -0
- package/package.json +5 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/elevenlabs/wire.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ProviderRequestOptions } from '@earendil-works/pi-ai';\nimport type { AudioFormat, CharAlignment } from '../audio.ts';\nimport { isRecord } from '../openai/wire.ts';\n\nconst boundary = 'pi-audio-elevenlabs-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\nconst sttFormats = new Set([\n 'pcm_8000',\n 'pcm_16000',\n 'pcm_22050',\n 'pcm_24000',\n 'pcm_44100',\n 'pcm_48000',\n 'ulaw_8000',\n]);\n\nexport interface ElevenFile {\n data: Uint8Array;\n mimeType: string;\n filename: string;\n}\n\ninterface HeaderModel {\n provider: string;\n headers?: Record<string, 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 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 pcmS16(mimeType: string): boolean {\n const type = mediaType(mimeType);\n if (type !== 'audio/pcm' && type !== 'audio/l16') return false;\n const rate = parameter(mimeType, 'rate');\n if (rate === undefined) return true;\n return rate === '16000';\n}\n\nexport function encodeForm(file: ElevenFile, 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 const header =\n `--${boundary}\\r\\nContent-Disposition: form-data; name=\"file\"; filename=\"${file.filename}\"\\r\\n` +\n `Content-Type: ${file.mimeType}\\r\\n\\r\\n`;\n write(header);\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\nfunction headerValue(\n headers: Record<string, string | null> | undefined,\n name: string\n): string | undefined {\n if (!headers) return undefined;\n const expected = name.toLowerCase();\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === expected && value !== null && value.trim() !== '') return value;\n }\n return undefined;\n}\n\nexport function xiHeaders(\n model: HeaderModel,\n options: ProviderRequestOptions | undefined\n): Record<string, string> {\n const headers: Record<string, string> = { ...model.headers };\n const extra = options?.headers;\n if (extra) {\n for (const [key, value] of Object.entries(extra)) {\n if (value === null) delete headers[key];\n else headers[key] = value;\n }\n }\n if (headerValue(headers, 'xi-api-key') === undefined) {\n const apiKey = options?.apiKey;\n if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);\n headers['xi-api-key'] = apiKey;\n }\n return headers;\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 outputFormatName(format: AudioFormat): string {\n if (format.encoding === 'mp3' && format.sampleRate === 44100) return 'mp3_44100_128';\n if (format.encoding === 'mp3' && format.sampleRate === 24000) return 'mp3_24000_48';\n if (format.encoding === 'mp3' && format.sampleRate === 22050) return 'mp3_22050_32';\n if (format.encoding === 'pcm16') return `pcm_${format.sampleRate}`;\n if (format.encoding === 'wav') return `wav_${format.sampleRate}`;\n if (format.encoding === 'mulaw') return 'ulaw_8000';\n if (format.encoding === 'alaw') return 'alaw_8000';\n if (format.encoding === 'opus') return 'opus_48000_128';\n throw new Error(`Unsupported speech format ${format.encoding}`);\n}\n\nexport function sttAudioFormat(format: AudioFormat): string {\n const name = format.encoding === 'mulaw' ? 'ulaw_8000' : `pcm_${format.sampleRate}`;\n if (format.encoding !== 'mulaw' && format.encoding !== 'pcm16') {\n throw new Error(`Unsupported audio format ${format.encoding}`);\n }\n if (!sttFormats.has(name)) throw new Error(`Unsupported audio format ${format.encoding}`);\n return name;\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\nfunction numberList(value: unknown): number[] | undefined {\n if (!Array.isArray(value)) return undefined;\n const numbers: number[] = [];\n for (const item of value) {\n if (typeof item === 'number') numbers.push(item);\n }\n return numbers;\n}\n\nfunction durationMs(start: number | undefined, end: number | undefined): number {\n if (start === undefined || end === undefined) return 0;\n return (end - start) * 1000;\n}\n\nexport function alignmentFrom(value: unknown): CharAlignment | undefined {\n if (!isRecord(value)) return undefined;\n const startMs = numberList(value.charStartTimesMs);\n const durationMsList = numberList(value.charDurationsMs);\n if (startMs && durationMsList) return { startMs, durationMs: durationMsList };\n const starts = numberList(value.character_start_times_seconds);\n const ends = numberList(value.character_end_times_seconds);\n if (!starts || !ends) return undefined;\n return {\n startMs: starts.map((start) => start * 1000),\n durationMs: starts.map((start, index) => durationMs(start, ends[index])),\n };\n}\n"],
|
|
5
|
+
"mappings": "AAEA,SAAS,gBAAgB;AAEzB,MAAM,WAAW;AAEjB,MAAM,YAAoC;AAAA,EACxC,aAAa;AAAA,EACb,cAAc;AAAA,EACd,eAAe;AAAA,EACf,cAAc;AAAA,EACd,aAAa;AAAA,EACb,cAAc;AAAA,EACd,aAAa;AAAA,EACb,cAAc;AAAA,EACd,aAAa;AAAA,EACb,aAAa;AAAA,EACb,eAAe;AAAA,EACf,eAAe;AAAA,EACf,cAAc;AAAA,EACd,aAAa;AAAA,EACb,aAAa;AAAA,EACb,aAAa;AAAA,EACb,cAAc;AAChB;AAEA,MAAM,aAAa,oBAAI,IAAI;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAaM,SAAS,UAAU,UAA0B;AAClD,QAAM,OAAO,SAAS,MAAM,GAAG,EAAE,CAAC,GAAG,KAAK,EAAE,YAAY;AACxD,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,0BAA0B,QAAQ,EAAE;AAC/D,SAAO;AACT;AAEO,SAAS,cAAc,UAA0B;AACtD,QAAM,OAAO,UAAU,UAAU,QAAQ,CAAC;AAC1C,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,0BAA0B,QAAQ,EAAE;AAC/D,SAAO;AACT;AAEA,SAAS,UAAU,UAAkB,MAAkC;AACrE,aAAW,QAAQ,SAAS,MAAM,GAAG,EAAE,MAAM,CAAC,GAAG;AAC/C,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAM,MAAM,OAAO,CAAC,GAAG,KAAK,EAAE,YAAY;AAC1C,UAAM,QAAQ,OAAO,CAAC,GAAG,KAAK;AAC9B,QAAI,QAAQ,QAAQ,MAAO,QAAO;AAAA,EACpC;AACA,SAAO;AACT;AAEO,SAAS,OAAO,UAA2B;AAChD,QAAM,OAAO,UAAU,QAAQ;AAC/B,MAAI,SAAS,eAAe,SAAS,YAAa,QAAO;AACzD,QAAM,OAAO,UAAU,UAAU,MAAM;AACvC,MAAI,SAAS,OAAW,QAAO;AAC/B,SAAO,SAAS;AAClB;AAEO,SAAS,WAAW,MAAkB,QAA6C;AACxF,QAAM,SAAuB,CAAC;AAC9B,QAAM,UAAU,IAAI,YAAY;AAChC,QAAM,QAAQ,CAAC,UAAkB;AAC/B,WAAO,KAAK,QAAQ,OAAO,KAAK,CAAC;AAAA,EACnC;AACA,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAClD,UAAM,KAAK,QAAQ;AAAA,wCAA6C,IAAI;AAAA;AAAA,EAAY,KAAK;AAAA,CAAM;AAAA,EAC7F;AACA,QAAM,SACJ,KAAK,QAAQ;AAAA,yDAA8D,KAAK,QAAQ;AAAA,gBACvE,KAAK,QAAQ;AAAA;AAAA;AAChC,QAAM,MAAM;AACZ,SAAO,KAAK,KAAK,IAAI;AACrB,QAAM;AAAA,IAAS,QAAQ;AAAA,CAAQ;AAC/B,QAAM,OAAO,IAAI,WAAW,OAAO,OAAO,CAAC,KAAK,UAAU,MAAM,MAAM,QAAQ,CAAC,CAAC;AAChF,MAAI,SAAS;AACb,aAAW,SAAS,QAAQ;AAC1B,SAAK,IAAI,OAAO,MAAM;AACtB,cAAU,MAAM;AAAA,EAClB;AACA,SAAO,KAAK;AACd;AAEO,SAAS,WAAmB;AACjC,SAAO,iCAAiC,QAAQ;AAClD;AAEA,SAAS,YACP,SACA,MACoB;AACpB,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,WAAW,KAAK,YAAY;AAClC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,QAAI,IAAI,YAAY,MAAM,YAAY,UAAU,QAAQ,MAAM,KAAK,MAAM,GAAI,QAAO;AAAA,EACtF;AACA,SAAO;AACT;AAEO,SAAS,UACd,OACA,SACwB;AACxB,QAAM,UAAkC,EAAE,GAAG,MAAM,QAAQ;AAC3D,QAAM,QAAQ,SAAS;AACvB,MAAI,OAAO;AACT,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,UAAI,UAAU,KAAM,QAAO,QAAQ,GAAG;AAAA,UACjC,SAAQ,GAAG,IAAI;AAAA,IACtB;AAAA,EACF;AACA,MAAI,YAAY,SAAS,YAAY,MAAM,QAAW;AACpD,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,4BAA4B,MAAM,QAAQ,EAAE;AACzE,YAAQ,YAAY,IAAI;AAAA,EAC1B;AACA,SAAO;AACT;AAEO,SAAS,UAAU,SAAiB,MAAmB;AAC5D,QAAM,MAAM,IAAI,IAAI,OAAO;AAC3B,MAAI,WAAW,IAAI,aAAa,WAAW,SAAS;AACpD,MAAI,WAAW,GAAG,IAAI,SAAS,QAAQ,OAAO,EAAE,CAAC,IAAI,IAAI;AACzD,MAAI,SAAS;AACb,SAAO;AACT;AAEO,SAAS,iBAAiB,QAA6B;AAC5D,MAAI,OAAO,aAAa,SAAS,OAAO,eAAe,MAAO,QAAO;AACrE,MAAI,OAAO,aAAa,SAAS,OAAO,eAAe,KAAO,QAAO;AACrE,MAAI,OAAO,aAAa,SAAS,OAAO,eAAe,MAAO,QAAO;AACrE,MAAI,OAAO,aAAa,QAAS,QAAO,OAAO,OAAO,UAAU;AAChE,MAAI,OAAO,aAAa,MAAO,QAAO,OAAO,OAAO,UAAU;AAC9D,MAAI,OAAO,aAAa,QAAS,QAAO;AACxC,MAAI,OAAO,aAAa,OAAQ,QAAO;AACvC,MAAI,OAAO,aAAa,OAAQ,QAAO;AACvC,QAAM,IAAI,MAAM,6BAA6B,OAAO,QAAQ,EAAE;AAChE;AAEO,SAAS,eAAe,QAA6B;AAC1D,QAAM,OAAO,OAAO,aAAa,UAAU,cAAc,OAAO,OAAO,UAAU;AACjF,MAAI,OAAO,aAAa,WAAW,OAAO,aAAa,SAAS;AAC9D,UAAM,IAAI,MAAM,4BAA4B,OAAO,QAAQ,EAAE;AAAA,EAC/D;AACA,MAAI,CAAC,WAAW,IAAI,IAAI,EAAG,OAAM,IAAI,MAAM,4BAA4B,OAAO,QAAQ,EAAE;AACxF,SAAO;AACT;AAEO,SAAS,cAAc,KAAa,SAA4C;AACrF,QAAM,SAAS;AAIf,SAAO,IAAI,OAAO,KAAK,EAAE,QAAQ,CAAC;AACpC;AAEO,SAAS,UACd,QACA,OACA,MACe;AACf,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,SAAS;AACb,WAAO,iBAAiB,QAAQ,MAAM;AACpC,eAAS;AACT,cAAQ;AAAA,IACV,CAAC;AACD,WAAO,iBAAiB,SAAS,MAAM;AACrC,WAAK,GAAG,KAAK,gBAAgB;AAC7B,UAAI,CAAC,OAAQ,SAAQ;AAAA,IACvB,CAAC;AACD,WAAO,iBAAiB,SAAS,MAAM;AACrC,UAAI,CAAC,QAAQ;AACX,aAAK,GAAG,KAAK,gBAAgB;AAC7B,gBAAQ;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAsB,YAAY,MAAgC;AAChE,MAAI,OAAO,SAAS,SAAU,QAAO;AACrC,MAAI,gBAAgB,YAAa,QAAO,IAAI,YAAY,EAAE,OAAO,IAAI;AACrE,MAAI,gBAAgB,WAAY,QAAO,IAAI,YAAY,EAAE,OAAO,IAAI;AACpE,MAAI,OAAO,SAAS,eAAe,gBAAgB,KAAM,QAAO,KAAK,KAAK;AAC1E,QAAM,IAAI,MAAM,4BAA4B;AAC9C;AAEO,SAAS,WAAW,MAAmB,OAA6B;AACzE,SACE,KAAK,aAAa,MAAM,YACxB,KAAK,eAAe,MAAM,cAC1B,KAAK,aAAa,MAAM;AAE5B;AAEA,SAAS,WAAW,OAAsC;AACxD,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO;AAClC,QAAM,UAAoB,CAAC;AAC3B,aAAW,QAAQ,OAAO;AACxB,QAAI,OAAO,SAAS,SAAU,SAAQ,KAAK,IAAI;AAAA,EACjD;AACA,SAAO;AACT;AAEA,SAAS,WAAW,OAA2B,KAAiC;AAC9E,MAAI,UAAU,UAAa,QAAQ,OAAW,QAAO;AACrD,UAAQ,MAAM,SAAS;AACzB;AAEO,SAAS,cAAc,OAA2C;AACvE,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAC7B,QAAM,UAAU,WAAW,MAAM,gBAAgB;AACjD,QAAM,iBAAiB,WAAW,MAAM,eAAe;AACvD,MAAI,WAAW,eAAgB,QAAO,EAAE,SAAS,YAAY,eAAe;AAC5E,QAAM,SAAS,WAAW,MAAM,6BAA6B;AAC7D,QAAM,OAAO,WAAW,MAAM,2BAA2B;AACzD,MAAI,CAAC,UAAU,CAAC,KAAM,QAAO;AAC7B,SAAO;AAAA,IACL,SAAS,OAAO,IAAI,CAAC,UAAU,QAAQ,GAAI;AAAA,IAC3C,YAAY,OAAO,IAAI,CAAC,OAAO,UAAU,WAAW,OAAO,KAAK,KAAK,CAAC,CAAC;AAAA,EACzE;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from "./registry.js";
|
|
25
25
|
import { SpeechEventStream } from "./speech.js";
|
|
26
26
|
import { askAgent, bindThinker, talkerTools } from "./talker.js";
|
|
27
|
+
import { mintRealtimeToken } from "./tokens.js";
|
|
27
28
|
import { TranscriptionEventStream } from "./transcription.js";
|
|
28
29
|
import { voiceTurns } from "./turns.js";
|
|
29
30
|
import { addAudioCounters, priceAudio } from "./usage.js";
|
|
@@ -46,6 +47,7 @@ export {
|
|
|
46
47
|
getVoiceModel,
|
|
47
48
|
listVoiceModels,
|
|
48
49
|
listen,
|
|
50
|
+
mintRealtimeToken,
|
|
49
51
|
planDelivery,
|
|
50
52
|
planTruncation,
|
|
51
53
|
priceAudio,
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["export type {\n AudioChunk,\n AudioEncoding,\n AudioFormat,\n AudioSink,\n AudioSource,\n AudioTag,\n CharAlignment,\n TextSpan,\n} from './audio.ts';\nexport { resolveVoiceAuth } from './auth.ts';\nexport { blankUsage } from './blank-usage.ts';\nexport {\n convertAudio,\n convertAudioSource,\n decodeAudioBase64,\n encodeAudioBase64,\n} from './convert.ts';\nexport { VoiceRequestError } from './errors.ts';\nexport type {\n Delivery,\n DeliverySupport,\n KnownRealtimeApi,\n KnownSpeechApi,\n KnownTranscriptionApi,\n RealtimeApi,\n RealtimeModel,\n SpeechApi,\n SpeechModel,\n TranscriptionApi,\n TranscriptionModel,\n VoiceInfo,\n VoiceModel,\n VoiceModelType,\n} from './models.ts';\nexport {\n getVoiceModel,\n listVoiceModels,\n registerVoiceModel,\n} from './models.ts';\nexport type {\n DeliveryPlan,\n RealtimeApiProvider,\n RealtimeContext,\n RealtimeEvent,\n RealtimeOptions,\n RealtimeSession,\n RealtimeSocket,\n RealtimeToken,\n TruncationPlan,\n} from './realtime.ts';\nexport { planDelivery, planTruncation, RealtimeLedger, realtimeSocket } from './realtime.ts';\nexport {\n connectRealtime,\n listen,\n registerRealtimeApiProvider,\n registerSpeechApiProvider,\n registerTranscriptionApiProvider,\n speak,\n transcribe,\n} from './registry.ts';\nexport type { SpeechApiProvider, SpeechEvent, SpeechInput } from './speech.ts';\nexport { SpeechEventStream } from './speech.ts';\nexport type { VoiceAgentEvent, VoiceMessageChange } from './spoken.ts';\nexport type {\n BindThinkerOptions,\n BoundThinker,\n Thinker,\n ThinkerCaption,\n ThinkerPhase,\n ThinkerUpdate,\n} from './talker.ts';\nexport { askAgent, bindThinker, talkerTools } from './talker.ts';\nexport type {\n AudioInput,\n ListenOptions,\n Transcript,\n TranscriptionApiProvider,\n TranscriptionEvent,\n TranscriptSegment,\n} from './transcription.ts';\nexport { TranscriptionEventStream } from './transcription.ts';\nexport type {\n BargeIn,\n CaptionEvent,\n EchoRecommendation,\n VoiceConversation,\n VoiceCut,\n VoiceModels,\n VoicePhase,\n VoiceSession,\n VoiceTurnMeta,\n VoiceTurnOptions,\n} from './turns.ts';\nexport { voiceTurns } from './turns.ts';\nexport type {\n AudioCost,\n AudioCounters,\n AudioUsage,\n RealtimeAudioCost,\n SpeechAudioCost,\n TranscriptionAudioCost,\n} from './usage.ts';\nexport { addAudioCounters, priceAudio } from './usage.ts';\nexport type { VadHook, VadUpdate } from './vad.ts';\nexport { energyVad } from './vad.ts';\n"],
|
|
5
|
-
"mappings": "AAUA,SAAS,wBAAwB;AACjC,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,yBAAyB;AAiBlC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAYP,SAAS,cAAc,gBAAgB,gBAAgB,sBAAsB;AAC7E;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,yBAAyB;AAUlC,SAAS,UAAU,aAAa,mBAAmB;
|
|
4
|
+
"sourcesContent": ["export type {\n AudioChunk,\n AudioEncoding,\n AudioFormat,\n AudioSink,\n AudioSource,\n AudioTag,\n CharAlignment,\n TextSpan,\n} from './audio.ts';\nexport { resolveVoiceAuth } from './auth.ts';\nexport { blankUsage } from './blank-usage.ts';\nexport {\n convertAudio,\n convertAudioSource,\n decodeAudioBase64,\n encodeAudioBase64,\n} from './convert.ts';\nexport { VoiceRequestError } from './errors.ts';\nexport type {\n Delivery,\n DeliverySupport,\n KnownRealtimeApi,\n KnownSpeechApi,\n KnownTranscriptionApi,\n RealtimeApi,\n RealtimeModel,\n SpeechApi,\n SpeechModel,\n TranscriptionApi,\n TranscriptionModel,\n VoiceInfo,\n VoiceModel,\n VoiceModelType,\n} from './models.ts';\nexport {\n getVoiceModel,\n listVoiceModels,\n registerVoiceModel,\n} from './models.ts';\nexport type {\n DeliveryPlan,\n RealtimeApiProvider,\n RealtimeContext,\n RealtimeEvent,\n RealtimeOptions,\n RealtimeSession,\n RealtimeSocket,\n RealtimeToken,\n TruncationPlan,\n} from './realtime.ts';\nexport { planDelivery, planTruncation, RealtimeLedger, realtimeSocket } from './realtime.ts';\nexport {\n connectRealtime,\n listen,\n registerRealtimeApiProvider,\n registerSpeechApiProvider,\n registerTranscriptionApiProvider,\n speak,\n transcribe,\n} from './registry.ts';\nexport type { SpeechApiProvider, SpeechEvent, SpeechInput } from './speech.ts';\nexport { SpeechEventStream } from './speech.ts';\nexport type { VoiceAgentEvent, VoiceMessageChange } from './spoken.ts';\nexport type {\n BindThinkerOptions,\n BoundThinker,\n Thinker,\n ThinkerCaption,\n ThinkerPhase,\n ThinkerUpdate,\n} from './talker.ts';\nexport { askAgent, bindThinker, talkerTools } from './talker.ts';\nexport type { RealtimeConstraints } from './tokens.ts';\nexport { mintRealtimeToken } from './tokens.ts';\nexport type {\n AudioInput,\n ListenOptions,\n Transcript,\n TranscriptionApiProvider,\n TranscriptionEvent,\n TranscriptSegment,\n} from './transcription.ts';\nexport { TranscriptionEventStream } from './transcription.ts';\nexport type {\n BargeIn,\n CaptionEvent,\n EchoRecommendation,\n VoiceConversation,\n VoiceCut,\n VoiceModels,\n VoicePhase,\n VoiceSession,\n VoiceTurnMeta,\n VoiceTurnOptions,\n} from './turns.ts';\nexport { voiceTurns } from './turns.ts';\nexport type {\n AudioCost,\n AudioCounters,\n AudioUsage,\n RealtimeAudioCost,\n SpeechAudioCost,\n TranscriptionAudioCost,\n} from './usage.ts';\nexport { addAudioCounters, priceAudio } from './usage.ts';\nexport type { VadHook, VadUpdate } from './vad.ts';\nexport { energyVad } from './vad.ts';\n"],
|
|
5
|
+
"mappings": "AAUA,SAAS,wBAAwB;AACjC,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,yBAAyB;AAiBlC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAYP,SAAS,cAAc,gBAAgB,gBAAgB,sBAAsB;AAC7E;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,yBAAyB;AAUlC,SAAS,UAAU,aAAa,mBAAmB;AAEnD,SAAS,yBAAyB;AASlC,SAAS,gCAAgC;AAazC,SAAS,kBAAkB;AAS3B,SAAS,kBAAkB,kBAAkB;AAE7C,SAAS,iBAAiB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { VoiceRequestError } from "./errors.js";
|
|
2
|
+
const openaiSecrets = "https://api.openai.com/v1/realtime/client_secrets";
|
|
3
|
+
const xaiSecrets = "https://api.x.ai/v1/realtime/client_secrets";
|
|
4
|
+
const geminiTokens = "https://generativelanguage.googleapis.com/v1beta/auth_tokens";
|
|
5
|
+
const elevenSigned = "https://api.elevenlabs.io/v1/convai/conversation/get-signed-url";
|
|
6
|
+
const elevenWebRtc = "https://api.elevenlabs.io/v1/convai/conversation/token";
|
|
7
|
+
const fishSessions = "https://api.fish.audio/v1/agent/sessions";
|
|
8
|
+
const fifteenMinutes = 15 * 60 * 1e3;
|
|
9
|
+
const thirtyMinutes = 30 * 60 * 1e3;
|
|
10
|
+
const oneMinute = 60 * 1e3;
|
|
11
|
+
const xaiLifetimeSeconds = 300;
|
|
12
|
+
function apiKey(options) {
|
|
13
|
+
const key = options.apiKey;
|
|
14
|
+
if (typeof key === "string" && key.length > 0) return key;
|
|
15
|
+
throw new VoiceRequestError("error", "missing realtime credential");
|
|
16
|
+
}
|
|
17
|
+
function mergedHeaders(options, defaults) {
|
|
18
|
+
const headers = { ...defaults };
|
|
19
|
+
const extra = options.headers;
|
|
20
|
+
if (extra === void 0) return headers;
|
|
21
|
+
for (const [key, value] of Object.entries(extra)) {
|
|
22
|
+
if (value === null) delete headers[key];
|
|
23
|
+
else headers[key] = value;
|
|
24
|
+
}
|
|
25
|
+
return headers;
|
|
26
|
+
}
|
|
27
|
+
function omitUndefined(source) {
|
|
28
|
+
const out = {};
|
|
29
|
+
for (const [key, value] of Object.entries(source)) {
|
|
30
|
+
if (value !== void 0) out[key] = value;
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
function functionTool(tool) {
|
|
35
|
+
return {
|
|
36
|
+
type: "function",
|
|
37
|
+
name: tool.name,
|
|
38
|
+
description: tool.description,
|
|
39
|
+
parameters: tool.parameters
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function declaration(tool) {
|
|
43
|
+
return {
|
|
44
|
+
name: tool.name,
|
|
45
|
+
description: tool.description,
|
|
46
|
+
parameters: tool.parameters
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function requiredAgent(constraints) {
|
|
50
|
+
const id = constraints.agentId;
|
|
51
|
+
if (typeof id === "string" && id.length > 0) return id;
|
|
52
|
+
throw new VoiceRequestError("error", "missing agent id");
|
|
53
|
+
}
|
|
54
|
+
function expiresAt(value, fallback) {
|
|
55
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
56
|
+
return value < 1e12 ? value * 1e3 : value;
|
|
57
|
+
}
|
|
58
|
+
if (typeof value === "string") {
|
|
59
|
+
const parsed = Date.parse(value);
|
|
60
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
61
|
+
}
|
|
62
|
+
return fallback;
|
|
63
|
+
}
|
|
64
|
+
function fromField(field) {
|
|
65
|
+
return (json, fallback) => expiresAt(json[field], fallback);
|
|
66
|
+
}
|
|
67
|
+
function openaiBody(model, constraints) {
|
|
68
|
+
const session = omitUndefined({
|
|
69
|
+
type: "realtime",
|
|
70
|
+
model: model.id,
|
|
71
|
+
instructions: constraints.instructions,
|
|
72
|
+
tools: constraints.tools?.map(functionTool)
|
|
73
|
+
});
|
|
74
|
+
if (constraints.voice !== void 0) session.audio = { output: { voice: constraints.voice } };
|
|
75
|
+
return { session };
|
|
76
|
+
}
|
|
77
|
+
function geminiBody(model, constraints, now) {
|
|
78
|
+
const instructions = constraints.instructions;
|
|
79
|
+
const voice = constraints.voice;
|
|
80
|
+
const tools = constraints.tools;
|
|
81
|
+
const config = omitUndefined({
|
|
82
|
+
responseModalities: ["AUDIO"],
|
|
83
|
+
sessionResumption: {},
|
|
84
|
+
systemInstruction: instructions === void 0 ? void 0 : { parts: [{ text: instructions }] },
|
|
85
|
+
speechConfig: voice === void 0 ? void 0 : { voiceConfig: { prebuiltVoiceConfig: { voiceName: voice } } },
|
|
86
|
+
tools: tools === void 0 ? void 0 : [{ functionDeclarations: tools.map(declaration) }]
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
uses: constraints.uses ?? 1,
|
|
90
|
+
expireTime: new Date(now + thirtyMinutes).toISOString(),
|
|
91
|
+
newSessionExpireTime: new Date(now + oneMinute).toISOString(),
|
|
92
|
+
liveConnectConstraints: { model: `models/${model.id}`, config }
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function fishBody(constraints) {
|
|
96
|
+
const overrides = omitUndefined({
|
|
97
|
+
system_prompt: constraints.instructions,
|
|
98
|
+
voice_id: constraints.voice
|
|
99
|
+
});
|
|
100
|
+
return omitUndefined({
|
|
101
|
+
agent_id: requiredAgent(constraints),
|
|
102
|
+
overrides: Object.keys(overrides).length > 0 ? overrides : void 0
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function webrtcOnly(model) {
|
|
106
|
+
return model.transports.length === 1 && model.transports[0] === "webrtc";
|
|
107
|
+
}
|
|
108
|
+
function elevenUrl(model, constraints) {
|
|
109
|
+
const url = new URL(webrtcOnly(model) ? elevenWebRtc : elevenSigned);
|
|
110
|
+
url.searchParams.set("agent_id", requiredAgent(constraints));
|
|
111
|
+
return url.toString();
|
|
112
|
+
}
|
|
113
|
+
function mintCall(model, constraints, options, key, now) {
|
|
114
|
+
const jsonHeaders = { "Content-Type": "application/json" };
|
|
115
|
+
switch (model.api) {
|
|
116
|
+
case "openai-realtime":
|
|
117
|
+
return {
|
|
118
|
+
url: openaiSecrets,
|
|
119
|
+
method: "POST",
|
|
120
|
+
headers: mergedHeaders(options, { ...jsonHeaders, Authorization: `Bearer ${key}` }),
|
|
121
|
+
body: JSON.stringify(openaiBody(model, constraints)),
|
|
122
|
+
field: "value",
|
|
123
|
+
expires: fromField("expires_at"),
|
|
124
|
+
fallbackExpires: now
|
|
125
|
+
};
|
|
126
|
+
case "xai-realtime":
|
|
127
|
+
return {
|
|
128
|
+
url: xaiSecrets,
|
|
129
|
+
method: "POST",
|
|
130
|
+
headers: mergedHeaders(options, { ...jsonHeaders, Authorization: `Bearer ${key}` }),
|
|
131
|
+
body: JSON.stringify({ expires_after: { seconds: xaiLifetimeSeconds } }),
|
|
132
|
+
field: "value",
|
|
133
|
+
expires: fromField("expires_at"),
|
|
134
|
+
fallbackExpires: now + xaiLifetimeSeconds * 1e3
|
|
135
|
+
};
|
|
136
|
+
case "gemini-live":
|
|
137
|
+
return {
|
|
138
|
+
url: geminiTokens,
|
|
139
|
+
method: "POST",
|
|
140
|
+
headers: mergedHeaders(options, { ...jsonHeaders, "x-goog-api-key": key }),
|
|
141
|
+
body: JSON.stringify(geminiBody(model, constraints, now)),
|
|
142
|
+
field: "name",
|
|
143
|
+
expires: fromField("expireTime"),
|
|
144
|
+
fallbackExpires: now + thirtyMinutes
|
|
145
|
+
};
|
|
146
|
+
case "elevenlabs-agents":
|
|
147
|
+
return {
|
|
148
|
+
url: elevenUrl(model, constraints),
|
|
149
|
+
method: "GET",
|
|
150
|
+
headers: mergedHeaders(options, { "xi-api-key": key }),
|
|
151
|
+
field: webrtcOnly(model) ? "token" : "signed_url",
|
|
152
|
+
expires: fromField("expires_at"),
|
|
153
|
+
fallbackExpires: now + fifteenMinutes
|
|
154
|
+
};
|
|
155
|
+
case "fish-agents":
|
|
156
|
+
return {
|
|
157
|
+
url: fishSessions,
|
|
158
|
+
method: "POST",
|
|
159
|
+
headers: mergedHeaders(options, { ...jsonHeaders, Authorization: `Bearer ${key}` }),
|
|
160
|
+
body: JSON.stringify(fishBody(constraints)),
|
|
161
|
+
field: "token",
|
|
162
|
+
expires: fromField("expires_at"),
|
|
163
|
+
fallbackExpires: now
|
|
164
|
+
};
|
|
165
|
+
default:
|
|
166
|
+
throw new VoiceRequestError("error", "unsupported realtime api");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function readValue(json, field) {
|
|
170
|
+
if (typeof json !== "object" || json === null) {
|
|
171
|
+
throw new VoiceRequestError("error", "mint failed (empty token)");
|
|
172
|
+
}
|
|
173
|
+
const value = json[field];
|
|
174
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
175
|
+
throw new VoiceRequestError("error", "mint failed (empty token)");
|
|
176
|
+
}
|
|
177
|
+
async function mintRealtimeToken(model, constraints, options) {
|
|
178
|
+
const settings = options ?? {};
|
|
179
|
+
const key = apiKey(settings);
|
|
180
|
+
const now = Date.now();
|
|
181
|
+
const call = mintCall(model, constraints, settings, key, now);
|
|
182
|
+
const fetch = settings.fetch ?? globalThis.fetch;
|
|
183
|
+
const response = await fetch(call.url, {
|
|
184
|
+
method: call.method,
|
|
185
|
+
headers: call.headers,
|
|
186
|
+
body: call.body,
|
|
187
|
+
signal: settings.signal
|
|
188
|
+
});
|
|
189
|
+
if (!response.ok) throw new VoiceRequestError("error", `mint failed (${response.status})`);
|
|
190
|
+
const json = await response.json();
|
|
191
|
+
return {
|
|
192
|
+
value: readValue(json, call.field),
|
|
193
|
+
expiresAt: call.expires(json, call.fallbackExpires),
|
|
194
|
+
model: model.id
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
export {
|
|
198
|
+
mintRealtimeToken
|
|
199
|
+
};
|
|
200
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/tokens.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ProviderRequestOptions, Tool } from '@earendil-works/pi-ai';\nimport { VoiceRequestError } from './errors.ts';\nimport type { RealtimeModel } from './models.ts';\nimport type { RealtimeToken } from './realtime.ts';\n\nexport interface RealtimeConstraints {\n instructions?: string;\n tools?: Tool[];\n voice?: string;\n uses?: number;\n agentId?: string;\n}\n\nconst openaiSecrets = 'https://api.openai.com/v1/realtime/client_secrets';\nconst xaiSecrets = 'https://api.x.ai/v1/realtime/client_secrets';\nconst geminiTokens = 'https://generativelanguage.googleapis.com/v1beta/auth_tokens';\nconst elevenSigned = 'https://api.elevenlabs.io/v1/convai/conversation/get-signed-url';\nconst elevenWebRtc = 'https://api.elevenlabs.io/v1/convai/conversation/token';\nconst fishSessions = 'https://api.fish.audio/v1/agent/sessions';\nconst fifteenMinutes = 15 * 60 * 1000;\nconst thirtyMinutes = 30 * 60 * 1000;\nconst oneMinute = 60 * 1000;\nconst xaiLifetimeSeconds = 300;\n\ntype Json = Record<string, unknown>;\n\ninterface MintCall {\n url: string;\n method: 'GET' | 'POST';\n headers: Record<string, string>;\n body?: string;\n field: string;\n expires: (json: Json, fallback: number) => number;\n fallbackExpires: number;\n}\n\nfunction apiKey(options: ProviderRequestOptions): 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 mergedHeaders(\n options: ProviderRequestOptions,\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 omitUndefined(source: Json): Json {\n const out: Json = {};\n for (const [key, value] of Object.entries(source)) {\n if (value !== undefined) out[key] = value;\n }\n return out;\n}\n\nfunction functionTool(tool: Tool): Json {\n return {\n type: 'function',\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters as unknown,\n };\n}\n\nfunction declaration(tool: Tool): Json {\n return {\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters as unknown,\n };\n}\n\nfunction requiredAgent(constraints: RealtimeConstraints): string {\n const id = constraints.agentId;\n if (typeof id === 'string' && id.length > 0) return id;\n throw new VoiceRequestError('error', 'missing agent id');\n}\n\nfunction expiresAt(value: unknown, fallback: number): number {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return value < 1e12 ? value * 1000 : value;\n }\n if (typeof value === 'string') {\n const parsed = Date.parse(value);\n if (Number.isFinite(parsed)) return parsed;\n }\n return fallback;\n}\n\nfunction fromField(field: string): (json: Json, fallback: number) => number {\n return (json, fallback) => expiresAt(json[field], fallback);\n}\n\nfunction openaiBody(model: RealtimeModel<string>, constraints: RealtimeConstraints): Json {\n const session = omitUndefined({\n type: 'realtime',\n model: model.id,\n instructions: constraints.instructions,\n tools: constraints.tools?.map(functionTool),\n });\n if (constraints.voice !== undefined) session.audio = { output: { voice: constraints.voice } };\n return { session };\n}\n\nfunction geminiBody(\n model: RealtimeModel<string>,\n constraints: RealtimeConstraints,\n now: number\n): Json {\n const instructions = constraints.instructions;\n const voice = constraints.voice;\n const tools = constraints.tools;\n const config = omitUndefined({\n responseModalities: ['AUDIO'],\n sessionResumption: {},\n systemInstruction: instructions === undefined ? undefined : { parts: [{ text: instructions }] },\n speechConfig:\n voice === undefined\n ? undefined\n : { voiceConfig: { prebuiltVoiceConfig: { voiceName: voice } } },\n tools: tools === undefined ? undefined : [{ functionDeclarations: tools.map(declaration) }],\n });\n return {\n uses: constraints.uses ?? 1,\n expireTime: new Date(now + thirtyMinutes).toISOString(),\n newSessionExpireTime: new Date(now + oneMinute).toISOString(),\n liveConnectConstraints: { model: `models/${model.id}`, config },\n };\n}\n\nfunction fishBody(constraints: RealtimeConstraints): Json {\n const overrides = omitUndefined({\n system_prompt: constraints.instructions,\n voice_id: constraints.voice,\n });\n return omitUndefined({\n agent_id: requiredAgent(constraints),\n overrides: Object.keys(overrides).length > 0 ? overrides : undefined,\n });\n}\n\nfunction webrtcOnly(model: RealtimeModel<string>): boolean {\n return model.transports.length === 1 && model.transports[0] === 'webrtc';\n}\n\nfunction elevenUrl(model: RealtimeModel<string>, constraints: RealtimeConstraints): string {\n const url = new URL(webrtcOnly(model) ? elevenWebRtc : elevenSigned);\n url.searchParams.set('agent_id', requiredAgent(constraints));\n return url.toString();\n}\n\nfunction mintCall(\n model: RealtimeModel<string>,\n constraints: RealtimeConstraints,\n options: ProviderRequestOptions,\n key: string,\n now: number\n): MintCall {\n const jsonHeaders = { 'Content-Type': 'application/json' };\n switch (model.api) {\n case 'openai-realtime':\n return {\n url: openaiSecrets,\n method: 'POST',\n headers: mergedHeaders(options, { ...jsonHeaders, Authorization: `Bearer ${key}` }),\n body: JSON.stringify(openaiBody(model, constraints)),\n field: 'value',\n expires: fromField('expires_at'),\n fallbackExpires: now,\n };\n case 'xai-realtime':\n return {\n url: xaiSecrets,\n method: 'POST',\n headers: mergedHeaders(options, { ...jsonHeaders, Authorization: `Bearer ${key}` }),\n body: JSON.stringify({ expires_after: { seconds: xaiLifetimeSeconds } }),\n field: 'value',\n expires: fromField('expires_at'),\n fallbackExpires: now + xaiLifetimeSeconds * 1000,\n };\n case 'gemini-live':\n return {\n url: geminiTokens,\n method: 'POST',\n headers: mergedHeaders(options, { ...jsonHeaders, 'x-goog-api-key': key }),\n body: JSON.stringify(geminiBody(model, constraints, now)),\n field: 'name',\n expires: fromField('expireTime'),\n fallbackExpires: now + thirtyMinutes,\n };\n case 'elevenlabs-agents':\n return {\n url: elevenUrl(model, constraints),\n method: 'GET',\n headers: mergedHeaders(options, { 'xi-api-key': key }),\n field: webrtcOnly(model) ? 'token' : 'signed_url',\n expires: fromField('expires_at'),\n fallbackExpires: now + fifteenMinutes,\n };\n case 'fish-agents':\n return {\n url: fishSessions,\n method: 'POST',\n headers: mergedHeaders(options, { ...jsonHeaders, Authorization: `Bearer ${key}` }),\n body: JSON.stringify(fishBody(constraints)),\n field: 'token',\n expires: fromField('expires_at'),\n fallbackExpires: now,\n };\n default:\n throw new VoiceRequestError('error', 'unsupported realtime api');\n }\n}\n\nfunction readValue(json: unknown, field: string): string {\n if (typeof json !== 'object' || json === null) {\n throw new VoiceRequestError('error', 'mint failed (empty token)');\n }\n const value = (json as Json)[field];\n if (typeof value === 'string' && value.length > 0) return value;\n throw new VoiceRequestError('error', 'mint failed (empty token)');\n}\n\nexport async function mintRealtimeToken(\n model: RealtimeModel<string>,\n constraints: RealtimeConstraints,\n options?: ProviderRequestOptions\n): Promise<RealtimeToken> {\n const settings = options ?? {};\n const key = apiKey(settings);\n const now = Date.now();\n const call = mintCall(model, constraints, settings, key, now);\n const fetch = settings.fetch ?? globalThis.fetch;\n const response = await fetch(call.url, {\n method: call.method,\n headers: call.headers,\n body: call.body,\n signal: settings.signal,\n });\n if (!response.ok) throw new VoiceRequestError('error', `mint failed (${response.status})`);\n const json: unknown = await response.json();\n return {\n value: readValue(json, call.field),\n expiresAt: call.expires(json as Json, call.fallbackExpires),\n model: model.id,\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerElevenLabs(): () => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { registerElevenLabs } from './catalog.ts';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AudioChunk } from '../audio.ts';
|
|
2
|
+
import type { TranscriptionModel } from '../models.ts';
|
|
3
|
+
import { type ListenOptions, TranscriptionEventStream } from '../transcription.ts';
|
|
4
|
+
export declare function listenElevenLabs(model: TranscriptionModel<string>, audio: AsyncIterable<AudioChunk>, options?: ListenOptions): TranscriptionEventStream;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ProviderRequestOptions } from '@earendil-works/pi-ai';
|
|
2
|
+
import type { SpeechModel } from '../models.ts';
|
|
3
|
+
import { SpeechEventStream, type SpeechInput } from '../speech.ts';
|
|
4
|
+
export declare function speakElevenLabs(model: SpeechModel<string>, input: SpeechInput, options?: ProviderRequestOptions): SpeechEventStream;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ProviderRequestOptions } from '@earendil-works/pi-ai';
|
|
2
|
+
import type { TranscriptionModel } from '../models.ts';
|
|
3
|
+
import type { AudioInput, Transcript } from '../transcription.ts';
|
|
4
|
+
export declare function transcribeElevenLabs(model: TranscriptionModel<string>, input: AudioInput, options?: ProviderRequestOptions): Promise<Transcript>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ProviderRequestOptions } from '@earendil-works/pi-ai';
|
|
2
|
+
import type { AudioFormat, CharAlignment } from '../audio.ts';
|
|
3
|
+
export interface ElevenFile {
|
|
4
|
+
data: Uint8Array;
|
|
5
|
+
mimeType: string;
|
|
6
|
+
filename: string;
|
|
7
|
+
}
|
|
8
|
+
interface HeaderModel {
|
|
9
|
+
provider: string;
|
|
10
|
+
headers?: Record<string, string>;
|
|
11
|
+
}
|
|
12
|
+
export declare function mediaType(mimeType: string): string;
|
|
13
|
+
export declare function audioFilename(mimeType: string): string;
|
|
14
|
+
export declare function pcmS16(mimeType: string): boolean;
|
|
15
|
+
export declare function encodeForm(file: ElevenFile, fields: Record<string, string>): ArrayBuffer;
|
|
16
|
+
export declare function formType(): string;
|
|
17
|
+
export declare function xiHeaders(model: HeaderModel, options: ProviderRequestOptions | undefined): Record<string, string>;
|
|
18
|
+
export declare function socketUrl(baseUrl: string, path: string): URL;
|
|
19
|
+
export declare function outputFormatName(format: AudioFormat): string;
|
|
20
|
+
export declare function sttAudioFormat(format: AudioFormat): string;
|
|
21
|
+
export declare function connectSocket(url: string, headers: Record<string, string>): WebSocket;
|
|
22
|
+
export declare function awaitOpen(socket: WebSocket, label: string, fail: (message: string) => void): Promise<void>;
|
|
23
|
+
export declare function messageText(data: unknown): Promise<string>;
|
|
24
|
+
export declare function sameFormat(left: AudioFormat, right: AudioFormat): boolean;
|
|
25
|
+
export declare function alignmentFrom(value: unknown): CharAlignment | undefined;
|
|
26
|
+
export {};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export { SpeechEventStream } from './speech.ts';
|
|
|
13
13
|
export type { VoiceAgentEvent, VoiceMessageChange } from './spoken.ts';
|
|
14
14
|
export type { BindThinkerOptions, BoundThinker, Thinker, ThinkerCaption, ThinkerPhase, ThinkerUpdate, } from './talker.ts';
|
|
15
15
|
export { askAgent, bindThinker, talkerTools } from './talker.ts';
|
|
16
|
+
export type { RealtimeConstraints } from './tokens.ts';
|
|
17
|
+
export { mintRealtimeToken } from './tokens.ts';
|
|
16
18
|
export type { AudioInput, ListenOptions, Transcript, TranscriptionApiProvider, TranscriptionEvent, TranscriptSegment, } from './transcription.ts';
|
|
17
19
|
export { TranscriptionEventStream } from './transcription.ts';
|
|
18
20
|
export type { BargeIn, CaptionEvent, EchoRecommendation, VoiceConversation, VoiceCut, VoiceModels, VoicePhase, VoiceSession, VoiceTurnMeta, VoiceTurnOptions, } from './turns.ts';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ProviderRequestOptions, Tool } from '@earendil-works/pi-ai';
|
|
2
|
+
import type { RealtimeModel } from './models.ts';
|
|
3
|
+
import type { RealtimeToken } from './realtime.ts';
|
|
4
|
+
export interface RealtimeConstraints {
|
|
5
|
+
instructions?: string;
|
|
6
|
+
tools?: Tool[];
|
|
7
|
+
voice?: string;
|
|
8
|
+
uses?: number;
|
|
9
|
+
agentId?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function mintRealtimeToken(model: RealtimeModel<string>, constraints: RealtimeConstraints, options?: ProviderRequestOptions): Promise<RealtimeToken>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/pi-audio",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.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",
|
|
@@ -46,6 +46,10 @@
|
|
|
46
46
|
"types": "./dist/types/xai/index.d.ts",
|
|
47
47
|
"default": "./dist/xai/index.js"
|
|
48
48
|
},
|
|
49
|
+
"./elevenlabs": {
|
|
50
|
+
"types": "./dist/types/elevenlabs/index.d.ts",
|
|
51
|
+
"default": "./dist/elevenlabs/index.js"
|
|
52
|
+
},
|
|
49
53
|
"./durable": {
|
|
50
54
|
"types": "./dist/types/durable/index.d.ts",
|
|
51
55
|
"default": "./dist/durable/index.js"
|