@happyvertical/speech 0.105.10 → 0.105.11

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
@@ -535,4 +535,6 @@ Assistant transcripts expose optional provider `responseId`. `speaking` retains
535
535
 
536
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
537
 
538
+ An already-finished cancellation (`response_cancel_not_active`) is ignored only when nested `error.event_id` matches an outstanding cancellation issued by this session. Correlation is consumed once, bounded to the latest 32 requests, and cleared on close. Missing, unmatched, replayed, evicted or other errors remain visible.
539
+
538
540
  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,CAgnB1B"}
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;AAKpB,iGAAiG;AACjG,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,wBAAwB,GAChC,wBAAwB,CAkoB1B"}
@@ -1,6 +1,7 @@
1
1
  import { a as SpeechProviderError, r as SpeechConfigurationError } from "./chunks/errors-Cs0vX-mV.js";
2
2
  //#region src/conversation/openai-webrtc.ts
3
3
  var ADAPTER = "openai-conversation";
4
+ var MAX_PENDING_CANCELLATIONS = 32;
4
5
  /** Browser WebRTC transport. No credentials, Node dependencies or globals are read at import. */
5
6
  function createOpenAIWebRTCVoiceSession(options) {
6
7
  let state = "idle";
@@ -14,6 +15,7 @@ function createOpenAIWebRTCVoiceSession(options) {
14
15
  let responseId;
15
16
  let responseActive = false;
16
17
  let pendingResponseCreateEventId;
18
+ const pendingCancellations = /* @__PURE__ */ new Set();
17
19
  let responseQueued = false;
18
20
  let queuedResponseInstructions;
19
21
  let speaking = false;
@@ -79,6 +81,7 @@ function createOpenAIWebRTCVoiceSession(options) {
79
81
  responseId = void 0;
80
82
  queuedResponseInstructions = void 0;
81
83
  turns.clear();
84
+ pendingCancellations.clear();
82
85
  }
83
86
  function fail(message) {
84
87
  if (state === "closed" || state === "failed") return;
@@ -220,6 +223,7 @@ function createOpenAIWebRTCVoiceSession(options) {
220
223
  break;
221
224
  case "error": {
222
225
  const error = record(event.error);
226
+ if (typeof error.event_id === "string" && pendingCancellations.delete(error.event_id) && error.code === "response_cancel_not_active") break;
223
227
  const rejectedPendingResponse = pendingResponseCreateEventId !== void 0 && error.event_id === pendingResponseCreateEventId;
224
228
  if (rejectedPendingResponse) {
225
229
  pendingResponseCreateEventId = void 0;
@@ -375,10 +379,19 @@ function createOpenAIWebRTCVoiceSession(options) {
375
379
  },
376
380
  interrupt() {
377
381
  if (state !== "connected") return;
378
- if (responseActive) send({
379
- type: "response.cancel",
380
- ...responseId ? { response_id: responseId } : {}
381
- });
382
+ if (responseActive) {
383
+ const eventId = `hv_${crypto.randomUUID().replaceAll("-", "").slice(0, 24)}`;
384
+ send({
385
+ event_id: eventId,
386
+ type: "response.cancel",
387
+ ...responseId ? { response_id: responseId } : {}
388
+ });
389
+ pendingCancellations.add(eventId);
390
+ if (pendingCancellations.size > MAX_PENDING_CANCELLATIONS) {
391
+ const oldest = pendingCancellations.values().next().value;
392
+ if (oldest) pendingCancellations.delete(oldest);
393
+ }
394
+ }
382
395
  send({ type: "output_audio_buffer.clear" });
383
396
  playback(false, {
384
397
  ...playbackResponseId ? { responseId: playbackResponseId } : {},
@@ -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 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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"}
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';\nconst MAX_PENDING_CANCELLATIONS = 32;\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 const pendingCancellations = new Set<string>();\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 pendingCancellations.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 // Only nested error.event_id identifies the client request; the outer\n // event_id identifies the server event and grants no correlation.\n const ownCancellation =\n typeof error.event_id === 'string' &&\n pendingCancellations.delete(error.event_id);\n if (ownCancellation && error.code === 'response_cancel_not_active')\n break;\n\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 const eventId = `hv_${crypto.randomUUID().replaceAll('-', '').slice(0, 24)}`;\n send({\n event_id: eventId,\n type: 'response.cancel',\n ...(responseId ? { response_id: responseId } : {}),\n });\n pendingCancellations.add(eventId);\n if (pendingCancellations.size > MAX_PENDING_CANCELLATIONS) {\n const oldest = pendingCancellations.values().next().value;\n if (oldest) pendingCancellations.delete(oldest);\n }\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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/speech",
3
- "version": "0.105.10",
3
+ "version": "0.105.11",
4
4
  "description": "Speech provider abstraction for STT and TTS backends",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",