@happyvertical/speech 0.105.9 → 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
@@ -533,4 +533,8 @@ 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
+
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
+
536
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,CA0lB1B"}
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"}
@@ -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":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/conversation/types.ts"],"names":[],"mappings":"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"}
@@ -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;
@@ -196,9 +199,15 @@ function createOpenAIWebRTCVoiceSession(options) {
196
199
  const wasActive = responseActive;
197
200
  pendingResponseCreateEventId = void 0;
198
201
  responseActive = false;
202
+ const status = response.status;
203
+ const safeStatus = status === "completed" || status === "incomplete" || status === "cancelled" || status === "failed" ? status : void 0;
204
+ const reason = record(response.status_details).reason;
205
+ const safeReason = status === "incomplete" && (reason === "max_output_tokens" || reason === "content_filter") || status === "cancelled" && (reason === "turn_detected" || reason === "client_cancelled") ? reason : void 0;
199
206
  emit("response", {
200
207
  active: false,
201
- responseId
208
+ responseId,
209
+ ...safeStatus ? { status: safeStatus } : {},
210
+ ...safeReason ? { statusReason: safeReason } : {}
202
211
  });
203
212
  if (response.usage && typeof response.usage === "object") emit("usage", record(response.usage));
204
213
  if (response.status === "failed" || response.status === "incomplete") emit("error", new SpeechProviderError(ADAPTER, response.status === "incomplete" ? "Voice response incomplete" : "Voice response failed"));
@@ -214,6 +223,7 @@ function createOpenAIWebRTCVoiceSession(options) {
214
223
  break;
215
224
  case "error": {
216
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;
217
227
  const rejectedPendingResponse = pendingResponseCreateEventId !== void 0 && error.event_id === pendingResponseCreateEventId;
218
228
  if (rejectedPendingResponse) {
219
229
  pendingResponseCreateEventId = void 0;
@@ -369,10 +379,19 @@ function createOpenAIWebRTCVoiceSession(options) {
369
379
  },
370
380
  interrupt() {
371
381
  if (state !== "connected") return;
372
- if (responseActive) send({
373
- type: "response.cancel",
374
- ...responseId ? { response_id: responseId } : {}
375
- });
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
+ }
376
395
  send({ type: "output_audio_buffer.clear" });
377
396
  playback(false, {
378
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 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';\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.9",
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",