@happyvertical/speech 0.105.9 → 0.105.10
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
CHANGED
|
@@ -533,4 +533,6 @@ Connection timeout includes microphone permission acquisition. Closing or timing
|
|
|
533
533
|
|
|
534
534
|
Assistant transcripts expose optional provider `responseId`. `speaking` retains its boolean first callback argument and adds optional `{ responseId, reason }` metadata. Provider reasons are `started`, `stopped`, and `cleared`; local reasons are `interrupted`, `muted`, and `closed`. Playback identity comes from output-buffer events independently of generation. Identity changes can emit `true` again; a late stop/clear for an older response emits its metadata with the current boolean without stopping newer playback. Missing identity stays absent; local reasons use only known playback identity. Muted identity changes do not emit redundant false notifications. Failure cleanup preserves known playback identity without an intentional `closed` reason; only caller-initiated close reports that reason.
|
|
535
535
|
|
|
536
|
+
Terminal `response.done` callbacks add optional allowlisted `status` (`completed`, `incomplete`, `cancelled`, `failed`) and `statusReason` (`max_output_tokens` or `content_filter` for incomplete; `turn_detected` or `client_cancelled` for cancelled). Unknown, malformed or mismatched values are omitted. Raw provider errors are never copied; transcript finalization remains distinct from playback completion.
|
|
537
|
+
|
|
536
538
|
Protocol reference: [OpenAI WebRTC](https://developers.openai.com/api/docs/guides/voice-webrtc), [server controls](https://developers.openai.com/api/docs/guides/voice-server-controls). Real media behavior still requires browser QA; deterministic transport tests do not prove microphone permissions or echo cancellation.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-webrtc.d.ts","sourceRoot":"","sources":["../../src/conversation/openai-webrtc.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,wBAAwB,EACxB,wBAAwB,EAMzB,MAAM,YAAY,CAAC;AAIpB,iGAAiG;AACjG,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,wBAAwB,GAChC,wBAAwB,
|
|
1
|
+
{"version":3,"file":"openai-webrtc.d.ts","sourceRoot":"","sources":["../../src/conversation/openai-webrtc.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,wBAAwB,EACxB,wBAAwB,EAMzB,MAAM,YAAY,CAAC;AAIpB,iGAAiG;AACjG,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,wBAAwB,GAChC,wBAAwB,CAgnB1B"}
|
|
@@ -28,6 +28,9 @@ export interface VoiceSessionEvents {
|
|
|
28
28
|
response: {
|
|
29
29
|
active: boolean;
|
|
30
30
|
responseId?: string;
|
|
31
|
+
/** Safe terminal fields from response.done; unknown provider values are omitted. */
|
|
32
|
+
status?: 'completed' | 'incomplete' | 'cancelled' | 'failed';
|
|
33
|
+
statusReason?: 'max_output_tokens' | 'content_filter' | 'turn_detected' | 'client_cancelled';
|
|
31
34
|
};
|
|
32
35
|
usage: Record<string, unknown>;
|
|
33
36
|
toolCall: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/conversation/types.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,QAAQ,CAAC;AAEb,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,0EAA0E;AAC1E,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,mFAAmF;AACnF,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EACH,SAAS,GACT,SAAS,GACT,SAAS,GACT,aAAa,GACb,OAAO,GACP,QAAQ,CAAC;CACd;AAED,MAAM,WAAW,yBACf,SAAQ,wBAAwB,EAC9B,qBAAqB;CAAG;AAE5B,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,iBAAiB,CAAC;IACzB,UAAU,EAAE,eAAe,CAAC;IAC5B,qEAAqE;IACrE,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/conversation/types.ts"],"names":[],"mappings":"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"}
|
package/dist/conversation.js
CHANGED
|
@@ -196,9 +196,15 @@ function createOpenAIWebRTCVoiceSession(options) {
|
|
|
196
196
|
const wasActive = responseActive;
|
|
197
197
|
pendingResponseCreateEventId = void 0;
|
|
198
198
|
responseActive = false;
|
|
199
|
+
const status = response.status;
|
|
200
|
+
const safeStatus = status === "completed" || status === "incomplete" || status === "cancelled" || status === "failed" ? status : void 0;
|
|
201
|
+
const reason = record(response.status_details).reason;
|
|
202
|
+
const safeReason = status === "incomplete" && (reason === "max_output_tokens" || reason === "content_filter") || status === "cancelled" && (reason === "turn_detected" || reason === "client_cancelled") ? reason : void 0;
|
|
199
203
|
emit("response", {
|
|
200
204
|
active: false,
|
|
201
|
-
responseId
|
|
205
|
+
responseId,
|
|
206
|
+
...safeStatus ? { status: safeStatus } : {},
|
|
207
|
+
...safeReason ? { statusReason: safeReason } : {}
|
|
202
208
|
});
|
|
203
209
|
if (response.usage && typeof response.usage === "object") emit("usage", record(response.usage));
|
|
204
210
|
if (response.status === "failed" || response.status === "incomplete") emit("error", new SpeechProviderError(ADAPTER, response.status === "incomplete" ? "Voice response incomplete" : "Voice response failed"));
|
package/dist/conversation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation.js","names":[],"sources":["../src/conversation/openai-webrtc.ts"],"sourcesContent":["import {\n SpeechConfigurationError,\n SpeechProviderError,\n} from '../shared/errors.js';\nimport type {\n OpenAIWebRTCVoiceOptions,\n OpenAIWebRTCVoiceSession,\n VoiceSessionEventMetadata,\n VoiceSessionEvents,\n VoiceSessionState,\n VoiceSpeakingMetadata,\n VoiceTranscript,\n} from './types.js';\n\nconst ADAPTER = 'openai-conversation';\n\n/** Browser WebRTC transport. No credentials, Node dependencies or globals are read at import. */\nexport function createOpenAIWebRTCVoiceSession(\n options: OpenAIWebRTCVoiceOptions,\n): OpenAIWebRTCVoiceSession {\n let state: VoiceSessionState = 'idle';\n let pending: Promise<void> | undefined;\n let peer: RTCPeerConnection | undefined;\n let channel: RTCDataChannel | undefined;\n let microphone: MediaStream | undefined;\n let audio: HTMLAudioElement | undefined;\n let micMuted = options.micMuted ?? false;\n let outputMuted = options.outputMuted ?? false;\n let responseId: string | undefined;\n let responseActive = false;\n let pendingResponseCreateEventId: string | undefined;\n let responseQueued = false;\n let queuedResponseInstructions: string | undefined;\n let speaking = false;\n let playbackResponseId: string | undefined;\n const abort = new AbortController();\n const listeners = new Map<\n keyof VoiceSessionEvents,\n Set<(value: never, metadata?: VoiceSessionEventMetadata) => void>\n >();\n const turns = new Map<string, VoiceTranscript>();\n const disposers: (() => void)[] = [];\n\n function emit<E extends keyof VoiceSessionEvents>(\n event: E,\n value: VoiceSessionEvents[E],\n metadata?: VoiceSessionEventMetadata,\n ): void {\n for (const listener of listeners.get(event) ?? []) {\n if (metadata) listener(value as never, metadata);\n else listener(value as never);\n }\n }\n function change(next: VoiceSessionState): void {\n state = next;\n emit('state', next);\n }\n function playback(\n value: boolean,\n metadata?: VoiceSpeakingMetadata,\n identityChanged = false,\n ): void {\n if (speaking === value && !identityChanged) return;\n speaking = value;\n emit('speaking', value, metadata);\n }\n function playbackEvent(\n active: boolean,\n reason: 'started' | 'stopped' | 'cleared',\n providerId: unknown,\n ): void {\n const id = identity(providerId);\n const metadata = { ...(id ? { responseId: id } : {}), reason };\n if (!active && id && playbackResponseId && id !== playbackResponseId) {\n // A late drain for A does not stop the current B playback.\n emit('speaking', speaking, metadata);\n return;\n }\n const changed = active && id !== playbackResponseId;\n playbackResponseId = active ? id : undefined;\n playback(active && !outputMuted, metadata, changed && !outputMuted);\n }\n function cleanup(reason?: 'closed'): void {\n abort.abort();\n for (const dispose of disposers.splice(0)) dispose();\n microphone?.getTracks().forEach((track) => {\n track.stop();\n });\n microphone = undefined;\n if (channel) {\n channel.onopen =\n channel.onmessage =\n channel.onclose =\n channel.onerror =\n null;\n channel.close();\n }\n if (peer) {\n peer.ontrack = peer.onconnectionstatechange = null;\n peer.close();\n }\n if (audio) {\n audio.pause();\n audio.srcObject = null;\n }\n playback(false, {\n ...(playbackResponseId ? { responseId: playbackResponseId } : {}),\n ...(reason ? { reason } : {}),\n });\n playbackResponseId = undefined;\n responseActive = false;\n pendingResponseCreateEventId = undefined;\n responseQueued = false;\n responseId = undefined;\n queuedResponseInstructions = undefined;\n turns.clear();\n }\n function fail(message: string): void {\n if (state === 'closed' || state === 'failed') return;\n state = 'failed';\n cleanup();\n emit('state', state);\n emit('error', new SpeechProviderError(ADAPTER, message));\n }\n function send(event: Record<string, unknown>): void {\n if (state !== 'connected' || channel?.readyState !== 'open')\n throw new SpeechConfigurationError(\n 'Voice session is not connected',\n ADAPTER,\n );\n channel.send(JSON.stringify(event));\n }\n function createResponse(instructions?: string): void {\n // A response is active as soon as the request is sent. Waiting for\n // response.created leaves a gap in which a typed turn can create a\n // conflicting default-conversation response. The event ID lets an error\n // prove that this speculative request was rejected before it became active.\n const eventId = `hv_${crypto.randomUUID().replaceAll('-', '').slice(0, 24)}`;\n responseId = undefined;\n send({\n event_id: eventId,\n type: 'response.create',\n ...(instructions ? { response: { instructions } } : {}),\n });\n pendingResponseCreateEventId = eventId;\n responseActive = true;\n }\n function requestResponse(instructions?: string): void {\n if (responseActive) {\n responseQueued = true;\n if (instructions) queuedResponseInstructions = instructions;\n return;\n }\n responseQueued = false;\n const nextInstructions = instructions ?? queuedResponseInstructions;\n queuedResponseInstructions = undefined;\n createResponse(nextInstructions);\n }\n function releaseQueuedResponse(): void {\n if (!responseQueued || responseActive || state !== 'connected') return;\n responseQueued = false;\n const instructions = queuedResponseInstructions;\n queuedResponseInstructions = undefined;\n createResponse(instructions);\n }\n function transcript(\n itemId: unknown,\n role: VoiceTranscript['role'],\n text: unknown,\n final: boolean,\n delta = false,\n providerResponseId?: unknown,\n ): void {\n if (typeof itemId !== 'string' || typeof text !== 'string') return;\n const prior = turns.get(itemId);\n // Late duplicates/deltas must not overwrite a final turn or its role.\n if (prior?.final || (prior && prior.role !== role)) return;\n const turn = {\n itemId,\n role,\n text: delta ? (prior?.text ?? '') + text : text,\n final,\n ...((identity(providerResponseId) ?? prior?.responseId)\n ? { responseId: identity(providerResponseId) ?? prior?.responseId }\n : {}),\n };\n turns.set(itemId, turn);\n emit('transcript', turn);\n }\n function receive(data: unknown): void {\n let event: Record<string, unknown>;\n try {\n const parsed: unknown =\n typeof data === 'string' ? JSON.parse(data) : data;\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))\n return;\n event = parsed as Record<string, unknown>;\n } catch {\n return;\n }\n switch (event.type) {\n case 'conversation.item.input_audio_transcription.delta':\n transcript(event.item_id, 'user', event.delta, false, true);\n break;\n case 'conversation.item.input_audio_transcription.completed':\n transcript(event.item_id, 'user', event.transcript, true);\n break;\n case 'conversation.item.input_audio_transcription.failed':\n emit(\n 'error',\n new SpeechProviderError(ADAPTER, 'Input transcription failed'),\n );\n break;\n case 'response.output_audio_transcript.delta':\n transcript(\n event.item_id,\n 'assistant',\n event.delta,\n false,\n true,\n event.response_id,\n );\n break;\n case 'response.output_audio_transcript.done':\n transcript(\n event.item_id,\n 'assistant',\n event.transcript,\n true,\n false,\n event.response_id,\n );\n break;\n case 'response.output_text.delta':\n transcript(\n event.item_id,\n 'assistant',\n event.delta,\n false,\n true,\n event.response_id,\n );\n break;\n case 'response.output_text.done':\n transcript(\n event.item_id,\n 'assistant',\n event.text,\n true,\n false,\n event.response_id,\n );\n break;\n case 'input_audio_buffer.speech_started':\n emit('inputSpeech', true, {\n itemId:\n typeof event.item_id === 'string' && event.item_id\n ? event.item_id\n : undefined,\n });\n break;\n case 'input_audio_buffer.speech_stopped':\n emit('inputSpeech', false, {\n itemId:\n typeof event.item_id === 'string' && event.item_id\n ? event.item_id\n : undefined,\n });\n break;\n case 'output_audio_buffer.started':\n playbackEvent(true, 'started', event.response_id);\n break;\n case 'output_audio_buffer.stopped':\n playbackEvent(false, 'stopped', event.response_id);\n break;\n case 'output_audio_buffer.cleared':\n playbackEvent(false, 'cleared', event.response_id);\n break;\n case 'response.created': {\n const response = record(event.response);\n responseId = typeof response.id === 'string' ? response.id : undefined;\n pendingResponseCreateEventId = undefined;\n responseActive = true;\n emit('response', { active: true, responseId });\n break;\n }\n case 'response.done': {\n const response = record(event.response);\n const wasActive = responseActive;\n pendingResponseCreateEventId = undefined;\n responseActive = false;\n emit('response', { active: false, responseId });\n if (response.usage && typeof response.usage === 'object')\n emit('usage', record(response.usage));\n if (response.status === 'failed' || response.status === 'incomplete')\n emit(\n 'error',\n new SpeechProviderError(\n ADAPTER,\n response.status === 'incomplete'\n ? 'Voice response incomplete'\n : 'Voice response failed',\n ),\n );\n // A response listener may synchronously request a tool continuation.\n // That request consumes the coalesced typed turn, so only drain when it\n // remains pending after listeners have run.\n if (wasActive) releaseQueuedResponse();\n break;\n }\n case 'response.function_call_arguments.done':\n if (\n typeof event.call_id === 'string' &&\n typeof event.name === 'string' &&\n typeof event.arguments === 'string'\n )\n emit('toolCall', {\n callId: event.call_id,\n name: event.name,\n arguments: event.arguments,\n });\n break;\n case 'error': {\n const error = record(event.error);\n const rejectedPendingResponse =\n pendingResponseCreateEventId !== undefined &&\n error.event_id === pendingResponseCreateEventId;\n if (rejectedPendingResponse) {\n pendingResponseCreateEventId = undefined;\n responseActive = false;\n responseId = undefined;\n }\n emit(\n 'error',\n new SpeechProviderError(\n ADAPTER,\n 'Realtime provider rejected a conversation event',\n ),\n );\n // Error handlers can synchronously request a retry. If none did, a\n // correlated rejection must release the coalesced typed response.\n if (rejectedPendingResponse) releaseQueuedResponse();\n break;\n }\n }\n }\n\n const session: OpenAIWebRTCVoiceSession = {\n get state() {\n return state;\n },\n connect() {\n if (pending) return pending;\n if (state === 'connected') return Promise.resolve();\n if (state !== 'idle')\n return Promise.reject(\n new SpeechConfigurationError(\n 'Create a new voice session after close or failure',\n ADAPTER,\n ),\n );\n const timeoutMs = options.connectTimeoutMs ?? 20_000;\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)\n return Promise.reject(\n new SpeechConfigurationError(\n 'connectTimeoutMs must be positive',\n ADAPTER,\n ),\n );\n change('connecting');\n const signal = AbortSignal.any([\n abort.signal,\n ...(options.signal ? [options.signal] : []),\n ]);\n pending = (async () => {\n const timeout = setTimeout(\n () => fail('Voice connection timed out'),\n timeoutMs,\n );\n const onAbort = () => {\n if (state !== 'closed' && state !== 'failed')\n fail('Voice connection cancelled');\n };\n signal.addEventListener('abort', onAbort, { once: true });\n disposers.push(() => signal.removeEventListener('abort', onAbort));\n try {\n signal.throwIfAborted();\n // Permission acquisition cannot be aborted; dispose even if it resolves after close.\n const acquisition = options.getMicrophone().then((stream) => {\n if (signal.aborted)\n stream.getTracks().forEach((track) => {\n track.stop();\n });\n else microphone = stream;\n return stream;\n });\n const stream = await abortable(acquisition, signal);\n signal.throwIfAborted();\n stream.getAudioTracks().forEach((track) => {\n track.enabled = !micMuted;\n const ended = () =>\n fail('Microphone ended; allow access and start a new session');\n track.addEventListener('ended', ended);\n disposers.push(() => track.removeEventListener('ended', ended));\n if (track.readyState === 'ended')\n throw new SpeechConfigurationError(\n 'Microphone stream has ended',\n ADAPTER,\n );\n });\n if (!stream.getAudioTracks().length)\n throw new SpeechConfigurationError(\n 'Microphone stream has no audio tracks',\n ADAPTER,\n );\n peer = options.createPeerConnection?.() ?? new RTCPeerConnection();\n audio = options.audio ?? new Audio();\n audio.autoplay = true;\n audio.muted = outputMuted;\n peer.ontrack = (event) => {\n if (!audio || signal.aborted) return;\n audio.srcObject =\n event.streams[0] ?? new MediaStream([event.track]);\n void audio\n .play()\n .catch(() =>\n emit(\n 'error',\n new SpeechProviderError(\n ADAPTER,\n 'Audio playback needs a user gesture',\n ),\n ),\n );\n };\n peer.onconnectionstatechange = () => {\n if (\n peer?.connectionState === 'failed' ||\n peer?.connectionState === 'disconnected'\n )\n fail('Voice connection lost; start a new session');\n };\n for (const track of stream.getAudioTracks())\n peer.addTrack(track, stream);\n channel = peer.createDataChannel('oai-events');\n channel.onmessage = (event) => receive(event.data);\n // Attach before SDP negotiation; some transports open during setRemoteDescription.\n const opened = new Promise<void>((resolve, reject) => {\n const cancel = () =>\n reject(\n new SpeechProviderError(ADAPTER, 'Voice connection cancelled'),\n );\n signal.addEventListener('abort', cancel, { once: true });\n disposers.push(() => signal.removeEventListener('abort', cancel));\n if (channel) {\n channel.onopen = () => {\n signal.removeEventListener('abort', cancel);\n resolve();\n };\n channel.onclose = () => {\n fail('Voice channel closed');\n reject(\n new SpeechProviderError(ADAPTER, 'Voice channel closed'),\n );\n };\n channel.onerror = () => {\n fail('Voice channel failed');\n reject(\n new SpeechProviderError(ADAPTER, 'Voice channel failed'),\n );\n };\n }\n });\n // Keep an early rejection handled while negotiation is pending.\n void opened.catch(() => undefined);\n const offer = await abortable(peer.createOffer(), signal);\n await abortable(peer.setLocalDescription(offer), signal);\n signal.throwIfAborted();\n if (!offer.sdp)\n throw new SpeechConfigurationError(\n 'WebRTC did not produce an SDP offer',\n ADAPTER,\n );\n const answer = await abortable(\n options.negotiate(offer.sdp, signal),\n signal,\n );\n signal.throwIfAborted();\n await abortable(\n peer.setRemoteDescription({ type: 'answer', sdp: answer }),\n signal,\n );\n await opened;\n signal.throwIfAborted();\n change('connected');\n } catch (error) {\n fail('Unable to connect voice session');\n throw error;\n } finally {\n clearTimeout(timeout);\n }\n })().finally(() => {\n pending = undefined;\n });\n return pending;\n },\n sendText(text) {\n const trimmed = text.trim();\n if (!trimmed || trimmed.length > 16_000)\n throw new SpeechConfigurationError(\n 'Text must contain 1–16000 characters',\n ADAPTER,\n );\n const id = `hv_${crypto.randomUUID().replaceAll('-', '').slice(0, 24)}`;\n send({\n type: 'conversation.item.create',\n item: {\n id,\n type: 'message',\n role: 'user',\n content: [{ type: 'input_text', text: trimmed }],\n },\n });\n transcript(id, 'user', trimmed, true);\n session.respond();\n return id;\n },\n appendAssistantContext(text) {\n const trimmed = text.trim();\n if (!trimmed || trimmed.length > 16_000)\n throw new SpeechConfigurationError(\n 'Text must contain 1–16000 characters',\n ADAPTER,\n );\n const id = `hv_${crypto.randomUUID().replaceAll('-', '').slice(0, 24)}`;\n send({\n type: 'conversation.item.create',\n item: {\n id,\n type: 'message',\n role: 'assistant',\n content: [{ type: 'output_text', text: trimmed }],\n },\n });\n return id;\n },\n commitInput(respond = true) {\n send({ type: 'input_audio_buffer.commit' });\n if (respond) session.respond();\n },\n respond(instructions) {\n requestResponse(instructions);\n },\n interrupt() {\n if (state !== 'connected') return;\n if (responseActive)\n send({\n type: 'response.cancel',\n ...(responseId ? { response_id: responseId } : {}),\n });\n send({ type: 'output_audio_buffer.clear' });\n playback(false, {\n ...(playbackResponseId ? { responseId: playbackResponseId } : {}),\n reason: 'interrupted',\n });\n playbackResponseId = undefined;\n },\n setMicMuted(muted) {\n micMuted = muted;\n microphone?.getAudioTracks().forEach((track) => {\n track.enabled = !muted;\n });\n if (muted && state === 'connected')\n send({ type: 'input_audio_buffer.clear' });\n },\n setOutputMuted(muted) {\n outputMuted = muted;\n if (audio) audio.muted = muted;\n if (muted)\n playback(false, {\n ...(playbackResponseId ? { responseId: playbackResponseId } : {}),\n reason: 'muted',\n });\n },\n submitToolResult(callId, result) {\n send({\n type: 'conversation.item.create',\n item: {\n type: 'function_call_output',\n call_id: callId,\n output: JSON.stringify(result),\n },\n });\n },\n close() {\n if (state === 'closed') return;\n state = 'closed';\n cleanup('closed');\n emit('state', state);\n listeners.clear();\n },\n on(event, listener) {\n const set = listeners.get(event) ?? new Set();\n set.add(\n listener as (\n value: never,\n metadata?: VoiceSessionEventMetadata,\n ) => void,\n );\n listeners.set(event, set);\n return () => {\n set.delete(\n listener as (\n value: never,\n metadata?: VoiceSessionEventMetadata,\n ) => void,\n );\n };\n },\n };\n return session;\n}\n\nfunction identity(value: unknown): string | undefined {\n return typeof value === 'string' && value ? value : undefined;\n}\n\nfunction record(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\nfunction abortable<T>(promise: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) return Promise.reject(signal.reason);\n return new Promise((resolve, reject) => {\n const cancel = () => reject(signal.reason);\n signal.addEventListener('abort', cancel, { once: true });\n promise\n .then(resolve, reject)\n .finally(() => signal.removeEventListener('abort', cancel));\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"conversation.js","names":[],"sources":["../src/conversation/openai-webrtc.ts"],"sourcesContent":["import {\n SpeechConfigurationError,\n SpeechProviderError,\n} from '../shared/errors.js';\nimport type {\n OpenAIWebRTCVoiceOptions,\n OpenAIWebRTCVoiceSession,\n VoiceSessionEventMetadata,\n VoiceSessionEvents,\n VoiceSessionState,\n VoiceSpeakingMetadata,\n VoiceTranscript,\n} from './types.js';\n\nconst ADAPTER = 'openai-conversation';\n\n/** Browser WebRTC transport. No credentials, Node dependencies or globals are read at import. */\nexport function createOpenAIWebRTCVoiceSession(\n options: OpenAIWebRTCVoiceOptions,\n): OpenAIWebRTCVoiceSession {\n let state: VoiceSessionState = 'idle';\n let pending: Promise<void> | undefined;\n let peer: RTCPeerConnection | undefined;\n let channel: RTCDataChannel | undefined;\n let microphone: MediaStream | undefined;\n let audio: HTMLAudioElement | undefined;\n let micMuted = options.micMuted ?? false;\n let outputMuted = options.outputMuted ?? false;\n let responseId: string | undefined;\n let responseActive = false;\n let pendingResponseCreateEventId: string | undefined;\n let responseQueued = false;\n let queuedResponseInstructions: string | undefined;\n let speaking = false;\n let playbackResponseId: string | undefined;\n const abort = new AbortController();\n const listeners = new Map<\n keyof VoiceSessionEvents,\n Set<(value: never, metadata?: VoiceSessionEventMetadata) => void>\n >();\n const turns = new Map<string, VoiceTranscript>();\n const disposers: (() => void)[] = [];\n\n function emit<E extends keyof VoiceSessionEvents>(\n event: E,\n value: VoiceSessionEvents[E],\n metadata?: VoiceSessionEventMetadata,\n ): void {\n for (const listener of listeners.get(event) ?? []) {\n if (metadata) listener(value as never, metadata);\n else listener(value as never);\n }\n }\n function change(next: VoiceSessionState): void {\n state = next;\n emit('state', next);\n }\n function playback(\n value: boolean,\n metadata?: VoiceSpeakingMetadata,\n identityChanged = false,\n ): void {\n if (speaking === value && !identityChanged) return;\n speaking = value;\n emit('speaking', value, metadata);\n }\n function playbackEvent(\n active: boolean,\n reason: 'started' | 'stopped' | 'cleared',\n providerId: unknown,\n ): void {\n const id = identity(providerId);\n const metadata = { ...(id ? { responseId: id } : {}), reason };\n if (!active && id && playbackResponseId && id !== playbackResponseId) {\n // A late drain for A does not stop the current B playback.\n emit('speaking', speaking, metadata);\n return;\n }\n const changed = active && id !== playbackResponseId;\n playbackResponseId = active ? id : undefined;\n playback(active && !outputMuted, metadata, changed && !outputMuted);\n }\n function cleanup(reason?: 'closed'): void {\n abort.abort();\n for (const dispose of disposers.splice(0)) dispose();\n microphone?.getTracks().forEach((track) => {\n track.stop();\n });\n microphone = undefined;\n if (channel) {\n channel.onopen =\n channel.onmessage =\n channel.onclose =\n channel.onerror =\n null;\n channel.close();\n }\n if (peer) {\n peer.ontrack = peer.onconnectionstatechange = null;\n peer.close();\n }\n if (audio) {\n audio.pause();\n audio.srcObject = null;\n }\n playback(false, {\n ...(playbackResponseId ? { responseId: playbackResponseId } : {}),\n ...(reason ? { reason } : {}),\n });\n playbackResponseId = undefined;\n responseActive = false;\n pendingResponseCreateEventId = undefined;\n responseQueued = false;\n responseId = undefined;\n queuedResponseInstructions = undefined;\n turns.clear();\n }\n function fail(message: string): void {\n if (state === 'closed' || state === 'failed') return;\n state = 'failed';\n cleanup();\n emit('state', state);\n emit('error', new SpeechProviderError(ADAPTER, message));\n }\n function send(event: Record<string, unknown>): void {\n if (state !== 'connected' || channel?.readyState !== 'open')\n throw new SpeechConfigurationError(\n 'Voice session is not connected',\n ADAPTER,\n );\n channel.send(JSON.stringify(event));\n }\n function createResponse(instructions?: string): void {\n // A response is active as soon as the request is sent. Waiting for\n // response.created leaves a gap in which a typed turn can create a\n // conflicting default-conversation response. The event ID lets an error\n // prove that this speculative request was rejected before it became active.\n const eventId = `hv_${crypto.randomUUID().replaceAll('-', '').slice(0, 24)}`;\n responseId = undefined;\n send({\n event_id: eventId,\n type: 'response.create',\n ...(instructions ? { response: { instructions } } : {}),\n });\n pendingResponseCreateEventId = eventId;\n responseActive = true;\n }\n function requestResponse(instructions?: string): void {\n if (responseActive) {\n responseQueued = true;\n if (instructions) queuedResponseInstructions = instructions;\n return;\n }\n responseQueued = false;\n const nextInstructions = instructions ?? queuedResponseInstructions;\n queuedResponseInstructions = undefined;\n createResponse(nextInstructions);\n }\n function releaseQueuedResponse(): void {\n if (!responseQueued || responseActive || state !== 'connected') return;\n responseQueued = false;\n const instructions = queuedResponseInstructions;\n queuedResponseInstructions = undefined;\n createResponse(instructions);\n }\n function transcript(\n itemId: unknown,\n role: VoiceTranscript['role'],\n text: unknown,\n final: boolean,\n delta = false,\n providerResponseId?: unknown,\n ): void {\n if (typeof itemId !== 'string' || typeof text !== 'string') return;\n const prior = turns.get(itemId);\n // Late duplicates/deltas must not overwrite a final turn or its role.\n if (prior?.final || (prior && prior.role !== role)) return;\n const turn = {\n itemId,\n role,\n text: delta ? (prior?.text ?? '') + text : text,\n final,\n ...((identity(providerResponseId) ?? prior?.responseId)\n ? { responseId: identity(providerResponseId) ?? prior?.responseId }\n : {}),\n };\n turns.set(itemId, turn);\n emit('transcript', turn);\n }\n function receive(data: unknown): void {\n let event: Record<string, unknown>;\n try {\n const parsed: unknown =\n typeof data === 'string' ? JSON.parse(data) : data;\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))\n return;\n event = parsed as Record<string, unknown>;\n } catch {\n return;\n }\n switch (event.type) {\n case 'conversation.item.input_audio_transcription.delta':\n transcript(event.item_id, 'user', event.delta, false, true);\n break;\n case 'conversation.item.input_audio_transcription.completed':\n transcript(event.item_id, 'user', event.transcript, true);\n break;\n case 'conversation.item.input_audio_transcription.failed':\n emit(\n 'error',\n new SpeechProviderError(ADAPTER, 'Input transcription failed'),\n );\n break;\n case 'response.output_audio_transcript.delta':\n transcript(\n event.item_id,\n 'assistant',\n event.delta,\n false,\n true,\n event.response_id,\n );\n break;\n case 'response.output_audio_transcript.done':\n transcript(\n event.item_id,\n 'assistant',\n event.transcript,\n true,\n false,\n event.response_id,\n );\n break;\n case 'response.output_text.delta':\n transcript(\n event.item_id,\n 'assistant',\n event.delta,\n false,\n true,\n event.response_id,\n );\n break;\n case 'response.output_text.done':\n transcript(\n event.item_id,\n 'assistant',\n event.text,\n true,\n false,\n event.response_id,\n );\n break;\n case 'input_audio_buffer.speech_started':\n emit('inputSpeech', true, {\n itemId:\n typeof event.item_id === 'string' && event.item_id\n ? event.item_id\n : undefined,\n });\n break;\n case 'input_audio_buffer.speech_stopped':\n emit('inputSpeech', false, {\n itemId:\n typeof event.item_id === 'string' && event.item_id\n ? event.item_id\n : undefined,\n });\n break;\n case 'output_audio_buffer.started':\n playbackEvent(true, 'started', event.response_id);\n break;\n case 'output_audio_buffer.stopped':\n playbackEvent(false, 'stopped', event.response_id);\n break;\n case 'output_audio_buffer.cleared':\n playbackEvent(false, 'cleared', event.response_id);\n break;\n case 'response.created': {\n const response = record(event.response);\n responseId = typeof response.id === 'string' ? response.id : undefined;\n pendingResponseCreateEventId = undefined;\n responseActive = true;\n emit('response', { active: true, responseId });\n break;\n }\n case 'response.done': {\n const response = record(event.response);\n const wasActive = responseActive;\n pendingResponseCreateEventId = undefined;\n responseActive = false;\n const status = response.status;\n const safeStatus =\n status === 'completed' ||\n status === 'incomplete' ||\n status === 'cancelled' ||\n status === 'failed'\n ? status\n : undefined;\n const reason = record(response.status_details).reason;\n const safeReason =\n (status === 'incomplete' &&\n (reason === 'max_output_tokens' || reason === 'content_filter')) ||\n (status === 'cancelled' &&\n (reason === 'turn_detected' || reason === 'client_cancelled'))\n ? reason\n : undefined;\n emit('response', {\n active: false,\n responseId,\n ...(safeStatus ? { status: safeStatus } : {}),\n ...(safeReason ? { statusReason: safeReason } : {}),\n });\n\n if (response.usage && typeof response.usage === 'object')\n emit('usage', record(response.usage));\n if (response.status === 'failed' || response.status === 'incomplete')\n emit(\n 'error',\n new SpeechProviderError(\n ADAPTER,\n response.status === 'incomplete'\n ? 'Voice response incomplete'\n : 'Voice response failed',\n ),\n );\n // A response listener may synchronously request a tool continuation.\n // That request consumes the coalesced typed turn, so only drain when it\n // remains pending after listeners have run.\n if (wasActive) releaseQueuedResponse();\n break;\n }\n case 'response.function_call_arguments.done':\n if (\n typeof event.call_id === 'string' &&\n typeof event.name === 'string' &&\n typeof event.arguments === 'string'\n )\n emit('toolCall', {\n callId: event.call_id,\n name: event.name,\n arguments: event.arguments,\n });\n break;\n case 'error': {\n const error = record(event.error);\n const rejectedPendingResponse =\n pendingResponseCreateEventId !== undefined &&\n error.event_id === pendingResponseCreateEventId;\n if (rejectedPendingResponse) {\n pendingResponseCreateEventId = undefined;\n responseActive = false;\n responseId = undefined;\n }\n emit(\n 'error',\n new SpeechProviderError(\n ADAPTER,\n 'Realtime provider rejected a conversation event',\n ),\n );\n // Error handlers can synchronously request a retry. If none did, a\n // correlated rejection must release the coalesced typed response.\n if (rejectedPendingResponse) releaseQueuedResponse();\n break;\n }\n }\n }\n\n const session: OpenAIWebRTCVoiceSession = {\n get state() {\n return state;\n },\n connect() {\n if (pending) return pending;\n if (state === 'connected') return Promise.resolve();\n if (state !== 'idle')\n return Promise.reject(\n new SpeechConfigurationError(\n 'Create a new voice session after close or failure',\n ADAPTER,\n ),\n );\n const timeoutMs = options.connectTimeoutMs ?? 20_000;\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)\n return Promise.reject(\n new SpeechConfigurationError(\n 'connectTimeoutMs must be positive',\n ADAPTER,\n ),\n );\n change('connecting');\n const signal = AbortSignal.any([\n abort.signal,\n ...(options.signal ? [options.signal] : []),\n ]);\n pending = (async () => {\n const timeout = setTimeout(\n () => fail('Voice connection timed out'),\n timeoutMs,\n );\n const onAbort = () => {\n if (state !== 'closed' && state !== 'failed')\n fail('Voice connection cancelled');\n };\n signal.addEventListener('abort', onAbort, { once: true });\n disposers.push(() => signal.removeEventListener('abort', onAbort));\n try {\n signal.throwIfAborted();\n // Permission acquisition cannot be aborted; dispose even if it resolves after close.\n const acquisition = options.getMicrophone().then((stream) => {\n if (signal.aborted)\n stream.getTracks().forEach((track) => {\n track.stop();\n });\n else microphone = stream;\n return stream;\n });\n const stream = await abortable(acquisition, signal);\n signal.throwIfAborted();\n stream.getAudioTracks().forEach((track) => {\n track.enabled = !micMuted;\n const ended = () =>\n fail('Microphone ended; allow access and start a new session');\n track.addEventListener('ended', ended);\n disposers.push(() => track.removeEventListener('ended', ended));\n if (track.readyState === 'ended')\n throw new SpeechConfigurationError(\n 'Microphone stream has ended',\n ADAPTER,\n );\n });\n if (!stream.getAudioTracks().length)\n throw new SpeechConfigurationError(\n 'Microphone stream has no audio tracks',\n ADAPTER,\n );\n peer = options.createPeerConnection?.() ?? new RTCPeerConnection();\n audio = options.audio ?? new Audio();\n audio.autoplay = true;\n audio.muted = outputMuted;\n peer.ontrack = (event) => {\n if (!audio || signal.aborted) return;\n audio.srcObject =\n event.streams[0] ?? new MediaStream([event.track]);\n void audio\n .play()\n .catch(() =>\n emit(\n 'error',\n new SpeechProviderError(\n ADAPTER,\n 'Audio playback needs a user gesture',\n ),\n ),\n );\n };\n peer.onconnectionstatechange = () => {\n if (\n peer?.connectionState === 'failed' ||\n peer?.connectionState === 'disconnected'\n )\n fail('Voice connection lost; start a new session');\n };\n for (const track of stream.getAudioTracks())\n peer.addTrack(track, stream);\n channel = peer.createDataChannel('oai-events');\n channel.onmessage = (event) => receive(event.data);\n // Attach before SDP negotiation; some transports open during setRemoteDescription.\n const opened = new Promise<void>((resolve, reject) => {\n const cancel = () =>\n reject(\n new SpeechProviderError(ADAPTER, 'Voice connection cancelled'),\n );\n signal.addEventListener('abort', cancel, { once: true });\n disposers.push(() => signal.removeEventListener('abort', cancel));\n if (channel) {\n channel.onopen = () => {\n signal.removeEventListener('abort', cancel);\n resolve();\n };\n channel.onclose = () => {\n fail('Voice channel closed');\n reject(\n new SpeechProviderError(ADAPTER, 'Voice channel closed'),\n );\n };\n channel.onerror = () => {\n fail('Voice channel failed');\n reject(\n new SpeechProviderError(ADAPTER, 'Voice channel failed'),\n );\n };\n }\n });\n // Keep an early rejection handled while negotiation is pending.\n void opened.catch(() => undefined);\n const offer = await abortable(peer.createOffer(), signal);\n await abortable(peer.setLocalDescription(offer), signal);\n signal.throwIfAborted();\n if (!offer.sdp)\n throw new SpeechConfigurationError(\n 'WebRTC did not produce an SDP offer',\n ADAPTER,\n );\n const answer = await abortable(\n options.negotiate(offer.sdp, signal),\n signal,\n );\n signal.throwIfAborted();\n await abortable(\n peer.setRemoteDescription({ type: 'answer', sdp: answer }),\n signal,\n );\n await opened;\n signal.throwIfAborted();\n change('connected');\n } catch (error) {\n fail('Unable to connect voice session');\n throw error;\n } finally {\n clearTimeout(timeout);\n }\n })().finally(() => {\n pending = undefined;\n });\n return pending;\n },\n sendText(text) {\n const trimmed = text.trim();\n if (!trimmed || trimmed.length > 16_000)\n throw new SpeechConfigurationError(\n 'Text must contain 1–16000 characters',\n ADAPTER,\n );\n const id = `hv_${crypto.randomUUID().replaceAll('-', '').slice(0, 24)}`;\n send({\n type: 'conversation.item.create',\n item: {\n id,\n type: 'message',\n role: 'user',\n content: [{ type: 'input_text', text: trimmed }],\n },\n });\n transcript(id, 'user', trimmed, true);\n session.respond();\n return id;\n },\n appendAssistantContext(text) {\n const trimmed = text.trim();\n if (!trimmed || trimmed.length > 16_000)\n throw new SpeechConfigurationError(\n 'Text must contain 1–16000 characters',\n ADAPTER,\n );\n const id = `hv_${crypto.randomUUID().replaceAll('-', '').slice(0, 24)}`;\n send({\n type: 'conversation.item.create',\n item: {\n id,\n type: 'message',\n role: 'assistant',\n content: [{ type: 'output_text', text: trimmed }],\n },\n });\n return id;\n },\n commitInput(respond = true) {\n send({ type: 'input_audio_buffer.commit' });\n if (respond) session.respond();\n },\n respond(instructions) {\n requestResponse(instructions);\n },\n interrupt() {\n if (state !== 'connected') return;\n if (responseActive)\n send({\n type: 'response.cancel',\n ...(responseId ? { response_id: responseId } : {}),\n });\n send({ type: 'output_audio_buffer.clear' });\n playback(false, {\n ...(playbackResponseId ? { responseId: playbackResponseId } : {}),\n reason: 'interrupted',\n });\n playbackResponseId = undefined;\n },\n setMicMuted(muted) {\n micMuted = muted;\n microphone?.getAudioTracks().forEach((track) => {\n track.enabled = !muted;\n });\n if (muted && state === 'connected')\n send({ type: 'input_audio_buffer.clear' });\n },\n setOutputMuted(muted) {\n outputMuted = muted;\n if (audio) audio.muted = muted;\n if (muted)\n playback(false, {\n ...(playbackResponseId ? { responseId: playbackResponseId } : {}),\n reason: 'muted',\n });\n },\n submitToolResult(callId, result) {\n send({\n type: 'conversation.item.create',\n item: {\n type: 'function_call_output',\n call_id: callId,\n output: JSON.stringify(result),\n },\n });\n },\n close() {\n if (state === 'closed') return;\n state = 'closed';\n cleanup('closed');\n emit('state', state);\n listeners.clear();\n },\n on(event, listener) {\n const set = listeners.get(event) ?? new Set();\n set.add(\n listener as (\n value: never,\n metadata?: VoiceSessionEventMetadata,\n ) => void,\n );\n listeners.set(event, set);\n return () => {\n set.delete(\n listener as (\n value: never,\n metadata?: VoiceSessionEventMetadata,\n ) => void,\n );\n };\n },\n };\n return session;\n}\n\nfunction identity(value: unknown): string | undefined {\n return typeof value === 'string' && value ? value : undefined;\n}\n\nfunction record(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\nfunction abortable<T>(promise: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) return Promise.reject(signal.reason);\n return new Promise((resolve, reject) => {\n const cancel = () => reject(signal.reason);\n signal.addEventListener('abort', cancel, { once: true });\n promise\n .then(resolve, reject)\n .finally(() => signal.removeEventListener('abort', cancel));\n });\n}\n"],"mappings":"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"}
|