@ai-ecoverse/pi-audio 1.18.0 → 1.18.1
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 +1 -1
- package/dist/gemini-live/session.js +32 -15
- package/dist/gemini-live/session.js.map +2 -2
- package/dist/openai-realtime/session.js +35 -9
- package/dist/openai-realtime/session.js.map +2 -2
- package/dist/openai-realtime/socket.js +12 -0
- package/dist/openai-realtime/socket.js.map +2 -2
- package/dist/talker.js +71 -27
- package/dist/talker.js.map +3 -3
- package/dist/types/openai-realtime/socket.d.ts +1 -0
- package/dist/types/talker.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ pi-audio does not apply that patch. Hosts such as slicc-agent record monkeypatch
|
|
|
52
52
|
|
|
53
53
|
`@ai-ecoverse/pi-audio/elevenlabs-agents` registers `elevenlabs-agent`, a hosted cascade (`provisioning: "agent"`, `truncation: "server"`, `liveToolUpdate: false`). `provisionAgent` creates or updates the agent and reuses it when the spec hash matches. pi tools become client tools. A string `llm` is a vendor model name. A `CustomLlm` sets `llm` to `custom-llm` and sends `custom_llm.url`, `model_id`, `api_type`, and `request_headers` (ElevenLabs `api_key` is a workspace secret reference, not a raw token). `connectRealtime` uses a `RealtimeToken` from `mintRealtimeToken` (`get-signed-url`) when one is passed. Otherwise an API key provisions the agent and mints the signed URL. Silent `inject` sends `contextual_update` with `context_id` `pi-audio`. `close()` prices `audio.inputSeconds` from the wall-clock session at $0.08 per minute.
|
|
54
54
|
|
|
55
|
-
`talkerTools` is `ask_agent` and `read`. pi-audio defines `askAgent` (a pi `Tool` whose parameters are `request` and optional `context`) and the `Thinker` interface. The host implements `Thinker` and supplies `read`. `bindThinker` runs direct tools with `directTimeoutMs` and truncates their text to `directMaxChars`. On a blocking `ask_agent` it answers with `ack` immediately and injects the result later. On an async call it `respond`s with the result and `options.result`. Progress is injected with `options.progress`. `tool_call_cancelled` aborts the thinker. The binding also yields `thinker_progress` / `thinker_result` captions and `thinking` / `tool` / `idle` phases.
|
|
55
|
+
`talkerTools` is `ask_agent` and `read`. pi-audio defines `askAgent` (a pi `Tool` whose parameters are `request` and optional `context`) and the `Thinker` interface. The host implements `Thinker` and supplies `read`. `bindThinker` runs direct tools with `directTimeoutMs` and truncates their text to `directMaxChars`. On a blocking `ask_agent` it answers with `ack` immediately and injects the result later. On an async call it `respond`s with the result and `options.result`. Progress is injected with `options.progress`. `tool_call_cancelled` aborts the thinker and any direct tool. A blocking failure is injected after the acknowledgement, with a `thinker_result` caption. `Thinker.ask` receives the optional `context` string. Closing the session aborts work that is still running. The binding also yields `thinker_progress` / `thinker_result` captions and `thinking` / `tool` / `idle` phases.
|
|
56
56
|
|
|
57
57
|
## Development
|
|
58
58
|
|
|
@@ -205,6 +205,8 @@ class GeminiSession {
|
|
|
205
205
|
#closed = false;
|
|
206
206
|
#ended = false;
|
|
207
207
|
#meters;
|
|
208
|
+
#toolGroups = /* @__PURE__ */ new Map();
|
|
209
|
+
#toolGroupOf = /* @__PURE__ */ new Map();
|
|
208
210
|
constructor(model, context, vocabulary, open, target, wire) {
|
|
209
211
|
this.model = model;
|
|
210
212
|
this.#context = context;
|
|
@@ -285,20 +287,14 @@ class GeminiSession {
|
|
|
285
287
|
this.#assertOpen();
|
|
286
288
|
const send = () => {
|
|
287
289
|
const mode = delivery === void 0 ? "interrupt" : delivery;
|
|
288
|
-
this.#
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
status: result.isError ? "error" : "ok",
|
|
297
|
-
retryable: false,
|
|
298
|
-
message: resultText(result)
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
]
|
|
290
|
+
this.#queueTool(callId, {
|
|
291
|
+
id: callId,
|
|
292
|
+
name: result.toolName,
|
|
293
|
+
scheduling: scheduling[mode],
|
|
294
|
+
response: {
|
|
295
|
+
status: result.isError ? "error" : "ok",
|
|
296
|
+
retryable: false,
|
|
297
|
+
message: resultText(result)
|
|
302
298
|
}
|
|
303
299
|
});
|
|
304
300
|
};
|
|
@@ -440,23 +436,44 @@ class GeminiSession {
|
|
|
440
436
|
next.send(JSON.stringify({ setup: this.#setup(handle) }));
|
|
441
437
|
return next;
|
|
442
438
|
}
|
|
439
|
+
#queueTool(callId, response) {
|
|
440
|
+
const groupId = this.#toolGroupOf.get(callId) ?? "";
|
|
441
|
+
const group = groupId.length === 0 ? void 0 : this.#toolGroups.get(groupId);
|
|
442
|
+
if (group === void 0) {
|
|
443
|
+
this.#send({ toolResponse: { functionResponses: [response] } });
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
group.responses.push(response);
|
|
447
|
+
group.pending.delete(callId);
|
|
448
|
+
this.#toolGroupOf.delete(callId);
|
|
449
|
+
if (group.pending.size > 0) return;
|
|
450
|
+
this.#toolGroups.delete(groupId);
|
|
451
|
+
this.#send({ toolResponse: { functionResponses: group.responses } });
|
|
452
|
+
}
|
|
443
453
|
#onToolCall(value) {
|
|
444
454
|
const call = record(value);
|
|
445
455
|
if (call === void 0 || !Array.isArray(call.functionCalls)) return;
|
|
456
|
+
const ids = [];
|
|
446
457
|
for (const entry of call.functionCalls) {
|
|
447
458
|
const fn = record(entry);
|
|
448
459
|
if (fn === void 0) continue;
|
|
460
|
+
const id = text(fn.id);
|
|
461
|
+
if (id.length > 0) ids.push(id);
|
|
449
462
|
this.#push({
|
|
450
463
|
type: "tool_call",
|
|
451
464
|
blocking: this.model.toolCalls === "blocking",
|
|
452
465
|
call: {
|
|
453
466
|
type: "toolCall",
|
|
454
|
-
id
|
|
467
|
+
id,
|
|
455
468
|
name: text(fn.name),
|
|
456
469
|
arguments: argumentsOf(fn.args)
|
|
457
470
|
}
|
|
458
471
|
});
|
|
459
472
|
}
|
|
473
|
+
const groupId = ids.at(0);
|
|
474
|
+
if (groupId === void 0) return;
|
|
475
|
+
this.#toolGroups.set(groupId, { pending: new Set(ids), responses: [] });
|
|
476
|
+
for (const id of ids) this.#toolGroupOf.set(id, groupId);
|
|
460
477
|
}
|
|
461
478
|
#onCancel(value) {
|
|
462
479
|
const cancellation = record(value);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/gemini-live/session.ts"],
|
|
4
|
-
"sourcesContent": ["import type { JsonObject, Message, Tool, ToolResultMessage, Usage } from '@earendil-works/pi-ai';\nimport type { AudioChunk, AudioFormat, TextSpan } from '../audio.ts';\nimport { blankUsage } from '../blank-usage.ts';\nimport { decodeAudioBase64, encodeAudioBase64 } from '../convert.ts';\nimport { VoiceRequestError } from '../errors.ts';\nimport type { Delivery, RealtimeModel } from '../models.ts';\nimport {\n openVoiceSocket,\n type VoiceSocketConstructor,\n type VoiceWire,\n type VoiceWireTarget,\n} from '../openai-realtime/socket.ts';\nimport type {\n RealtimeContext,\n RealtimeEvent,\n RealtimeOptions,\n RealtimeSession,\n} from '../realtime.ts';\nimport { planTruncation, RealtimeLedger, realtimeSocket } from '../realtime.ts';\n\nexport type VoiceWireFactory = (target: VoiceWireTarget) => VoiceWire;\n\nexport interface GeminiOptions {\n wire?: VoiceWireFactory;\n socket?: VoiceSocketConstructor;\n vocabulary?: string[];\n}\n\nconst scheduling: Record<Delivery, string> = {\n interrupt: 'INTERRUPT',\n when_idle: 'WHEN_IDLE',\n silent: 'SILENT',\n};\n\ninterface PendingAudio {\n chunks: AudioChunk[];\n cursor: number;\n}\n\ninterface IteratorResultEvent {\n value?: RealtimeEvent;\n done: boolean;\n}\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n\nfunction text(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nfunction credential(options: RealtimeOptions | undefined): { secret: string; ephemeral: boolean } {\n const token = options?.token?.value;\n if (typeof token === 'string' && token.length > 0) return { secret: token, ephemeral: true };\n const apiKey = options?.apiKey;\n if (typeof apiKey === 'string' && apiKey.length > 0) return { secret: apiKey, ephemeral: false };\n throw new VoiceRequestError('error', 'missing realtime credential');\n}\n\nfunction headersFor(options: RealtimeOptions | undefined): Record<string, string> {\n const headers: Record<string, string> = {};\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 sessionUrl(model: RealtimeModel<string>, secret: string, ephemeral: boolean): string {\n const url = new URL(model.baseUrl);\n if (ephemeral) url.searchParams.set('access_token', secret);\n else url.searchParams.set('key', secret);\n return url.toString();\n}\n\nfunction timeLeftMs(value: unknown): number {\n if (typeof value === 'number' && Number.isFinite(value)) return Math.round(value * 1000);\n if (typeof value === 'string') {\n const matched = /^(\\d+(?:\\.\\d+)?)s$/.exec(value);\n if (matched !== null) return Math.round(Number(matched[1]) * 1000);\n const asNumber = Number(value);\n if (Number.isFinite(asNumber)) return Math.round(asNumber * 1000);\n }\n const duration = record(value);\n if (duration !== undefined && typeof duration.seconds === 'number') {\n const nanos = typeof duration.nanos === 'number' ? duration.nanos : 0;\n return Math.round(duration.seconds * 1000 + nanos / 1e6);\n }\n return 0;\n}\n\nfunction count(source: Record<string, unknown>, key: string): number {\n const value = source[key];\n return typeof value === 'number' ? value : 0;\n}\n\nfunction usageFrom(value: unknown): Usage {\n const usage = record(value);\n if (usage === undefined) return blankUsage();\n const input = count(usage, 'promptTokenCount');\n const response = count(usage, 'responseTokenCount');\n const candidates = count(usage, 'candidatesTokenCount');\n const output = response > 0 ? response : candidates;\n return {\n input,\n output,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: input + output,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n };\n}\n\nfunction argumentsOf(value: unknown): JsonObject {\n if (typeof value === 'string') {\n try {\n const parsed: unknown = JSON.parse(value);\n const object = record(parsed);\n if (object === undefined) return {};\n return object as JsonObject;\n } catch {\n return {};\n }\n }\n const object = record(value);\n if (object === undefined) return {};\n return object as JsonObject;\n}\n\nfunction resultText(result: ToolResultMessage): string {\n const parts: string[] = [];\n for (const part of result.content) {\n if (part.type === 'text') parts.push(part.text);\n }\n return parts.join('\\n');\n}\n\nfunction historyTurns(messages: Message[] | undefined): JsonObject[] {\n if (messages === undefined) return [];\n const turns: JsonObject[] = [];\n for (const message of messages) {\n if (message.role !== 'user' && message.role !== 'assistant') continue;\n const parts: JsonObject[] = [];\n for (const part of message.content) {\n if (typeof part === 'string') {\n parts.push({ text: part });\n continue;\n }\n if (part.type === 'text') parts.push({ text: part.text });\n }\n if (parts.length === 0) continue;\n turns.push({ role: message.role === 'assistant' ? 'model' : 'user', parts });\n }\n return turns;\n}\n\nfunction declaration(tool: Tool, model: RealtimeModel<string>): JsonObject {\n const fn: JsonObject = {\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters as unknown as JsonObject,\n };\n if (model.id === 'gemini-live-2.5-flash-native-audio') fn.behavior = 'NON_BLOCKING';\n if (model.toolCalls === 'blocking') fn.behavior = 'BLOCKING';\n return fn;\n}\n\nfunction transcription(model: RealtimeModel<string>, vocabulary: string[] | undefined): JsonObject {\n if (model.id !== 'gemini-3.8-live') return {};\n if (vocabulary === undefined) return {};\n return { customVocabulary: vocabulary };\n}\n\nfunction setupBody(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n handle: string | undefined,\n vocabulary: string[] | undefined,\n turns: JsonObject[]\n): JsonObject {\n const setup: JsonObject = {\n model: `models/${model.id}`,\n generationConfig: { responseModalities: ['AUDIO'] },\n systemInstruction: { parts: [{ text: context.instructions }] },\n speechConfig: {\n voiceConfig: { prebuiltVoiceConfig: { voiceName: context.voice } },\n },\n inputAudioTranscription: transcription(model, vocabulary),\n outputAudioTranscription: {},\n contextWindowCompression: {\n triggerTokens: 100_000,\n slidingWindow: { targetTokens: 80_000 },\n },\n sessionResumption: handle === undefined ? {} : { handle },\n };\n if (context.tools.length > 0) {\n setup.tools = [{ functionDeclarations: context.tools.map((tool) => declaration(tool, model)) }];\n }\n if (turns.length > 0) setup.historyConfig = { initialHistoryInClientContent: true };\n return setup;\n}\n\nfunction spanFor(itemId: string, start: number, end: number): TextSpan {\n return { itemId, start, end };\n}\n\nfunction takeAudio(\n pending: PendingAudio,\n itemId: string,\n format: AudioFormat,\n end: number | undefined\n): RealtimeEvent[] {\n const chunks = pending.chunks;\n pending.chunks = [];\n if (chunks.length === 0) return [];\n const start = pending.cursor;\n if (end !== undefined) pending.cursor = end;\n const width = end === undefined ? 0 : end - start;\n const total = chunks.reduce((sum, chunk) => sum + chunk.data.length, 0);\n let used = 0;\n let cursor = start;\n return chunks.map((chunk, index) => {\n const event: RealtimeEvent = {\n type: 'output_audio',\n itemId,\n chunk: { data: chunk.data, format, timestamp: chunk.timestamp },\n };\n if (end === undefined) return event;\n const last = index === chunks.length - 1;\n const next = last ? end : start + Math.round(((used + chunk.data.length) / total) * width);\n used += chunk.data.length;\n event.span = spanFor(itemId, cursor, next);\n cursor = next;\n return event;\n });\n}\n\nclass GeminiSession implements RealtimeSession {\n readonly model: RealtimeModel<string>;\n readonly #context: RealtimeContext;\n readonly #vocabulary: string[] | undefined;\n readonly #turns: JsonObject[];\n readonly #open: VoiceWireFactory;\n readonly #target: VoiceWireTarget;\n readonly #ledger = new RealtimeLedger();\n readonly #queue: RealtimeEvent[] = [];\n readonly #waiters: Array<(result: IteratorResultEvent) => void> = [];\n readonly #outgoing: JsonObject[] = [];\n readonly #format: AudioFormat;\n #wire: VoiceWire;\n #pending: PendingAudio = { chunks: [], cursor: 0 };\n #item = 'g0';\n #turn = 0;\n #heard = '';\n #spoken = '';\n #handle: string | undefined;\n #tokens: Usage = blankUsage();\n #ready = false;\n #historySent = false;\n #closed = false;\n #ended = false;\n #meters: Usage | undefined;\n\n constructor(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n vocabulary: string[] | undefined,\n open: VoiceWireFactory,\n target: VoiceWireTarget,\n wire: VoiceWire\n ) {\n this.model = model;\n this.#context = context;\n this.#vocabulary = vocabulary;\n this.#turns = historyTurns(context.history);\n this.#open = open;\n this.#target = target;\n this.#wire = wire;\n this.#format = model.outputFormat;\n }\n\n start(handle: string | undefined): void {\n if (handle !== undefined && handle.length > 0) this.#handle = handle;\n this.#wire.send(JSON.stringify({ setup: this.#setup(handle) }));\n void this.#read(this.#wire);\n }\n\n #setup(handle: string | undefined): JsonObject {\n return setupBody(this.model, this.#context, handle, this.#vocabulary, this.#turns);\n }\n\n #assertOpen(): void {\n if (this.#closed) throw new VoiceRequestError('error', 'session is closed');\n }\n\n #send(event: JsonObject): void {\n if (!this.#ready) {\n this.#outgoing.push(event);\n return;\n }\n this.#wire.send(JSON.stringify(event));\n }\n\n #push(event: RealtimeEvent): void {\n if (this.#closed) return;\n this.#ledger.observe(event);\n const waiter = this.#waiters.shift();\n if (waiter !== undefined) waiter({ value: event, done: false });\n else this.#queue.push(event);\n }\n\n #end(): void {\n if (this.#ended) return;\n this.#ended = true;\n while (this.#waiters.length > 0) this.#waiters.shift()?.({ done: true });\n }\n\n appendAudio(chunk: AudioChunk): void {\n this.#assertOpen();\n this.#ledger.addInput(chunk);\n this.#send({\n realtimeInput: {\n audio: {\n data: encodeAudioBase64(chunk.data),\n mimeType: `audio/pcm;rate=${String(chunk.format.sampleRate)}`,\n },\n },\n });\n }\n\n appendVideoFrame(jpeg: Uint8Array): void {\n this.#assertOpen();\n if (this.model.video === undefined) {\n throw new VoiceRequestError('error', 'video is unsupported');\n }\n this.#send({\n realtimeInput: { video: { data: encodeAudioBase64(jpeg), mimeType: 'image/jpeg' } },\n });\n }\n\n sendText(value: string): void {\n this.#assertOpen();\n this.#ledger.addText();\n this.#send(this.#client(value, true));\n }\n\n inject(value: string, delivery: Delivery): void {\n this.#assertOpen();\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n this.#ledger.addText();\n if (delivery === 'silent') this.#send(this.#client(value, false));\n else if (delivery === 'when_idle') this.#send({ realtimeInput: { text: value } });\n else this.#send(this.#client(value, true));\n });\n }\n\n respond(callId: string, result: ToolResultMessage, delivery?: Delivery): void {\n this.#assertOpen();\n const send = () => {\n const mode = delivery === undefined ? 'interrupt' : delivery;\n this.#send({\n toolResponse: {\n functionResponses: [\n {\n id: callId,\n name: result.toolName,\n scheduling: scheduling[mode],\n response: {\n status: result.isError ? 'error' : 'ok',\n retryable: false,\n message: resultText(result),\n },\n },\n ],\n },\n });\n };\n if (delivery === undefined) {\n send();\n return;\n }\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n send();\n });\n }\n\n interrupt(heard?: { itemId: string; heardMs: number }): void {\n this.#assertOpen();\n planTruncation(this.model.truncation, heard);\n this.#ledger.settle();\n }\n\n update(patch: Partial<Pick<RealtimeContext, 'instructions' | 'tools' | 'voice'>>): void {\n this.#assertOpen();\n if (patch.tools !== undefined && !this.model.liveToolUpdate) {\n throw new VoiceRequestError('error', 'live tool update is unsupported');\n }\n }\n\n async close(): Promise<{ usage: Usage }> {\n if (this.#meters !== undefined) return { usage: this.#meters };\n this.#closed = true;\n this.#ledger.settle();\n this.#wire.close();\n this.#end();\n this.#meters = this.#ledger.usage(this.model.audioCost);\n return { usage: this.#meters };\n }\n\n #client(value: string, turnComplete: boolean): JsonObject {\n return {\n clientContent: {\n turns: [{ role: 'user', parts: [{ text: value }] }],\n turnComplete,\n },\n };\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<RealtimeEvent> {\n while (true) {\n const queued = this.#queue.shift();\n if (queued !== undefined) {\n yield queued;\n continue;\n }\n if (this.#closed) return;\n const next = await new Promise<IteratorResultEvent>((resolve) => {\n this.#waiters.push(resolve);\n });\n if (next.done === true || next.value === undefined) return;\n yield next.value;\n }\n }\n\n #audio(encoded: string): void {\n if (encoded.length === 0) return;\n this.#pending.chunks.push({\n data: decodeAudioBase64(encoded),\n format: this.#format,\n timestamp: 0,\n });\n }\n\n #transcript(delta: string, final: boolean): void {\n const end = this.#pending.cursor + delta.length;\n const tagged = delta.length === 0 ? undefined : end;\n for (const event of takeAudio(this.#pending, this.#item, this.#format, tagged)) {\n this.#push(event);\n }\n this.#spoken += delta;\n this.#push({ type: 'output_transcript', itemId: this.#item, text: delta, final });\n }\n\n #input(transcript: string, final: boolean): void {\n if (transcript.length > 0) this.#heard = transcript;\n this.#push({ type: 'input_transcript', itemId: this.#item, text: this.#heard, final });\n }\n\n #finish(interrupted: boolean): void {\n for (const event of takeAudio(this.#pending, this.#item, this.#format, undefined)) {\n this.#push(event);\n }\n if (this.#spoken.length > 0) {\n this.#push({\n type: 'output_transcript',\n itemId: this.#item,\n text: this.#spoken,\n final: true,\n });\n }\n if (this.#heard.length > 0) {\n this.#push({ type: 'input_transcript', itemId: this.#item, text: this.#heard, final: true });\n }\n this.#push({\n type: 'response_done',\n itemId: this.#item,\n interrupted,\n usage: this.#tokens,\n });\n this.#tokens = blankUsage();\n this.#spoken = '';\n this.#heard = '';\n this.#turn += 1;\n this.#item = `g${String(this.#turn)}`;\n this.#pending = { chunks: [], cursor: 0 };\n }\n\n #onSetup(value: unknown): void {\n const done = record(value);\n const sessionId = done === undefined ? '' : text(done.sessionId);\n this.#push({ type: 'session_start', sessionId });\n if (!this.#historySent && this.#turns.length > 0) {\n this.#historySent = true;\n this.#wire.send(\n JSON.stringify({ clientContent: { turns: this.#turns, turnComplete: false } })\n );\n }\n this.#ready = true;\n const pending = this.#outgoing.splice(0);\n for (const event of pending) this.#wire.send(JSON.stringify(event));\n }\n\n #onResume(value: unknown): void {\n const update = record(value);\n if (update === undefined) return;\n const handle = text(update.newHandle);\n if (handle.length === 0 || update.resumable !== true) return;\n this.#handle = handle;\n this.#push({ type: 'resumption', handle });\n }\n\n #onGoAway(value: unknown): 'stop' | VoiceWire {\n const gone = record(value);\n const left = gone === undefined ? 0 : timeLeftMs(gone.timeLeft);\n this.#push({ type: 'go_away', timeLeftMs: left });\n const handle = this.#handle;\n if (handle === undefined) {\n this.#push({ type: 'error', error: 'session ended', fatal: true });\n return 'stop';\n }\n const next = this.#open(this.#target);\n this.#wire = next;\n this.#ready = false;\n next.send(JSON.stringify({ setup: this.#setup(handle) }));\n return next;\n }\n\n #onToolCall(value: unknown): void {\n const call = record(value);\n if (call === undefined || !Array.isArray(call.functionCalls)) return;\n for (const entry of call.functionCalls) {\n const fn = record(entry);\n if (fn === undefined) continue;\n this.#push({\n type: 'tool_call',\n blocking: this.model.toolCalls === 'blocking',\n call: {\n type: 'toolCall',\n id: text(fn.id),\n name: text(fn.name),\n arguments: argumentsOf(fn.args),\n },\n });\n }\n }\n\n #onCancel(value: unknown): void {\n const cancellation = record(value);\n if (cancellation === undefined || !Array.isArray(cancellation.ids)) return;\n for (const id of cancellation.ids) {\n if (typeof id !== 'string') continue;\n this.#push({ type: 'tool_call_cancelled', callId: id });\n }\n }\n\n #onServer(value: unknown): void {\n const content = record(value);\n if (content === undefined) return;\n this.#modelTurn(content.modelTurn);\n this.#caption(content.outputTranscription, 'output');\n this.#caption(content.inputTranscription, 'input');\n if (content.interrupted === true) {\n this.#push({ type: 'input_speech_start' });\n this.#finish(true);\n return;\n }\n if (content.turnComplete === true) this.#finish(false);\n }\n\n #modelTurn(value: unknown): void {\n const turn = record(value);\n if (turn === undefined || !Array.isArray(turn.parts)) return;\n for (const part of turn.parts) {\n const entry = record(part);\n if (entry === undefined) continue;\n const inline = record(entry.inlineData);\n if (inline === undefined) continue;\n if (!text(inline.mimeType).startsWith('audio/')) continue;\n this.#audio(text(inline.data));\n }\n }\n\n #caption(value: unknown, kind: 'input' | 'output'): void {\n const caption = record(value);\n if (caption === undefined) return;\n if (kind === 'input') this.#input(text(caption.text), false);\n else this.#transcript(text(caption.text), false);\n }\n\n #onError(value: unknown): void {\n const detail = record(value);\n if (detail === undefined) {\n const message = text(value);\n this.#push({\n type: 'error',\n error: message.length > 0 ? message : 'realtime error',\n fatal: false,\n });\n return;\n }\n const code = text(detail.code);\n const message = text(detail.message);\n let fatal = false;\n if (code === 'MAX_DURATION' || code === 'SESSION_EXPIRED') fatal = true;\n this.#push({\n type: 'error',\n error: message.length > 0 ? message : 'realtime error',\n fatal,\n });\n }\n\n #apply(event: Record<string, unknown>): 'ok' | 'stop' | VoiceWire {\n if (event.usageMetadata !== undefined) this.#tokens = usageFrom(event.usageMetadata);\n if (event.inputTranscription !== undefined) this.#caption(event.inputTranscription, 'input');\n if (event.outputTranscription !== undefined) this.#caption(event.outputTranscription, 'output');\n if (event.setupComplete !== undefined) this.#onSetup(event.setupComplete);\n if (event.sessionResumptionUpdate !== undefined) this.#onResume(event.sessionResumptionUpdate);\n if (event.toolCall !== undefined) this.#onToolCall(event.toolCall);\n if (event.toolCallCancellation !== undefined) this.#onCancel(event.toolCallCancellation);\n if (event.serverContent !== undefined) this.#onServer(event.serverContent);\n if (event.error !== undefined) this.#onError(event.error);\n if (event.goAway !== undefined) return this.#onGoAway(event.goAway);\n return 'ok';\n }\n\n #ingest(raw: string): 'ok' | 'stop' | VoiceWire {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n this.#push({ type: 'error', error: 'invalid realtime event', fatal: false });\n return 'ok';\n }\n const event = record(parsed);\n if (event === undefined) return 'ok';\n return this.#apply(event);\n }\n\n async #read(wire: VoiceWire): Promise<void> {\n let reconnect: VoiceWire | undefined;\n try {\n for await (const raw of wire.incoming) {\n const action = this.#ingest(raw);\n if (action === 'stop') return;\n if (action !== 'ok') {\n reconnect = action;\n return;\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.#push({ type: 'error', error: message, fatal: true });\n } finally {\n if (reconnect !== undefined) {\n wire.close();\n void this.#read(reconnect);\n } else {\n this.#closed = true;\n this.#end();\n }\n }\n }\n}\n\nexport async function connectGeminiSession(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n options: RealtimeOptions | undefined,\n gemini: GeminiOptions\n): Promise<RealtimeSession> {\n if (options?.signal?.aborted === true) {\n throw new VoiceRequestError('aborted', 'session aborted');\n }\n const { secret, ephemeral } = credential(options);\n const socket = realtimeSocket(sessionUrl(model, secret, ephemeral), options, []);\n const target: VoiceWireTarget = {\n url: socket.url,\n protocols: socket.protocols,\n headers: headersFor(options),\n };\n const open =\n gemini.wire !== undefined\n ? gemini.wire\n : (wireTarget: VoiceWireTarget) => openVoiceSocket(wireTarget, gemini.socket);\n const session = new GeminiSession(model, context, gemini.vocabulary, open, target, open(target));\n const handle = options?.resumeHandle;\n session.start(handle);\n const signal = options?.signal;\n if (signal !== undefined) {\n signal.addEventListener('abort', () => void session.close(), { once: true });\n }\n return session;\n}\n\nexport function pcmFormat(sampleRate: number): AudioFormat {\n return { encoding: 'pcm16', sampleRate, channels: 1 };\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { JsonObject, Message, Tool, ToolResultMessage, Usage } from '@earendil-works/pi-ai';\nimport type { AudioChunk, AudioFormat, TextSpan } from '../audio.ts';\nimport { blankUsage } from '../blank-usage.ts';\nimport { decodeAudioBase64, encodeAudioBase64 } from '../convert.ts';\nimport { VoiceRequestError } from '../errors.ts';\nimport type { Delivery, RealtimeModel } from '../models.ts';\nimport {\n openVoiceSocket,\n type VoiceSocketConstructor,\n type VoiceWire,\n type VoiceWireTarget,\n} from '../openai-realtime/socket.ts';\nimport type {\n RealtimeContext,\n RealtimeEvent,\n RealtimeOptions,\n RealtimeSession,\n} from '../realtime.ts';\nimport { planTruncation, RealtimeLedger, realtimeSocket } from '../realtime.ts';\n\nexport type VoiceWireFactory = (target: VoiceWireTarget) => VoiceWire;\n\nexport interface GeminiOptions {\n wire?: VoiceWireFactory;\n socket?: VoiceSocketConstructor;\n vocabulary?: string[];\n}\n\nconst scheduling: Record<Delivery, string> = {\n interrupt: 'INTERRUPT',\n when_idle: 'WHEN_IDLE',\n silent: 'SILENT',\n};\n\ninterface PendingAudio {\n chunks: AudioChunk[];\n cursor: number;\n}\n\ninterface IteratorResultEvent {\n value?: RealtimeEvent;\n done: boolean;\n}\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n\nfunction text(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nfunction credential(options: RealtimeOptions | undefined): { secret: string; ephemeral: boolean } {\n const token = options?.token?.value;\n if (typeof token === 'string' && token.length > 0) return { secret: token, ephemeral: true };\n const apiKey = options?.apiKey;\n if (typeof apiKey === 'string' && apiKey.length > 0) return { secret: apiKey, ephemeral: false };\n throw new VoiceRequestError('error', 'missing realtime credential');\n}\n\nfunction headersFor(options: RealtimeOptions | undefined): Record<string, string> {\n const headers: Record<string, string> = {};\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 sessionUrl(model: RealtimeModel<string>, secret: string, ephemeral: boolean): string {\n const url = new URL(model.baseUrl);\n if (ephemeral) url.searchParams.set('access_token', secret);\n else url.searchParams.set('key', secret);\n return url.toString();\n}\n\nfunction timeLeftMs(value: unknown): number {\n if (typeof value === 'number' && Number.isFinite(value)) return Math.round(value * 1000);\n if (typeof value === 'string') {\n const matched = /^(\\d+(?:\\.\\d+)?)s$/.exec(value);\n if (matched !== null) return Math.round(Number(matched[1]) * 1000);\n const asNumber = Number(value);\n if (Number.isFinite(asNumber)) return Math.round(asNumber * 1000);\n }\n const duration = record(value);\n if (duration !== undefined && typeof duration.seconds === 'number') {\n const nanos = typeof duration.nanos === 'number' ? duration.nanos : 0;\n return Math.round(duration.seconds * 1000 + nanos / 1e6);\n }\n return 0;\n}\n\nfunction count(source: Record<string, unknown>, key: string): number {\n const value = source[key];\n return typeof value === 'number' ? value : 0;\n}\n\nfunction usageFrom(value: unknown): Usage {\n const usage = record(value);\n if (usage === undefined) return blankUsage();\n const input = count(usage, 'promptTokenCount');\n const response = count(usage, 'responseTokenCount');\n const candidates = count(usage, 'candidatesTokenCount');\n const output = response > 0 ? response : candidates;\n return {\n input,\n output,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: input + output,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n };\n}\n\nfunction argumentsOf(value: unknown): JsonObject {\n if (typeof value === 'string') {\n try {\n const parsed: unknown = JSON.parse(value);\n const object = record(parsed);\n if (object === undefined) return {};\n return object as JsonObject;\n } catch {\n return {};\n }\n }\n const object = record(value);\n if (object === undefined) return {};\n return object as JsonObject;\n}\n\nfunction resultText(result: ToolResultMessage): string {\n const parts: string[] = [];\n for (const part of result.content) {\n if (part.type === 'text') parts.push(part.text);\n }\n return parts.join('\\n');\n}\n\nfunction historyTurns(messages: Message[] | undefined): JsonObject[] {\n if (messages === undefined) return [];\n const turns: JsonObject[] = [];\n for (const message of messages) {\n if (message.role !== 'user' && message.role !== 'assistant') continue;\n const parts: JsonObject[] = [];\n for (const part of message.content) {\n if (typeof part === 'string') {\n parts.push({ text: part });\n continue;\n }\n if (part.type === 'text') parts.push({ text: part.text });\n }\n if (parts.length === 0) continue;\n turns.push({ role: message.role === 'assistant' ? 'model' : 'user', parts });\n }\n return turns;\n}\n\nfunction declaration(tool: Tool, model: RealtimeModel<string>): JsonObject {\n const fn: JsonObject = {\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters as unknown as JsonObject,\n };\n if (model.id === 'gemini-live-2.5-flash-native-audio') fn.behavior = 'NON_BLOCKING';\n if (model.toolCalls === 'blocking') fn.behavior = 'BLOCKING';\n return fn;\n}\n\nfunction transcription(model: RealtimeModel<string>, vocabulary: string[] | undefined): JsonObject {\n if (model.id !== 'gemini-3.8-live') return {};\n if (vocabulary === undefined) return {};\n return { customVocabulary: vocabulary };\n}\n\nfunction setupBody(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n handle: string | undefined,\n vocabulary: string[] | undefined,\n turns: JsonObject[]\n): JsonObject {\n const setup: JsonObject = {\n model: `models/${model.id}`,\n generationConfig: { responseModalities: ['AUDIO'] },\n systemInstruction: { parts: [{ text: context.instructions }] },\n speechConfig: {\n voiceConfig: { prebuiltVoiceConfig: { voiceName: context.voice } },\n },\n inputAudioTranscription: transcription(model, vocabulary),\n outputAudioTranscription: {},\n contextWindowCompression: {\n triggerTokens: 100_000,\n slidingWindow: { targetTokens: 80_000 },\n },\n sessionResumption: handle === undefined ? {} : { handle },\n };\n if (context.tools.length > 0) {\n setup.tools = [{ functionDeclarations: context.tools.map((tool) => declaration(tool, model)) }];\n }\n if (turns.length > 0) setup.historyConfig = { initialHistoryInClientContent: true };\n return setup;\n}\n\nfunction spanFor(itemId: string, start: number, end: number): TextSpan {\n return { itemId, start, end };\n}\n\nfunction takeAudio(\n pending: PendingAudio,\n itemId: string,\n format: AudioFormat,\n end: number | undefined\n): RealtimeEvent[] {\n const chunks = pending.chunks;\n pending.chunks = [];\n if (chunks.length === 0) return [];\n const start = pending.cursor;\n if (end !== undefined) pending.cursor = end;\n const width = end === undefined ? 0 : end - start;\n const total = chunks.reduce((sum, chunk) => sum + chunk.data.length, 0);\n let used = 0;\n let cursor = start;\n return chunks.map((chunk, index) => {\n const event: RealtimeEvent = {\n type: 'output_audio',\n itemId,\n chunk: { data: chunk.data, format, timestamp: chunk.timestamp },\n };\n if (end === undefined) return event;\n const last = index === chunks.length - 1;\n const next = last ? end : start + Math.round(((used + chunk.data.length) / total) * width);\n used += chunk.data.length;\n event.span = spanFor(itemId, cursor, next);\n cursor = next;\n return event;\n });\n}\n\nclass GeminiSession implements RealtimeSession {\n readonly model: RealtimeModel<string>;\n readonly #context: RealtimeContext;\n readonly #vocabulary: string[] | undefined;\n readonly #turns: JsonObject[];\n readonly #open: VoiceWireFactory;\n readonly #target: VoiceWireTarget;\n readonly #ledger = new RealtimeLedger();\n readonly #queue: RealtimeEvent[] = [];\n readonly #waiters: Array<(result: IteratorResultEvent) => void> = [];\n readonly #outgoing: JsonObject[] = [];\n readonly #format: AudioFormat;\n #wire: VoiceWire;\n #pending: PendingAudio = { chunks: [], cursor: 0 };\n #item = 'g0';\n #turn = 0;\n #heard = '';\n #spoken = '';\n #handle: string | undefined;\n #tokens: Usage = blankUsage();\n #ready = false;\n #historySent = false;\n #closed = false;\n #ended = false;\n #meters: Usage | undefined;\n readonly #toolGroups = new Map<string, { pending: Set<string>; responses: JsonObject[] }>();\n readonly #toolGroupOf = new Map<string, string>();\n\n constructor(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n vocabulary: string[] | undefined,\n open: VoiceWireFactory,\n target: VoiceWireTarget,\n wire: VoiceWire\n ) {\n this.model = model;\n this.#context = context;\n this.#vocabulary = vocabulary;\n this.#turns = historyTurns(context.history);\n this.#open = open;\n this.#target = target;\n this.#wire = wire;\n this.#format = model.outputFormat;\n }\n\n start(handle: string | undefined): void {\n if (handle !== undefined && handle.length > 0) this.#handle = handle;\n this.#wire.send(JSON.stringify({ setup: this.#setup(handle) }));\n void this.#read(this.#wire);\n }\n\n #setup(handle: string | undefined): JsonObject {\n return setupBody(this.model, this.#context, handle, this.#vocabulary, this.#turns);\n }\n\n #assertOpen(): void {\n if (this.#closed) throw new VoiceRequestError('error', 'session is closed');\n }\n\n #send(event: JsonObject): void {\n if (!this.#ready) {\n this.#outgoing.push(event);\n return;\n }\n this.#wire.send(JSON.stringify(event));\n }\n\n #push(event: RealtimeEvent): void {\n if (this.#closed) return;\n this.#ledger.observe(event);\n const waiter = this.#waiters.shift();\n if (waiter !== undefined) waiter({ value: event, done: false });\n else this.#queue.push(event);\n }\n\n #end(): void {\n if (this.#ended) return;\n this.#ended = true;\n while (this.#waiters.length > 0) this.#waiters.shift()?.({ done: true });\n }\n\n appendAudio(chunk: AudioChunk): void {\n this.#assertOpen();\n this.#ledger.addInput(chunk);\n this.#send({\n realtimeInput: {\n audio: {\n data: encodeAudioBase64(chunk.data),\n mimeType: `audio/pcm;rate=${String(chunk.format.sampleRate)}`,\n },\n },\n });\n }\n\n appendVideoFrame(jpeg: Uint8Array): void {\n this.#assertOpen();\n if (this.model.video === undefined) {\n throw new VoiceRequestError('error', 'video is unsupported');\n }\n this.#send({\n realtimeInput: { video: { data: encodeAudioBase64(jpeg), mimeType: 'image/jpeg' } },\n });\n }\n\n sendText(value: string): void {\n this.#assertOpen();\n this.#ledger.addText();\n this.#send(this.#client(value, true));\n }\n\n inject(value: string, delivery: Delivery): void {\n this.#assertOpen();\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n this.#ledger.addText();\n if (delivery === 'silent') this.#send(this.#client(value, false));\n else if (delivery === 'when_idle') this.#send({ realtimeInput: { text: value } });\n else this.#send(this.#client(value, true));\n });\n }\n\n respond(callId: string, result: ToolResultMessage, delivery?: Delivery): void {\n this.#assertOpen();\n const send = () => {\n const mode = delivery === undefined ? 'interrupt' : delivery;\n this.#queueTool(callId, {\n id: callId,\n name: result.toolName,\n scheduling: scheduling[mode],\n response: {\n status: result.isError ? 'error' : 'ok',\n retryable: false,\n message: resultText(result),\n },\n });\n };\n if (delivery === undefined) {\n send();\n return;\n }\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n send();\n });\n }\n\n interrupt(heard?: { itemId: string; heardMs: number }): void {\n this.#assertOpen();\n planTruncation(this.model.truncation, heard);\n this.#ledger.settle();\n }\n\n update(patch: Partial<Pick<RealtimeContext, 'instructions' | 'tools' | 'voice'>>): void {\n this.#assertOpen();\n if (patch.tools !== undefined && !this.model.liveToolUpdate) {\n throw new VoiceRequestError('error', 'live tool update is unsupported');\n }\n }\n\n async close(): Promise<{ usage: Usage }> {\n if (this.#meters !== undefined) return { usage: this.#meters };\n this.#closed = true;\n this.#ledger.settle();\n this.#wire.close();\n this.#end();\n this.#meters = this.#ledger.usage(this.model.audioCost);\n return { usage: this.#meters };\n }\n\n #client(value: string, turnComplete: boolean): JsonObject {\n return {\n clientContent: {\n turns: [{ role: 'user', parts: [{ text: value }] }],\n turnComplete,\n },\n };\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<RealtimeEvent> {\n while (true) {\n const queued = this.#queue.shift();\n if (queued !== undefined) {\n yield queued;\n continue;\n }\n if (this.#closed) return;\n const next = await new Promise<IteratorResultEvent>((resolve) => {\n this.#waiters.push(resolve);\n });\n if (next.done === true || next.value === undefined) return;\n yield next.value;\n }\n }\n\n #audio(encoded: string): void {\n if (encoded.length === 0) return;\n this.#pending.chunks.push({\n data: decodeAudioBase64(encoded),\n format: this.#format,\n timestamp: 0,\n });\n }\n\n #transcript(delta: string, final: boolean): void {\n const end = this.#pending.cursor + delta.length;\n const tagged = delta.length === 0 ? undefined : end;\n for (const event of takeAudio(this.#pending, this.#item, this.#format, tagged)) {\n this.#push(event);\n }\n this.#spoken += delta;\n this.#push({ type: 'output_transcript', itemId: this.#item, text: delta, final });\n }\n\n #input(transcript: string, final: boolean): void {\n if (transcript.length > 0) this.#heard = transcript;\n this.#push({ type: 'input_transcript', itemId: this.#item, text: this.#heard, final });\n }\n\n #finish(interrupted: boolean): void {\n for (const event of takeAudio(this.#pending, this.#item, this.#format, undefined)) {\n this.#push(event);\n }\n if (this.#spoken.length > 0) {\n this.#push({\n type: 'output_transcript',\n itemId: this.#item,\n text: this.#spoken,\n final: true,\n });\n }\n if (this.#heard.length > 0) {\n this.#push({ type: 'input_transcript', itemId: this.#item, text: this.#heard, final: true });\n }\n this.#push({\n type: 'response_done',\n itemId: this.#item,\n interrupted,\n usage: this.#tokens,\n });\n this.#tokens = blankUsage();\n this.#spoken = '';\n this.#heard = '';\n this.#turn += 1;\n this.#item = `g${String(this.#turn)}`;\n this.#pending = { chunks: [], cursor: 0 };\n }\n\n #onSetup(value: unknown): void {\n const done = record(value);\n const sessionId = done === undefined ? '' : text(done.sessionId);\n this.#push({ type: 'session_start', sessionId });\n if (!this.#historySent && this.#turns.length > 0) {\n this.#historySent = true;\n this.#wire.send(\n JSON.stringify({ clientContent: { turns: this.#turns, turnComplete: false } })\n );\n }\n this.#ready = true;\n const pending = this.#outgoing.splice(0);\n for (const event of pending) this.#wire.send(JSON.stringify(event));\n }\n\n #onResume(value: unknown): void {\n const update = record(value);\n if (update === undefined) return;\n const handle = text(update.newHandle);\n if (handle.length === 0 || update.resumable !== true) return;\n this.#handle = handle;\n this.#push({ type: 'resumption', handle });\n }\n\n #onGoAway(value: unknown): 'stop' | VoiceWire {\n const gone = record(value);\n const left = gone === undefined ? 0 : timeLeftMs(gone.timeLeft);\n this.#push({ type: 'go_away', timeLeftMs: left });\n const handle = this.#handle;\n if (handle === undefined) {\n this.#push({ type: 'error', error: 'session ended', fatal: true });\n return 'stop';\n }\n const next = this.#open(this.#target);\n this.#wire = next;\n this.#ready = false;\n next.send(JSON.stringify({ setup: this.#setup(handle) }));\n return next;\n }\n\n #queueTool(callId: string, response: JsonObject): void {\n const groupId = this.#toolGroupOf.get(callId) ?? '';\n const group = groupId.length === 0 ? undefined : this.#toolGroups.get(groupId);\n if (group === undefined) {\n this.#send({ toolResponse: { functionResponses: [response] } });\n return;\n }\n group.responses.push(response);\n group.pending.delete(callId);\n this.#toolGroupOf.delete(callId);\n if (group.pending.size > 0) return;\n this.#toolGroups.delete(groupId);\n this.#send({ toolResponse: { functionResponses: group.responses } });\n }\n\n #onToolCall(value: unknown): void {\n const call = record(value);\n if (call === undefined || !Array.isArray(call.functionCalls)) return;\n const ids: string[] = [];\n for (const entry of call.functionCalls) {\n const fn = record(entry);\n if (fn === undefined) continue;\n const id = text(fn.id);\n if (id.length > 0) ids.push(id);\n this.#push({\n type: 'tool_call',\n blocking: this.model.toolCalls === 'blocking',\n call: {\n type: 'toolCall',\n id,\n name: text(fn.name),\n arguments: argumentsOf(fn.args),\n },\n });\n }\n const groupId = ids.at(0);\n if (groupId === undefined) return;\n this.#toolGroups.set(groupId, { pending: new Set(ids), responses: [] });\n for (const id of ids) this.#toolGroupOf.set(id, groupId);\n }\n\n #onCancel(value: unknown): void {\n const cancellation = record(value);\n if (cancellation === undefined || !Array.isArray(cancellation.ids)) return;\n for (const id of cancellation.ids) {\n if (typeof id !== 'string') continue;\n this.#push({ type: 'tool_call_cancelled', callId: id });\n }\n }\n\n #onServer(value: unknown): void {\n const content = record(value);\n if (content === undefined) return;\n this.#modelTurn(content.modelTurn);\n this.#caption(content.outputTranscription, 'output');\n this.#caption(content.inputTranscription, 'input');\n if (content.interrupted === true) {\n this.#push({ type: 'input_speech_start' });\n this.#finish(true);\n return;\n }\n if (content.turnComplete === true) this.#finish(false);\n }\n\n #modelTurn(value: unknown): void {\n const turn = record(value);\n if (turn === undefined || !Array.isArray(turn.parts)) return;\n for (const part of turn.parts) {\n const entry = record(part);\n if (entry === undefined) continue;\n const inline = record(entry.inlineData);\n if (inline === undefined) continue;\n if (!text(inline.mimeType).startsWith('audio/')) continue;\n this.#audio(text(inline.data));\n }\n }\n\n #caption(value: unknown, kind: 'input' | 'output'): void {\n const caption = record(value);\n if (caption === undefined) return;\n if (kind === 'input') this.#input(text(caption.text), false);\n else this.#transcript(text(caption.text), false);\n }\n\n #onError(value: unknown): void {\n const detail = record(value);\n if (detail === undefined) {\n const message = text(value);\n this.#push({\n type: 'error',\n error: message.length > 0 ? message : 'realtime error',\n fatal: false,\n });\n return;\n }\n const code = text(detail.code);\n const message = text(detail.message);\n let fatal = false;\n if (code === 'MAX_DURATION' || code === 'SESSION_EXPIRED') fatal = true;\n this.#push({\n type: 'error',\n error: message.length > 0 ? message : 'realtime error',\n fatal,\n });\n }\n\n #apply(event: Record<string, unknown>): 'ok' | 'stop' | VoiceWire {\n if (event.usageMetadata !== undefined) this.#tokens = usageFrom(event.usageMetadata);\n if (event.inputTranscription !== undefined) this.#caption(event.inputTranscription, 'input');\n if (event.outputTranscription !== undefined) this.#caption(event.outputTranscription, 'output');\n if (event.setupComplete !== undefined) this.#onSetup(event.setupComplete);\n if (event.sessionResumptionUpdate !== undefined) this.#onResume(event.sessionResumptionUpdate);\n if (event.toolCall !== undefined) this.#onToolCall(event.toolCall);\n if (event.toolCallCancellation !== undefined) this.#onCancel(event.toolCallCancellation);\n if (event.serverContent !== undefined) this.#onServer(event.serverContent);\n if (event.error !== undefined) this.#onError(event.error);\n if (event.goAway !== undefined) return this.#onGoAway(event.goAway);\n return 'ok';\n }\n\n #ingest(raw: string): 'ok' | 'stop' | VoiceWire {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n this.#push({ type: 'error', error: 'invalid realtime event', fatal: false });\n return 'ok';\n }\n const event = record(parsed);\n if (event === undefined) return 'ok';\n return this.#apply(event);\n }\n\n async #read(wire: VoiceWire): Promise<void> {\n let reconnect: VoiceWire | undefined;\n try {\n for await (const raw of wire.incoming) {\n const action = this.#ingest(raw);\n if (action === 'stop') return;\n if (action !== 'ok') {\n reconnect = action;\n return;\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.#push({ type: 'error', error: message, fatal: true });\n } finally {\n if (reconnect !== undefined) {\n wire.close();\n void this.#read(reconnect);\n } else {\n this.#closed = true;\n this.#end();\n }\n }\n }\n}\n\nexport async function connectGeminiSession(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n options: RealtimeOptions | undefined,\n gemini: GeminiOptions\n): Promise<RealtimeSession> {\n if (options?.signal?.aborted === true) {\n throw new VoiceRequestError('aborted', 'session aborted');\n }\n const { secret, ephemeral } = credential(options);\n const socket = realtimeSocket(sessionUrl(model, secret, ephemeral), options, []);\n const target: VoiceWireTarget = {\n url: socket.url,\n protocols: socket.protocols,\n headers: headersFor(options),\n };\n const open =\n gemini.wire !== undefined\n ? gemini.wire\n : (wireTarget: VoiceWireTarget) => openVoiceSocket(wireTarget, gemini.socket);\n const session = new GeminiSession(model, context, gemini.vocabulary, open, target, open(target));\n const handle = options?.resumeHandle;\n session.start(handle);\n const signal = options?.signal;\n if (signal !== undefined) {\n signal.addEventListener('abort', () => void session.close(), { once: true });\n }\n return session;\n}\n\nexport function pcmFormat(sampleRate: number): AudioFormat {\n return { encoding: 'pcm16', sampleRate, channels: 1 };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["object", "message"]
|
|
7
7
|
}
|
|
@@ -51,6 +51,7 @@ function openaiSession(context, model) {
|
|
|
51
51
|
audio: {
|
|
52
52
|
input: {
|
|
53
53
|
format: { type: "audio/pcm", rate: model.inputFormat.sampleRate },
|
|
54
|
+
transcription: { model: "gpt-4o-mini-transcribe" },
|
|
54
55
|
turn_detection: { type: "server_vad" }
|
|
55
56
|
},
|
|
56
57
|
output: {
|
|
@@ -68,7 +69,10 @@ function xaiSession(context, model, replace) {
|
|
|
68
69
|
tools: context.tools.map(functionTool),
|
|
69
70
|
resumption: { enabled: true },
|
|
70
71
|
audio: {
|
|
71
|
-
input: {
|
|
72
|
+
input: {
|
|
73
|
+
format: { type: "audio/pcm", rate: model.inputFormat.sampleRate },
|
|
74
|
+
transcription: { model: "grok-transcribe" }
|
|
75
|
+
},
|
|
72
76
|
output: { format: { type: "audio/pcm", rate: model.outputFormat.sampleRate } }
|
|
73
77
|
}
|
|
74
78
|
};
|
|
@@ -179,6 +183,9 @@ class VendorSession {
|
|
|
179
183
|
#closed = false;
|
|
180
184
|
#usage;
|
|
181
185
|
#lastItem = "";
|
|
186
|
+
#busy = false;
|
|
187
|
+
#heldCreate = false;
|
|
188
|
+
#calls = /* @__PURE__ */ new Set();
|
|
182
189
|
constructor(model, context, dialect, replace, wire) {
|
|
183
190
|
this.model = model;
|
|
184
191
|
this.#context = context;
|
|
@@ -202,7 +209,7 @@ class VendorSession {
|
|
|
202
209
|
this.#assertOpen();
|
|
203
210
|
this.#ledger.addText();
|
|
204
211
|
this.#send(userItem(textValue));
|
|
205
|
-
this.#
|
|
212
|
+
this.#armCreate(false);
|
|
206
213
|
}
|
|
207
214
|
inject(textValue, delivery) {
|
|
208
215
|
this.#assertOpen();
|
|
@@ -216,7 +223,9 @@ class VendorSession {
|
|
|
216
223
|
this.#assertOpen();
|
|
217
224
|
const send = () => {
|
|
218
225
|
this.#send(toolOutput(callId, result));
|
|
219
|
-
|
|
226
|
+
this.#calls.delete(callId);
|
|
227
|
+
if (delivery !== "silent") this.#armCreate(true);
|
|
228
|
+
else if (this.#heldCreate && this.#calls.size === 0) this.#armCreate(false);
|
|
220
229
|
};
|
|
221
230
|
if (delivery === void 0) {
|
|
222
231
|
send();
|
|
@@ -294,7 +303,7 @@ class VendorSession {
|
|
|
294
303
|
return;
|
|
295
304
|
}
|
|
296
305
|
this.#send(userItem(textValue));
|
|
297
|
-
if (delivery !== "silent") this.#
|
|
306
|
+
if (delivery !== "silent") this.#armCreate(false);
|
|
298
307
|
}
|
|
299
308
|
#send(event) {
|
|
300
309
|
this.#wire.send(JSON.stringify(event));
|
|
@@ -329,12 +338,26 @@ class VendorSession {
|
|
|
329
338
|
timestamp: 0
|
|
330
339
|
});
|
|
331
340
|
}
|
|
332
|
-
#
|
|
341
|
+
#armCreate(waitForCalls) {
|
|
342
|
+
if (this.#busy || waitForCalls && this.#calls.size > 0) {
|
|
343
|
+
this.#heldCreate = true;
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
this.#heldCreate = false;
|
|
347
|
+
this.#busy = true;
|
|
348
|
+
this.#send(responseCreate());
|
|
349
|
+
}
|
|
350
|
+
#releaseResponse() {
|
|
351
|
+
this.#busy = false;
|
|
352
|
+
if (this.#heldCreate && this.#calls.size === 0) this.#armCreate(false);
|
|
353
|
+
}
|
|
354
|
+
#transcript(itemId, value, final) {
|
|
333
355
|
const pending = this.#pendingFor(itemId);
|
|
334
|
-
const
|
|
335
|
-
const
|
|
356
|
+
const addition = final ? value.slice(pending.cursor) : value;
|
|
357
|
+
const end = pending.cursor + addition.length;
|
|
358
|
+
const tagged = addition.length === 0 ? void 0 : end;
|
|
336
359
|
for (const event of takeAudio(pending, itemId, this.#format, tagged)) this.#push(event);
|
|
337
|
-
this.#push({ type: "output_transcript", itemId, text:
|
|
360
|
+
this.#push({ type: "output_transcript", itemId, text: value, final });
|
|
338
361
|
}
|
|
339
362
|
#input(itemId, transcript, mode, final) {
|
|
340
363
|
const previous = this.#inputText.get(itemId) ?? "";
|
|
@@ -348,18 +371,21 @@ class VendorSession {
|
|
|
348
371
|
return text(object[key]);
|
|
349
372
|
}
|
|
350
373
|
#functionCall(event) {
|
|
374
|
+
const id = text(event.call_id);
|
|
375
|
+
if (id.length > 0) this.#calls.add(id);
|
|
351
376
|
this.#push({
|
|
352
377
|
type: "tool_call",
|
|
353
378
|
blocking: this.model.toolCalls === "blocking",
|
|
354
379
|
call: {
|
|
355
380
|
type: "toolCall",
|
|
356
|
-
id
|
|
381
|
+
id,
|
|
357
382
|
name: text(event.name),
|
|
358
383
|
arguments: argumentsOf(event.arguments)
|
|
359
384
|
}
|
|
360
385
|
});
|
|
361
386
|
}
|
|
362
387
|
#responseDone(event) {
|
|
388
|
+
this.#releaseResponse();
|
|
363
389
|
const response = record(event.response);
|
|
364
390
|
const output = response === void 0 ? void 0 : response.output;
|
|
365
391
|
const first = Array.isArray(output) ? record(output[0]) : void 0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/openai-realtime/session.ts"],
|
|
4
|
-
"sourcesContent": ["import type { JsonObject, Tool, ToolResultMessage, Usage } from '@earendil-works/pi-ai';\nimport type { AudioChunk, AudioFormat, TextSpan } from '../audio.ts';\nimport { blankUsage } from '../blank-usage.ts';\nimport { decodeAudioBase64, encodeAudioBase64 } from '../convert.ts';\nimport { VoiceRequestError } from '../errors.ts';\nimport type { Delivery, RealtimeModel } from '../models.ts';\nimport type {\n RealtimeContext,\n RealtimeEvent,\n RealtimeOptions,\n RealtimeSession,\n} from '../realtime.ts';\nimport { planTruncation, RealtimeLedger, realtimeSocket } from '../realtime.ts';\nimport {\n openVoiceSocket,\n type VoiceSocketConstructor,\n type VoiceWire,\n type VoiceWireTarget,\n} from './socket.ts';\n\nexport type VoiceWireFactory = (target: VoiceWireTarget) => VoiceWire;\n\nexport interface AdapterOptions {\n wire?: VoiceWireFactory;\n socket?: VoiceSocketConstructor;\n replace?: Record<string, string>;\n}\n\nexport interface VendorSessionOptions extends AdapterOptions {\n dialect: 'openai' | 'xai';\n}\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n\nfunction text(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nfunction credential(options: RealtimeOptions | undefined): string {\n const token = options?.token?.value;\n if (typeof token === 'string' && token.length > 0) return token;\n const apiKey = options?.apiKey;\n if (typeof apiKey === 'string' && apiKey.length > 0) return apiKey;\n throw new VoiceRequestError('error', 'missing realtime credential');\n}\n\nfunction headersFor(\n options: RealtimeOptions | undefined,\n secret: string,\n bearer: boolean\n): Record<string, string> {\n const headers: Record<string, string> = {};\n if (bearer) headers.Authorization = `Bearer ${secret}`;\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 sessionUrl(model: RealtimeModel<string>, resumeHandle: string | undefined): string {\n const params = new URLSearchParams({ model: model.id });\n if (resumeHandle !== undefined) params.set('conversation_id', resumeHandle);\n return `${model.baseUrl}?${params.toString()}`;\n}\n\nfunction functionTool(tool: Tool): JsonObject {\n return {\n type: 'function',\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters as unknown as JsonObject,\n };\n}\n\nfunction openaiSession(context: RealtimeContext, model: RealtimeModel<string>): JsonObject {\n return {\n type: 'realtime',\n instructions: context.instructions,\n tools: context.tools.map(functionTool),\n audio: {\n input: {\n format: { type: 'audio/pcm', rate: model.inputFormat.sampleRate },\n turn_detection: { type: 'server_vad' },\n },\n output: {\n format: { type: 'audio/pcm', rate: model.outputFormat.sampleRate },\n voice: context.voice,\n },\n },\n };\n}\n\nfunction xaiSession(\n context: RealtimeContext,\n model: RealtimeModel<string>,\n replace: Record<string, string> | undefined\n): JsonObject {\n const session: JsonObject = {\n voice: context.voice,\n instructions: context.instructions,\n turn_detection: { type: 'server_vad' },\n tools: context.tools.map(functionTool),\n resumption: { enabled: true },\n audio: {\n input: { format: { type: 'audio/pcm', rate: model.inputFormat.sampleRate } },\n output: { format: { type: 'audio/pcm', rate: model.outputFormat.sampleRate } },\n },\n };\n if (replace !== undefined) session.replace = replace;\n return session;\n}\n\nfunction userItem(value: string): JsonObject {\n return {\n type: 'conversation.item.create',\n item: {\n type: 'message',\n role: 'user',\n content: [{ type: 'input_text', text: value }],\n },\n };\n}\n\nfunction responseCreate(): JsonObject {\n return { type: 'response.create' };\n}\n\nfunction resultText(result: ToolResultMessage): string {\n const parts: string[] = [];\n for (const part of result.content) {\n if (part.type === 'text') parts.push(part.text);\n }\n return parts.join('\\n');\n}\n\nfunction toolOutput(callId: string, result: ToolResultMessage): JsonObject {\n return {\n type: 'conversation.item.create',\n item: { type: 'function_call_output', call_id: callId, output: resultText(result) },\n };\n}\n\nfunction forceMessage(value: string): JsonObject {\n return {\n type: 'conversation.item.create',\n item: {\n type: 'force_message',\n role: 'assistant',\n content: [{ type: 'output_text', text: value }],\n },\n };\n}\n\ninterface PendingAudio {\n chunks: AudioChunk[];\n cursor: number;\n}\n\nfunction spanFor(itemId: string, start: number, end: number): TextSpan {\n return { itemId, start, end };\n}\n\nfunction takeAudio(\n pending: PendingAudio,\n itemId: string,\n format: AudioFormat,\n end: number | undefined\n): RealtimeEvent[] {\n const chunks = pending.chunks;\n pending.chunks = [];\n if (chunks.length === 0) return [];\n const start = pending.cursor;\n if (end !== undefined) pending.cursor = end;\n const width = end === undefined ? 0 : end - start;\n const total = chunks.reduce((sum, chunk) => sum + chunk.data.length, 0);\n let used = 0;\n let cursor = start;\n return chunks.map((chunk, index) => {\n const event: RealtimeEvent = {\n type: 'output_audio',\n itemId,\n chunk: { data: chunk.data, format, timestamp: chunk.timestamp },\n };\n if (end === undefined || total === 0) return event;\n const last = index === chunks.length - 1;\n const next = last ? end : start + Math.round(((used + chunk.data.length) / total) * width);\n used += chunk.data.length;\n event.span = spanFor(itemId, cursor, next);\n cursor = next;\n return event;\n });\n}\n\nfunction argumentsOf(value: unknown): JsonObject {\n if (typeof value !== 'string') return {};\n try {\n const parsed: unknown = JSON.parse(value);\n const object = record(parsed);\n if (object === undefined) return {};\n return object as JsonObject;\n } catch {\n return {};\n }\n}\n\nfunction responseUsage(value: unknown): Usage {\n const usage = record(value);\n if (usage === undefined) return blankUsage();\n const input = typeof usage.input_tokens === 'number' ? usage.input_tokens : 0;\n const output = typeof usage.output_tokens === 'number' ? usage.output_tokens : 0;\n return {\n input,\n output,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: input + output,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n };\n}\n\nclass VendorSession implements RealtimeSession {\n readonly model: RealtimeModel<string>;\n readonly #dialect: 'openai' | 'xai';\n readonly #context: RealtimeContext;\n readonly #replace: Record<string, string> | undefined;\n readonly #wire: VoiceWire;\n readonly #ledger = new RealtimeLedger();\n readonly #format: AudioFormat;\n readonly #pending = new Map<string, PendingAudio>();\n readonly #inputText = new Map<string, string>();\n readonly #queue: RealtimeEvent[] = [];\n readonly #waiters: Array<(result: IteratorResult<RealtimeEvent>) => void> = [];\n #closed = false;\n #usage: Usage | undefined;\n #lastItem = '';\n\n constructor(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n dialect: 'openai' | 'xai',\n replace: Record<string, string> | undefined,\n wire: VoiceWire\n ) {\n this.model = model;\n this.#context = context;\n this.#dialect = dialect;\n this.#replace = replace;\n this.#wire = wire;\n this.#format = model.outputFormat;\n }\n\n start(options: RealtimeOptions | undefined): void {\n this.#send(this.#sessionUpdate(this.#context));\n const signal = options?.signal;\n if (signal !== undefined) signal.addEventListener('abort', () => void this.close());\n void this.#read();\n }\n\n appendAudio(chunk: AudioChunk): void {\n this.#assertOpen();\n this.#ledger.addInput(chunk);\n this.#send({ type: 'input_audio_buffer.append', audio: encodeAudioBase64(chunk.data) });\n }\n\n sendText(textValue: string): void {\n this.#assertOpen();\n this.#ledger.addText();\n this.#send(userItem(textValue));\n this.#send(responseCreate());\n }\n\n inject(textValue: string, delivery: Delivery): void {\n this.#assertOpen();\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n this.#ledger.addText();\n this.#sendInject(textValue, delivery);\n });\n }\n\n respond(callId: string, result: ToolResultMessage, delivery?: Delivery): void {\n this.#assertOpen();\n const send = (): void => {\n this.#send(toolOutput(callId, result));\n if (delivery !== 'silent') this.#send(responseCreate());\n };\n if (delivery === undefined) {\n send();\n return;\n }\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n send();\n });\n }\n\n interrupt(heard?: { itemId: string; heardMs: number }): void {\n this.#assertOpen();\n this.#send({ type: 'response.cancel' });\n const plan = planTruncation(this.model.truncation, heard);\n if (plan.item !== undefined) {\n this.#send({\n type: 'conversation.item.truncate',\n item_id: plan.item.itemId,\n content_index: 0,\n audio_end_ms: plan.item.audioEndMs,\n });\n }\n this.#ledger.settle();\n }\n\n update(patch: Partial<Pick<RealtimeContext, 'instructions' | 'tools' | 'voice'>>): void {\n this.#assertOpen();\n if (patch.tools !== undefined && !this.model.liveToolUpdate) {\n throw new VoiceRequestError('error', 'live tool update is unsupported');\n }\n let instructions = this.#context.instructions;\n if (patch.instructions !== undefined) instructions = patch.instructions;\n let tools = this.#context.tools;\n if (patch.tools !== undefined) tools = patch.tools;\n let voice = this.#context.voice;\n if (patch.voice !== undefined) voice = patch.voice;\n const context: RealtimeContext = { instructions, tools, voice };\n if (patch.instructions !== undefined) this.#context.instructions = context.instructions;\n if (patch.tools !== undefined) this.#context.tools = context.tools;\n if (patch.voice !== undefined) this.#context.voice = context.voice;\n this.#send(this.#sessionUpdate(context));\n }\n\n async close(): Promise<{ usage: Usage }> {\n if (this.#usage !== undefined) return { usage: this.#usage };\n this.#ledger.settle();\n this.#closed = true;\n this.#wire.close();\n this.#end();\n this.#usage = this.#ledger.usage(this.model.audioCost);\n return { usage: this.#usage };\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<RealtimeEvent> {\n while (true) {\n const queued = this.#queue.shift();\n if (queued !== undefined) {\n yield queued;\n continue;\n }\n if (this.#closed) return;\n const next = await new Promise<IteratorResult<RealtimeEvent>>((resolve) => {\n this.#waiters.push(resolve);\n });\n if (next.done === true || next.value === undefined) return;\n yield next.value;\n }\n }\n\n #sessionUpdate(context: RealtimeContext): JsonObject {\n if (this.#dialect === 'openai') {\n return { type: 'session.update', session: openaiSession(context, this.model) };\n }\n return { type: 'session.update', session: xaiSession(context, this.model, this.#replace) };\n }\n\n #sendInject(textValue: string, delivery: Delivery): void {\n if (this.#dialect === 'xai' && delivery !== 'silent') {\n this.#send(forceMessage(textValue));\n return;\n }\n this.#send(userItem(textValue));\n if (delivery !== 'silent') this.#send(responseCreate());\n }\n\n #send(event: JsonObject): void {\n this.#wire.send(JSON.stringify(event));\n }\n\n #assertOpen(): void {\n if (this.#closed) throw new VoiceRequestError('error', 'session is closed');\n }\n\n #push(event: RealtimeEvent): void {\n if (this.#closed) return;\n this.#ledger.observe(event);\n const waiter = this.#waiters.shift();\n if (waiter !== undefined) waiter({ value: event, done: false });\n else this.#queue.push(event);\n }\n\n #end(): void {\n for (const waiter of this.#waiters) waiter({ value: undefined, done: true });\n this.#waiters.length = 0;\n }\n\n #pendingFor(itemId: string): PendingAudio {\n const existing = this.#pending.get(itemId);\n if (existing !== undefined) return existing;\n const created = { chunks: [], cursor: 0 };\n this.#pending.set(itemId, created);\n return created;\n }\n\n #audio(itemId: string, encoded: string): void {\n this.#lastItem = itemId;\n const pending = this.#pendingFor(itemId);\n pending.chunks.push({\n data: decodeAudioBase64(encoded),\n format: this.#format,\n timestamp: 0,\n });\n }\n\n #transcript(itemId: string, delta: string, final: boolean): void {\n const pending = this.#pendingFor(itemId);\n const end = pending.cursor + delta.length;\n const tagged = delta.length === 0 ? undefined : end;\n for (const event of takeAudio(pending, itemId, this.#format, tagged)) this.#push(event);\n this.#push({ type: 'output_transcript', itemId, text: delta, final });\n }\n\n #input(itemId: string, transcript: string, mode: 'append' | 'replace', final: boolean): void {\n const previous = this.#inputText.get(itemId) ?? '';\n const next = mode === 'append' ? `${previous}${transcript}` : transcript;\n this.#inputText.set(itemId, next);\n this.#push({ type: 'input_transcript', itemId, text: next, final });\n }\n\n #field(value: unknown, key: string): string {\n const object = record(value);\n if (object === undefined) return '';\n return text(object[key]);\n }\n\n #functionCall(event: Record<string, unknown>): void {\n this.#push({\n type: 'tool_call',\n blocking: this.model.toolCalls === 'blocking',\n call: {\n type: 'toolCall',\n id: text(event.call_id),\n name: text(event.name),\n arguments: argumentsOf(event.arguments),\n },\n });\n }\n\n #responseDone(event: Record<string, unknown>): void {\n const response = record(event.response);\n const output = response === undefined ? undefined : response.output;\n const first = Array.isArray(output) ? record(output[0]) : undefined;\n let itemId = this.#lastItem;\n if (first !== undefined && text(first.id).length > 0) itemId = text(first.id);\n const pending = this.#pendingFor(itemId);\n for (const audioEvent of takeAudio(pending, itemId, this.#format, undefined)) {\n this.#push(audioEvent);\n }\n const status = response === undefined ? '' : text(response.status);\n const usage = response === undefined ? undefined : response.usage;\n this.#push({\n type: 'response_done',\n itemId,\n interrupted: status === 'cancelled',\n usage: responseUsage(usage),\n });\n }\n\n #serverError(event: Record<string, unknown>): void {\n const detail = record(event.error);\n const code = detail === undefined ? '' : text(detail.code);\n const message = detail === undefined ? '' : text(detail.message);\n let fatal = false;\n if (code === 'max_duration') fatal = true;\n if (code === 'session_expired') fatal = true;\n this.#push({ type: 'error', error: message, fatal });\n }\n\n #apply(event: Record<string, unknown>): void {\n const type = text(event.type);\n if (type === 'session.created') {\n this.#push({ type: 'session_start', sessionId: this.#field(event.session, 'id') });\n return;\n }\n if (type === 'conversation.created') {\n this.#push({ type: 'resumption', handle: this.#field(event.conversation, 'id') });\n return;\n }\n if (type === 'input_audio_buffer.speech_started') {\n this.#push({ type: 'input_speech_start' });\n return;\n }\n if (type === 'conversation.item.input_audio_transcription.delta') {\n this.#input(text(event.item_id), text(event.delta), 'append', false);\n return;\n }\n if (type === 'conversation.item.input_audio_transcription.updated') {\n this.#input(text(event.item_id), text(event.transcript), 'replace', false);\n return;\n }\n if (type === 'conversation.item.input_audio_transcription.completed') {\n this.#input(text(event.item_id), text(event.transcript), 'replace', true);\n return;\n }\n if (type === 'response.output_audio.delta') {\n this.#audio(text(event.item_id), text(event.delta));\n return;\n }\n if (type === 'response.output_audio_transcript.delta') {\n this.#transcript(text(event.item_id), text(event.delta), false);\n return;\n }\n if (type === 'response.output_audio_transcript.done') {\n this.#transcript(text(event.item_id), text(event.transcript), true);\n return;\n }\n if (type === 'response.function_call_arguments.done') {\n this.#functionCall(event);\n return;\n }\n if (type === 'response.done') {\n this.#responseDone(event);\n return;\n }\n if (type === 'error') this.#serverError(event);\n }\n\n async #read(): Promise<void> {\n try {\n for await (const raw of this.#wire.incoming) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n this.#push({ type: 'error', error: 'invalid realtime event', fatal: false });\n continue;\n }\n const event = record(parsed);\n if (event === undefined || typeof event.type !== 'string') continue;\n this.#apply(event);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.#push({ type: 'error', error: message, fatal: true });\n } finally {\n this.#closed = true;\n this.#end();\n }\n }\n}\n\nexport async function connectVendorSession(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n options: RealtimeOptions | undefined,\n vendor: VendorSessionOptions\n): Promise<RealtimeSession> {\n if (options?.signal?.aborted === true) {\n throw new VoiceRequestError('aborted', 'session aborted');\n }\n if (options?.transport === 'webrtc' && vendor.wire === undefined) {\n throw new VoiceRequestError('error', 'webrtc requires a data channel wire');\n }\n const secret = credential(options);\n const token = options?.token?.value;\n const ephemeral = typeof token === 'string' && token.length > 0;\n const protocols: string[] = [];\n if (vendor.dialect === 'openai') protocols.push(`openai-insecure-api-key.${secret}`, 'realtime');\n else if (ephemeral) protocols.push(`xai-client-secret.${secret}`);\n else protocols.push('realtime');\n const resumeHandle = vendor.dialect === 'xai' ? options?.resumeHandle : undefined;\n const socket = realtimeSocket(sessionUrl(model, resumeHandle), options, protocols);\n const target: VoiceWireTarget = {\n url: socket.url,\n protocols: socket.protocols,\n headers: headersFor(options, secret, vendor.dialect === 'openai' || !ephemeral),\n };\n const open =\n vendor.wire !== undefined\n ? vendor.wire\n : (wireTarget: VoiceWireTarget) => openVoiceSocket(wireTarget, vendor.socket);\n const session = new VendorSession(model, context, vendor.dialect, vendor.replace, open(target));\n session.start(options);\n return session;\n}\n\nexport function pcmFormat(sampleRate: number): AudioFormat {\n return { encoding: 'pcm16', sampleRate, channels: 1 };\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { JsonObject, Tool, ToolResultMessage, Usage } from '@earendil-works/pi-ai';\nimport type { AudioChunk, AudioFormat, TextSpan } from '../audio.ts';\nimport { blankUsage } from '../blank-usage.ts';\nimport { decodeAudioBase64, encodeAudioBase64 } from '../convert.ts';\nimport { VoiceRequestError } from '../errors.ts';\nimport type { Delivery, RealtimeModel } from '../models.ts';\nimport type {\n RealtimeContext,\n RealtimeEvent,\n RealtimeOptions,\n RealtimeSession,\n} from '../realtime.ts';\nimport { planTruncation, RealtimeLedger, realtimeSocket } from '../realtime.ts';\nimport {\n openVoiceSocket,\n type VoiceSocketConstructor,\n type VoiceWire,\n type VoiceWireTarget,\n} from './socket.ts';\n\nexport type VoiceWireFactory = (target: VoiceWireTarget) => VoiceWire;\n\nexport interface AdapterOptions {\n wire?: VoiceWireFactory;\n socket?: VoiceSocketConstructor;\n replace?: Record<string, string>;\n}\n\nexport interface VendorSessionOptions extends AdapterOptions {\n dialect: 'openai' | 'xai';\n}\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n\nfunction text(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nfunction credential(options: RealtimeOptions | undefined): string {\n const token = options?.token?.value;\n if (typeof token === 'string' && token.length > 0) return token;\n const apiKey = options?.apiKey;\n if (typeof apiKey === 'string' && apiKey.length > 0) return apiKey;\n throw new VoiceRequestError('error', 'missing realtime credential');\n}\n\nfunction headersFor(\n options: RealtimeOptions | undefined,\n secret: string,\n bearer: boolean\n): Record<string, string> {\n const headers: Record<string, string> = {};\n if (bearer) headers.Authorization = `Bearer ${secret}`;\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 sessionUrl(model: RealtimeModel<string>, resumeHandle: string | undefined): string {\n const params = new URLSearchParams({ model: model.id });\n if (resumeHandle !== undefined) params.set('conversation_id', resumeHandle);\n return `${model.baseUrl}?${params.toString()}`;\n}\n\nfunction functionTool(tool: Tool): JsonObject {\n return {\n type: 'function',\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters as unknown as JsonObject,\n };\n}\n\nfunction openaiSession(context: RealtimeContext, model: RealtimeModel<string>): JsonObject {\n return {\n type: 'realtime',\n instructions: context.instructions,\n tools: context.tools.map(functionTool),\n audio: {\n input: {\n format: { type: 'audio/pcm', rate: model.inputFormat.sampleRate },\n transcription: { model: 'gpt-4o-mini-transcribe' },\n turn_detection: { type: 'server_vad' },\n },\n output: {\n format: { type: 'audio/pcm', rate: model.outputFormat.sampleRate },\n voice: context.voice,\n },\n },\n };\n}\n\nfunction xaiSession(\n context: RealtimeContext,\n model: RealtimeModel<string>,\n replace: Record<string, string> | undefined\n): JsonObject {\n const session: JsonObject = {\n voice: context.voice,\n instructions: context.instructions,\n turn_detection: { type: 'server_vad' },\n tools: context.tools.map(functionTool),\n resumption: { enabled: true },\n audio: {\n input: {\n format: { type: 'audio/pcm', rate: model.inputFormat.sampleRate },\n transcription: { model: 'grok-transcribe' },\n },\n output: { format: { type: 'audio/pcm', rate: model.outputFormat.sampleRate } },\n },\n };\n if (replace !== undefined) session.replace = replace;\n return session;\n}\n\nfunction userItem(value: string): JsonObject {\n return {\n type: 'conversation.item.create',\n item: {\n type: 'message',\n role: 'user',\n content: [{ type: 'input_text', text: value }],\n },\n };\n}\n\nfunction responseCreate(): JsonObject {\n return { type: 'response.create' };\n}\n\nfunction resultText(result: ToolResultMessage): string {\n const parts: string[] = [];\n for (const part of result.content) {\n if (part.type === 'text') parts.push(part.text);\n }\n return parts.join('\\n');\n}\n\nfunction toolOutput(callId: string, result: ToolResultMessage): JsonObject {\n return {\n type: 'conversation.item.create',\n item: { type: 'function_call_output', call_id: callId, output: resultText(result) },\n };\n}\n\nfunction forceMessage(value: string): JsonObject {\n return {\n type: 'conversation.item.create',\n item: {\n type: 'force_message',\n role: 'assistant',\n content: [{ type: 'output_text', text: value }],\n },\n };\n}\n\ninterface PendingAudio {\n chunks: AudioChunk[];\n cursor: number;\n}\n\nfunction spanFor(itemId: string, start: number, end: number): TextSpan {\n return { itemId, start, end };\n}\n\nfunction takeAudio(\n pending: PendingAudio,\n itemId: string,\n format: AudioFormat,\n end: number | undefined\n): RealtimeEvent[] {\n const chunks = pending.chunks;\n pending.chunks = [];\n if (chunks.length === 0) return [];\n const start = pending.cursor;\n if (end !== undefined) pending.cursor = end;\n const width = end === undefined ? 0 : end - start;\n const total = chunks.reduce((sum, chunk) => sum + chunk.data.length, 0);\n let used = 0;\n let cursor = start;\n return chunks.map((chunk, index) => {\n const event: RealtimeEvent = {\n type: 'output_audio',\n itemId,\n chunk: { data: chunk.data, format, timestamp: chunk.timestamp },\n };\n if (end === undefined || total === 0) return event;\n const last = index === chunks.length - 1;\n const next = last ? end : start + Math.round(((used + chunk.data.length) / total) * width);\n used += chunk.data.length;\n event.span = spanFor(itemId, cursor, next);\n cursor = next;\n return event;\n });\n}\n\nfunction argumentsOf(value: unknown): JsonObject {\n if (typeof value !== 'string') return {};\n try {\n const parsed: unknown = JSON.parse(value);\n const object = record(parsed);\n if (object === undefined) return {};\n return object as JsonObject;\n } catch {\n return {};\n }\n}\n\nfunction responseUsage(value: unknown): Usage {\n const usage = record(value);\n if (usage === undefined) return blankUsage();\n const input = typeof usage.input_tokens === 'number' ? usage.input_tokens : 0;\n const output = typeof usage.output_tokens === 'number' ? usage.output_tokens : 0;\n return {\n input,\n output,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: input + output,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n };\n}\n\nclass VendorSession implements RealtimeSession {\n readonly model: RealtimeModel<string>;\n readonly #dialect: 'openai' | 'xai';\n readonly #context: RealtimeContext;\n readonly #replace: Record<string, string> | undefined;\n readonly #wire: VoiceWire;\n readonly #ledger = new RealtimeLedger();\n readonly #format: AudioFormat;\n readonly #pending = new Map<string, PendingAudio>();\n readonly #inputText = new Map<string, string>();\n readonly #queue: RealtimeEvent[] = [];\n readonly #waiters: Array<(result: IteratorResult<RealtimeEvent>) => void> = [];\n #closed = false;\n #usage: Usage | undefined;\n #lastItem = '';\n #busy = false;\n #heldCreate = false;\n readonly #calls = new Set<string>();\n\n constructor(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n dialect: 'openai' | 'xai',\n replace: Record<string, string> | undefined,\n wire: VoiceWire\n ) {\n this.model = model;\n this.#context = context;\n this.#dialect = dialect;\n this.#replace = replace;\n this.#wire = wire;\n this.#format = model.outputFormat;\n }\n\n start(options: RealtimeOptions | undefined): void {\n this.#send(this.#sessionUpdate(this.#context));\n const signal = options?.signal;\n if (signal !== undefined) signal.addEventListener('abort', () => void this.close());\n void this.#read();\n }\n\n appendAudio(chunk: AudioChunk): void {\n this.#assertOpen();\n this.#ledger.addInput(chunk);\n this.#send({ type: 'input_audio_buffer.append', audio: encodeAudioBase64(chunk.data) });\n }\n\n sendText(textValue: string): void {\n this.#assertOpen();\n this.#ledger.addText();\n this.#send(userItem(textValue));\n this.#armCreate(false);\n }\n\n inject(textValue: string, delivery: Delivery): void {\n this.#assertOpen();\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n this.#ledger.addText();\n this.#sendInject(textValue, delivery);\n });\n }\n\n respond(callId: string, result: ToolResultMessage, delivery?: Delivery): void {\n this.#assertOpen();\n const send = (): void => {\n this.#send(toolOutput(callId, result));\n this.#calls.delete(callId);\n if (delivery !== 'silent') this.#armCreate(true);\n else if (this.#heldCreate && this.#calls.size === 0) this.#armCreate(false);\n };\n if (delivery === undefined) {\n send();\n return;\n }\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n send();\n });\n }\n\n interrupt(heard?: { itemId: string; heardMs: number }): void {\n this.#assertOpen();\n this.#send({ type: 'response.cancel' });\n const plan = planTruncation(this.model.truncation, heard);\n if (plan.item !== undefined) {\n this.#send({\n type: 'conversation.item.truncate',\n item_id: plan.item.itemId,\n content_index: 0,\n audio_end_ms: plan.item.audioEndMs,\n });\n }\n this.#ledger.settle();\n }\n\n update(patch: Partial<Pick<RealtimeContext, 'instructions' | 'tools' | 'voice'>>): void {\n this.#assertOpen();\n if (patch.tools !== undefined && !this.model.liveToolUpdate) {\n throw new VoiceRequestError('error', 'live tool update is unsupported');\n }\n let instructions = this.#context.instructions;\n if (patch.instructions !== undefined) instructions = patch.instructions;\n let tools = this.#context.tools;\n if (patch.tools !== undefined) tools = patch.tools;\n let voice = this.#context.voice;\n if (patch.voice !== undefined) voice = patch.voice;\n const context: RealtimeContext = { instructions, tools, voice };\n if (patch.instructions !== undefined) this.#context.instructions = context.instructions;\n if (patch.tools !== undefined) this.#context.tools = context.tools;\n if (patch.voice !== undefined) this.#context.voice = context.voice;\n this.#send(this.#sessionUpdate(context));\n }\n\n async close(): Promise<{ usage: Usage }> {\n if (this.#usage !== undefined) return { usage: this.#usage };\n this.#ledger.settle();\n this.#closed = true;\n this.#wire.close();\n this.#end();\n this.#usage = this.#ledger.usage(this.model.audioCost);\n return { usage: this.#usage };\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<RealtimeEvent> {\n while (true) {\n const queued = this.#queue.shift();\n if (queued !== undefined) {\n yield queued;\n continue;\n }\n if (this.#closed) return;\n const next = await new Promise<IteratorResult<RealtimeEvent>>((resolve) => {\n this.#waiters.push(resolve);\n });\n if (next.done === true || next.value === undefined) return;\n yield next.value;\n }\n }\n\n #sessionUpdate(context: RealtimeContext): JsonObject {\n if (this.#dialect === 'openai') {\n return { type: 'session.update', session: openaiSession(context, this.model) };\n }\n return { type: 'session.update', session: xaiSession(context, this.model, this.#replace) };\n }\n\n #sendInject(textValue: string, delivery: Delivery): void {\n if (this.#dialect === 'xai' && delivery !== 'silent') {\n this.#send(forceMessage(textValue));\n return;\n }\n this.#send(userItem(textValue));\n if (delivery !== 'silent') this.#armCreate(false);\n }\n\n #send(event: JsonObject): void {\n this.#wire.send(JSON.stringify(event));\n }\n\n #assertOpen(): void {\n if (this.#closed) throw new VoiceRequestError('error', 'session is closed');\n }\n\n #push(event: RealtimeEvent): void {\n if (this.#closed) return;\n this.#ledger.observe(event);\n const waiter = this.#waiters.shift();\n if (waiter !== undefined) waiter({ value: event, done: false });\n else this.#queue.push(event);\n }\n\n #end(): void {\n for (const waiter of this.#waiters) waiter({ value: undefined, done: true });\n this.#waiters.length = 0;\n }\n\n #pendingFor(itemId: string): PendingAudio {\n const existing = this.#pending.get(itemId);\n if (existing !== undefined) return existing;\n const created = { chunks: [], cursor: 0 };\n this.#pending.set(itemId, created);\n return created;\n }\n\n #audio(itemId: string, encoded: string): void {\n this.#lastItem = itemId;\n const pending = this.#pendingFor(itemId);\n pending.chunks.push({\n data: decodeAudioBase64(encoded),\n format: this.#format,\n timestamp: 0,\n });\n }\n\n #armCreate(waitForCalls: boolean): void {\n if (this.#busy || (waitForCalls && this.#calls.size > 0)) {\n this.#heldCreate = true;\n return;\n }\n this.#heldCreate = false;\n this.#busy = true;\n this.#send(responseCreate());\n }\n\n #releaseResponse(): void {\n this.#busy = false;\n if (this.#heldCreate && this.#calls.size === 0) this.#armCreate(false);\n }\n\n #transcript(itemId: string, value: string, final: boolean): void {\n const pending = this.#pendingFor(itemId);\n const addition = final ? value.slice(pending.cursor) : value;\n const end = pending.cursor + addition.length;\n const tagged = addition.length === 0 ? undefined : end;\n for (const event of takeAudio(pending, itemId, this.#format, tagged)) this.#push(event);\n this.#push({ type: 'output_transcript', itemId, text: value, final });\n }\n\n #input(itemId: string, transcript: string, mode: 'append' | 'replace', final: boolean): void {\n const previous = this.#inputText.get(itemId) ?? '';\n const next = mode === 'append' ? `${previous}${transcript}` : transcript;\n this.#inputText.set(itemId, next);\n this.#push({ type: 'input_transcript', itemId, text: next, final });\n }\n\n #field(value: unknown, key: string): string {\n const object = record(value);\n if (object === undefined) return '';\n return text(object[key]);\n }\n\n #functionCall(event: Record<string, unknown>): void {\n const id = text(event.call_id);\n if (id.length > 0) this.#calls.add(id);\n this.#push({\n type: 'tool_call',\n blocking: this.model.toolCalls === 'blocking',\n call: {\n type: 'toolCall',\n id,\n name: text(event.name),\n arguments: argumentsOf(event.arguments),\n },\n });\n }\n\n #responseDone(event: Record<string, unknown>): void {\n this.#releaseResponse();\n const response = record(event.response);\n const output = response === undefined ? undefined : response.output;\n const first = Array.isArray(output) ? record(output[0]) : undefined;\n let itemId = this.#lastItem;\n if (first !== undefined && text(first.id).length > 0) itemId = text(first.id);\n const pending = this.#pendingFor(itemId);\n for (const audioEvent of takeAudio(pending, itemId, this.#format, undefined)) {\n this.#push(audioEvent);\n }\n const status = response === undefined ? '' : text(response.status);\n const usage = response === undefined ? undefined : response.usage;\n this.#push({\n type: 'response_done',\n itemId,\n interrupted: status === 'cancelled',\n usage: responseUsage(usage),\n });\n }\n\n #serverError(event: Record<string, unknown>): void {\n const detail = record(event.error);\n const code = detail === undefined ? '' : text(detail.code);\n const message = detail === undefined ? '' : text(detail.message);\n let fatal = false;\n if (code === 'max_duration') fatal = true;\n if (code === 'session_expired') fatal = true;\n this.#push({ type: 'error', error: message, fatal });\n }\n\n #apply(event: Record<string, unknown>): void {\n const type = text(event.type);\n if (type === 'session.created') {\n this.#push({ type: 'session_start', sessionId: this.#field(event.session, 'id') });\n return;\n }\n if (type === 'conversation.created') {\n this.#push({ type: 'resumption', handle: this.#field(event.conversation, 'id') });\n return;\n }\n if (type === 'input_audio_buffer.speech_started') {\n this.#push({ type: 'input_speech_start' });\n return;\n }\n if (type === 'conversation.item.input_audio_transcription.delta') {\n this.#input(text(event.item_id), text(event.delta), 'append', false);\n return;\n }\n if (type === 'conversation.item.input_audio_transcription.updated') {\n this.#input(text(event.item_id), text(event.transcript), 'replace', false);\n return;\n }\n if (type === 'conversation.item.input_audio_transcription.completed') {\n this.#input(text(event.item_id), text(event.transcript), 'replace', true);\n return;\n }\n if (type === 'response.output_audio.delta') {\n this.#audio(text(event.item_id), text(event.delta));\n return;\n }\n if (type === 'response.output_audio_transcript.delta') {\n this.#transcript(text(event.item_id), text(event.delta), false);\n return;\n }\n if (type === 'response.output_audio_transcript.done') {\n this.#transcript(text(event.item_id), text(event.transcript), true);\n return;\n }\n if (type === 'response.function_call_arguments.done') {\n this.#functionCall(event);\n return;\n }\n if (type === 'response.done') {\n this.#responseDone(event);\n return;\n }\n if (type === 'error') this.#serverError(event);\n }\n\n async #read(): Promise<void> {\n try {\n for await (const raw of this.#wire.incoming) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n this.#push({ type: 'error', error: 'invalid realtime event', fatal: false });\n continue;\n }\n const event = record(parsed);\n if (event === undefined || typeof event.type !== 'string') continue;\n this.#apply(event);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.#push({ type: 'error', error: message, fatal: true });\n } finally {\n this.#closed = true;\n this.#end();\n }\n }\n}\n\nexport async function connectVendorSession(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n options: RealtimeOptions | undefined,\n vendor: VendorSessionOptions\n): Promise<RealtimeSession> {\n if (options?.signal?.aborted === true) {\n throw new VoiceRequestError('aborted', 'session aborted');\n }\n if (options?.transport === 'webrtc' && vendor.wire === undefined) {\n throw new VoiceRequestError('error', 'webrtc requires a data channel wire');\n }\n const secret = credential(options);\n const token = options?.token?.value;\n const ephemeral = typeof token === 'string' && token.length > 0;\n const protocols: string[] = [];\n if (vendor.dialect === 'openai') protocols.push(`openai-insecure-api-key.${secret}`, 'realtime');\n else if (ephemeral) protocols.push(`xai-client-secret.${secret}`);\n else protocols.push('realtime');\n const resumeHandle = vendor.dialect === 'xai' ? options?.resumeHandle : undefined;\n const socket = realtimeSocket(sessionUrl(model, resumeHandle), options, protocols);\n const target: VoiceWireTarget = {\n url: socket.url,\n protocols: socket.protocols,\n headers: headersFor(options, secret, vendor.dialect === 'openai' || !ephemeral),\n };\n const open =\n vendor.wire !== undefined\n ? vendor.wire\n : (wireTarget: VoiceWireTarget) => openVoiceSocket(wireTarget, vendor.socket);\n const session = new VendorSession(model, context, vendor.dialect, vendor.replace, open(target));\n session.start(options);\n return session;\n}\n\nexport function pcmFormat(sampleRate: number): AudioFormat {\n return { encoding: 'pcm16', sampleRate, channels: 1 };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,8 +4,10 @@ function openVoiceSocket(target, Socket = WebSocket) {
|
|
|
4
4
|
headers: target.headers
|
|
5
5
|
});
|
|
6
6
|
const queue = [];
|
|
7
|
+
const outgoing = [];
|
|
7
8
|
const waiters = [];
|
|
8
9
|
let closed = false;
|
|
10
|
+
let opened = false;
|
|
9
11
|
const push = (value) => {
|
|
10
12
|
if (closed) return;
|
|
11
13
|
const waiter = waiters.shift();
|
|
@@ -18,6 +20,11 @@ function openVoiceSocket(target, Socket = WebSocket) {
|
|
|
18
20
|
for (const waiter of waiters) waiter({ value: void 0, done: true });
|
|
19
21
|
waiters.length = 0;
|
|
20
22
|
};
|
|
23
|
+
socket.addEventListener("open", () => {
|
|
24
|
+
opened = true;
|
|
25
|
+
const queued = outgoing.splice(0);
|
|
26
|
+
for (const data of queued) socket.send(data);
|
|
27
|
+
});
|
|
21
28
|
socket.addEventListener("message", (event) => {
|
|
22
29
|
push(typeof event.data === "string" ? event.data : String(event.data));
|
|
23
30
|
});
|
|
@@ -25,6 +32,11 @@ function openVoiceSocket(target, Socket = WebSocket) {
|
|
|
25
32
|
socket.addEventListener("error", end);
|
|
26
33
|
return {
|
|
27
34
|
send(data) {
|
|
35
|
+
if (closed) return;
|
|
36
|
+
if (!opened) {
|
|
37
|
+
outgoing.push(data);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
28
40
|
socket.send(data);
|
|
29
41
|
},
|
|
30
42
|
close() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/openai-realtime/socket.ts"],
|
|
4
|
-
"sourcesContent": ["export interface VoiceWireTarget {\n url: string;\n protocols: string[];\n headers: Record<string, string>;\n}\n\nexport interface VoiceWire {\n send(data: string): void;\n close(): void;\n incoming: AsyncIterable<string>;\n}\n\nexport interface VoiceSocket {\n send(data: string): void;\n close(): void;\n addEventListener(type: 'message', listener: (event: { data: unknown }) => void): void;\n addEventListener(type: 'close', listener: () => void): void;\n addEventListener(type: 'error', listener: () => void): void;\n}\n\nexport type VoiceSocketInit = {\n protocols: string[];\n headers: Record<string, string>;\n};\n\nexport type VoiceSocketConstructor = new (url: string, init: VoiceSocketInit) => VoiceSocket;\n\nexport function openVoiceSocket(\n target: VoiceWireTarget,\n Socket: VoiceSocketConstructor = WebSocket as unknown as VoiceSocketConstructor\n): VoiceWire {\n const socket = new Socket(target.url, {\n protocols: target.protocols,\n headers: target.headers,\n });\n const queue: string[] = [];\n const waiters: Array<(result: IteratorResult<string>) => void> = [];\n let closed = false;\n\n const push = (value: string): void => {\n if (closed) return;\n const waiter = waiters.shift();\n if (waiter !== undefined) waiter({ value, done: false });\n else queue.push(value);\n };\n\n const end = (): void => {\n if (closed) return;\n closed = true;\n for (const waiter of waiters) waiter({ value: undefined, done: true });\n waiters.length = 0;\n };\n\n socket.addEventListener('message', (event) => {\n push(typeof event.data === 'string' ? event.data : String(event.data));\n });\n socket.addEventListener('close', end);\n socket.addEventListener('error', end);\n\n return {\n send(data) {\n socket.send(data);\n },\n close() {\n socket.close();\n end();\n },\n incoming: {\n async *[Symbol.asyncIterator]() {\n while (true) {\n const queued = queue.shift();\n if (queued !== undefined) {\n yield queued;\n continue;\n }\n if (closed) return;\n const next = await new Promise<IteratorResult<string>>((resolve) => {\n waiters.push(resolve);\n });\n if (next.done === true || next.value === undefined) return;\n yield next.value;\n }\n },\n },\n };\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["export interface VoiceWireTarget {\n url: string;\n protocols: string[];\n headers: Record<string, string>;\n}\n\nexport interface VoiceWire {\n send(data: string): void;\n close(): void;\n incoming: AsyncIterable<string>;\n}\n\nexport interface VoiceSocket {\n send(data: string): void;\n close(): void;\n addEventListener(type: 'open', listener: () => void): void;\n addEventListener(type: 'message', listener: (event: { data: unknown }) => void): void;\n addEventListener(type: 'close', listener: () => void): void;\n addEventListener(type: 'error', listener: () => void): void;\n}\n\nexport type VoiceSocketInit = {\n protocols: string[];\n headers: Record<string, string>;\n};\n\nexport type VoiceSocketConstructor = new (url: string, init: VoiceSocketInit) => VoiceSocket;\n\nexport function openVoiceSocket(\n target: VoiceWireTarget,\n Socket: VoiceSocketConstructor = WebSocket as unknown as VoiceSocketConstructor\n): VoiceWire {\n const socket = new Socket(target.url, {\n protocols: target.protocols,\n headers: target.headers,\n });\n const queue: string[] = [];\n const outgoing: string[] = [];\n const waiters: Array<(result: IteratorResult<string>) => void> = [];\n let closed = false;\n let opened = false;\n\n const push = (value: string): void => {\n if (closed) return;\n const waiter = waiters.shift();\n if (waiter !== undefined) waiter({ value, done: false });\n else queue.push(value);\n };\n\n const end = (): void => {\n if (closed) return;\n closed = true;\n for (const waiter of waiters) waiter({ value: undefined, done: true });\n waiters.length = 0;\n };\n\n socket.addEventListener('open', () => {\n opened = true;\n const queued = outgoing.splice(0);\n for (const data of queued) socket.send(data);\n });\n socket.addEventListener('message', (event) => {\n push(typeof event.data === 'string' ? event.data : String(event.data));\n });\n socket.addEventListener('close', end);\n socket.addEventListener('error', end);\n\n return {\n send(data) {\n if (closed) return;\n if (!opened) {\n outgoing.push(data);\n return;\n }\n socket.send(data);\n },\n close() {\n socket.close();\n end();\n },\n incoming: {\n async *[Symbol.asyncIterator]() {\n while (true) {\n const queued = queue.shift();\n if (queued !== undefined) {\n yield queued;\n continue;\n }\n if (closed) return;\n const next = await new Promise<IteratorResult<string>>((resolve) => {\n waiters.push(resolve);\n });\n if (next.done === true || next.value === undefined) return;\n yield next.value;\n }\n },\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "AA4BO,SAAS,gBACd,QACA,SAAiC,WACtB;AACX,QAAM,SAAS,IAAI,OAAO,OAAO,KAAK;AAAA,IACpC,WAAW,OAAO;AAAA,IAClB,SAAS,OAAO;AAAA,EAClB,CAAC;AACD,QAAM,QAAkB,CAAC;AACzB,QAAM,WAAqB,CAAC;AAC5B,QAAM,UAA2D,CAAC;AAClE,MAAI,SAAS;AACb,MAAI,SAAS;AAEb,QAAM,OAAO,CAAC,UAAwB;AACpC,QAAI,OAAQ;AACZ,UAAM,SAAS,QAAQ,MAAM;AAC7B,QAAI,WAAW,OAAW,QAAO,EAAE,OAAO,MAAM,MAAM,CAAC;AAAA,QAClD,OAAM,KAAK,KAAK;AAAA,EACvB;AAEA,QAAM,MAAM,MAAY;AACtB,QAAI,OAAQ;AACZ,aAAS;AACT,eAAW,UAAU,QAAS,QAAO,EAAE,OAAO,QAAW,MAAM,KAAK,CAAC;AACrE,YAAQ,SAAS;AAAA,EACnB;AAEA,SAAO,iBAAiB,QAAQ,MAAM;AACpC,aAAS;AACT,UAAM,SAAS,SAAS,OAAO,CAAC;AAChC,eAAW,QAAQ,OAAQ,QAAO,KAAK,IAAI;AAAA,EAC7C,CAAC;AACD,SAAO,iBAAiB,WAAW,CAAC,UAAU;AAC5C,SAAK,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,EACvE,CAAC;AACD,SAAO,iBAAiB,SAAS,GAAG;AACpC,SAAO,iBAAiB,SAAS,GAAG;AAEpC,SAAO;AAAA,IACL,KAAK,MAAM;AACT,UAAI,OAAQ;AACZ,UAAI,CAAC,QAAQ;AACX,iBAAS,KAAK,IAAI;AAClB;AAAA,MACF;AACA,aAAO,KAAK,IAAI;AAAA,IAClB;AAAA,IACA,QAAQ;AACN,aAAO,MAAM;AACb,UAAI;AAAA,IACN;AAAA,IACA,UAAU;AAAA,MACR,QAAQ,OAAO,aAAa,IAAI;AAC9B,eAAO,MAAM;AACX,gBAAM,SAAS,MAAM,MAAM;AAC3B,cAAI,WAAW,QAAW;AACxB,kBAAM;AACN;AAAA,UACF;AACA,cAAI,OAAQ;AACZ,gBAAM,OAAO,MAAM,IAAI,QAAgC,CAAC,YAAY;AAClE,oBAAQ,KAAK,OAAO;AAAA,UACtB,CAAC;AACD,cAAI,KAAK,SAAS,QAAQ,KAAK,UAAU,OAAW;AACpD,gBAAM,KAAK;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/talker.js
CHANGED
|
@@ -88,6 +88,16 @@ function capResult(result, maxChars) {
|
|
|
88
88
|
});
|
|
89
89
|
return { ...result, content };
|
|
90
90
|
}
|
|
91
|
+
function requestContext(call) {
|
|
92
|
+
const value = call.arguments.context;
|
|
93
|
+
return typeof value === "string" ? value : void 0;
|
|
94
|
+
}
|
|
95
|
+
function track(pending, promise) {
|
|
96
|
+
pending.add(promise);
|
|
97
|
+
void promise.finally(() => {
|
|
98
|
+
pending.delete(promise);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
91
101
|
function bindThinker(session, thinker, direct, options) {
|
|
92
102
|
const captions = new PushStream();
|
|
93
103
|
const phases = new PushStream();
|
|
@@ -102,42 +112,31 @@ function bindThinker(session, thinker, direct, options) {
|
|
|
102
112
|
async function run(session, thinker, direct, options, captions, phases) {
|
|
103
113
|
const heard = [];
|
|
104
114
|
const running = /* @__PURE__ */ new Map();
|
|
115
|
+
const pending = /* @__PURE__ */ new Set();
|
|
105
116
|
for await (const event of session) {
|
|
106
117
|
if (event.type === "input_transcript" && event.final) heard.push(`user: ${event.text}`);
|
|
107
118
|
if (event.type === "output_transcript" && event.final) heard.push(`talker: ${event.text}`);
|
|
108
119
|
if (event.type === "tool_call_cancelled") {
|
|
109
|
-
|
|
110
|
-
if (!job2) continue;
|
|
111
|
-
job2.abort.abort();
|
|
112
|
-
if (!job2.answered) {
|
|
113
|
-
job2.answered = true;
|
|
114
|
-
send(() => session.respond(event.callId, errorResult(job2.call, new Error("cancelled"))));
|
|
115
|
-
}
|
|
120
|
+
cancelJob(session, running, event.callId);
|
|
116
121
|
continue;
|
|
117
122
|
}
|
|
118
123
|
if (event.type !== "tool_call") continue;
|
|
119
124
|
const call = event.call;
|
|
120
125
|
if (call.name !== askAgent.name) {
|
|
121
|
-
phases
|
|
122
|
-
phase: "tool",
|
|
123
|
-
toolName: call.name,
|
|
124
|
-
toolCallId: call.id,
|
|
125
|
-
args: call.arguments
|
|
126
|
-
});
|
|
127
|
-
void direct(call, AbortSignal.timeout(options.directTimeoutMs)).then((result) => {
|
|
128
|
-
send(() => session.respond(call.id, capResult(result, options.directMaxChars)));
|
|
129
|
-
phases.push({ phase: "idle" });
|
|
130
|
-
}).catch((error) => {
|
|
131
|
-
send(() => session.respond(call.id, errorResult(call, error)));
|
|
132
|
-
phases.push({ phase: "idle" });
|
|
133
|
-
});
|
|
126
|
+
track(pending, runDirect(session, direct, options, phases, running, call));
|
|
134
127
|
continue;
|
|
135
128
|
}
|
|
136
|
-
const job = {
|
|
129
|
+
const job = {
|
|
130
|
+
abort: new AbortController(),
|
|
131
|
+
answered: false,
|
|
132
|
+
dropped: false,
|
|
133
|
+
call,
|
|
134
|
+
kind: "ask"
|
|
135
|
+
};
|
|
137
136
|
running.set(call.id, job);
|
|
138
137
|
phases.push({ phase: "thinking" });
|
|
139
138
|
if (event.blocking) send(() => session.respond(call.id, acknowledged(call, options.ack)));
|
|
140
|
-
|
|
139
|
+
const ask = followAsk(
|
|
141
140
|
session,
|
|
142
141
|
thinker,
|
|
143
142
|
options,
|
|
@@ -149,14 +148,52 @@ async function run(session, thinker, direct, options, captions, phases) {
|
|
|
149
148
|
event.blocking,
|
|
150
149
|
job
|
|
151
150
|
);
|
|
151
|
+
track(pending, ask);
|
|
152
152
|
}
|
|
153
|
+
for (const job of running.values()) {
|
|
154
|
+
job.dropped = true;
|
|
155
|
+
job.abort.abort();
|
|
156
|
+
}
|
|
157
|
+
await Promise.allSettled([...pending]);
|
|
158
|
+
}
|
|
159
|
+
function cancelJob(session, running, callId) {
|
|
160
|
+
const job = running.get(callId);
|
|
161
|
+
if (!job) return;
|
|
162
|
+
job.abort.abort();
|
|
163
|
+
job.dropped = true;
|
|
164
|
+
if (job.kind !== "ask" || job.answered) return;
|
|
165
|
+
job.answered = true;
|
|
166
|
+
send(() => session.respond(callId, errorResult(job.call, new Error("cancelled"))));
|
|
167
|
+
}
|
|
168
|
+
function runDirect(session, direct, options, phases, running, call) {
|
|
169
|
+
const abort = new AbortController();
|
|
170
|
+
const signal = AbortSignal.any([abort.signal, AbortSignal.timeout(options.directTimeoutMs)]);
|
|
171
|
+
const job = { abort, answered: false, dropped: false, call, kind: "direct" };
|
|
172
|
+
running.set(call.id, job);
|
|
173
|
+
phases.push({
|
|
174
|
+
phase: "tool",
|
|
175
|
+
toolName: call.name,
|
|
176
|
+
toolCallId: call.id,
|
|
177
|
+
args: call.arguments
|
|
178
|
+
});
|
|
179
|
+
return direct(call, signal).then((result) => {
|
|
180
|
+
if (job.dropped) return;
|
|
181
|
+
send(() => session.respond(call.id, capResult(result, options.directMaxChars)));
|
|
182
|
+
}).catch((error) => {
|
|
183
|
+
if (job.dropped) return;
|
|
184
|
+
send(() => session.respond(call.id, errorResult(call, error)));
|
|
185
|
+
}).finally(() => {
|
|
186
|
+
running.delete(call.id);
|
|
187
|
+
phases.push({ phase: "idle" });
|
|
188
|
+
});
|
|
153
189
|
}
|
|
154
190
|
async function followAsk(session, thinker, options, captions, phases, running, heard, call, blocking, job) {
|
|
155
191
|
try {
|
|
156
192
|
for await (const update of thinker.ask(
|
|
157
193
|
String(call.arguments.request),
|
|
158
194
|
heard.slice(-heardLimit),
|
|
159
|
-
job.abort.signal
|
|
195
|
+
job.abort.signal,
|
|
196
|
+
requestContext(call)
|
|
160
197
|
)) {
|
|
161
198
|
if (job.abort.signal.aborted) break;
|
|
162
199
|
if (update.type === "progress") {
|
|
@@ -175,10 +212,17 @@ async function followAsk(session, thinker, options, captions, phases, running, h
|
|
|
175
212
|
else send(() => session.respond(call.id, update.message, options.result));
|
|
176
213
|
}
|
|
177
214
|
} catch (error) {
|
|
178
|
-
if (
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
215
|
+
if (job.abort.signal.aborted || job.answered) return;
|
|
216
|
+
job.answered = true;
|
|
217
|
+
const message = errorResult(call, error);
|
|
218
|
+
captions.push({
|
|
219
|
+
type: "thinker_result",
|
|
220
|
+
callId: call.id,
|
|
221
|
+
summary: textOf(message),
|
|
222
|
+
isError: true
|
|
223
|
+
});
|
|
224
|
+
if (blocking) send(() => session.inject(textOf(message), options.result));
|
|
225
|
+
else send(() => session.respond(call.id, message));
|
|
182
226
|
} finally {
|
|
183
227
|
running.delete(call.id);
|
|
184
228
|
phases.push({ phase: "idle" });
|
package/dist/talker.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/talker.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n type JsonObject,\n type Tool,\n type ToolCall,\n type ToolResultMessage,\n Type,\n} from '@earendil-works/pi-ai';\nimport { VoiceRequestError } from './errors.ts';\nimport type { Delivery } from './models.ts';\nimport type { RealtimeSession } from './realtime.ts';\n\nconst heardLimit = 20;\n\nexport const talkerTools = ['ask_agent', 'read'] as const;\n\nexport const askAgent: Tool = {\n name: 'ask_agent',\n description:\n 'Hand a request to the coding agent. Use for anything that needs files, the browser, tools or more than a few seconds of thought.',\n parameters: Type.Object({\n request: Type.String(),\n context: Type.Optional(Type.String()),\n }),\n};\n\nexport type ThinkerUpdate =\n | { type: 'progress'; text: string }\n | { type: 'result'; message: ToolResultMessage };\n\nexport interface Thinker {\n ask(request: string
|
|
5
|
-
"mappings": "AAAA;AAAA,EAKE;AAAA,OACK;AACP,SAAS,yBAAyB;AAIlC,MAAM,aAAa;AAEZ,MAAM,cAAc,CAAC,aAAa,MAAM;AAExC,MAAM,WAAiB;AAAA,EAC5B,MAAM;AAAA,EACN,aACE;AAAA,EACF,YAAY,KAAK,OAAO;AAAA,IACtB,SAAS,KAAK,OAAO;AAAA,IACrB,SAAS,KAAK,SAAS,KAAK,OAAO,CAAC;AAAA,EACtC,CAAC;AACH;
|
|
6
|
-
"names": [
|
|
4
|
+
"sourcesContent": ["import {\n type JsonObject,\n type Tool,\n type ToolCall,\n type ToolResultMessage,\n Type,\n} from '@earendil-works/pi-ai';\nimport { VoiceRequestError } from './errors.ts';\nimport type { Delivery } from './models.ts';\nimport type { RealtimeSession } from './realtime.ts';\n\nconst heardLimit = 20;\n\nexport const talkerTools = ['ask_agent', 'read'] as const;\n\nexport const askAgent: Tool = {\n name: 'ask_agent',\n description:\n 'Hand a request to the coding agent. Use for anything that needs files, the browser, tools or more than a few seconds of thought.',\n parameters: Type.Object({\n request: Type.String(),\n context: Type.Optional(Type.String()),\n }),\n};\n\nexport type ThinkerUpdate =\n | { type: 'progress'; text: string }\n | { type: 'result'; message: ToolResultMessage };\n\nexport interface Thinker {\n ask(\n request: string,\n heard: string[],\n signal: AbortSignal,\n context?: string\n ): AsyncIterable<ThinkerUpdate>;\n}\n\nexport interface BindThinkerOptions {\n progress: Delivery;\n result: Delivery;\n ack: string;\n directTimeoutMs: number;\n directMaxChars: number;\n}\n\nexport type ThinkerCaption =\n | { type: 'thinker_progress'; callId: string; text: string }\n | { type: 'thinker_result'; callId: string; summary: string; isError: boolean };\n\nexport type ThinkerPhase =\n | { phase: 'thinking' }\n | { phase: 'tool'; toolName: string; toolCallId: string; args: JsonObject }\n | { phase: 'idle' };\n\nexport interface BoundThinker {\n captions: AsyncIterable<ThinkerCaption>;\n phases: AsyncIterable<ThinkerPhase>;\n done: Promise<void>;\n}\n\nclass PushStream<T> implements AsyncIterable<T> {\n readonly #queue: T[] = [];\n readonly #waiters: Array<(result: { value?: T; done: boolean }) => void> = [];\n #closed = false;\n\n push(value: T): void {\n if (this.#closed) return;\n const waiter = this.#waiters.shift();\n if (waiter) waiter({ value, done: false });\n else this.#queue.push(value);\n }\n\n close(): void {\n if (this.#closed) return;\n this.#closed = true;\n while (this.#waiters.length > 0) this.#waiters.shift()?.({ done: true });\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<T> {\n while (true) {\n const queued = this.#queue.shift();\n if (queued !== undefined) {\n yield queued;\n continue;\n }\n if (this.#closed) return;\n const next = await new Promise<{ value?: T; done: boolean }>((resolve) =>\n this.#waiters.push(resolve)\n );\n if (next.done || next.value === undefined) return;\n yield next.value;\n }\n }\n}\n\nfunction send(action: () => void): void {\n try {\n action();\n } catch (error) {\n if (!(error instanceof VoiceRequestError)) throw error;\n }\n}\n\nfunction textOf(message: ToolResultMessage): string {\n return message.content\n .filter((part) => part.type === 'text')\n .map((part) => part.text)\n .join('\\n');\n}\n\nfunction acknowledged(call: ToolCall, ack: string): ToolResultMessage {\n return {\n role: 'toolResult',\n toolCallId: call.id,\n toolName: call.name,\n content: [{ type: 'text', text: ack }],\n isError: false,\n timestamp: Date.now(),\n };\n}\n\nfunction errorResult(call: ToolCall, error: unknown): ToolResultMessage {\n return {\n role: 'toolResult',\n toolCallId: call.id,\n toolName: call.name,\n content: [{ type: 'text', text: error instanceof Error ? error.message : String(error) }],\n isError: true,\n timestamp: Date.now(),\n };\n}\n\nfunction capResult(result: ToolResultMessage, maxChars: number): ToolResultMessage {\n let left = maxChars;\n const content = result.content.map((part) => {\n if (part.type !== 'text') return part;\n if (left <= 0) return { ...part, text: '' };\n if (part.text.length <= left) {\n left -= part.text.length;\n return part;\n }\n const text = part.text.slice(0, left);\n left = 0;\n return { ...part, text };\n });\n return { ...result, content };\n}\n\ninterface AskJob {\n abort: AbortController;\n answered: boolean;\n dropped: boolean;\n call: ToolCall;\n kind: 'ask' | 'direct';\n}\n\nfunction requestContext(call: ToolCall): string | undefined {\n const value = call.arguments.context;\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction track(pending: Set<Promise<void>>, promise: Promise<void>): void {\n pending.add(promise);\n void promise.finally(() => {\n pending.delete(promise);\n });\n}\n\nexport function bindThinker(\n session: RealtimeSession,\n thinker: Thinker,\n direct: (call: ToolCall, signal: AbortSignal) => Promise<ToolResultMessage>,\n options: BindThinkerOptions\n): BoundThinker {\n const captions = new PushStream<ThinkerCaption>();\n const phases = new PushStream<ThinkerPhase>();\n const done = run(session, thinker, direct, options, captions, phases).finally(() => {\n captions.close();\n phases.close();\n captions.close();\n phases.close();\n });\n return { captions, phases, done };\n}\n\nasync function run(\n session: RealtimeSession,\n thinker: Thinker,\n direct: (call: ToolCall, signal: AbortSignal) => Promise<ToolResultMessage>,\n options: BindThinkerOptions,\n captions: PushStream<ThinkerCaption>,\n phases: PushStream<ThinkerPhase>\n): Promise<void> {\n const heard: string[] = [];\n const running = new Map<string, AskJob>();\n const pending = new Set<Promise<void>>();\n for await (const event of session) {\n if (event.type === 'input_transcript' && event.final) heard.push(`user: ${event.text}`);\n if (event.type === 'output_transcript' && event.final) heard.push(`talker: ${event.text}`);\n if (event.type === 'tool_call_cancelled') {\n cancelJob(session, running, event.callId);\n continue;\n }\n if (event.type !== 'tool_call') continue;\n const call = event.call;\n if (call.name !== askAgent.name) {\n track(pending, runDirect(session, direct, options, phases, running, call));\n continue;\n }\n const job: AskJob = {\n abort: new AbortController(),\n answered: false,\n dropped: false,\n call,\n kind: 'ask',\n };\n running.set(call.id, job);\n phases.push({ phase: 'thinking' });\n if (event.blocking) send(() => session.respond(call.id, acknowledged(call, options.ack)));\n const ask = followAsk(\n session,\n thinker,\n options,\n captions,\n phases,\n running,\n heard,\n call,\n event.blocking,\n job\n );\n track(pending, ask);\n }\n for (const job of running.values()) {\n job.dropped = true;\n job.abort.abort();\n }\n await Promise.allSettled([...pending]);\n}\n\nfunction cancelJob(session: RealtimeSession, running: Map<string, AskJob>, callId: string): void {\n const job = running.get(callId);\n if (!job) return;\n job.abort.abort();\n job.dropped = true;\n if (job.kind !== 'ask' || job.answered) return;\n job.answered = true;\n send(() => session.respond(callId, errorResult(job.call, new Error('cancelled'))));\n}\n\nfunction runDirect(\n session: RealtimeSession,\n direct: (call: ToolCall, signal: AbortSignal) => Promise<ToolResultMessage>,\n options: BindThinkerOptions,\n phases: PushStream<ThinkerPhase>,\n running: Map<string, AskJob>,\n call: ToolCall\n): Promise<void> {\n const abort = new AbortController();\n const signal = AbortSignal.any([abort.signal, AbortSignal.timeout(options.directTimeoutMs)]);\n const job: AskJob = { abort, answered: false, dropped: false, call, kind: 'direct' };\n running.set(call.id, job);\n phases.push({\n phase: 'tool',\n toolName: call.name,\n toolCallId: call.id,\n args: call.arguments,\n });\n return direct(call, signal)\n .then((result) => {\n if (job.dropped) return;\n send(() => session.respond(call.id, capResult(result, options.directMaxChars)));\n })\n .catch((error: unknown) => {\n if (job.dropped) return;\n send(() => session.respond(call.id, errorResult(call, error)));\n })\n .finally(() => {\n running.delete(call.id);\n phases.push({ phase: 'idle' });\n });\n}\n\nasync function followAsk(\n session: RealtimeSession,\n thinker: Thinker,\n options: BindThinkerOptions,\n captions: PushStream<ThinkerCaption>,\n phases: PushStream<ThinkerPhase>,\n running: Map<string, AskJob>,\n heard: string[],\n call: ToolCall,\n blocking: boolean,\n job: AskJob\n): Promise<void> {\n try {\n for await (const update of thinker.ask(\n String(call.arguments.request),\n heard.slice(-heardLimit),\n job.abort.signal,\n requestContext(call)\n )) {\n if (job.abort.signal.aborted) break;\n if (update.type === 'progress') {\n captions.push({ type: 'thinker_progress', callId: call.id, text: update.text });\n send(() => session.inject(update.text, options.progress));\n continue;\n }\n job.answered = true;\n captions.push({\n type: 'thinker_result',\n callId: call.id,\n summary: textOf(update.message),\n isError: update.message.isError,\n });\n if (blocking) send(() => session.inject(textOf(update.message), options.result));\n else send(() => session.respond(call.id, update.message, options.result));\n }\n } catch (error) {\n if (job.abort.signal.aborted || job.answered) return;\n job.answered = true;\n const message = errorResult(call, error);\n captions.push({\n type: 'thinker_result',\n callId: call.id,\n summary: textOf(message),\n isError: true,\n });\n if (blocking) send(() => session.inject(textOf(message), options.result));\n else send(() => session.respond(call.id, message));\n } finally {\n running.delete(call.id);\n phases.push({ phase: 'idle' });\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
7
|
}
|
|
@@ -11,6 +11,7 @@ export interface VoiceWire {
|
|
|
11
11
|
export interface VoiceSocket {
|
|
12
12
|
send(data: string): void;
|
|
13
13
|
close(): void;
|
|
14
|
+
addEventListener(type: 'open', listener: () => void): void;
|
|
14
15
|
addEventListener(type: 'message', listener: (event: {
|
|
15
16
|
data: unknown;
|
|
16
17
|
}) => void): void;
|
package/dist/types/talker.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type ThinkerUpdate = {
|
|
|
11
11
|
message: ToolResultMessage;
|
|
12
12
|
};
|
|
13
13
|
export interface Thinker {
|
|
14
|
-
ask(request: string, heard: string[], signal: AbortSignal): AsyncIterable<ThinkerUpdate>;
|
|
14
|
+
ask(request: string, heard: string[], signal: AbortSignal, context?: string): AsyncIterable<ThinkerUpdate>;
|
|
15
15
|
}
|
|
16
16
|
export interface BindThinkerOptions {
|
|
17
17
|
progress: Delivery;
|
package/package.json
CHANGED