@livekit/agents-plugin-openai 1.0.2 → 1.0.3

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.
@@ -752,6 +752,7 @@ class RealtimeSession extends import_agents.llm.RealtimeSession {
752
752
  });
753
753
  }
754
754
  handleResponseCreated(event) {
755
+ var _a;
755
756
  if (!event.response.id) {
756
757
  throw new Error("response.id is missing");
757
758
  }
@@ -762,17 +763,23 @@ class RealtimeSession extends import_agents.llm.RealtimeSession {
762
763
  _doneFut: new import_agents.Future(),
763
764
  _createdTimestamp: Date.now()
764
765
  };
765
- if (!event.response.metadata || !event.response.metadata.client_event_id) return;
766
- const handle = this.responseCreatedFutures[event.response.metadata.client_event_id];
767
- if (handle) {
768
- delete this.responseCreatedFutures[event.response.metadata.client_event_id];
769
- this.responseCreatedFutures[event.response.id] = handle;
770
- }
771
- this.emit("generation_created", {
766
+ const generationEv = {
772
767
  messageStream: this.currentGeneration.messageChannel.stream(),
773
768
  functionStream: this.currentGeneration.functionChannel.stream(),
774
769
  userInitiated: false
775
- });
770
+ };
771
+ const clientEventId = (_a = event.response.metadata) == null ? void 0 : _a.client_event_id;
772
+ if (clientEventId) {
773
+ const handle = this.responseCreatedFutures[clientEventId];
774
+ if (handle) {
775
+ delete this.responseCreatedFutures[clientEventId];
776
+ generationEv.userInitiated = true;
777
+ if (!handle.doneFut.done) {
778
+ handle.doneFut.resolve(generationEv);
779
+ }
780
+ }
781
+ }
782
+ this.emit("generation_created", generationEv);
776
783
  }
777
784
  handleResponseOutputItemAdded(event) {
778
785
  if (!this.currentGeneration) {
@@ -787,7 +794,7 @@ class RealtimeSession extends import_agents.llm.RealtimeSession {
787
794
  const itemType = event.item.type;
788
795
  const responseId = event.response_id;
789
796
  if (itemType !== "message") {
790
- this.emitGenerationEvent(responseId);
797
+ this.resolveGeneration(responseId);
791
798
  this.textModeRecoveryRetries = 0;
792
799
  return;
793
800
  }
@@ -853,7 +860,7 @@ class RealtimeSession extends import_agents.llm.RealtimeSession {
853
860
  const itemType = event.part.type;
854
861
  const responseId = event.response_id;
855
862
  if (itemType === "audio") {
856
- this.emitGenerationEvent(responseId);
863
+ this.resolveGeneration(responseId);
857
864
  if (this.textModeRecoveryRetries > 0) {
858
865
  this.#logger.info(
859
866
  { retries: this.textModeRecoveryRetries },
@@ -1073,7 +1080,7 @@ class RealtimeSession extends import_agents.llm.RealtimeSession {
1073
1080
  });
1074
1081
  return handle;
1075
1082
  }
1076
- emitGenerationEvent(responseId) {
1083
+ resolveGeneration(responseId) {
1077
1084
  if (!this.currentGeneration) {
1078
1085
  throw new Error("currentGeneration is not set");
1079
1086
  }
@@ -1092,8 +1099,6 @@ class RealtimeSession extends import_agents.llm.RealtimeSession {
1092
1099
  handle.doneFut.resolve(generation_ev);
1093
1100
  }
1094
1101
  }
1095
- this.#logger.debug({ responseId }, "Emitting generation_created event");
1096
- this.emit("generation_created", generation_ev);
1097
1102
  }
1098
1103
  }
1099
1104
  function livekitItemToOpenAIItem(item) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/realtime/realtime_model.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { metrics } from '@livekit/agents';\nimport {\n type APIConnectOptions,\n APIConnectionError,\n APIError,\n AudioByteStream,\n DEFAULT_API_CONNECT_OPTIONS,\n Future,\n Queue,\n Task,\n cancelAndWait,\n delay,\n isAPIError,\n llm,\n log,\n shortuuid,\n stream,\n} from '@livekit/agents';\nimport { Mutex } from '@livekit/mutex';\nimport type { AudioResampler } from '@livekit/rtc-node';\nimport { AudioFrame, combineAudioFrames } from '@livekit/rtc-node';\nimport type { GenerationCreatedEvent } from 'agents/dist/llm/realtime.js';\nimport { type MessageEvent, WebSocket } from 'ws';\nimport * as api_proto from './api_proto.js';\n\nconst SAMPLE_RATE = 24000;\nconst NUM_CHANNELS = 1;\nconst BASE_URL = 'https://api.openai.com/v1';\n\nconst MOCK_AUDIO_ID_PREFIX = 'lk_mock_audio_item_';\n\ninterface RealtimeOptions {\n model: api_proto.Model;\n voice: api_proto.Voice;\n temperature: number;\n toolChoice?: llm.ToolChoice;\n inputAudioTranscription?: api_proto.InputAudioTranscription | null;\n // TODO(shubhra): add inputAudioNoiseReduction\n turnDetection?: api_proto.TurnDetectionType | null;\n maxResponseOutputTokens?: number | 'inf';\n speed?: number;\n // TODO(shubhra): add openai tracing options\n apiKey?: string;\n baseURL: string;\n isAzure: boolean;\n azureDeployment?: string;\n entraToken?: string;\n apiVersion?: string;\n maxSessionDuration: number;\n // reset the connection after this many seconds if provided\n connOptions: APIConnectOptions;\n}\n\ninterface MessageGeneration {\n messageId: string;\n textChannel: stream.StreamChannel<string>;\n audioChannel: stream.StreamChannel<AudioFrame>;\n audioTranscript: string;\n}\n\ninterface ResponseGeneration {\n messageChannel: stream.StreamChannel<llm.MessageGeneration>;\n functionChannel: stream.StreamChannel<llm.FunctionCall>;\n messages: Map<string, MessageGeneration>;\n\n /** @internal */\n _doneFut: Future;\n /** @internal */\n _createdTimestamp: number;\n /** @internal */\n _firstTokenTimestamp?: number;\n}\n\nclass CreateResponseHandle {\n instructions?: string;\n doneFut: Future<llm.GenerationCreatedEvent>;\n // TODO(shubhra): add timeout\n constructor({ instructions }: { instructions?: string }) {\n this.instructions = instructions;\n this.doneFut = new Future();\n }\n}\n\n// default values got from a \"default\" session from their API\nconst DEFAULT_FIRST_RETRY_INTERVAL_MS = 100;\nconst DEFAULT_TEMPERATURE = 0.8;\nconst DEFAULT_TURN_DETECTION: api_proto.TurnDetectionType = {\n type: 'server_vad',\n threshold: 0.5,\n prefix_padding_ms: 300,\n silence_duration_ms: 200,\n create_response: true,\n interrupt_response: true,\n};\nconst DEFAULT_INPUT_AUDIO_TRANSCRIPTION: api_proto.InputAudioTranscription = {\n model: 'gpt-4o-mini-transcribe',\n};\nconst DEFAULT_TOOL_CHOICE: llm.ToolChoice = 'auto';\nconst DEFAULT_MAX_RESPONSE_OUTPUT_TOKENS: number | 'inf' = 'inf';\n\nconst AZURE_DEFAULT_INPUT_AUDIO_TRANSCRIPTION: api_proto.InputAudioTranscription = {\n model: 'whisper-1',\n};\n\nconst AZURE_DEFAULT_TURN_DETECTION: api_proto.TurnDetectionType = {\n type: 'server_vad',\n threshold: 0.5,\n prefix_padding_ms: 300,\n silence_duration_ms: 200,\n create_response: true,\n};\n\nconst DEFAULT_MAX_SESSION_DURATION = 20 * 60 * 1000; // 20 minutes\n\nconst DEFAULT_REALTIME_MODEL_OPTIONS = {\n model: 'gpt-realtime',\n voice: 'marin',\n temperature: DEFAULT_TEMPERATURE,\n inputAudioTranscription: DEFAULT_INPUT_AUDIO_TRANSCRIPTION,\n turnDetection: DEFAULT_TURN_DETECTION,\n toolChoice: DEFAULT_TOOL_CHOICE,\n maxResponseOutputTokens: DEFAULT_MAX_RESPONSE_OUTPUT_TOKENS,\n maxSessionDuration: DEFAULT_MAX_SESSION_DURATION,\n connOptions: DEFAULT_API_CONNECT_OPTIONS,\n};\nexport class RealtimeModel extends llm.RealtimeModel {\n sampleRate = api_proto.SAMPLE_RATE;\n numChannels = api_proto.NUM_CHANNELS;\n inFrameSize = api_proto.IN_FRAME_SIZE;\n outFrameSize = api_proto.OUT_FRAME_SIZE;\n\n /* @internal */\n _options: RealtimeOptions;\n\n constructor(\n options: {\n model?: string;\n voice?: string;\n temperature?: number;\n toolChoice?: llm.ToolChoice;\n baseURL?: string;\n inputAudioTranscription?: api_proto.InputAudioTranscription | null;\n // TODO(shubhra): add inputAudioNoiseReduction\n turnDetection?: api_proto.TurnDetectionType | null;\n speed?: number;\n // TODO(shubhra): add openai tracing options\n azureDeployment?: string;\n apiKey?: string;\n entraToken?: string;\n apiVersion?: string;\n maxSessionDuration?: number;\n connOptions?: APIConnectOptions;\n } = {},\n ) {\n super({\n messageTruncation: true,\n turnDetection: options.turnDetection !== null,\n userTranscription: options.inputAudioTranscription !== null,\n autoToolReplyGeneration: false,\n });\n\n const isAzure = !!(options.apiVersion || options.entraToken || options.azureDeployment);\n\n if (options.apiKey === '' && !isAzure) {\n throw new Error(\n 'OpenAI API key is required, either using the argument or by setting the OPENAI_API_KEY environment variable',\n );\n }\n\n const apiKey = options.apiKey || process.env.OPENAI_API_KEY;\n\n if (!apiKey && !isAzure) {\n throw new Error(\n 'OpenAI API key is required, either using the argument or by setting the OPENAI_API_KEY environment variable',\n );\n }\n\n if (!options.baseURL && isAzure) {\n const azureEndpoint = process.env.AZURE_OPENAI_ENDPOINT;\n if (!azureEndpoint) {\n throw new Error(\n 'Missing Azure endpoint. Please pass base_url or set AZURE_OPENAI_ENDPOINT environment variable.',\n );\n }\n options.baseURL = `${azureEndpoint.replace(/\\/$/, '')}/openai`;\n }\n\n this._options = {\n ...DEFAULT_REALTIME_MODEL_OPTIONS,\n ...options,\n baseURL: options.baseURL || BASE_URL,\n apiKey,\n isAzure,\n model: options.model || DEFAULT_REALTIME_MODEL_OPTIONS.model,\n };\n }\n\n /**\n * Create a RealtimeModel instance configured for Azure OpenAI Service.\n *\n * @param azureDeployment - The name of your Azure OpenAI deployment.\n * @param azureEndpoint - The endpoint URL for your Azure OpenAI resource. If undefined, will attempt to read from the environment variable AZURE_OPENAI_ENDPOINT.\n * @param apiVersion - API version to use with Azure OpenAI Service. If undefined, will attempt to read from the environment variable OPENAI_API_VERSION.\n * @param apiKey - Azure OpenAI API key. If undefined, will attempt to read from the environment variable AZURE_OPENAI_API_KEY.\n * @param entraToken - Azure Entra authentication token. Required if not using API key authentication.\n * @param baseURL - Base URL for the API endpoint. If undefined, constructed from the azure_endpoint.\n * @param voice - Voice setting for audio outputs. Defaults to \"alloy\".\n * @param inputAudioTranscription - Options for transcribing input audio. Defaults to @see DEFAULT_INPUT_AUDIO_TRANSCRIPTION.\n * @param turnDetection - Options for server-based voice activity detection (VAD). Defaults to @see DEFAULT_SERVER_VAD_OPTIONS.\n * @param temperature - Sampling temperature for response generation. Defaults to @see DEFAULT_TEMPERATURE.\n * @param speed - Speed of the audio output. Defaults to 1.0.\n * @param maxResponseOutputTokens - Maximum number of tokens in the response. Defaults to @see DEFAULT_MAX_RESPONSE_OUTPUT_TOKENS.\n * @param maxSessionDuration - Maximum duration of the session in milliseconds. Defaults to @see DEFAULT_MAX_SESSION_DURATION.\n *\n * @returns A RealtimeModel instance configured for Azure OpenAI Service.\n *\n * @throws Error if required Azure parameters are missing or invalid.\n */\n static withAzure({\n azureDeployment,\n azureEndpoint,\n apiVersion,\n apiKey,\n entraToken,\n baseURL,\n voice = 'alloy',\n inputAudioTranscription = AZURE_DEFAULT_INPUT_AUDIO_TRANSCRIPTION,\n turnDetection = AZURE_DEFAULT_TURN_DETECTION,\n temperature = 0.8,\n speed,\n }: {\n azureDeployment: string;\n azureEndpoint?: string;\n apiVersion?: string;\n apiKey?: string;\n entraToken?: string;\n baseURL?: string;\n voice?: string;\n inputAudioTranscription?: api_proto.InputAudioTranscription;\n // TODO(shubhra): add inputAudioNoiseReduction\n turnDetection?: api_proto.TurnDetectionType;\n temperature?: number;\n speed?: number;\n }) {\n apiKey = apiKey || process.env.AZURE_OPENAI_API_KEY;\n if (!apiKey && !entraToken) {\n throw new Error(\n 'Missing credentials. Please pass one of `apiKey`, `entraToken`, or the `AZURE_OPENAI_API_KEY` environment variable.',\n );\n }\n\n apiVersion = apiVersion || process.env.OPENAI_API_VERSION;\n if (!apiVersion) {\n throw new Error(\n 'Must provide either the `apiVersion` argument or the `OPENAI_API_VERSION` environment variable',\n );\n }\n\n if (!baseURL) {\n azureEndpoint = azureEndpoint || process.env.AZURE_OPENAI_ENDPOINT;\n if (!azureEndpoint) {\n throw new Error(\n 'Missing Azure endpoint. Please pass the `azure_endpoint` parameter or set the `AZURE_OPENAI_ENDPOINT` environment variable.',\n );\n }\n baseURL = `${azureEndpoint.replace(/\\/$/, '')}/openai`;\n }\n\n return new RealtimeModel({\n voice,\n inputAudioTranscription,\n turnDetection,\n temperature,\n speed,\n apiKey,\n azureDeployment,\n apiVersion,\n entraToken,\n baseURL,\n });\n }\n\n session() {\n return new RealtimeSession(this);\n }\n\n async close() {\n return;\n }\n}\n\nfunction processBaseURL({\n baseURL,\n model,\n isAzure = false,\n azureDeployment,\n apiVersion,\n}: {\n baseURL: string;\n model: string;\n isAzure: boolean;\n azureDeployment?: string;\n apiVersion?: string;\n}): string {\n const url = new URL([baseURL, 'realtime'].join('/'));\n\n if (url.protocol === 'https:') {\n url.protocol = 'wss:';\n }\n\n // ensure \"/realtime\" is added if the path is empty OR \"/v1\"\n if (!url.pathname || ['', '/v1', '/openai'].includes(url.pathname.replace(/\\/$/, ''))) {\n url.pathname = url.pathname.replace(/\\/$/, '') + '/realtime';\n } else {\n url.pathname = url.pathname.replace(/\\/$/, '');\n }\n\n const queryParams: Record<string, string> = {};\n if (isAzure) {\n if (apiVersion) {\n queryParams['api-version'] = apiVersion;\n }\n if (azureDeployment) {\n queryParams['deployment'] = azureDeployment;\n }\n } else {\n queryParams['model'] = model;\n }\n\n for (const [key, value] of Object.entries(queryParams)) {\n url.searchParams.set(key, value);\n }\n\n return url.toString();\n}\n\n/**\n * A session for the OpenAI Realtime API.\n *\n * This class is used to interact with the OpenAI Realtime API.\n * It is responsible for sending events to the OpenAI Realtime API and receiving events from it.\n *\n * It exposes two more events:\n * - openai_server_event_received: expose the raw server events from the OpenAI Realtime API\n * - openai_client_event_queued: expose the raw client events sent to the OpenAI Realtime API\n */\nexport class RealtimeSession extends llm.RealtimeSession {\n private _tools: llm.ToolContext = {};\n private remoteChatCtx: llm.RemoteChatContext = new llm.RemoteChatContext();\n private messageChannel = new Queue<api_proto.ClientEvent>();\n private inputResampler?: AudioResampler;\n private instructions?: string;\n private oaiRealtimeModel: RealtimeModel;\n private currentGeneration?: ResponseGeneration;\n private responseCreatedFutures: { [id: string]: CreateResponseHandle } = {};\n\n private textModeRecoveryRetries: number = 0;\n\n private itemCreateFutures: { [id: string]: Future } = {};\n private itemDeleteFutures: { [id: string]: Future } = {};\n\n private updateChatCtxLock = new Mutex();\n private updateFuncCtxLock = new Mutex();\n\n // 100ms chunks\n private bstream = new AudioByteStream(SAMPLE_RATE, NUM_CHANNELS, SAMPLE_RATE / 10);\n\n private pushedDurationMs: number = 0;\n\n #logger = log();\n #task: Task<void>;\n #closed = false;\n\n constructor(realtimeModel: RealtimeModel) {\n super(realtimeModel);\n\n this.oaiRealtimeModel = realtimeModel;\n\n this.#task = Task.from(({ signal }) => this.#mainTask(signal));\n\n this.sendEvent(this.createSessionUpdateEvent());\n }\n\n sendEvent(command: api_proto.ClientEvent): void {\n this.messageChannel.put(command);\n }\n\n private createSessionUpdateEvent(): api_proto.SessionUpdateEvent {\n return {\n type: 'session.update',\n session: {\n model: this.oaiRealtimeModel._options.model,\n voice: this.oaiRealtimeModel._options.voice,\n input_audio_format: 'pcm16',\n output_audio_format: 'pcm16',\n modalities: ['text', 'audio'],\n turn_detection: this.oaiRealtimeModel._options.turnDetection,\n input_audio_transcription: this.oaiRealtimeModel._options.inputAudioTranscription,\n // TODO(shubhra): add inputAudioNoiseReduction\n temperature: this.oaiRealtimeModel._options.temperature,\n tool_choice: toOaiToolChoice(this.oaiRealtimeModel._options.toolChoice),\n max_response_output_tokens:\n this.oaiRealtimeModel._options.maxResponseOutputTokens === Infinity\n ? 'inf'\n : this.oaiRealtimeModel._options.maxResponseOutputTokens,\n // TODO(shubhra): add tracing options\n instructions: this.instructions,\n speed: this.oaiRealtimeModel._options.speed,\n },\n };\n }\n\n get chatCtx() {\n return this.remoteChatCtx.toChatCtx();\n }\n\n get tools() {\n return { ...this._tools } as llm.ToolContext;\n }\n\n async updateChatCtx(_chatCtx: llm.ChatContext): Promise<void> {\n const unlock = await this.updateChatCtxLock.lock();\n const events = this.createChatCtxUpdateEvents(_chatCtx);\n const futures: Future<void>[] = [];\n\n for (const event of events) {\n const future = new Future<void>();\n futures.push(future);\n\n if (event.type === 'conversation.item.create') {\n this.itemCreateFutures[event.item.id] = future;\n } else if (event.type == 'conversation.item.delete') {\n this.itemDeleteFutures[event.item_id] = future;\n }\n\n this.sendEvent(event);\n }\n\n if (futures.length === 0) {\n unlock();\n return;\n }\n\n try {\n // wait for futures to resolve or timeout\n await Promise.race([\n Promise.all(futures),\n delay(5000).then(() => {\n throw new Error('Chat ctx update events timed out');\n }),\n ]);\n } catch (e) {\n this.#logger.error((e as Error).message);\n throw e;\n } finally {\n unlock();\n }\n }\n\n private createChatCtxUpdateEvents(\n chatCtx: llm.ChatContext,\n addMockAudio: boolean = false,\n ): (api_proto.ConversationItemCreateEvent | api_proto.ConversationItemDeleteEvent)[] {\n const newChatCtx = chatCtx.copy();\n if (addMockAudio) {\n newChatCtx.items.push(createMockAudioItem());\n } else {\n // clean up existing mock audio items\n newChatCtx.items = newChatCtx.items.filter(\n (item) => !item.id.startsWith(MOCK_AUDIO_ID_PREFIX),\n );\n }\n\n const events: (\n | api_proto.ConversationItemCreateEvent\n | api_proto.ConversationItemDeleteEvent\n )[] = [];\n\n const diffOps = llm.computeChatCtxDiff(this.chatCtx, newChatCtx);\n for (const op of diffOps.toRemove) {\n events.push({\n type: 'conversation.item.delete',\n item_id: op,\n event_id: shortuuid('chat_ctx_delete_'),\n } as api_proto.ConversationItemDeleteEvent);\n }\n\n for (const [previousId, id] of diffOps.toCreate) {\n const chatItem = newChatCtx.getById(id);\n if (!chatItem) {\n throw new Error(`Chat item ${id} not found`);\n }\n events.push({\n type: 'conversation.item.create',\n item: livekitItemToOpenAIItem(chatItem),\n previous_item_id: previousId ?? undefined,\n event_id: shortuuid('chat_ctx_create_'),\n } as api_proto.ConversationItemCreateEvent);\n }\n return events;\n }\n\n async updateTools(_tools: llm.ToolContext): Promise<void> {\n const unlock = await this.updateFuncCtxLock.lock();\n const ev = this.createToolsUpdateEvent(_tools);\n this.sendEvent(ev);\n\n if (!ev.session.tools) {\n throw new Error('Tools are missing in the session update event');\n }\n\n // TODO(brian): these logics below are noops I think, leaving it here to keep\n // parity with the python but we should remove them later\n const retainedToolNames = new Set(ev.session.tools.map((tool) => tool.name));\n const retainedTools = Object.fromEntries(\n Object.entries(_tools).filter(\n ([name, tool]) => llm.isFunctionTool(tool) && retainedToolNames.has(name),\n ),\n );\n\n this._tools = retainedTools as llm.ToolContext;\n\n unlock();\n }\n\n private createToolsUpdateEvent(_tools: llm.ToolContext): api_proto.SessionUpdateEvent {\n const oaiTools: api_proto.Tool[] = [];\n\n for (const [name, tool] of Object.entries(_tools)) {\n if (!llm.isFunctionTool(tool)) {\n this.#logger.error({ name, tool }, \"OpenAI Realtime API doesn't support this tool type\");\n continue;\n }\n\n const { parameters: toolParameters, description } = tool;\n try {\n const parameters = llm.toJsonSchema(\n toolParameters,\n ) as unknown as api_proto.Tool['parameters'];\n\n oaiTools.push({\n name,\n description,\n parameters: parameters,\n type: 'function',\n });\n } catch (e) {\n this.#logger.error({ name, tool }, \"OpenAI Realtime API doesn't support this tool type\");\n continue;\n }\n }\n\n return {\n type: 'session.update',\n session: {\n model: this.oaiRealtimeModel._options.model,\n tools: oaiTools,\n },\n event_id: shortuuid('tools_update_'),\n };\n }\n\n async updateInstructions(_instructions: string): Promise<void> {\n const eventId = shortuuid('instructions_update_');\n this.sendEvent({\n type: 'session.update',\n session: {\n instructions: _instructions,\n },\n event_id: eventId,\n } as api_proto.SessionUpdateEvent);\n this.instructions = _instructions;\n }\n\n updateOptions({ toolChoice }: { toolChoice?: llm.ToolChoice }): void {\n const options: api_proto.SessionUpdateEvent['session'] = {};\n\n this.oaiRealtimeModel._options.toolChoice = toolChoice;\n options.tool_choice = toOaiToolChoice(toolChoice);\n\n // TODO(brian): add other options here\n\n this.sendEvent({\n type: 'session.update',\n session: options,\n event_id: shortuuid('options_update_'),\n });\n }\n\n pushAudio(frame: AudioFrame): void {\n for (const f of this.resampleAudio(frame)) {\n for (const nf of this.bstream.write(f.data.buffer)) {\n this.sendEvent({\n type: 'input_audio_buffer.append',\n audio: Buffer.from(nf.data.buffer).toString('base64'),\n } as api_proto.InputAudioBufferAppendEvent);\n // TODO(AJS-102): use frame.durationMs once available in rtc-node\n this.pushedDurationMs += (nf.samplesPerChannel / nf.sampleRate) * 1000;\n }\n }\n }\n\n async commitAudio(): Promise<void> {\n if (this.pushedDurationMs > 100) {\n // OpenAI requires at least 100ms of audio\n this.sendEvent({\n type: 'input_audio_buffer.commit',\n } as api_proto.InputAudioBufferCommitEvent);\n this.pushedDurationMs = 0;\n }\n }\n\n async clearAudio(): Promise<void> {\n this.sendEvent({\n type: 'input_audio_buffer.clear',\n } as api_proto.InputAudioBufferClearEvent);\n this.pushedDurationMs = 0;\n }\n\n async generateReply(instructions?: string): Promise<llm.GenerationCreatedEvent> {\n const handle = this.createResponse({ instructions, userInitiated: true });\n this.textModeRecoveryRetries = 0;\n return handle.doneFut.await;\n }\n\n async interrupt(): Promise<void> {\n this.sendEvent({\n type: 'response.cancel',\n } as api_proto.ResponseCancelEvent);\n }\n\n async truncate(_options: { messageId: string; audioEndMs: number }): Promise<void> {\n this.sendEvent({\n type: 'conversation.item.truncate',\n content_index: 0,\n item_id: _options.messageId,\n audio_end_ms: _options.audioEndMs,\n } as api_proto.ConversationItemTruncateEvent);\n }\n\n /// Truncates the data field of the event to the specified maxLength to avoid overwhelming logs\n /// with large amounts of base64 audio data.\n #loggableEvent(\n event: api_proto.ClientEvent | api_proto.ServerEvent,\n maxLength: number = 30,\n ): Record<string, unknown> {\n const untypedEvent: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(event)) {\n if (value !== undefined) {\n untypedEvent[key] = value;\n }\n }\n\n if (untypedEvent.audio && typeof untypedEvent.audio === 'string') {\n const truncatedData =\n untypedEvent.audio.slice(0, maxLength) + (untypedEvent.audio.length > maxLength ? '…' : '');\n return { ...untypedEvent, audio: truncatedData };\n }\n if (\n untypedEvent.delta &&\n typeof untypedEvent.delta === 'string' &&\n event.type === 'response.audio.delta'\n ) {\n const truncatedDelta =\n untypedEvent.delta.slice(0, maxLength) + (untypedEvent.delta.length > maxLength ? '…' : '');\n return { ...untypedEvent, delta: truncatedDelta };\n }\n return untypedEvent;\n }\n\n private async createWsConn(): Promise<WebSocket> {\n const headers: Record<string, string> = {\n 'User-Agent': 'LiveKit-Agents-JS',\n };\n\n if (this.oaiRealtimeModel._options.isAzure) {\n // Microsoft API has two ways of authentication\n // 1. Entra token set as `Bearer` token\n // 2. API key set as `api_key` header (also accepts query string)\n if (this.oaiRealtimeModel._options.entraToken) {\n headers.Authorization = `Bearer ${this.oaiRealtimeModel._options.entraToken}`;\n } else if (this.oaiRealtimeModel._options.apiKey) {\n headers['api-key'] = this.oaiRealtimeModel._options.apiKey;\n } else {\n throw new Error('Microsoft API key or entraToken is required');\n }\n } else {\n headers.Authorization = `Bearer ${this.oaiRealtimeModel._options.apiKey}`;\n headers['OpenAI-Beta'] = 'realtime=v1';\n }\n\n const url = processBaseURL({\n baseURL: this.oaiRealtimeModel._options.baseURL,\n model: this.oaiRealtimeModel._options.model,\n isAzure: this.oaiRealtimeModel._options.isAzure,\n apiVersion: this.oaiRealtimeModel._options.apiVersion,\n azureDeployment: this.oaiRealtimeModel._options.azureDeployment,\n });\n\n this.#logger.debug(`Connecting to OpenAI Realtime API at ${url}`);\n\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(url, { headers });\n let waiting = true;\n\n const timeout = setTimeout(() => {\n ws.close();\n reject(new Error('WebSocket connection timeout'));\n }, this.oaiRealtimeModel._options.connOptions.timeoutMs);\n\n ws.once('open', () => {\n if (!waiting) return;\n waiting = false;\n clearTimeout(timeout);\n resolve(ws);\n });\n\n ws.once('close', () => {\n if (!waiting) return;\n waiting = false;\n clearTimeout(timeout);\n reject(new Error('OpenAI Realtime API connection closed'));\n });\n });\n }\n\n async #mainTask(signal: AbortSignal): Promise<void> {\n let reconnecting = false;\n let numRetries = 0;\n let wsConn: WebSocket | null = null;\n const maxRetries = this.oaiRealtimeModel._options.connOptions.maxRetry;\n\n const reconnect = async () => {\n this.#logger.debug(\n {\n maxSessionDuration: this.oaiRealtimeModel._options.maxSessionDuration,\n },\n 'Reconnecting to OpenAI Realtime API',\n );\n\n const events: api_proto.ClientEvent[] = [];\n\n // options and instructions\n events.push(this.createSessionUpdateEvent());\n\n // tools\n if (Object.keys(this._tools).length > 0) {\n events.push(this.createToolsUpdateEvent(this._tools));\n }\n\n // chat context\n const chatCtx = this.chatCtx.copy({\n excludeFunctionCall: true,\n excludeInstructions: true,\n excludeEmptyMessage: true,\n });\n\n const oldChatCtx = this.remoteChatCtx;\n this.remoteChatCtx = new llm.RemoteChatContext();\n events.push(...this.createChatCtxUpdateEvents(chatCtx));\n\n try {\n for (const ev of events) {\n this.emit('openai_client_event_queued', ev);\n wsConn!.send(JSON.stringify(ev));\n }\n } catch (error) {\n this.remoteChatCtx = oldChatCtx;\n throw new APIConnectionError({\n message: 'Failed to send message to OpenAI Realtime API during session re-connection',\n });\n }\n\n this.#logger.debug('Reconnected to OpenAI Realtime API');\n\n this.emit('session_reconnected', {} as llm.RealtimeSessionReconnectedEvent);\n };\n\n reconnecting = false;\n while (!this.#closed && !signal.aborted) {\n this.#logger.debug('Creating WebSocket connection to OpenAI Realtime API');\n wsConn = await this.createWsConn();\n if (signal.aborted) break;\n\n try {\n if (reconnecting) {\n await reconnect();\n if (signal.aborted) break;\n numRetries = 0;\n }\n\n await this.runWs(wsConn);\n if (signal.aborted) break;\n } catch (error) {\n if (!isAPIError(error)) {\n this.emitError({ error: error as Error, recoverable: false });\n throw error;\n }\n\n if (maxRetries === 0 || !error.retryable) {\n this.emitError({ error: error as Error, recoverable: false });\n throw error;\n }\n\n if (numRetries === maxRetries) {\n this.emitError({ error: error as Error, recoverable: false });\n throw new APIConnectionError({\n message: `OpenAI Realtime API connection failed after ${numRetries} attempts`,\n options: {\n body: error,\n retryable: false,\n },\n });\n }\n\n this.emitError({ error: error as Error, recoverable: true });\n const retryInterval =\n numRetries === 0\n ? DEFAULT_FIRST_RETRY_INTERVAL_MS\n : this.oaiRealtimeModel._options.connOptions.retryIntervalMs;\n this.#logger.warn(\n {\n attempt: numRetries,\n maxRetries,\n error,\n },\n `OpenAI Realtime API connection failed, retrying in ${retryInterval / 1000}s`,\n );\n\n await delay(retryInterval);\n numRetries++;\n }\n\n reconnecting = true;\n }\n }\n\n private async runWs(wsConn: WebSocket): Promise<void> {\n const forwardEvents = async (signal: AbortSignal): Promise<void> => {\n const abortFuture = new Future<void>();\n signal.addEventListener('abort', () => abortFuture.resolve());\n\n while (!this.#closed && wsConn.readyState === WebSocket.OPEN && !signal.aborted) {\n try {\n const event = await Promise.race([this.messageChannel.get(), abortFuture.await]);\n if (signal.aborted || abortFuture.done || event === undefined) {\n break;\n }\n\n if (event.type !== 'input_audio_buffer.append') {\n this.#logger.debug(`(client) -> ${JSON.stringify(this.#loggableEvent(event))}`);\n }\n\n this.emit('openai_client_event_queued', event);\n wsConn.send(JSON.stringify(event));\n } catch (error) {\n break;\n }\n }\n\n wsConn.close();\n };\n\n const wsCloseFuture = new Future<void | Error>();\n\n wsConn.onerror = (error) => {\n wsCloseFuture.resolve(new APIConnectionError({ message: error.message }));\n };\n wsConn.onclose = () => {\n wsCloseFuture.resolve();\n };\n\n wsConn.onmessage = (message: MessageEvent) => {\n const event: api_proto.ServerEvent = JSON.parse(message.data as string);\n\n this.emit('openai_server_event_received', event);\n this.#logger.debug(`(server) <- ${JSON.stringify(this.#loggableEvent(event))}`);\n\n switch (event.type) {\n case 'input_audio_buffer.speech_started':\n this.handleInputAudioBufferSpeechStarted(event);\n break;\n case 'input_audio_buffer.speech_stopped':\n this.handleInputAudioBufferSpeechStopped(event);\n break;\n case 'response.created':\n this.handleResponseCreated(event);\n break;\n case 'response.output_item.added':\n this.handleResponseOutputItemAdded(event);\n break;\n case 'conversation.item.created':\n this.handleConversationItemCreated(event);\n break;\n case 'conversation.item.deleted':\n this.handleConversationItemDeleted(event);\n break;\n case 'conversation.item.input_audio_transcription.completed':\n this.handleConversationItemInputAudioTranscriptionCompleted(event);\n break;\n case 'conversation.item.input_audio_transcription.failed':\n this.handleConversationItemInputAudioTranscriptionFailed(event);\n break;\n case 'response.content_part.added':\n this.handleResponseContentPartAdded(event);\n break;\n case 'response.content_part.done':\n this.handleResponseContentPartDone(event);\n break;\n case 'response.audio_transcript.delta':\n this.handleResponseAudioTranscriptDelta(event);\n break;\n case 'response.audio.delta':\n this.handleResponseAudioDelta(event);\n break;\n case 'response.audio_transcript.done':\n this.handleResponseAudioTranscriptDone(event);\n break;\n case 'response.audio.done':\n this.handleResponseAudioDone(event);\n break;\n case 'response.output_item.done':\n this.handleResponseOutputItemDone(event);\n break;\n case 'response.done':\n this.handleResponseDone(event);\n break;\n case 'error':\n this.handleError(event);\n break;\n default:\n this.#logger.debug(`unhandled event: ${event.type}`);\n break;\n }\n };\n\n const sendTask = Task.from(({ signal }) => forwardEvents(signal));\n\n const wsTask = Task.from(({ signal }) => {\n const abortPromise = new Promise<void>((resolve) => {\n signal.addEventListener('abort', () => {\n resolve();\n });\n });\n\n return Promise.race([wsCloseFuture.await, abortPromise]);\n });\n\n const waitReconnectTask = Task.from(async ({ signal }) => {\n await delay(this.oaiRealtimeModel._options.maxSessionDuration, { signal });\n return new APIConnectionError({\n message: 'OpenAI Realtime API connection timeout',\n });\n });\n\n try {\n const result = await Promise.race([wsTask.result, sendTask.result, waitReconnectTask.result]);\n\n if (waitReconnectTask.done && this.currentGeneration) {\n await this.currentGeneration._doneFut.await;\n }\n\n if (result instanceof Error) {\n throw result;\n }\n } finally {\n await cancelAndWait([wsTask, sendTask, waitReconnectTask], 2000);\n wsConn.close();\n }\n }\n\n async close() {\n super.close();\n this.#closed = true;\n await this.#task;\n }\n\n private handleInputAudioBufferSpeechStarted(\n _event: api_proto.InputAudioBufferSpeechStartedEvent,\n ): void {\n this.emit('input_speech_started', {} as llm.InputSpeechStartedEvent);\n }\n\n private handleInputAudioBufferSpeechStopped(\n _event: api_proto.InputAudioBufferSpeechStoppedEvent,\n ): void {\n this.emit('input_speech_stopped', {\n userTranscriptionEnabled: this.oaiRealtimeModel._options.inputAudioTranscription !== null,\n } as llm.InputSpeechStoppedEvent);\n }\n\n private handleResponseCreated(event: api_proto.ResponseCreatedEvent): void {\n if (!event.response.id) {\n throw new Error('response.id is missing');\n }\n\n this.currentGeneration = {\n messageChannel: stream.createStreamChannel<llm.MessageGeneration>(),\n functionChannel: stream.createStreamChannel<llm.FunctionCall>(),\n messages: new Map(),\n _doneFut: new Future(),\n _createdTimestamp: Date.now(),\n };\n\n if (!event.response.metadata || !event.response.metadata.client_event_id) return;\n\n const handle = this.responseCreatedFutures[event.response.metadata.client_event_id];\n if (handle) {\n delete this.responseCreatedFutures[event.response.metadata.client_event_id];\n\n // set key to the response id\n this.responseCreatedFutures[event.response.id] = handle;\n }\n\n // the generation_created event is emitted when\n // 1. the response is not a message on response.output_item.added event\n // 2. the content is audio on response.content_part.added event\n // will try to recover from text response on response.content_part.done event\n this.emit('generation_created', {\n messageStream: this.currentGeneration.messageChannel.stream(),\n functionStream: this.currentGeneration.functionChannel.stream(),\n userInitiated: false,\n } as GenerationCreatedEvent);\n }\n\n private handleResponseOutputItemAdded(event: api_proto.ResponseOutputItemAddedEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n if (!event.item.type) {\n throw new Error('item.type is not set');\n }\n\n if (!event.response_id) {\n throw new Error('response_id is not set');\n }\n\n const itemType = event.item.type;\n const responseId = event.response_id;\n\n if (itemType !== 'message') {\n // emit immediately if it's not a message, otherwise wait response.content_part.added\n this.emitGenerationEvent(responseId);\n this.textModeRecoveryRetries = 0;\n return;\n }\n }\n\n private handleConversationItemCreated(event: api_proto.ConversationItemCreatedEvent): void {\n if (!event.item.id) {\n throw new Error('item.id is not set');\n }\n\n try {\n this.remoteChatCtx.insert(event.previous_item_id, openAIItemToLivekitItem(event.item));\n } catch (error) {\n this.#logger.error({ error, itemId: event.item.id }, 'failed to insert conversation item');\n }\n\n const fut = this.itemCreateFutures[event.item.id];\n if (fut) {\n fut.resolve();\n delete this.itemCreateFutures[event.item.id];\n }\n }\n\n private handleConversationItemDeleted(event: api_proto.ConversationItemDeletedEvent): void {\n if (!event.item_id) {\n throw new Error('item_id is not set');\n }\n\n try {\n this.remoteChatCtx.delete(event.item_id);\n } catch (error) {\n this.#logger.error({ error, itemId: event.item_id }, 'failed to delete conversation item');\n }\n\n const fut = this.itemDeleteFutures[event.item_id];\n if (fut) {\n fut.resolve();\n delete this.itemDeleteFutures[event.item_id];\n }\n }\n\n private handleConversationItemInputAudioTranscriptionCompleted(\n event: api_proto.ConversationItemInputAudioTranscriptionCompletedEvent,\n ): void {\n const remoteItem = this.remoteChatCtx.get(event.item_id);\n if (!remoteItem) {\n return;\n }\n\n const item = remoteItem.item;\n if (item instanceof llm.ChatMessage) {\n item.content.push(event.transcript);\n } else {\n throw new Error('item is not a chat message');\n }\n\n this.emit('input_audio_transcription_completed', {\n itemId: event.item_id,\n transcript: event.transcript,\n isFinal: true,\n } as llm.InputTranscriptionCompleted);\n }\n\n private handleConversationItemInputAudioTranscriptionFailed(\n event: api_proto.ConversationItemInputAudioTranscriptionFailedEvent,\n ): void {\n this.#logger.error(\n { error: event.error },\n 'OpenAI Realtime API failed to transcribe input audio',\n );\n }\n\n private handleResponseContentPartAdded(event: api_proto.ResponseContentPartAddedEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemId = event.item_id;\n const itemType = event.part.type;\n const responseId = event.response_id;\n\n if (itemType === 'audio') {\n this.emitGenerationEvent(responseId);\n if (this.textModeRecoveryRetries > 0) {\n this.#logger.info(\n { retries: this.textModeRecoveryRetries },\n 'recovered from text-only response',\n );\n this.textModeRecoveryRetries = 0;\n }\n\n const itemGeneration: MessageGeneration = {\n messageId: itemId,\n textChannel: stream.createStreamChannel<string>(),\n audioChannel: stream.createStreamChannel<AudioFrame>(),\n audioTranscript: '',\n };\n\n this.currentGeneration.messageChannel.write({\n messageId: itemId,\n textStream: itemGeneration.textChannel.stream(),\n audioStream: itemGeneration.audioChannel.stream(),\n });\n\n this.currentGeneration.messages.set(itemId, itemGeneration);\n this.currentGeneration._firstTokenTimestamp = Date.now();\n return;\n } else {\n this.interrupt();\n if (this.textModeRecoveryRetries === 0) {\n this.#logger.warn({ responseId }, 'received text-only response from OpenAI Realtime API');\n }\n }\n }\n\n private handleResponseContentPartDone(event: api_proto.ResponseContentPartDoneEvent): void {\n if (event.part.type !== 'text') {\n return;\n }\n\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n // TODO(shubhra): handle text mode recovery\n }\n\n private handleResponseAudioTranscriptDelta(\n event: api_proto.ResponseAudioTranscriptDeltaEvent,\n ): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemId = event.item_id;\n const delta = event.delta;\n\n // TODO (shubhra): add timed string support\n\n const itemGeneration = this.currentGeneration.messages.get(itemId);\n if (!itemGeneration) {\n throw new Error('itemGeneration is not set');\n } else {\n itemGeneration.textChannel.write(delta);\n itemGeneration.audioTranscript += delta;\n }\n }\n\n private handleResponseAudioDelta(event: api_proto.ResponseAudioDeltaEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemGeneration = this.currentGeneration.messages.get(event.item_id);\n if (!itemGeneration) {\n throw new Error('itemGeneration is not set');\n }\n\n const binaryString = atob(event.delta);\n const len = binaryString.length;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n itemGeneration.audioChannel.write(\n new AudioFrame(\n new Int16Array(bytes.buffer),\n api_proto.SAMPLE_RATE,\n api_proto.NUM_CHANNELS,\n bytes.length / 2,\n ),\n );\n }\n\n private handleResponseAudioTranscriptDone(\n _event: api_proto.ResponseAudioTranscriptDoneEvent,\n ): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n }\n\n private handleResponseAudioDone(_event: api_proto.ResponseAudioDoneEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n }\n\n private handleResponseOutputItemDone(event: api_proto.ResponseOutputItemDoneEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemId = event.item.id;\n const itemType = event.item.type;\n\n if (itemType === 'function_call') {\n const item = event.item;\n if (!item.call_id || !item.name || !item.arguments) {\n throw new Error('item is not a function call');\n }\n this.currentGeneration.functionChannel.write({\n callId: item.call_id,\n name: item.name,\n args: item.arguments,\n } as llm.FunctionCall);\n } else if (itemType === 'message') {\n const itemGeneration = this.currentGeneration.messages.get(itemId);\n if (!itemGeneration) {\n return;\n }\n // text response doesn't have itemGeneration\n itemGeneration.textChannel.close();\n itemGeneration.audioChannel.close();\n }\n }\n\n private handleResponseDone(_event: api_proto.ResponseDoneEvent): void {\n if (!this.currentGeneration) {\n // OpenAI has a race condition where we could receive response.done without any\n // previous response.created (This happens generally during interruption)\n return;\n }\n\n const createdTimestamp = this.currentGeneration._createdTimestamp;\n const firstTokenTimestamp = this.currentGeneration._firstTokenTimestamp;\n\n this.#logger.debug(\n {\n messageCount: this.currentGeneration.messages.size,\n },\n 'Closing generation channels in handleResponseDone',\n );\n\n for (const generation of this.currentGeneration.messages.values()) {\n generation.textChannel.close();\n generation.audioChannel.close();\n }\n\n this.currentGeneration.functionChannel.close();\n this.currentGeneration.messageChannel.close();\n\n for (const itemId of this.currentGeneration.messages.keys()) {\n const remoteItem = this.remoteChatCtx.get(itemId);\n if (remoteItem && remoteItem.item instanceof llm.ChatMessage) {\n remoteItem.item.content.push(this.currentGeneration.messages.get(itemId)!.audioTranscript);\n }\n }\n\n this.currentGeneration._doneFut.resolve();\n this.currentGeneration = undefined;\n\n // Calculate and emit metrics\n const usage = _event.response.usage;\n const ttft = firstTokenTimestamp ? firstTokenTimestamp - createdTimestamp : -1;\n const duration = (Date.now() - createdTimestamp) / 1000; // Convert to seconds\n\n const realtimeMetrics: metrics.RealtimeModelMetrics = {\n type: 'realtime_model_metrics',\n timestamp: createdTimestamp / 1000, // Convert to seconds\n requestId: _event.response.id || '',\n ttft,\n duration,\n cancelled: _event.response.status === 'cancelled',\n label: 'openai_realtime',\n inputTokens: usage?.input_tokens ?? 0,\n outputTokens: usage?.output_tokens ?? 0,\n totalTokens: usage?.total_tokens ?? 0,\n tokensPerSecond: duration > 0 ? (usage?.output_tokens ?? 0) / duration : 0,\n inputTokenDetails: {\n audioTokens: usage?.input_token_details?.audio_tokens ?? 0,\n textTokens: usage?.input_token_details?.text_tokens ?? 0,\n imageTokens: 0, // Not supported yet\n cachedTokens: usage?.input_token_details?.cached_tokens ?? 0,\n cachedTokensDetails: usage?.input_token_details?.cached_tokens_details\n ? {\n audioTokens: usage?.input_token_details?.cached_tokens_details?.audio_tokens ?? 0,\n textTokens: usage?.input_token_details?.cached_tokens_details?.text_tokens ?? 0,\n imageTokens: usage?.input_token_details?.cached_tokens_details?.image_tokens ?? 0,\n }\n : undefined,\n },\n outputTokenDetails: {\n textTokens: usage?.output_token_details?.text_tokens ?? 0,\n audioTokens: usage?.output_token_details?.audio_tokens ?? 0,\n imageTokens: 0,\n },\n };\n\n this.emit('metrics_collected', realtimeMetrics);\n // TODO(brian): handle response done but not complete\n }\n\n private handleError(event: api_proto.ErrorEvent): void {\n if (event.error.message.startsWith('Cancellation failed')) {\n return;\n }\n\n this.#logger.error({ error: event.error }, 'OpenAI Realtime API returned an error');\n this.emitError({\n error: new APIError(event.error.message, {\n body: event.error,\n retryable: true,\n }),\n recoverable: true,\n });\n\n // TODO(brian): set error for response future if it exists\n }\n\n private emitError({ error, recoverable }: { error: Error; recoverable: boolean }): void {\n // IMPORTANT: only emit error if there are listeners; otherwise emit will throw an error\n this.emit('error', {\n timestamp: Date.now(),\n // TODO(brian): add label\n label: '',\n error,\n recoverable,\n } as llm.RealtimeModelError);\n }\n\n private *resampleAudio(frame: AudioFrame): Generator<AudioFrame> {\n yield frame;\n }\n\n private createResponse({\n userInitiated,\n instructions,\n oldHandle,\n }: {\n userInitiated: boolean;\n instructions?: string;\n oldHandle?: CreateResponseHandle;\n }): CreateResponseHandle {\n const handle = oldHandle || new CreateResponseHandle({ instructions });\n if (oldHandle && instructions) {\n handle.instructions = instructions;\n }\n\n const eventId = shortuuid('response_create_');\n if (userInitiated) {\n this.responseCreatedFutures[eventId] = handle;\n }\n\n const response: api_proto.ResponseCreateEvent['response'] = {};\n if (instructions) response.instructions = instructions;\n if (userInitiated) response.metadata = { client_event_id: eventId };\n\n this.sendEvent({\n type: 'response.create',\n event_id: eventId,\n response: Object.keys(response).length > 0 ? response : undefined,\n });\n\n return handle;\n }\n\n private emitGenerationEvent(responseId: string): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const generation_ev: llm.GenerationCreatedEvent = {\n messageStream: this.currentGeneration.messageChannel.stream(),\n functionStream: this.currentGeneration.functionChannel.stream(),\n userInitiated: false,\n };\n\n const handle = this.responseCreatedFutures[responseId];\n if (handle) {\n delete this.responseCreatedFutures[responseId];\n generation_ev.userInitiated = true;\n if (handle.doneFut.done) {\n this.#logger.warn({ responseId }, 'response received after timeout');\n } else {\n handle.doneFut.resolve(generation_ev);\n }\n }\n\n this.#logger.debug({ responseId }, 'Emitting generation_created event');\n this.emit('generation_created', generation_ev);\n }\n}\n\nfunction livekitItemToOpenAIItem(item: llm.ChatItem): api_proto.ItemResource {\n switch (item.type) {\n case 'function_call':\n return {\n id: item.id,\n type: 'function_call',\n call_id: item.callId,\n name: item.name,\n arguments: item.args,\n } as api_proto.FunctionCallItem;\n case 'function_call_output':\n return {\n id: item.id,\n type: 'function_call_output',\n call_id: item.callId,\n output: item.output,\n } as api_proto.FunctionCallOutputItem;\n case 'message':\n const role = item.role === 'developer' ? 'system' : item.role;\n const contentList: api_proto.Content[] = [];\n for (const c of item.content) {\n if (typeof c === 'string') {\n contentList.push({\n type: role === 'assistant' ? 'text' : 'input_text',\n text: c,\n } as api_proto.InputTextContent);\n } else if (c.type === 'image_content') {\n // not supported for now\n continue;\n } else if (c.type === 'audio_content') {\n if (role === 'user') {\n const encodedAudio = Buffer.from(combineAudioFrames(c.frame).data).toString('base64');\n contentList.push({\n type: 'input_audio',\n audio: encodedAudio,\n } as api_proto.InputAudioContent);\n }\n }\n }\n return {\n id: item.id,\n type: 'message',\n role,\n content: contentList,\n } as api_proto.UserItem;\n }\n}\n\nfunction openAIItemToLivekitItem(item: api_proto.ItemResource): llm.ChatItem {\n if (!item.id) {\n throw new Error('item.id is not set');\n }\n\n switch (item.type) {\n case 'function_call':\n return llm.FunctionCall.create({\n id: item.id,\n callId: item.call_id,\n name: item.name,\n args: item.arguments,\n });\n case 'function_call_output':\n return llm.FunctionCallOutput.create({\n id: item.id,\n callId: item.call_id,\n output: item.output,\n isError: false,\n });\n case 'message':\n const content: llm.ChatContent[] = [];\n // item.content can be a single object or an array; normalize to array\n const contents = Array.isArray(item.content) ? item.content : [item.content];\n for (const c of contents) {\n if (c.type === 'text' || c.type === 'input_text') {\n content.push(c.text);\n }\n }\n return llm.ChatMessage.create({\n id: item.id,\n role: item.role,\n content,\n });\n }\n}\n\nfunction createMockAudioItem(durationSeconds: number = 2): llm.ChatMessage {\n const audioData = Buffer.alloc(durationSeconds * SAMPLE_RATE);\n return llm.ChatMessage.create({\n id: shortuuid(MOCK_AUDIO_ID_PREFIX),\n role: 'user',\n content: [\n {\n type: 'audio_content',\n frame: [\n new AudioFrame(\n new Int16Array(audioData.buffer),\n SAMPLE_RATE,\n NUM_CHANNELS,\n audioData.length / 2,\n ),\n ],\n } as llm.AudioContent,\n ],\n });\n}\n\nfunction toOaiToolChoice(toolChoice?: llm.ToolChoice): api_proto.ToolChoice {\n if (typeof toolChoice === 'string') {\n return toolChoice;\n }\n\n if (toolChoice?.type === 'function') {\n return toolChoice.function.name;\n }\n\n return 'auto';\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/realtime/realtime_model.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { metrics } from '@livekit/agents';\nimport {\n type APIConnectOptions,\n APIConnectionError,\n APIError,\n AudioByteStream,\n DEFAULT_API_CONNECT_OPTIONS,\n Future,\n Queue,\n Task,\n cancelAndWait,\n delay,\n isAPIError,\n llm,\n log,\n shortuuid,\n stream,\n} from '@livekit/agents';\nimport { Mutex } from '@livekit/mutex';\nimport type { AudioResampler } from '@livekit/rtc-node';\nimport { AudioFrame, combineAudioFrames } from '@livekit/rtc-node';\nimport type { GenerationCreatedEvent } from 'agents/dist/llm/realtime.js';\nimport { type MessageEvent, WebSocket } from 'ws';\nimport * as api_proto from './api_proto.js';\n\nconst SAMPLE_RATE = 24000;\nconst NUM_CHANNELS = 1;\nconst BASE_URL = 'https://api.openai.com/v1';\n\nconst MOCK_AUDIO_ID_PREFIX = 'lk_mock_audio_item_';\n\ninterface RealtimeOptions {\n model: api_proto.Model;\n voice: api_proto.Voice;\n temperature: number;\n toolChoice?: llm.ToolChoice;\n inputAudioTranscription?: api_proto.InputAudioTranscription | null;\n // TODO(shubhra): add inputAudioNoiseReduction\n turnDetection?: api_proto.TurnDetectionType | null;\n maxResponseOutputTokens?: number | 'inf';\n speed?: number;\n // TODO(shubhra): add openai tracing options\n apiKey?: string;\n baseURL: string;\n isAzure: boolean;\n azureDeployment?: string;\n entraToken?: string;\n apiVersion?: string;\n maxSessionDuration: number;\n // reset the connection after this many seconds if provided\n connOptions: APIConnectOptions;\n}\n\ninterface MessageGeneration {\n messageId: string;\n textChannel: stream.StreamChannel<string>;\n audioChannel: stream.StreamChannel<AudioFrame>;\n audioTranscript: string;\n}\n\ninterface ResponseGeneration {\n messageChannel: stream.StreamChannel<llm.MessageGeneration>;\n functionChannel: stream.StreamChannel<llm.FunctionCall>;\n messages: Map<string, MessageGeneration>;\n\n /** @internal */\n _doneFut: Future;\n /** @internal */\n _createdTimestamp: number;\n /** @internal */\n _firstTokenTimestamp?: number;\n}\n\nclass CreateResponseHandle {\n instructions?: string;\n doneFut: Future<llm.GenerationCreatedEvent>;\n // TODO(shubhra): add timeout\n constructor({ instructions }: { instructions?: string }) {\n this.instructions = instructions;\n this.doneFut = new Future();\n }\n}\n\n// default values got from a \"default\" session from their API\nconst DEFAULT_FIRST_RETRY_INTERVAL_MS = 100;\nconst DEFAULT_TEMPERATURE = 0.8;\nconst DEFAULT_TURN_DETECTION: api_proto.TurnDetectionType = {\n type: 'server_vad',\n threshold: 0.5,\n prefix_padding_ms: 300,\n silence_duration_ms: 200,\n create_response: true,\n interrupt_response: true,\n};\nconst DEFAULT_INPUT_AUDIO_TRANSCRIPTION: api_proto.InputAudioTranscription = {\n model: 'gpt-4o-mini-transcribe',\n};\nconst DEFAULT_TOOL_CHOICE: llm.ToolChoice = 'auto';\nconst DEFAULT_MAX_RESPONSE_OUTPUT_TOKENS: number | 'inf' = 'inf';\n\nconst AZURE_DEFAULT_INPUT_AUDIO_TRANSCRIPTION: api_proto.InputAudioTranscription = {\n model: 'whisper-1',\n};\n\nconst AZURE_DEFAULT_TURN_DETECTION: api_proto.TurnDetectionType = {\n type: 'server_vad',\n threshold: 0.5,\n prefix_padding_ms: 300,\n silence_duration_ms: 200,\n create_response: true,\n};\n\nconst DEFAULT_MAX_SESSION_DURATION = 20 * 60 * 1000; // 20 minutes\n\nconst DEFAULT_REALTIME_MODEL_OPTIONS = {\n model: 'gpt-realtime',\n voice: 'marin',\n temperature: DEFAULT_TEMPERATURE,\n inputAudioTranscription: DEFAULT_INPUT_AUDIO_TRANSCRIPTION,\n turnDetection: DEFAULT_TURN_DETECTION,\n toolChoice: DEFAULT_TOOL_CHOICE,\n maxResponseOutputTokens: DEFAULT_MAX_RESPONSE_OUTPUT_TOKENS,\n maxSessionDuration: DEFAULT_MAX_SESSION_DURATION,\n connOptions: DEFAULT_API_CONNECT_OPTIONS,\n};\nexport class RealtimeModel extends llm.RealtimeModel {\n sampleRate = api_proto.SAMPLE_RATE;\n numChannels = api_proto.NUM_CHANNELS;\n inFrameSize = api_proto.IN_FRAME_SIZE;\n outFrameSize = api_proto.OUT_FRAME_SIZE;\n\n /* @internal */\n _options: RealtimeOptions;\n\n constructor(\n options: {\n model?: string;\n voice?: string;\n temperature?: number;\n toolChoice?: llm.ToolChoice;\n baseURL?: string;\n inputAudioTranscription?: api_proto.InputAudioTranscription | null;\n // TODO(shubhra): add inputAudioNoiseReduction\n turnDetection?: api_proto.TurnDetectionType | null;\n speed?: number;\n // TODO(shubhra): add openai tracing options\n azureDeployment?: string;\n apiKey?: string;\n entraToken?: string;\n apiVersion?: string;\n maxSessionDuration?: number;\n connOptions?: APIConnectOptions;\n } = {},\n ) {\n super({\n messageTruncation: true,\n turnDetection: options.turnDetection !== null,\n userTranscription: options.inputAudioTranscription !== null,\n autoToolReplyGeneration: false,\n });\n\n const isAzure = !!(options.apiVersion || options.entraToken || options.azureDeployment);\n\n if (options.apiKey === '' && !isAzure) {\n throw new Error(\n 'OpenAI API key is required, either using the argument or by setting the OPENAI_API_KEY environment variable',\n );\n }\n\n const apiKey = options.apiKey || process.env.OPENAI_API_KEY;\n\n if (!apiKey && !isAzure) {\n throw new Error(\n 'OpenAI API key is required, either using the argument or by setting the OPENAI_API_KEY environment variable',\n );\n }\n\n if (!options.baseURL && isAzure) {\n const azureEndpoint = process.env.AZURE_OPENAI_ENDPOINT;\n if (!azureEndpoint) {\n throw new Error(\n 'Missing Azure endpoint. Please pass base_url or set AZURE_OPENAI_ENDPOINT environment variable.',\n );\n }\n options.baseURL = `${azureEndpoint.replace(/\\/$/, '')}/openai`;\n }\n\n this._options = {\n ...DEFAULT_REALTIME_MODEL_OPTIONS,\n ...options,\n baseURL: options.baseURL || BASE_URL,\n apiKey,\n isAzure,\n model: options.model || DEFAULT_REALTIME_MODEL_OPTIONS.model,\n };\n }\n\n /**\n * Create a RealtimeModel instance configured for Azure OpenAI Service.\n *\n * @param azureDeployment - The name of your Azure OpenAI deployment.\n * @param azureEndpoint - The endpoint URL for your Azure OpenAI resource. If undefined, will attempt to read from the environment variable AZURE_OPENAI_ENDPOINT.\n * @param apiVersion - API version to use with Azure OpenAI Service. If undefined, will attempt to read from the environment variable OPENAI_API_VERSION.\n * @param apiKey - Azure OpenAI API key. If undefined, will attempt to read from the environment variable AZURE_OPENAI_API_KEY.\n * @param entraToken - Azure Entra authentication token. Required if not using API key authentication.\n * @param baseURL - Base URL for the API endpoint. If undefined, constructed from the azure_endpoint.\n * @param voice - Voice setting for audio outputs. Defaults to \"alloy\".\n * @param inputAudioTranscription - Options for transcribing input audio. Defaults to @see DEFAULT_INPUT_AUDIO_TRANSCRIPTION.\n * @param turnDetection - Options for server-based voice activity detection (VAD). Defaults to @see DEFAULT_SERVER_VAD_OPTIONS.\n * @param temperature - Sampling temperature for response generation. Defaults to @see DEFAULT_TEMPERATURE.\n * @param speed - Speed of the audio output. Defaults to 1.0.\n * @param maxResponseOutputTokens - Maximum number of tokens in the response. Defaults to @see DEFAULT_MAX_RESPONSE_OUTPUT_TOKENS.\n * @param maxSessionDuration - Maximum duration of the session in milliseconds. Defaults to @see DEFAULT_MAX_SESSION_DURATION.\n *\n * @returns A RealtimeModel instance configured for Azure OpenAI Service.\n *\n * @throws Error if required Azure parameters are missing or invalid.\n */\n static withAzure({\n azureDeployment,\n azureEndpoint,\n apiVersion,\n apiKey,\n entraToken,\n baseURL,\n voice = 'alloy',\n inputAudioTranscription = AZURE_DEFAULT_INPUT_AUDIO_TRANSCRIPTION,\n turnDetection = AZURE_DEFAULT_TURN_DETECTION,\n temperature = 0.8,\n speed,\n }: {\n azureDeployment: string;\n azureEndpoint?: string;\n apiVersion?: string;\n apiKey?: string;\n entraToken?: string;\n baseURL?: string;\n voice?: string;\n inputAudioTranscription?: api_proto.InputAudioTranscription;\n // TODO(shubhra): add inputAudioNoiseReduction\n turnDetection?: api_proto.TurnDetectionType;\n temperature?: number;\n speed?: number;\n }) {\n apiKey = apiKey || process.env.AZURE_OPENAI_API_KEY;\n if (!apiKey && !entraToken) {\n throw new Error(\n 'Missing credentials. Please pass one of `apiKey`, `entraToken`, or the `AZURE_OPENAI_API_KEY` environment variable.',\n );\n }\n\n apiVersion = apiVersion || process.env.OPENAI_API_VERSION;\n if (!apiVersion) {\n throw new Error(\n 'Must provide either the `apiVersion` argument or the `OPENAI_API_VERSION` environment variable',\n );\n }\n\n if (!baseURL) {\n azureEndpoint = azureEndpoint || process.env.AZURE_OPENAI_ENDPOINT;\n if (!azureEndpoint) {\n throw new Error(\n 'Missing Azure endpoint. Please pass the `azure_endpoint` parameter or set the `AZURE_OPENAI_ENDPOINT` environment variable.',\n );\n }\n baseURL = `${azureEndpoint.replace(/\\/$/, '')}/openai`;\n }\n\n return new RealtimeModel({\n voice,\n inputAudioTranscription,\n turnDetection,\n temperature,\n speed,\n apiKey,\n azureDeployment,\n apiVersion,\n entraToken,\n baseURL,\n });\n }\n\n session() {\n return new RealtimeSession(this);\n }\n\n async close() {\n return;\n }\n}\n\nfunction processBaseURL({\n baseURL,\n model,\n isAzure = false,\n azureDeployment,\n apiVersion,\n}: {\n baseURL: string;\n model: string;\n isAzure: boolean;\n azureDeployment?: string;\n apiVersion?: string;\n}): string {\n const url = new URL([baseURL, 'realtime'].join('/'));\n\n if (url.protocol === 'https:') {\n url.protocol = 'wss:';\n }\n\n // ensure \"/realtime\" is added if the path is empty OR \"/v1\"\n if (!url.pathname || ['', '/v1', '/openai'].includes(url.pathname.replace(/\\/$/, ''))) {\n url.pathname = url.pathname.replace(/\\/$/, '') + '/realtime';\n } else {\n url.pathname = url.pathname.replace(/\\/$/, '');\n }\n\n const queryParams: Record<string, string> = {};\n if (isAzure) {\n if (apiVersion) {\n queryParams['api-version'] = apiVersion;\n }\n if (azureDeployment) {\n queryParams['deployment'] = azureDeployment;\n }\n } else {\n queryParams['model'] = model;\n }\n\n for (const [key, value] of Object.entries(queryParams)) {\n url.searchParams.set(key, value);\n }\n\n return url.toString();\n}\n\n/**\n * A session for the OpenAI Realtime API.\n *\n * This class is used to interact with the OpenAI Realtime API.\n * It is responsible for sending events to the OpenAI Realtime API and receiving events from it.\n *\n * It exposes two more events:\n * - openai_server_event_received: expose the raw server events from the OpenAI Realtime API\n * - openai_client_event_queued: expose the raw client events sent to the OpenAI Realtime API\n */\nexport class RealtimeSession extends llm.RealtimeSession {\n private _tools: llm.ToolContext = {};\n private remoteChatCtx: llm.RemoteChatContext = new llm.RemoteChatContext();\n private messageChannel = new Queue<api_proto.ClientEvent>();\n private inputResampler?: AudioResampler;\n private instructions?: string;\n private oaiRealtimeModel: RealtimeModel;\n private currentGeneration?: ResponseGeneration;\n private responseCreatedFutures: { [id: string]: CreateResponseHandle } = {};\n\n private textModeRecoveryRetries: number = 0;\n\n private itemCreateFutures: { [id: string]: Future } = {};\n private itemDeleteFutures: { [id: string]: Future } = {};\n\n private updateChatCtxLock = new Mutex();\n private updateFuncCtxLock = new Mutex();\n\n // 100ms chunks\n private bstream = new AudioByteStream(SAMPLE_RATE, NUM_CHANNELS, SAMPLE_RATE / 10);\n\n private pushedDurationMs: number = 0;\n\n #logger = log();\n #task: Task<void>;\n #closed = false;\n\n constructor(realtimeModel: RealtimeModel) {\n super(realtimeModel);\n\n this.oaiRealtimeModel = realtimeModel;\n\n this.#task = Task.from(({ signal }) => this.#mainTask(signal));\n\n this.sendEvent(this.createSessionUpdateEvent());\n }\n\n sendEvent(command: api_proto.ClientEvent): void {\n this.messageChannel.put(command);\n }\n\n private createSessionUpdateEvent(): api_proto.SessionUpdateEvent {\n return {\n type: 'session.update',\n session: {\n model: this.oaiRealtimeModel._options.model,\n voice: this.oaiRealtimeModel._options.voice,\n input_audio_format: 'pcm16',\n output_audio_format: 'pcm16',\n modalities: ['text', 'audio'],\n turn_detection: this.oaiRealtimeModel._options.turnDetection,\n input_audio_transcription: this.oaiRealtimeModel._options.inputAudioTranscription,\n // TODO(shubhra): add inputAudioNoiseReduction\n temperature: this.oaiRealtimeModel._options.temperature,\n tool_choice: toOaiToolChoice(this.oaiRealtimeModel._options.toolChoice),\n max_response_output_tokens:\n this.oaiRealtimeModel._options.maxResponseOutputTokens === Infinity\n ? 'inf'\n : this.oaiRealtimeModel._options.maxResponseOutputTokens,\n // TODO(shubhra): add tracing options\n instructions: this.instructions,\n speed: this.oaiRealtimeModel._options.speed,\n },\n };\n }\n\n get chatCtx() {\n return this.remoteChatCtx.toChatCtx();\n }\n\n get tools() {\n return { ...this._tools } as llm.ToolContext;\n }\n\n async updateChatCtx(_chatCtx: llm.ChatContext): Promise<void> {\n const unlock = await this.updateChatCtxLock.lock();\n const events = this.createChatCtxUpdateEvents(_chatCtx);\n const futures: Future<void>[] = [];\n\n for (const event of events) {\n const future = new Future<void>();\n futures.push(future);\n\n if (event.type === 'conversation.item.create') {\n this.itemCreateFutures[event.item.id] = future;\n } else if (event.type == 'conversation.item.delete') {\n this.itemDeleteFutures[event.item_id] = future;\n }\n\n this.sendEvent(event);\n }\n\n if (futures.length === 0) {\n unlock();\n return;\n }\n\n try {\n // wait for futures to resolve or timeout\n await Promise.race([\n Promise.all(futures),\n delay(5000).then(() => {\n throw new Error('Chat ctx update events timed out');\n }),\n ]);\n } catch (e) {\n this.#logger.error((e as Error).message);\n throw e;\n } finally {\n unlock();\n }\n }\n\n private createChatCtxUpdateEvents(\n chatCtx: llm.ChatContext,\n addMockAudio: boolean = false,\n ): (api_proto.ConversationItemCreateEvent | api_proto.ConversationItemDeleteEvent)[] {\n const newChatCtx = chatCtx.copy();\n if (addMockAudio) {\n newChatCtx.items.push(createMockAudioItem());\n } else {\n // clean up existing mock audio items\n newChatCtx.items = newChatCtx.items.filter(\n (item) => !item.id.startsWith(MOCK_AUDIO_ID_PREFIX),\n );\n }\n\n const events: (\n | api_proto.ConversationItemCreateEvent\n | api_proto.ConversationItemDeleteEvent\n )[] = [];\n\n const diffOps = llm.computeChatCtxDiff(this.chatCtx, newChatCtx);\n for (const op of diffOps.toRemove) {\n events.push({\n type: 'conversation.item.delete',\n item_id: op,\n event_id: shortuuid('chat_ctx_delete_'),\n } as api_proto.ConversationItemDeleteEvent);\n }\n\n for (const [previousId, id] of diffOps.toCreate) {\n const chatItem = newChatCtx.getById(id);\n if (!chatItem) {\n throw new Error(`Chat item ${id} not found`);\n }\n events.push({\n type: 'conversation.item.create',\n item: livekitItemToOpenAIItem(chatItem),\n previous_item_id: previousId ?? undefined,\n event_id: shortuuid('chat_ctx_create_'),\n } as api_proto.ConversationItemCreateEvent);\n }\n return events;\n }\n\n async updateTools(_tools: llm.ToolContext): Promise<void> {\n const unlock = await this.updateFuncCtxLock.lock();\n const ev = this.createToolsUpdateEvent(_tools);\n this.sendEvent(ev);\n\n if (!ev.session.tools) {\n throw new Error('Tools are missing in the session update event');\n }\n\n // TODO(brian): these logics below are noops I think, leaving it here to keep\n // parity with the python but we should remove them later\n const retainedToolNames = new Set(ev.session.tools.map((tool) => tool.name));\n const retainedTools = Object.fromEntries(\n Object.entries(_tools).filter(\n ([name, tool]) => llm.isFunctionTool(tool) && retainedToolNames.has(name),\n ),\n );\n\n this._tools = retainedTools as llm.ToolContext;\n\n unlock();\n }\n\n private createToolsUpdateEvent(_tools: llm.ToolContext): api_proto.SessionUpdateEvent {\n const oaiTools: api_proto.Tool[] = [];\n\n for (const [name, tool] of Object.entries(_tools)) {\n if (!llm.isFunctionTool(tool)) {\n this.#logger.error({ name, tool }, \"OpenAI Realtime API doesn't support this tool type\");\n continue;\n }\n\n const { parameters: toolParameters, description } = tool;\n try {\n const parameters = llm.toJsonSchema(\n toolParameters,\n ) as unknown as api_proto.Tool['parameters'];\n\n oaiTools.push({\n name,\n description,\n parameters: parameters,\n type: 'function',\n });\n } catch (e) {\n this.#logger.error({ name, tool }, \"OpenAI Realtime API doesn't support this tool type\");\n continue;\n }\n }\n\n return {\n type: 'session.update',\n session: {\n model: this.oaiRealtimeModel._options.model,\n tools: oaiTools,\n },\n event_id: shortuuid('tools_update_'),\n };\n }\n\n async updateInstructions(_instructions: string): Promise<void> {\n const eventId = shortuuid('instructions_update_');\n this.sendEvent({\n type: 'session.update',\n session: {\n instructions: _instructions,\n },\n event_id: eventId,\n } as api_proto.SessionUpdateEvent);\n this.instructions = _instructions;\n }\n\n updateOptions({ toolChoice }: { toolChoice?: llm.ToolChoice }): void {\n const options: api_proto.SessionUpdateEvent['session'] = {};\n\n this.oaiRealtimeModel._options.toolChoice = toolChoice;\n options.tool_choice = toOaiToolChoice(toolChoice);\n\n // TODO(brian): add other options here\n\n this.sendEvent({\n type: 'session.update',\n session: options,\n event_id: shortuuid('options_update_'),\n });\n }\n\n pushAudio(frame: AudioFrame): void {\n for (const f of this.resampleAudio(frame)) {\n for (const nf of this.bstream.write(f.data.buffer)) {\n this.sendEvent({\n type: 'input_audio_buffer.append',\n audio: Buffer.from(nf.data.buffer).toString('base64'),\n } as api_proto.InputAudioBufferAppendEvent);\n // TODO(AJS-102): use frame.durationMs once available in rtc-node\n this.pushedDurationMs += (nf.samplesPerChannel / nf.sampleRate) * 1000;\n }\n }\n }\n\n async commitAudio(): Promise<void> {\n if (this.pushedDurationMs > 100) {\n // OpenAI requires at least 100ms of audio\n this.sendEvent({\n type: 'input_audio_buffer.commit',\n } as api_proto.InputAudioBufferCommitEvent);\n this.pushedDurationMs = 0;\n }\n }\n\n async clearAudio(): Promise<void> {\n this.sendEvent({\n type: 'input_audio_buffer.clear',\n } as api_proto.InputAudioBufferClearEvent);\n this.pushedDurationMs = 0;\n }\n\n async generateReply(instructions?: string): Promise<llm.GenerationCreatedEvent> {\n const handle = this.createResponse({ instructions, userInitiated: true });\n this.textModeRecoveryRetries = 0;\n return handle.doneFut.await;\n }\n\n async interrupt(): Promise<void> {\n this.sendEvent({\n type: 'response.cancel',\n } as api_proto.ResponseCancelEvent);\n }\n\n async truncate(_options: { messageId: string; audioEndMs: number }): Promise<void> {\n this.sendEvent({\n type: 'conversation.item.truncate',\n content_index: 0,\n item_id: _options.messageId,\n audio_end_ms: _options.audioEndMs,\n } as api_proto.ConversationItemTruncateEvent);\n }\n\n /// Truncates the data field of the event to the specified maxLength to avoid overwhelming logs\n /// with large amounts of base64 audio data.\n #loggableEvent(\n event: api_proto.ClientEvent | api_proto.ServerEvent,\n maxLength: number = 30,\n ): Record<string, unknown> {\n const untypedEvent: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(event)) {\n if (value !== undefined) {\n untypedEvent[key] = value;\n }\n }\n\n if (untypedEvent.audio && typeof untypedEvent.audio === 'string') {\n const truncatedData =\n untypedEvent.audio.slice(0, maxLength) + (untypedEvent.audio.length > maxLength ? '…' : '');\n return { ...untypedEvent, audio: truncatedData };\n }\n if (\n untypedEvent.delta &&\n typeof untypedEvent.delta === 'string' &&\n event.type === 'response.audio.delta'\n ) {\n const truncatedDelta =\n untypedEvent.delta.slice(0, maxLength) + (untypedEvent.delta.length > maxLength ? '…' : '');\n return { ...untypedEvent, delta: truncatedDelta };\n }\n return untypedEvent;\n }\n\n private async createWsConn(): Promise<WebSocket> {\n const headers: Record<string, string> = {\n 'User-Agent': 'LiveKit-Agents-JS',\n };\n\n if (this.oaiRealtimeModel._options.isAzure) {\n // Microsoft API has two ways of authentication\n // 1. Entra token set as `Bearer` token\n // 2. API key set as `api_key` header (also accepts query string)\n if (this.oaiRealtimeModel._options.entraToken) {\n headers.Authorization = `Bearer ${this.oaiRealtimeModel._options.entraToken}`;\n } else if (this.oaiRealtimeModel._options.apiKey) {\n headers['api-key'] = this.oaiRealtimeModel._options.apiKey;\n } else {\n throw new Error('Microsoft API key or entraToken is required');\n }\n } else {\n headers.Authorization = `Bearer ${this.oaiRealtimeModel._options.apiKey}`;\n headers['OpenAI-Beta'] = 'realtime=v1';\n }\n\n const url = processBaseURL({\n baseURL: this.oaiRealtimeModel._options.baseURL,\n model: this.oaiRealtimeModel._options.model,\n isAzure: this.oaiRealtimeModel._options.isAzure,\n apiVersion: this.oaiRealtimeModel._options.apiVersion,\n azureDeployment: this.oaiRealtimeModel._options.azureDeployment,\n });\n\n this.#logger.debug(`Connecting to OpenAI Realtime API at ${url}`);\n\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(url, { headers });\n let waiting = true;\n\n const timeout = setTimeout(() => {\n ws.close();\n reject(new Error('WebSocket connection timeout'));\n }, this.oaiRealtimeModel._options.connOptions.timeoutMs);\n\n ws.once('open', () => {\n if (!waiting) return;\n waiting = false;\n clearTimeout(timeout);\n resolve(ws);\n });\n\n ws.once('close', () => {\n if (!waiting) return;\n waiting = false;\n clearTimeout(timeout);\n reject(new Error('OpenAI Realtime API connection closed'));\n });\n });\n }\n\n async #mainTask(signal: AbortSignal): Promise<void> {\n let reconnecting = false;\n let numRetries = 0;\n let wsConn: WebSocket | null = null;\n const maxRetries = this.oaiRealtimeModel._options.connOptions.maxRetry;\n\n const reconnect = async () => {\n this.#logger.debug(\n {\n maxSessionDuration: this.oaiRealtimeModel._options.maxSessionDuration,\n },\n 'Reconnecting to OpenAI Realtime API',\n );\n\n const events: api_proto.ClientEvent[] = [];\n\n // options and instructions\n events.push(this.createSessionUpdateEvent());\n\n // tools\n if (Object.keys(this._tools).length > 0) {\n events.push(this.createToolsUpdateEvent(this._tools));\n }\n\n // chat context\n const chatCtx = this.chatCtx.copy({\n excludeFunctionCall: true,\n excludeInstructions: true,\n excludeEmptyMessage: true,\n });\n\n const oldChatCtx = this.remoteChatCtx;\n this.remoteChatCtx = new llm.RemoteChatContext();\n events.push(...this.createChatCtxUpdateEvents(chatCtx));\n\n try {\n for (const ev of events) {\n this.emit('openai_client_event_queued', ev);\n wsConn!.send(JSON.stringify(ev));\n }\n } catch (error) {\n this.remoteChatCtx = oldChatCtx;\n throw new APIConnectionError({\n message: 'Failed to send message to OpenAI Realtime API during session re-connection',\n });\n }\n\n this.#logger.debug('Reconnected to OpenAI Realtime API');\n\n this.emit('session_reconnected', {} as llm.RealtimeSessionReconnectedEvent);\n };\n\n reconnecting = false;\n while (!this.#closed && !signal.aborted) {\n this.#logger.debug('Creating WebSocket connection to OpenAI Realtime API');\n wsConn = await this.createWsConn();\n if (signal.aborted) break;\n\n try {\n if (reconnecting) {\n await reconnect();\n if (signal.aborted) break;\n numRetries = 0;\n }\n\n await this.runWs(wsConn);\n if (signal.aborted) break;\n } catch (error) {\n if (!isAPIError(error)) {\n this.emitError({ error: error as Error, recoverable: false });\n throw error;\n }\n\n if (maxRetries === 0 || !error.retryable) {\n this.emitError({ error: error as Error, recoverable: false });\n throw error;\n }\n\n if (numRetries === maxRetries) {\n this.emitError({ error: error as Error, recoverable: false });\n throw new APIConnectionError({\n message: `OpenAI Realtime API connection failed after ${numRetries} attempts`,\n options: {\n body: error,\n retryable: false,\n },\n });\n }\n\n this.emitError({ error: error as Error, recoverable: true });\n const retryInterval =\n numRetries === 0\n ? DEFAULT_FIRST_RETRY_INTERVAL_MS\n : this.oaiRealtimeModel._options.connOptions.retryIntervalMs;\n this.#logger.warn(\n {\n attempt: numRetries,\n maxRetries,\n error,\n },\n `OpenAI Realtime API connection failed, retrying in ${retryInterval / 1000}s`,\n );\n\n await delay(retryInterval);\n numRetries++;\n }\n\n reconnecting = true;\n }\n }\n\n private async runWs(wsConn: WebSocket): Promise<void> {\n const forwardEvents = async (signal: AbortSignal): Promise<void> => {\n const abortFuture = new Future<void>();\n signal.addEventListener('abort', () => abortFuture.resolve());\n\n while (!this.#closed && wsConn.readyState === WebSocket.OPEN && !signal.aborted) {\n try {\n const event = await Promise.race([this.messageChannel.get(), abortFuture.await]);\n if (signal.aborted || abortFuture.done || event === undefined) {\n break;\n }\n\n if (event.type !== 'input_audio_buffer.append') {\n this.#logger.debug(`(client) -> ${JSON.stringify(this.#loggableEvent(event))}`);\n }\n\n this.emit('openai_client_event_queued', event);\n wsConn.send(JSON.stringify(event));\n } catch (error) {\n break;\n }\n }\n\n wsConn.close();\n };\n\n const wsCloseFuture = new Future<void | Error>();\n\n wsConn.onerror = (error) => {\n wsCloseFuture.resolve(new APIConnectionError({ message: error.message }));\n };\n wsConn.onclose = () => {\n wsCloseFuture.resolve();\n };\n\n wsConn.onmessage = (message: MessageEvent) => {\n const event: api_proto.ServerEvent = JSON.parse(message.data as string);\n\n this.emit('openai_server_event_received', event);\n this.#logger.debug(`(server) <- ${JSON.stringify(this.#loggableEvent(event))}`);\n\n switch (event.type) {\n case 'input_audio_buffer.speech_started':\n this.handleInputAudioBufferSpeechStarted(event);\n break;\n case 'input_audio_buffer.speech_stopped':\n this.handleInputAudioBufferSpeechStopped(event);\n break;\n case 'response.created':\n this.handleResponseCreated(event);\n break;\n case 'response.output_item.added':\n this.handleResponseOutputItemAdded(event);\n break;\n case 'conversation.item.created':\n this.handleConversationItemCreated(event);\n break;\n case 'conversation.item.deleted':\n this.handleConversationItemDeleted(event);\n break;\n case 'conversation.item.input_audio_transcription.completed':\n this.handleConversationItemInputAudioTranscriptionCompleted(event);\n break;\n case 'conversation.item.input_audio_transcription.failed':\n this.handleConversationItemInputAudioTranscriptionFailed(event);\n break;\n case 'response.content_part.added':\n this.handleResponseContentPartAdded(event);\n break;\n case 'response.content_part.done':\n this.handleResponseContentPartDone(event);\n break;\n case 'response.audio_transcript.delta':\n this.handleResponseAudioTranscriptDelta(event);\n break;\n case 'response.audio.delta':\n this.handleResponseAudioDelta(event);\n break;\n case 'response.audio_transcript.done':\n this.handleResponseAudioTranscriptDone(event);\n break;\n case 'response.audio.done':\n this.handleResponseAudioDone(event);\n break;\n case 'response.output_item.done':\n this.handleResponseOutputItemDone(event);\n break;\n case 'response.done':\n this.handleResponseDone(event);\n break;\n case 'error':\n this.handleError(event);\n break;\n default:\n this.#logger.debug(`unhandled event: ${event.type}`);\n break;\n }\n };\n\n const sendTask = Task.from(({ signal }) => forwardEvents(signal));\n\n const wsTask = Task.from(({ signal }) => {\n const abortPromise = new Promise<void>((resolve) => {\n signal.addEventListener('abort', () => {\n resolve();\n });\n });\n\n return Promise.race([wsCloseFuture.await, abortPromise]);\n });\n\n const waitReconnectTask = Task.from(async ({ signal }) => {\n await delay(this.oaiRealtimeModel._options.maxSessionDuration, { signal });\n return new APIConnectionError({\n message: 'OpenAI Realtime API connection timeout',\n });\n });\n\n try {\n const result = await Promise.race([wsTask.result, sendTask.result, waitReconnectTask.result]);\n\n if (waitReconnectTask.done && this.currentGeneration) {\n await this.currentGeneration._doneFut.await;\n }\n\n if (result instanceof Error) {\n throw result;\n }\n } finally {\n await cancelAndWait([wsTask, sendTask, waitReconnectTask], 2000);\n wsConn.close();\n }\n }\n\n async close() {\n super.close();\n this.#closed = true;\n await this.#task;\n }\n\n private handleInputAudioBufferSpeechStarted(\n _event: api_proto.InputAudioBufferSpeechStartedEvent,\n ): void {\n this.emit('input_speech_started', {} as llm.InputSpeechStartedEvent);\n }\n\n private handleInputAudioBufferSpeechStopped(\n _event: api_proto.InputAudioBufferSpeechStoppedEvent,\n ): void {\n this.emit('input_speech_stopped', {\n userTranscriptionEnabled: this.oaiRealtimeModel._options.inputAudioTranscription !== null,\n } as llm.InputSpeechStoppedEvent);\n }\n\n private handleResponseCreated(event: api_proto.ResponseCreatedEvent): void {\n if (!event.response.id) {\n throw new Error('response.id is missing');\n }\n\n this.currentGeneration = {\n messageChannel: stream.createStreamChannel<llm.MessageGeneration>(),\n functionChannel: stream.createStreamChannel<llm.FunctionCall>(),\n messages: new Map(),\n _doneFut: new Future(),\n _createdTimestamp: Date.now(),\n };\n\n // Build generation event and resolve client future (if any) before emitting,\n // matching Python behavior.\n const generationEv = {\n messageStream: this.currentGeneration.messageChannel.stream(),\n functionStream: this.currentGeneration.functionChannel.stream(),\n userInitiated: false,\n } as GenerationCreatedEvent;\n\n const clientEventId = event.response.metadata?.client_event_id;\n if (clientEventId) {\n const handle = this.responseCreatedFutures[clientEventId];\n if (handle) {\n delete this.responseCreatedFutures[clientEventId];\n generationEv.userInitiated = true;\n if (!handle.doneFut.done) {\n handle.doneFut.resolve(generationEv);\n }\n }\n }\n\n this.emit('generation_created', generationEv);\n }\n\n private handleResponseOutputItemAdded(event: api_proto.ResponseOutputItemAddedEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n if (!event.item.type) {\n throw new Error('item.type is not set');\n }\n\n if (!event.response_id) {\n throw new Error('response_id is not set');\n }\n\n const itemType = event.item.type;\n const responseId = event.response_id;\n\n if (itemType !== 'message') {\n // emit immediately if it's not a message, otherwise wait response.content_part.added\n this.resolveGeneration(responseId);\n this.textModeRecoveryRetries = 0;\n return;\n }\n }\n\n private handleConversationItemCreated(event: api_proto.ConversationItemCreatedEvent): void {\n if (!event.item.id) {\n throw new Error('item.id is not set');\n }\n\n try {\n this.remoteChatCtx.insert(event.previous_item_id, openAIItemToLivekitItem(event.item));\n } catch (error) {\n this.#logger.error({ error, itemId: event.item.id }, 'failed to insert conversation item');\n }\n\n const fut = this.itemCreateFutures[event.item.id];\n if (fut) {\n fut.resolve();\n delete this.itemCreateFutures[event.item.id];\n }\n }\n\n private handleConversationItemDeleted(event: api_proto.ConversationItemDeletedEvent): void {\n if (!event.item_id) {\n throw new Error('item_id is not set');\n }\n\n try {\n this.remoteChatCtx.delete(event.item_id);\n } catch (error) {\n this.#logger.error({ error, itemId: event.item_id }, 'failed to delete conversation item');\n }\n\n const fut = this.itemDeleteFutures[event.item_id];\n if (fut) {\n fut.resolve();\n delete this.itemDeleteFutures[event.item_id];\n }\n }\n\n private handleConversationItemInputAudioTranscriptionCompleted(\n event: api_proto.ConversationItemInputAudioTranscriptionCompletedEvent,\n ): void {\n const remoteItem = this.remoteChatCtx.get(event.item_id);\n if (!remoteItem) {\n return;\n }\n\n const item = remoteItem.item;\n if (item instanceof llm.ChatMessage) {\n item.content.push(event.transcript);\n } else {\n throw new Error('item is not a chat message');\n }\n\n this.emit('input_audio_transcription_completed', {\n itemId: event.item_id,\n transcript: event.transcript,\n isFinal: true,\n } as llm.InputTranscriptionCompleted);\n }\n\n private handleConversationItemInputAudioTranscriptionFailed(\n event: api_proto.ConversationItemInputAudioTranscriptionFailedEvent,\n ): void {\n this.#logger.error(\n { error: event.error },\n 'OpenAI Realtime API failed to transcribe input audio',\n );\n }\n\n private handleResponseContentPartAdded(event: api_proto.ResponseContentPartAddedEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemId = event.item_id;\n const itemType = event.part.type;\n const responseId = event.response_id;\n\n if (itemType === 'audio') {\n this.resolveGeneration(responseId);\n if (this.textModeRecoveryRetries > 0) {\n this.#logger.info(\n { retries: this.textModeRecoveryRetries },\n 'recovered from text-only response',\n );\n this.textModeRecoveryRetries = 0;\n }\n\n const itemGeneration: MessageGeneration = {\n messageId: itemId,\n textChannel: stream.createStreamChannel<string>(),\n audioChannel: stream.createStreamChannel<AudioFrame>(),\n audioTranscript: '',\n };\n\n this.currentGeneration.messageChannel.write({\n messageId: itemId,\n textStream: itemGeneration.textChannel.stream(),\n audioStream: itemGeneration.audioChannel.stream(),\n });\n\n this.currentGeneration.messages.set(itemId, itemGeneration);\n this.currentGeneration._firstTokenTimestamp = Date.now();\n return;\n } else {\n this.interrupt();\n if (this.textModeRecoveryRetries === 0) {\n this.#logger.warn({ responseId }, 'received text-only response from OpenAI Realtime API');\n }\n }\n }\n\n private handleResponseContentPartDone(event: api_proto.ResponseContentPartDoneEvent): void {\n if (event.part.type !== 'text') {\n return;\n }\n\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n // TODO(shubhra): handle text mode recovery\n }\n\n private handleResponseAudioTranscriptDelta(\n event: api_proto.ResponseAudioTranscriptDeltaEvent,\n ): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemId = event.item_id;\n const delta = event.delta;\n\n // TODO (shubhra): add timed string support\n\n const itemGeneration = this.currentGeneration.messages.get(itemId);\n if (!itemGeneration) {\n throw new Error('itemGeneration is not set');\n } else {\n itemGeneration.textChannel.write(delta);\n itemGeneration.audioTranscript += delta;\n }\n }\n\n private handleResponseAudioDelta(event: api_proto.ResponseAudioDeltaEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemGeneration = this.currentGeneration.messages.get(event.item_id);\n if (!itemGeneration) {\n throw new Error('itemGeneration is not set');\n }\n\n const binaryString = atob(event.delta);\n const len = binaryString.length;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n itemGeneration.audioChannel.write(\n new AudioFrame(\n new Int16Array(bytes.buffer),\n api_proto.SAMPLE_RATE,\n api_proto.NUM_CHANNELS,\n bytes.length / 2,\n ),\n );\n }\n\n private handleResponseAudioTranscriptDone(\n _event: api_proto.ResponseAudioTranscriptDoneEvent,\n ): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n }\n\n private handleResponseAudioDone(_event: api_proto.ResponseAudioDoneEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n }\n\n private handleResponseOutputItemDone(event: api_proto.ResponseOutputItemDoneEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemId = event.item.id;\n const itemType = event.item.type;\n\n if (itemType === 'function_call') {\n const item = event.item;\n if (!item.call_id || !item.name || !item.arguments) {\n throw new Error('item is not a function call');\n }\n this.currentGeneration.functionChannel.write({\n callId: item.call_id,\n name: item.name,\n args: item.arguments,\n } as llm.FunctionCall);\n } else if (itemType === 'message') {\n const itemGeneration = this.currentGeneration.messages.get(itemId);\n if (!itemGeneration) {\n return;\n }\n // text response doesn't have itemGeneration\n itemGeneration.textChannel.close();\n itemGeneration.audioChannel.close();\n }\n }\n\n private handleResponseDone(_event: api_proto.ResponseDoneEvent): void {\n if (!this.currentGeneration) {\n // OpenAI has a race condition where we could receive response.done without any\n // previous response.created (This happens generally during interruption)\n return;\n }\n\n const createdTimestamp = this.currentGeneration._createdTimestamp;\n const firstTokenTimestamp = this.currentGeneration._firstTokenTimestamp;\n\n this.#logger.debug(\n {\n messageCount: this.currentGeneration.messages.size,\n },\n 'Closing generation channels in handleResponseDone',\n );\n\n for (const generation of this.currentGeneration.messages.values()) {\n generation.textChannel.close();\n generation.audioChannel.close();\n }\n\n this.currentGeneration.functionChannel.close();\n this.currentGeneration.messageChannel.close();\n\n for (const itemId of this.currentGeneration.messages.keys()) {\n const remoteItem = this.remoteChatCtx.get(itemId);\n if (remoteItem && remoteItem.item instanceof llm.ChatMessage) {\n remoteItem.item.content.push(this.currentGeneration.messages.get(itemId)!.audioTranscript);\n }\n }\n\n this.currentGeneration._doneFut.resolve();\n this.currentGeneration = undefined;\n\n // Calculate and emit metrics\n const usage = _event.response.usage;\n const ttft = firstTokenTimestamp ? firstTokenTimestamp - createdTimestamp : -1;\n const duration = (Date.now() - createdTimestamp) / 1000; // Convert to seconds\n\n const realtimeMetrics: metrics.RealtimeModelMetrics = {\n type: 'realtime_model_metrics',\n timestamp: createdTimestamp / 1000, // Convert to seconds\n requestId: _event.response.id || '',\n ttft,\n duration,\n cancelled: _event.response.status === 'cancelled',\n label: 'openai_realtime',\n inputTokens: usage?.input_tokens ?? 0,\n outputTokens: usage?.output_tokens ?? 0,\n totalTokens: usage?.total_tokens ?? 0,\n tokensPerSecond: duration > 0 ? (usage?.output_tokens ?? 0) / duration : 0,\n inputTokenDetails: {\n audioTokens: usage?.input_token_details?.audio_tokens ?? 0,\n textTokens: usage?.input_token_details?.text_tokens ?? 0,\n imageTokens: 0, // Not supported yet\n cachedTokens: usage?.input_token_details?.cached_tokens ?? 0,\n cachedTokensDetails: usage?.input_token_details?.cached_tokens_details\n ? {\n audioTokens: usage?.input_token_details?.cached_tokens_details?.audio_tokens ?? 0,\n textTokens: usage?.input_token_details?.cached_tokens_details?.text_tokens ?? 0,\n imageTokens: usage?.input_token_details?.cached_tokens_details?.image_tokens ?? 0,\n }\n : undefined,\n },\n outputTokenDetails: {\n textTokens: usage?.output_token_details?.text_tokens ?? 0,\n audioTokens: usage?.output_token_details?.audio_tokens ?? 0,\n imageTokens: 0,\n },\n };\n\n this.emit('metrics_collected', realtimeMetrics);\n // TODO(brian): handle response done but not complete\n }\n\n private handleError(event: api_proto.ErrorEvent): void {\n if (event.error.message.startsWith('Cancellation failed')) {\n return;\n }\n\n this.#logger.error({ error: event.error }, 'OpenAI Realtime API returned an error');\n this.emitError({\n error: new APIError(event.error.message, {\n body: event.error,\n retryable: true,\n }),\n recoverable: true,\n });\n\n // TODO(brian): set error for response future if it exists\n }\n\n private emitError({ error, recoverable }: { error: Error; recoverable: boolean }): void {\n // IMPORTANT: only emit error if there are listeners; otherwise emit will throw an error\n this.emit('error', {\n timestamp: Date.now(),\n // TODO(brian): add label\n label: '',\n error,\n recoverable,\n } as llm.RealtimeModelError);\n }\n\n private *resampleAudio(frame: AudioFrame): Generator<AudioFrame> {\n yield frame;\n }\n\n private createResponse({\n userInitiated,\n instructions,\n oldHandle,\n }: {\n userInitiated: boolean;\n instructions?: string;\n oldHandle?: CreateResponseHandle;\n }): CreateResponseHandle {\n const handle = oldHandle || new CreateResponseHandle({ instructions });\n if (oldHandle && instructions) {\n handle.instructions = instructions;\n }\n\n const eventId = shortuuid('response_create_');\n if (userInitiated) {\n this.responseCreatedFutures[eventId] = handle;\n }\n\n const response: api_proto.ResponseCreateEvent['response'] = {};\n if (instructions) response.instructions = instructions;\n if (userInitiated) response.metadata = { client_event_id: eventId };\n\n this.sendEvent({\n type: 'response.create',\n event_id: eventId,\n response: Object.keys(response).length > 0 ? response : undefined,\n });\n\n return handle;\n }\n\n private resolveGeneration(responseId: string): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const generation_ev = {\n messageStream: this.currentGeneration.messageChannel.stream(),\n functionStream: this.currentGeneration.functionChannel.stream(),\n userInitiated: false,\n } as GenerationCreatedEvent;\n\n const handle = this.responseCreatedFutures[responseId];\n if (handle) {\n delete this.responseCreatedFutures[responseId];\n generation_ev.userInitiated = true;\n if (handle.doneFut.done) {\n this.#logger.warn({ responseId }, 'response received after timeout');\n } else {\n handle.doneFut.resolve(generation_ev);\n }\n }\n }\n}\n\nfunction livekitItemToOpenAIItem(item: llm.ChatItem): api_proto.ItemResource {\n switch (item.type) {\n case 'function_call':\n return {\n id: item.id,\n type: 'function_call',\n call_id: item.callId,\n name: item.name,\n arguments: item.args,\n } as api_proto.FunctionCallItem;\n case 'function_call_output':\n return {\n id: item.id,\n type: 'function_call_output',\n call_id: item.callId,\n output: item.output,\n } as api_proto.FunctionCallOutputItem;\n case 'message':\n const role = item.role === 'developer' ? 'system' : item.role;\n const contentList: api_proto.Content[] = [];\n for (const c of item.content) {\n if (typeof c === 'string') {\n contentList.push({\n type: role === 'assistant' ? 'text' : 'input_text',\n text: c,\n } as api_proto.InputTextContent);\n } else if (c.type === 'image_content') {\n // not supported for now\n continue;\n } else if (c.type === 'audio_content') {\n if (role === 'user') {\n const encodedAudio = Buffer.from(combineAudioFrames(c.frame).data).toString('base64');\n contentList.push({\n type: 'input_audio',\n audio: encodedAudio,\n } as api_proto.InputAudioContent);\n }\n }\n }\n return {\n id: item.id,\n type: 'message',\n role,\n content: contentList,\n } as api_proto.UserItem;\n }\n}\n\nfunction openAIItemToLivekitItem(item: api_proto.ItemResource): llm.ChatItem {\n if (!item.id) {\n throw new Error('item.id is not set');\n }\n\n switch (item.type) {\n case 'function_call':\n return llm.FunctionCall.create({\n id: item.id,\n callId: item.call_id,\n name: item.name,\n args: item.arguments,\n });\n case 'function_call_output':\n return llm.FunctionCallOutput.create({\n id: item.id,\n callId: item.call_id,\n output: item.output,\n isError: false,\n });\n case 'message':\n const content: llm.ChatContent[] = [];\n // item.content can be a single object or an array; normalize to array\n const contents = Array.isArray(item.content) ? item.content : [item.content];\n for (const c of contents) {\n if (c.type === 'text' || c.type === 'input_text') {\n content.push(c.text);\n }\n }\n return llm.ChatMessage.create({\n id: item.id,\n role: item.role,\n content,\n });\n }\n}\n\nfunction createMockAudioItem(durationSeconds: number = 2): llm.ChatMessage {\n const audioData = Buffer.alloc(durationSeconds * SAMPLE_RATE);\n return llm.ChatMessage.create({\n id: shortuuid(MOCK_AUDIO_ID_PREFIX),\n role: 'user',\n content: [\n {\n type: 'audio_content',\n frame: [\n new AudioFrame(\n new Int16Array(audioData.buffer),\n SAMPLE_RATE,\n NUM_CHANNELS,\n audioData.length / 2,\n ),\n ],\n } as llm.AudioContent,\n ],\n });\n}\n\nfunction toOaiToolChoice(toolChoice?: llm.ToolChoice): api_proto.ToolChoice {\n if (typeof toolChoice === 'string') {\n return toolChoice;\n }\n\n if (toolChoice?.type === 'function') {\n return toolChoice.function.name;\n }\n\n return 'auto';\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,oBAgBO;AACP,mBAAsB;AAEtB,sBAA+C;AAE/C,gBAA6C;AAC7C,gBAA2B;AAE3B,MAAM,cAAc;AACpB,MAAM,eAAe;AACrB,MAAM,WAAW;AAEjB,MAAM,uBAAuB;AA4C7B,MAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA;AAAA,EAEA,YAAY,EAAE,aAAa,GAA8B;AACvD,SAAK,eAAe;AACpB,SAAK,UAAU,IAAI,qBAAO;AAAA,EAC5B;AACF;AAGA,MAAM,kCAAkC;AACxC,MAAM,sBAAsB;AAC5B,MAAM,yBAAsD;AAAA,EAC1D,MAAM;AAAA,EACN,WAAW;AAAA,EACX,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,oBAAoB;AACtB;AACA,MAAM,oCAAuE;AAAA,EAC3E,OAAO;AACT;AACA,MAAM,sBAAsC;AAC5C,MAAM,qCAAqD;AAE3D,MAAM,0CAA6E;AAAA,EACjF,OAAO;AACT;AAEA,MAAM,+BAA4D;AAAA,EAChE,MAAM;AAAA,EACN,WAAW;AAAA,EACX,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,iBAAiB;AACnB;AAEA,MAAM,+BAA+B,KAAK,KAAK;AAE/C,MAAM,iCAAiC;AAAA,EACrC,OAAO;AAAA,EACP,OAAO;AAAA,EACP,aAAa;AAAA,EACb,yBAAyB;AAAA,EACzB,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,yBAAyB;AAAA,EACzB,oBAAoB;AAAA,EACpB,aAAa;AACf;AACO,MAAM,sBAAsB,kBAAI,cAAc;AAAA,EACnD,aAAa,UAAU;AAAA,EACvB,cAAc,UAAU;AAAA,EACxB,cAAc,UAAU;AAAA,EACxB,eAAe,UAAU;AAAA;AAAA,EAGzB;AAAA,EAEA,YACE,UAiBI,CAAC,GACL;AACA,UAAM;AAAA,MACJ,mBAAmB;AAAA,MACnB,eAAe,QAAQ,kBAAkB;AAAA,MACzC,mBAAmB,QAAQ,4BAA4B;AAAA,MACvD,yBAAyB;AAAA,IAC3B,CAAC;AAED,UAAM,UAAU,CAAC,EAAE,QAAQ,cAAc,QAAQ,cAAc,QAAQ;AAEvE,QAAI,QAAQ,WAAW,MAAM,CAAC,SAAS;AACrC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,SAAS,QAAQ,UAAU,QAAQ,IAAI;AAE7C,QAAI,CAAC,UAAU,CAAC,SAAS;AACvB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,QAAQ,WAAW,SAAS;AAC/B,YAAM,gBAAgB,QAAQ,IAAI;AAClC,UAAI,CAAC,eAAe;AAClB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,cAAQ,UAAU,GAAG,cAAc,QAAQ,OAAO,EAAE,CAAC;AAAA,IACvD;AAEA,SAAK,WAAW;AAAA,MACd,GAAG;AAAA,MACH,GAAG;AAAA,MACH,SAAS,QAAQ,WAAW;AAAA,MAC5B;AAAA,MACA;AAAA,MACA,OAAO,QAAQ,SAAS,+BAA+B;AAAA,IACzD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,OAAO,UAAU;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR,0BAA0B;AAAA,IAC1B,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd;AAAA,EACF,GAaG;AACD,aAAS,UAAU,QAAQ,IAAI;AAC/B,QAAI,CAAC,UAAU,CAAC,YAAY;AAC1B,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,iBAAa,cAAc,QAAQ,IAAI;AACvC,QAAI,CAAC,YAAY;AACf,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,SAAS;AACZ,sBAAgB,iBAAiB,QAAQ,IAAI;AAC7C,UAAI,CAAC,eAAe;AAClB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,gBAAU,GAAG,cAAc,QAAQ,OAAO,EAAE,CAAC;AAAA,IAC/C;AAEA,WAAO,IAAI,cAAc;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,UAAU;AACR,WAAO,IAAI,gBAAgB,IAAI;AAAA,EACjC;AAAA,EAEA,MAAM,QAAQ;AACZ;AAAA,EACF;AACF;AAEA,SAAS,eAAe;AAAA,EACtB;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AACF,GAMW;AACT,QAAM,MAAM,IAAI,IAAI,CAAC,SAAS,UAAU,EAAE,KAAK,GAAG,CAAC;AAEnD,MAAI,IAAI,aAAa,UAAU;AAC7B,QAAI,WAAW;AAAA,EACjB;AAGA,MAAI,CAAC,IAAI,YAAY,CAAC,IAAI,OAAO,SAAS,EAAE,SAAS,IAAI,SAAS,QAAQ,OAAO,EAAE,CAAC,GAAG;AACrF,QAAI,WAAW,IAAI,SAAS,QAAQ,OAAO,EAAE,IAAI;AAAA,EACnD,OAAO;AACL,QAAI,WAAW,IAAI,SAAS,QAAQ,OAAO,EAAE;AAAA,EAC/C;AAEA,QAAM,cAAsC,CAAC;AAC7C,MAAI,SAAS;AACX,QAAI,YAAY;AACd,kBAAY,aAAa,IAAI;AAAA,IAC/B;AACA,QAAI,iBAAiB;AACnB,kBAAY,YAAY,IAAI;AAAA,IAC9B;AAAA,EACF,OAAO;AACL,gBAAY,OAAO,IAAI;AAAA,EACzB;AAEA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,WAAW,GAAG;AACtD,QAAI,aAAa,IAAI,KAAK,KAAK;AAAA,EACjC;AAEA,SAAO,IAAI,SAAS;AACtB;AAYO,MAAM,wBAAwB,kBAAI,gBAAgB;AAAA,EAC/C,SAA0B,CAAC;AAAA,EAC3B,gBAAuC,IAAI,kBAAI,kBAAkB;AAAA,EACjE,iBAAiB,IAAI,oBAA6B;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,yBAAiE,CAAC;AAAA,EAElE,0BAAkC;AAAA,EAElC,oBAA8C,CAAC;AAAA,EAC/C,oBAA8C,CAAC;AAAA,EAE/C,oBAAoB,IAAI,mBAAM;AAAA,EAC9B,oBAAoB,IAAI,mBAAM;AAAA;AAAA,EAG9B,UAAU,IAAI,8BAAgB,aAAa,cAAc,cAAc,EAAE;AAAA,EAEzE,mBAA2B;AAAA,EAEnC,cAAU,mBAAI;AAAA,EACd;AAAA,EACA,UAAU;AAAA,EAEV,YAAY,eAA8B;AACxC,UAAM,aAAa;AAEnB,SAAK,mBAAmB;AAExB,SAAK,QAAQ,mBAAK,KAAK,CAAC,EAAE,OAAO,MAAM,KAAK,UAAU,MAAM,CAAC;AAE7D,SAAK,UAAU,KAAK,yBAAyB,CAAC;AAAA,EAChD;AAAA,EAEA,UAAU,SAAsC;AAC9C,SAAK,eAAe,IAAI,OAAO;AAAA,EACjC;AAAA,EAEQ,2BAAyD;AAC/D,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,QACP,OAAO,KAAK,iBAAiB,SAAS;AAAA,QACtC,OAAO,KAAK,iBAAiB,SAAS;AAAA,QACtC,oBAAoB;AAAA,QACpB,qBAAqB;AAAA,QACrB,YAAY,CAAC,QAAQ,OAAO;AAAA,QAC5B,gBAAgB,KAAK,iBAAiB,SAAS;AAAA,QAC/C,2BAA2B,KAAK,iBAAiB,SAAS;AAAA;AAAA,QAE1D,aAAa,KAAK,iBAAiB,SAAS;AAAA,QAC5C,aAAa,gBAAgB,KAAK,iBAAiB,SAAS,UAAU;AAAA,QACtE,4BACE,KAAK,iBAAiB,SAAS,4BAA4B,WACvD,QACA,KAAK,iBAAiB,SAAS;AAAA;AAAA,QAErC,cAAc,KAAK;AAAA,QACnB,OAAO,KAAK,iBAAiB,SAAS;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,IAAI,UAAU;AACZ,WAAO,KAAK,cAAc,UAAU;AAAA,EACtC;AAAA,EAEA,IAAI,QAAQ;AACV,WAAO,EAAE,GAAG,KAAK,OAAO;AAAA,EAC1B;AAAA,EAEA,MAAM,cAAc,UAA0C;AAC5D,UAAM,SAAS,MAAM,KAAK,kBAAkB,KAAK;AACjD,UAAM,SAAS,KAAK,0BAA0B,QAAQ;AACtD,UAAM,UAA0B,CAAC;AAEjC,eAAW,SAAS,QAAQ;AAC1B,YAAM,SAAS,IAAI,qBAAa;AAChC,cAAQ,KAAK,MAAM;AAEnB,UAAI,MAAM,SAAS,4BAA4B;AAC7C,aAAK,kBAAkB,MAAM,KAAK,EAAE,IAAI;AAAA,MAC1C,WAAW,MAAM,QAAQ,4BAA4B;AACnD,aAAK,kBAAkB,MAAM,OAAO,IAAI;AAAA,MAC1C;AAEA,WAAK,UAAU,KAAK;AAAA,IACtB;AAEA,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO;AACP;AAAA,IACF;AAEA,QAAI;AAEF,YAAM,QAAQ,KAAK;AAAA,QACjB,QAAQ,IAAI,OAAO;AAAA,YACnB,qBAAM,GAAI,EAAE,KAAK,MAAM;AACrB,gBAAM,IAAI,MAAM,kCAAkC;AAAA,QACpD,CAAC;AAAA,MACH,CAAC;AAAA,IACH,SAAS,GAAG;AACV,WAAK,QAAQ,MAAO,EAAY,OAAO;AACvC,YAAM;AAAA,IACR,UAAE;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,0BACN,SACA,eAAwB,OAC2D;AACnF,UAAM,aAAa,QAAQ,KAAK;AAChC,QAAI,cAAc;AAChB,iBAAW,MAAM,KAAK,oBAAoB,CAAC;AAAA,IAC7C,OAAO;AAEL,iBAAW,QAAQ,WAAW,MAAM;AAAA,QAClC,CAAC,SAAS,CAAC,KAAK,GAAG,WAAW,oBAAoB;AAAA,MACpD;AAAA,IACF;AAEA,UAAM,SAGA,CAAC;AAEP,UAAM,UAAU,kBAAI,mBAAmB,KAAK,SAAS,UAAU;AAC/D,eAAW,MAAM,QAAQ,UAAU;AACjC,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SAAS;AAAA,QACT,cAAU,yBAAU,kBAAkB;AAAA,MACxC,CAA0C;AAAA,IAC5C;AAEA,eAAW,CAAC,YAAY,EAAE,KAAK,QAAQ,UAAU;AAC/C,YAAM,WAAW,WAAW,QAAQ,EAAE;AACtC,UAAI,CAAC,UAAU;AACb,cAAM,IAAI,MAAM,aAAa,EAAE,YAAY;AAAA,MAC7C;AACA,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,MAAM,wBAAwB,QAAQ;AAAA,QACtC,kBAAkB,cAAc;AAAA,QAChC,cAAU,yBAAU,kBAAkB;AAAA,MACxC,CAA0C;AAAA,IAC5C;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY,QAAwC;AACxD,UAAM,SAAS,MAAM,KAAK,kBAAkB,KAAK;AACjD,UAAM,KAAK,KAAK,uBAAuB,MAAM;AAC7C,SAAK,UAAU,EAAE;AAEjB,QAAI,CAAC,GAAG,QAAQ,OAAO;AACrB,YAAM,IAAI,MAAM,+CAA+C;AAAA,IACjE;AAIA,UAAM,oBAAoB,IAAI,IAAI,GAAG,QAAQ,MAAM,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC;AAC3E,UAAM,gBAAgB,OAAO;AAAA,MAC3B,OAAO,QAAQ,MAAM,EAAE;AAAA,QACrB,CAAC,CAAC,MAAM,IAAI,MAAM,kBAAI,eAAe,IAAI,KAAK,kBAAkB,IAAI,IAAI;AAAA,MAC1E;AAAA,IACF;AAEA,SAAK,SAAS;AAEd,WAAO;AAAA,EACT;AAAA,EAEQ,uBAAuB,QAAuD;AACpF,UAAM,WAA6B,CAAC;AAEpC,eAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,UAAI,CAAC,kBAAI,eAAe,IAAI,GAAG;AAC7B,aAAK,QAAQ,MAAM,EAAE,MAAM,KAAK,GAAG,oDAAoD;AACvF;AAAA,MACF;AAEA,YAAM,EAAE,YAAY,gBAAgB,YAAY,IAAI;AACpD,UAAI;AACF,cAAM,aAAa,kBAAI;AAAA,UACrB;AAAA,QACF;AAEA,iBAAS,KAAK;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,UACA,MAAM;AAAA,QACR,CAAC;AAAA,MACH,SAAS,GAAG;AACV,aAAK,QAAQ,MAAM,EAAE,MAAM,KAAK,GAAG,oDAAoD;AACvF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,QACP,OAAO,KAAK,iBAAiB,SAAS;AAAA,QACtC,OAAO;AAAA,MACT;AAAA,MACA,cAAU,yBAAU,eAAe;AAAA,IACrC;AAAA,EACF;AAAA,EAEA,MAAM,mBAAmB,eAAsC;AAC7D,UAAM,cAAU,yBAAU,sBAAsB;AAChD,SAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,QACP,cAAc;AAAA,MAChB;AAAA,MACA,UAAU;AAAA,IACZ,CAAiC;AACjC,SAAK,eAAe;AAAA,EACtB;AAAA,EAEA,cAAc,EAAE,WAAW,GAA0C;AACnE,UAAM,UAAmD,CAAC;AAE1D,SAAK,iBAAiB,SAAS,aAAa;AAC5C,YAAQ,cAAc,gBAAgB,UAAU;AAIhD,SAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,MACT,cAAU,yBAAU,iBAAiB;AAAA,IACvC,CAAC;AAAA,EACH;AAAA,EAEA,UAAU,OAAyB;AACjC,eAAW,KAAK,KAAK,cAAc,KAAK,GAAG;AACzC,iBAAW,MAAM,KAAK,QAAQ,MAAM,EAAE,KAAK,MAAM,GAAG;AAClD,aAAK,UAAU;AAAA,UACb,MAAM;AAAA,UACN,OAAO,OAAO,KAAK,GAAG,KAAK,MAAM,EAAE,SAAS,QAAQ;AAAA,QACtD,CAA0C;AAE1C,aAAK,oBAAqB,GAAG,oBAAoB,GAAG,aAAc;AAAA,MACpE;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,cAA6B;AACjC,QAAI,KAAK,mBAAmB,KAAK;AAE/B,WAAK,UAAU;AAAA,QACb,MAAM;AAAA,MACR,CAA0C;AAC1C,WAAK,mBAAmB;AAAA,IAC1B;AAAA,EACF;AAAA,EAEA,MAAM,aAA4B;AAChC,SAAK,UAAU;AAAA,MACb,MAAM;AAAA,IACR,CAAyC;AACzC,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEA,MAAM,cAAc,cAA4D;AAC9E,UAAM,SAAS,KAAK,eAAe,EAAE,cAAc,eAAe,KAAK,CAAC;AACxE,SAAK,0BAA0B;AAC/B,WAAO,OAAO,QAAQ;AAAA,EACxB;AAAA,EAEA,MAAM,YAA2B;AAC/B,SAAK,UAAU;AAAA,MACb,MAAM;AAAA,IACR,CAAkC;AAAA,EACpC;AAAA,EAEA,MAAM,SAAS,UAAoE;AACjF,SAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN,eAAe;AAAA,MACf,SAAS,SAAS;AAAA,MAClB,cAAc,SAAS;AAAA,IACzB,CAA4C;AAAA,EAC9C;AAAA;AAAA;AAAA,EAIA,eACE,OACA,YAAoB,IACK;AACzB,UAAM,eAAwC,CAAC;AAC/C,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,UAAI,UAAU,QAAW;AACvB,qBAAa,GAAG,IAAI;AAAA,MACtB;AAAA,IACF;AAEA,QAAI,aAAa,SAAS,OAAO,aAAa,UAAU,UAAU;AAChE,YAAM,gBACJ,aAAa,MAAM,MAAM,GAAG,SAAS,KAAK,aAAa,MAAM,SAAS,YAAY,WAAM;AAC1F,aAAO,EAAE,GAAG,cAAc,OAAO,cAAc;AAAA,IACjD;AACA,QACE,aAAa,SACb,OAAO,aAAa,UAAU,YAC9B,MAAM,SAAS,wBACf;AACA,YAAM,iBACJ,aAAa,MAAM,MAAM,GAAG,SAAS,KAAK,aAAa,MAAM,SAAS,YAAY,WAAM;AAC1F,aAAO,EAAE,GAAG,cAAc,OAAO,eAAe;AAAA,IAClD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,eAAmC;AAC/C,UAAM,UAAkC;AAAA,MACtC,cAAc;AAAA,IAChB;AAEA,QAAI,KAAK,iBAAiB,SAAS,SAAS;AAI1C,UAAI,KAAK,iBAAiB,SAAS,YAAY;AAC7C,gBAAQ,gBAAgB,UAAU,KAAK,iBAAiB,SAAS,UAAU;AAAA,MAC7E,WAAW,KAAK,iBAAiB,SAAS,QAAQ;AAChD,gBAAQ,SAAS,IAAI,KAAK,iBAAiB,SAAS;AAAA,MACtD,OAAO;AACL,cAAM,IAAI,MAAM,6CAA6C;AAAA,MAC/D;AAAA,IACF,OAAO;AACL,cAAQ,gBAAgB,UAAU,KAAK,iBAAiB,SAAS,MAAM;AACvE,cAAQ,aAAa,IAAI;AAAA,IAC3B;AAEA,UAAM,MAAM,eAAe;AAAA,MACzB,SAAS,KAAK,iBAAiB,SAAS;AAAA,MACxC,OAAO,KAAK,iBAAiB,SAAS;AAAA,MACtC,SAAS,KAAK,iBAAiB,SAAS;AAAA,MACxC,YAAY,KAAK,iBAAiB,SAAS;AAAA,MAC3C,iBAAiB,KAAK,iBAAiB,SAAS;AAAA,IAClD,CAAC;AAED,SAAK,QAAQ,MAAM,wCAAwC,GAAG,EAAE;AAEhE,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,KAAK,IAAI,oBAAU,KAAK,EAAE,QAAQ,CAAC;AACzC,UAAI,UAAU;AAEd,YAAM,UAAU,WAAW,MAAM;AAC/B,WAAG,MAAM;AACT,eAAO,IAAI,MAAM,8BAA8B,CAAC;AAAA,MAClD,GAAG,KAAK,iBAAiB,SAAS,YAAY,SAAS;AAEvD,SAAG,KAAK,QAAQ,MAAM;AACpB,YAAI,CAAC,QAAS;AACd,kBAAU;AACV,qBAAa,OAAO;AACpB,gBAAQ,EAAE;AAAA,MACZ,CAAC;AAED,SAAG,KAAK,SAAS,MAAM;AACrB,YAAI,CAAC,QAAS;AACd,kBAAU;AACV,qBAAa,OAAO;AACpB,eAAO,IAAI,MAAM,uCAAuC,CAAC;AAAA,MAC3D,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,UAAU,QAAoC;AAClD,QAAI,eAAe;AACnB,QAAI,aAAa;AACjB,QAAI,SAA2B;AAC/B,UAAM,aAAa,KAAK,iBAAiB,SAAS,YAAY;AAE9D,UAAM,YAAY,YAAY;AAC5B,WAAK,QAAQ;AAAA,QACX;AAAA,UACE,oBAAoB,KAAK,iBAAiB,SAAS;AAAA,QACrD;AAAA,QACA;AAAA,MACF;AAEA,YAAM,SAAkC,CAAC;AAGzC,aAAO,KAAK,KAAK,yBAAyB,CAAC;AAG3C,UAAI,OAAO,KAAK,KAAK,MAAM,EAAE,SAAS,GAAG;AACvC,eAAO,KAAK,KAAK,uBAAuB,KAAK,MAAM,CAAC;AAAA,MACtD;AAGA,YAAM,UAAU,KAAK,QAAQ,KAAK;AAAA,QAChC,qBAAqB;AAAA,QACrB,qBAAqB;AAAA,QACrB,qBAAqB;AAAA,MACvB,CAAC;AAED,YAAM,aAAa,KAAK;AACxB,WAAK,gBAAgB,IAAI,kBAAI,kBAAkB;AAC/C,aAAO,KAAK,GAAG,KAAK,0BAA0B,OAAO,CAAC;AAEtD,UAAI;AACF,mBAAW,MAAM,QAAQ;AACvB,eAAK,KAAK,8BAA8B,EAAE;AAC1C,iBAAQ,KAAK,KAAK,UAAU,EAAE,CAAC;AAAA,QACjC;AAAA,MACF,SAAS,OAAO;AACd,aAAK,gBAAgB;AACrB,cAAM,IAAI,iCAAmB;AAAA,UAC3B,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AAEA,WAAK,QAAQ,MAAM,oCAAoC;AAEvD,WAAK,KAAK,uBAAuB,CAAC,CAAwC;AAAA,IAC5E;AAEA,mBAAe;AACf,WAAO,CAAC,KAAK,WAAW,CAAC,OAAO,SAAS;AACvC,WAAK,QAAQ,MAAM,sDAAsD;AACzE,eAAS,MAAM,KAAK,aAAa;AACjC,UAAI,OAAO,QAAS;AAEpB,UAAI;AACF,YAAI,cAAc;AAChB,gBAAM,UAAU;AAChB,cAAI,OAAO,QAAS;AACpB,uBAAa;AAAA,QACf;AAEA,cAAM,KAAK,MAAM,MAAM;AACvB,YAAI,OAAO,QAAS;AAAA,MACtB,SAAS,OAAO;AACd,YAAI,KAAC,0BAAW,KAAK,GAAG;AACtB,eAAK,UAAU,EAAE,OAAuB,aAAa,MAAM,CAAC;AAC5D,gBAAM;AAAA,QACR;AAEA,YAAI,eAAe,KAAK,CAAC,MAAM,WAAW;AACxC,eAAK,UAAU,EAAE,OAAuB,aAAa,MAAM,CAAC;AAC5D,gBAAM;AAAA,QACR;AAEA,YAAI,eAAe,YAAY;AAC7B,eAAK,UAAU,EAAE,OAAuB,aAAa,MAAM,CAAC;AAC5D,gBAAM,IAAI,iCAAmB;AAAA,YAC3B,SAAS,+CAA+C,UAAU;AAAA,YAClE,SAAS;AAAA,cACP,MAAM;AAAA,cACN,WAAW;AAAA,YACb;AAAA,UACF,CAAC;AAAA,QACH;AAEA,aAAK,UAAU,EAAE,OAAuB,aAAa,KAAK,CAAC;AAC3D,cAAM,gBACJ,eAAe,IACX,kCACA,KAAK,iBAAiB,SAAS,YAAY;AACjD,aAAK,QAAQ;AAAA,UACX;AAAA,YACE,SAAS;AAAA,YACT;AAAA,YACA;AAAA,UACF;AAAA,UACA,sDAAsD,gBAAgB,GAAI;AAAA,QAC5E;AAEA,kBAAM,qBAAM,aAAa;AACzB;AAAA,MACF;AAEA,qBAAe;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,MAAc,MAAM,QAAkC;AACpD,UAAM,gBAAgB,OAAO,WAAuC;AAClE,YAAM,cAAc,IAAI,qBAAa;AACrC,aAAO,iBAAiB,SAAS,MAAM,YAAY,QAAQ,CAAC;AAE5D,aAAO,CAAC,KAAK,WAAW,OAAO,eAAe,oBAAU,QAAQ,CAAC,OAAO,SAAS;AAC/E,YAAI;AACF,gBAAM,QAAQ,MAAM,QAAQ,KAAK,CAAC,KAAK,eAAe,IAAI,GAAG,YAAY,KAAK,CAAC;AAC/E,cAAI,OAAO,WAAW,YAAY,QAAQ,UAAU,QAAW;AAC7D;AAAA,UACF;AAEA,cAAI,MAAM,SAAS,6BAA6B;AAC9C,iBAAK,QAAQ,MAAM,eAAe,KAAK,UAAU,KAAK,eAAe,KAAK,CAAC,CAAC,EAAE;AAAA,UAChF;AAEA,eAAK,KAAK,8BAA8B,KAAK;AAC7C,iBAAO,KAAK,KAAK,UAAU,KAAK,CAAC;AAAA,QACnC,SAAS,OAAO;AACd;AAAA,QACF;AAAA,MACF;AAEA,aAAO,MAAM;AAAA,IACf;AAEA,UAAM,gBAAgB,IAAI,qBAAqB;AAE/C,WAAO,UAAU,CAAC,UAAU;AAC1B,oBAAc,QAAQ,IAAI,iCAAmB,EAAE,SAAS,MAAM,QAAQ,CAAC,CAAC;AAAA,IAC1E;AACA,WAAO,UAAU,MAAM;AACrB,oBAAc,QAAQ;AAAA,IACxB;AAEA,WAAO,YAAY,CAAC,YAA0B;AAC5C,YAAM,QAA+B,KAAK,MAAM,QAAQ,IAAc;AAEtE,WAAK,KAAK,gCAAgC,KAAK;AAC/C,WAAK,QAAQ,MAAM,eAAe,KAAK,UAAU,KAAK,eAAe,KAAK,CAAC,CAAC,EAAE;AAE9E,cAAQ,MAAM,MAAM;AAAA,QAClB,KAAK;AACH,eAAK,oCAAoC,KAAK;AAC9C;AAAA,QACF,KAAK;AACH,eAAK,oCAAoC,KAAK;AAC9C;AAAA,QACF,KAAK;AACH,eAAK,sBAAsB,KAAK;AAChC;AAAA,QACF,KAAK;AACH,eAAK,8BAA8B,KAAK;AACxC;AAAA,QACF,KAAK;AACH,eAAK,8BAA8B,KAAK;AACxC;AAAA,QACF,KAAK;AACH,eAAK,8BAA8B,KAAK;AACxC;AAAA,QACF,KAAK;AACH,eAAK,uDAAuD,KAAK;AACjE;AAAA,QACF,KAAK;AACH,eAAK,oDAAoD,KAAK;AAC9D;AAAA,QACF,KAAK;AACH,eAAK,+BAA+B,KAAK;AACzC;AAAA,QACF,KAAK;AACH,eAAK,8BAA8B,KAAK;AACxC;AAAA,QACF,KAAK;AACH,eAAK,mCAAmC,KAAK;AAC7C;AAAA,QACF,KAAK;AACH,eAAK,yBAAyB,KAAK;AACnC;AAAA,QACF,KAAK;AACH,eAAK,kCAAkC,KAAK;AAC5C;AAAA,QACF,KAAK;AACH,eAAK,wBAAwB,KAAK;AAClC;AAAA,QACF,KAAK;AACH,eAAK,6BAA6B,KAAK;AACvC;AAAA,QACF,KAAK;AACH,eAAK,mBAAmB,KAAK;AAC7B;AAAA,QACF,KAAK;AACH,eAAK,YAAY,KAAK;AACtB;AAAA,QACF;AACE,eAAK,QAAQ,MAAM,oBAAoB,MAAM,IAAI,EAAE;AACnD;AAAA,MACJ;AAAA,IACF;AAEA,UAAM,WAAW,mBAAK,KAAK,CAAC,EAAE,OAAO,MAAM,cAAc,MAAM,CAAC;AAEhE,UAAM,SAAS,mBAAK,KAAK,CAAC,EAAE,OAAO,MAAM;AACvC,YAAM,eAAe,IAAI,QAAc,CAAC,YAAY;AAClD,eAAO,iBAAiB,SAAS,MAAM;AACrC,kBAAQ;AAAA,QACV,CAAC;AAAA,MACH,CAAC;AAED,aAAO,QAAQ,KAAK,CAAC,cAAc,OAAO,YAAY,CAAC;AAAA,IACzD,CAAC;AAED,UAAM,oBAAoB,mBAAK,KAAK,OAAO,EAAE,OAAO,MAAM;AACxD,gBAAM,qBAAM,KAAK,iBAAiB,SAAS,oBAAoB,EAAE,OAAO,CAAC;AACzE,aAAO,IAAI,iCAAmB;AAAA,QAC5B,SAAS;AAAA,MACX,CAAC;AAAA,IACH,CAAC;AAED,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,KAAK,CAAC,OAAO,QAAQ,SAAS,QAAQ,kBAAkB,MAAM,CAAC;AAE5F,UAAI,kBAAkB,QAAQ,KAAK,mBAAmB;AACpD,cAAM,KAAK,kBAAkB,SAAS;AAAA,MACxC;AAEA,UAAI,kBAAkB,OAAO;AAC3B,cAAM;AAAA,MACR;AAAA,IACF,UAAE;AACA,gBAAM,6BAAc,CAAC,QAAQ,UAAU,iBAAiB,GAAG,GAAI;AAC/D,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ;AACZ,UAAM,MAAM;AACZ,SAAK,UAAU;AACf,UAAM,KAAK;AAAA,EACb;AAAA,EAEQ,oCACN,QACM;AACN,SAAK,KAAK,wBAAwB,CAAC,CAAgC;AAAA,EACrE;AAAA,EAEQ,oCACN,QACM;AACN,SAAK,KAAK,wBAAwB;AAAA,MAChC,0BAA0B,KAAK,iBAAiB,SAAS,4BAA4B;AAAA,IACvF,CAAgC;AAAA,EAClC;AAAA,EAEQ,sBAAsB,OAA6C;AAl+B7E;AAm+BI,QAAI,CAAC,MAAM,SAAS,IAAI;AACtB,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAEA,SAAK,oBAAoB;AAAA,MACvB,gBAAgB,qBAAO,oBAA2C;AAAA,MAClE,iBAAiB,qBAAO,oBAAsC;AAAA,MAC9D,UAAU,oBAAI,IAAI;AAAA,MAClB,UAAU,IAAI,qBAAO;AAAA,MACrB,mBAAmB,KAAK,IAAI;AAAA,IAC9B;AAIA,UAAM,eAAe;AAAA,MACnB,eAAe,KAAK,kBAAkB,eAAe,OAAO;AAAA,MAC5D,gBAAgB,KAAK,kBAAkB,gBAAgB,OAAO;AAAA,MAC9D,eAAe;AAAA,IACjB;AAEA,UAAM,iBAAgB,WAAM,SAAS,aAAf,mBAAyB;AAC/C,QAAI,eAAe;AACjB,YAAM,SAAS,KAAK,uBAAuB,aAAa;AACxD,UAAI,QAAQ;AACV,eAAO,KAAK,uBAAuB,aAAa;AAChD,qBAAa,gBAAgB;AAC7B,YAAI,CAAC,OAAO,QAAQ,MAAM;AACxB,iBAAO,QAAQ,QAAQ,YAAY;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AAEA,SAAK,KAAK,sBAAsB,YAAY;AAAA,EAC9C;AAAA,EAEQ,8BAA8B,OAAqD;AACzF,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,QAAI,CAAC,MAAM,KAAK,MAAM;AACpB,YAAM,IAAI,MAAM,sBAAsB;AAAA,IACxC;AAEA,QAAI,CAAC,MAAM,aAAa;AACtB,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAEA,UAAM,WAAW,MAAM,KAAK;AAC5B,UAAM,aAAa,MAAM;AAEzB,QAAI,aAAa,WAAW;AAE1B,WAAK,kBAAkB,UAAU;AACjC,WAAK,0BAA0B;AAC/B;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,8BAA8B,OAAqD;AACzF,QAAI,CAAC,MAAM,KAAK,IAAI;AAClB,YAAM,IAAI,MAAM,oBAAoB;AAAA,IACtC;AAEA,QAAI;AACF,WAAK,cAAc,OAAO,MAAM,kBAAkB,wBAAwB,MAAM,IAAI,CAAC;AAAA,IACvF,SAAS,OAAO;AACd,WAAK,QAAQ,MAAM,EAAE,OAAO,QAAQ,MAAM,KAAK,GAAG,GAAG,oCAAoC;AAAA,IAC3F;AAEA,UAAM,MAAM,KAAK,kBAAkB,MAAM,KAAK,EAAE;AAChD,QAAI,KAAK;AACP,UAAI,QAAQ;AACZ,aAAO,KAAK,kBAAkB,MAAM,KAAK,EAAE;AAAA,IAC7C;AAAA,EACF;AAAA,EAEQ,8BAA8B,OAAqD;AACzF,QAAI,CAAC,MAAM,SAAS;AAClB,YAAM,IAAI,MAAM,oBAAoB;AAAA,IACtC;AAEA,QAAI;AACF,WAAK,cAAc,OAAO,MAAM,OAAO;AAAA,IACzC,SAAS,OAAO;AACd,WAAK,QAAQ,MAAM,EAAE,OAAO,QAAQ,MAAM,QAAQ,GAAG,oCAAoC;AAAA,IAC3F;AAEA,UAAM,MAAM,KAAK,kBAAkB,MAAM,OAAO;AAChD,QAAI,KAAK;AACP,UAAI,QAAQ;AACZ,aAAO,KAAK,kBAAkB,MAAM,OAAO;AAAA,IAC7C;AAAA,EACF;AAAA,EAEQ,uDACN,OACM;AACN,UAAM,aAAa,KAAK,cAAc,IAAI,MAAM,OAAO;AACvD,QAAI,CAAC,YAAY;AACf;AAAA,IACF;AAEA,UAAM,OAAO,WAAW;AACxB,QAAI,gBAAgB,kBAAI,aAAa;AACnC,WAAK,QAAQ,KAAK,MAAM,UAAU;AAAA,IACpC,OAAO;AACL,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAEA,SAAK,KAAK,uCAAuC;AAAA,MAC/C,QAAQ,MAAM;AAAA,MACd,YAAY,MAAM;AAAA,MAClB,SAAS;AAAA,IACX,CAAoC;AAAA,EACtC;AAAA,EAEQ,oDACN,OACM;AACN,SAAK,QAAQ;AAAA,MACX,EAAE,OAAO,MAAM,MAAM;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,+BAA+B,OAAsD;AAC3F,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,UAAM,SAAS,MAAM;AACrB,UAAM,WAAW,MAAM,KAAK;AAC5B,UAAM,aAAa,MAAM;AAEzB,QAAI,aAAa,SAAS;AACxB,WAAK,kBAAkB,UAAU;AACjC,UAAI,KAAK,0BAA0B,GAAG;AACpC,aAAK,QAAQ;AAAA,UACX,EAAE,SAAS,KAAK,wBAAwB;AAAA,UACxC;AAAA,QACF;AACA,aAAK,0BAA0B;AAAA,MACjC;AAEA,YAAM,iBAAoC;AAAA,QACxC,WAAW;AAAA,QACX,aAAa,qBAAO,oBAA4B;AAAA,QAChD,cAAc,qBAAO,oBAAgC;AAAA,QACrD,iBAAiB;AAAA,MACnB;AAEA,WAAK,kBAAkB,eAAe,MAAM;AAAA,QAC1C,WAAW;AAAA,QACX,YAAY,eAAe,YAAY,OAAO;AAAA,QAC9C,aAAa,eAAe,aAAa,OAAO;AAAA,MAClD,CAAC;AAED,WAAK,kBAAkB,SAAS,IAAI,QAAQ,cAAc;AAC1D,WAAK,kBAAkB,uBAAuB,KAAK,IAAI;AACvD;AAAA,IACF,OAAO;AACL,WAAK,UAAU;AACf,UAAI,KAAK,4BAA4B,GAAG;AACtC,aAAK,QAAQ,KAAK,EAAE,WAAW,GAAG,sDAAsD;AAAA,MAC1F;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,8BAA8B,OAAqD;AACzF,QAAI,MAAM,KAAK,SAAS,QAAQ;AAC9B;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAAA,EAGF;AAAA,EAEQ,mCACN,OACM;AACN,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,UAAM,SAAS,MAAM;AACrB,UAAM,QAAQ,MAAM;AAIpB,UAAM,iBAAiB,KAAK,kBAAkB,SAAS,IAAI,MAAM;AACjE,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAI,MAAM,2BAA2B;AAAA,IAC7C,OAAO;AACL,qBAAe,YAAY,MAAM,KAAK;AACtC,qBAAe,mBAAmB;AAAA,IACpC;AAAA,EACF;AAAA,EAEQ,yBAAyB,OAAgD;AAC/E,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,UAAM,iBAAiB,KAAK,kBAAkB,SAAS,IAAI,MAAM,OAAO;AACxE,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAI,MAAM,2BAA2B;AAAA,IAC7C;AAEA,UAAM,eAAe,KAAK,MAAM,KAAK;AACrC,UAAM,MAAM,aAAa;AACzB,UAAM,QAAQ,IAAI,WAAW,GAAG;AAChC,aAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,YAAM,CAAC,IAAI,aAAa,WAAW,CAAC;AAAA,IACtC;AAEA,mBAAe,aAAa;AAAA,MAC1B,IAAI;AAAA,QACF,IAAI,WAAW,MAAM,MAAM;AAAA,QAC3B,UAAU;AAAA,QACV,UAAU;AAAA,QACV,MAAM,SAAS;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,kCACN,QACM;AACN,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAAA,EACF;AAAA,EAEQ,wBAAwB,QAAgD;AAC9E,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAAA,EACF;AAAA,EAEQ,6BAA6B,OAAoD;AACvF,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,UAAM,SAAS,MAAM,KAAK;AAC1B,UAAM,WAAW,MAAM,KAAK;AAE5B,QAAI,aAAa,iBAAiB;AAChC,YAAM,OAAO,MAAM;AACnB,UAAI,CAAC,KAAK,WAAW,CAAC,KAAK,QAAQ,CAAC,KAAK,WAAW;AAClD,cAAM,IAAI,MAAM,6BAA6B;AAAA,MAC/C;AACA,WAAK,kBAAkB,gBAAgB,MAAM;AAAA,QAC3C,QAAQ,KAAK;AAAA,QACb,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,MACb,CAAqB;AAAA,IACvB,WAAW,aAAa,WAAW;AACjC,YAAM,iBAAiB,KAAK,kBAAkB,SAAS,IAAI,MAAM;AACjE,UAAI,CAAC,gBAAgB;AACnB;AAAA,MACF;AAEA,qBAAe,YAAY,MAAM;AACjC,qBAAe,aAAa,MAAM;AAAA,IACpC;AAAA,EACF;AAAA,EAEQ,mBAAmB,QAA2C;AAnvCxE;AAovCI,QAAI,CAAC,KAAK,mBAAmB;AAG3B;AAAA,IACF;AAEA,UAAM,mBAAmB,KAAK,kBAAkB;AAChD,UAAM,sBAAsB,KAAK,kBAAkB;AAEnD,SAAK,QAAQ;AAAA,MACX;AAAA,QACE,cAAc,KAAK,kBAAkB,SAAS;AAAA,MAChD;AAAA,MACA;AAAA,IACF;AAEA,eAAW,cAAc,KAAK,kBAAkB,SAAS,OAAO,GAAG;AACjE,iBAAW,YAAY,MAAM;AAC7B,iBAAW,aAAa,MAAM;AAAA,IAChC;AAEA,SAAK,kBAAkB,gBAAgB,MAAM;AAC7C,SAAK,kBAAkB,eAAe,MAAM;AAE5C,eAAW,UAAU,KAAK,kBAAkB,SAAS,KAAK,GAAG;AAC3D,YAAM,aAAa,KAAK,cAAc,IAAI,MAAM;AAChD,UAAI,cAAc,WAAW,gBAAgB,kBAAI,aAAa;AAC5D,mBAAW,KAAK,QAAQ,KAAK,KAAK,kBAAkB,SAAS,IAAI,MAAM,EAAG,eAAe;AAAA,MAC3F;AAAA,IACF;AAEA,SAAK,kBAAkB,SAAS,QAAQ;AACxC,SAAK,oBAAoB;AAGzB,UAAM,QAAQ,OAAO,SAAS;AAC9B,UAAM,OAAO,sBAAsB,sBAAsB,mBAAmB;AAC5E,UAAM,YAAY,KAAK,IAAI,IAAI,oBAAoB;AAEnD,UAAM,kBAAgD;AAAA,MACpD,MAAM;AAAA,MACN,WAAW,mBAAmB;AAAA;AAAA,MAC9B,WAAW,OAAO,SAAS,MAAM;AAAA,MACjC;AAAA,MACA;AAAA,MACA,WAAW,OAAO,SAAS,WAAW;AAAA,MACtC,OAAO;AAAA,MACP,cAAa,+BAAO,iBAAgB;AAAA,MACpC,eAAc,+BAAO,kBAAiB;AAAA,MACtC,cAAa,+BAAO,iBAAgB;AAAA,MACpC,iBAAiB,WAAW,MAAK,+BAAO,kBAAiB,KAAK,WAAW;AAAA,MACzE,mBAAmB;AAAA,QACjB,eAAa,oCAAO,wBAAP,mBAA4B,iBAAgB;AAAA,QACzD,cAAY,oCAAO,wBAAP,mBAA4B,gBAAe;AAAA,QACvD,aAAa;AAAA;AAAA,QACb,gBAAc,oCAAO,wBAAP,mBAA4B,kBAAiB;AAAA,QAC3D,uBAAqB,oCAAO,wBAAP,mBAA4B,yBAC7C;AAAA,UACE,eAAa,0CAAO,wBAAP,mBAA4B,0BAA5B,mBAAmD,iBAAgB;AAAA,UAChF,cAAY,0CAAO,wBAAP,mBAA4B,0BAA5B,mBAAmD,gBAAe;AAAA,UAC9E,eAAa,0CAAO,wBAAP,mBAA4B,0BAA5B,mBAAmD,iBAAgB;AAAA,QAClF,IACA;AAAA,MACN;AAAA,MACA,oBAAoB;AAAA,QAClB,cAAY,oCAAO,yBAAP,mBAA6B,gBAAe;AAAA,QACxD,eAAa,oCAAO,yBAAP,mBAA6B,iBAAgB;AAAA,QAC1D,aAAa;AAAA,MACf;AAAA,IACF;AAEA,SAAK,KAAK,qBAAqB,eAAe;AAAA,EAEhD;AAAA,EAEQ,YAAY,OAAmC;AACrD,QAAI,MAAM,MAAM,QAAQ,WAAW,qBAAqB,GAAG;AACzD;AAAA,IACF;AAEA,SAAK,QAAQ,MAAM,EAAE,OAAO,MAAM,MAAM,GAAG,uCAAuC;AAClF,SAAK,UAAU;AAAA,MACb,OAAO,IAAI,uBAAS,MAAM,MAAM,SAAS;AAAA,QACvC,MAAM,MAAM;AAAA,QACZ,WAAW;AAAA,MACb,CAAC;AAAA,MACD,aAAa;AAAA,IACf,CAAC;AAAA,EAGH;AAAA,EAEQ,UAAU,EAAE,OAAO,YAAY,GAAiD;AAEtF,SAAK,KAAK,SAAS;AAAA,MACjB,WAAW,KAAK,IAAI;AAAA;AAAA,MAEpB,OAAO;AAAA,MACP;AAAA,MACA;AAAA,IACF,CAA2B;AAAA,EAC7B;AAAA,EAEA,CAAS,cAAc,OAA0C;AAC/D,UAAM;AAAA,EACR;AAAA,EAEQ,eAAe;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAIyB;AACvB,UAAM,SAAS,aAAa,IAAI,qBAAqB,EAAE,aAAa,CAAC;AACrE,QAAI,aAAa,cAAc;AAC7B,aAAO,eAAe;AAAA,IACxB;AAEA,UAAM,cAAU,yBAAU,kBAAkB;AAC5C,QAAI,eAAe;AACjB,WAAK,uBAAuB,OAAO,IAAI;AAAA,IACzC;AAEA,UAAM,WAAsD,CAAC;AAC7D,QAAI,aAAc,UAAS,eAAe;AAC1C,QAAI,cAAe,UAAS,WAAW,EAAE,iBAAiB,QAAQ;AAElE,SAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN,UAAU;AAAA,MACV,UAAU,OAAO,KAAK,QAAQ,EAAE,SAAS,IAAI,WAAW;AAAA,IAC1D,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAEQ,kBAAkB,YAA0B;AAClD,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,UAAM,gBAAgB;AAAA,MACpB,eAAe,KAAK,kBAAkB,eAAe,OAAO;AAAA,MAC5D,gBAAgB,KAAK,kBAAkB,gBAAgB,OAAO;AAAA,MAC9D,eAAe;AAAA,IACjB;AAEA,UAAM,SAAS,KAAK,uBAAuB,UAAU;AACrD,QAAI,QAAQ;AACV,aAAO,KAAK,uBAAuB,UAAU;AAC7C,oBAAc,gBAAgB;AAC9B,UAAI,OAAO,QAAQ,MAAM;AACvB,aAAK,QAAQ,KAAK,EAAE,WAAW,GAAG,iCAAiC;AAAA,MACrE,OAAO;AACL,eAAO,QAAQ,QAAQ,aAAa;AAAA,MACtC;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,wBAAwB,MAA4C;AAC3E,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO;AAAA,QACL,IAAI,KAAK;AAAA,QACT,MAAM;AAAA,QACN,SAAS,KAAK;AAAA,QACd,MAAM,KAAK;AAAA,QACX,WAAW,KAAK;AAAA,MAClB;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,IAAI,KAAK;AAAA,QACT,MAAM;AAAA,QACN,SAAS,KAAK;AAAA,QACd,QAAQ,KAAK;AAAA,MACf;AAAA,IACF,KAAK;AACH,YAAM,OAAO,KAAK,SAAS,cAAc,WAAW,KAAK;AACzD,YAAM,cAAmC,CAAC;AAC1C,iBAAW,KAAK,KAAK,SAAS;AAC5B,YAAI,OAAO,MAAM,UAAU;AACzB,sBAAY,KAAK;AAAA,YACf,MAAM,SAAS,cAAc,SAAS;AAAA,YACtC,MAAM;AAAA,UACR,CAA+B;AAAA,QACjC,WAAW,EAAE,SAAS,iBAAiB;AAErC;AAAA,QACF,WAAW,EAAE,SAAS,iBAAiB;AACrC,cAAI,SAAS,QAAQ;AACnB,kBAAM,eAAe,OAAO,SAAK,oCAAmB,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,QAAQ;AACpF,wBAAY,KAAK;AAAA,cACf,MAAM;AAAA,cACN,OAAO;AAAA,YACT,CAAgC;AAAA,UAClC;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,QACL,IAAI,KAAK;AAAA,QACT,MAAM;AAAA,QACN;AAAA,QACA,SAAS;AAAA,MACX;AAAA,EACJ;AACF;AAEA,SAAS,wBAAwB,MAA4C;AAC3E,MAAI,CAAC,KAAK,IAAI;AACZ,UAAM,IAAI,MAAM,oBAAoB;AAAA,EACtC;AAEA,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO,kBAAI,aAAa,OAAO;AAAA,QAC7B,IAAI,KAAK;AAAA,QACT,QAAQ,KAAK;AAAA,QACb,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,MACb,CAAC;AAAA,IACH,KAAK;AACH,aAAO,kBAAI,mBAAmB,OAAO;AAAA,QACnC,IAAI,KAAK;AAAA,QACT,QAAQ,KAAK;AAAA,QACb,QAAQ,KAAK;AAAA,QACb,SAAS;AAAA,MACX,CAAC;AAAA,IACH,KAAK;AACH,YAAM,UAA6B,CAAC;AAEpC,YAAM,WAAW,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,UAAU,CAAC,KAAK,OAAO;AAC3E,iBAAW,KAAK,UAAU;AACxB,YAAI,EAAE,SAAS,UAAU,EAAE,SAAS,cAAc;AAChD,kBAAQ,KAAK,EAAE,IAAI;AAAA,QACrB;AAAA,MACF;AACA,aAAO,kBAAI,YAAY,OAAO;AAAA,QAC5B,IAAI,KAAK;AAAA,QACT,MAAM,KAAK;AAAA,QACX;AAAA,MACF,CAAC;AAAA,EACL;AACF;AAEA,SAAS,oBAAoB,kBAA0B,GAAoB;AACzE,QAAM,YAAY,OAAO,MAAM,kBAAkB,WAAW;AAC5D,SAAO,kBAAI,YAAY,OAAO;AAAA,IAC5B,QAAI,yBAAU,oBAAoB;AAAA,IAClC,MAAM;AAAA,IACN,SAAS;AAAA,MACP;AAAA,QACE,MAAM;AAAA,QACN,OAAO;AAAA,UACL,IAAI;AAAA,YACF,IAAI,WAAW,UAAU,MAAM;AAAA,YAC/B;AAAA,YACA;AAAA,YACA,UAAU,SAAS;AAAA,UACrB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,SAAS,gBAAgB,YAAmD;AAC1E,MAAI,OAAO,eAAe,UAAU;AAClC,WAAO;AAAA,EACT;AAEA,OAAI,yCAAY,UAAS,YAAY;AACnC,WAAO,WAAW,SAAS;AAAA,EAC7B;AAEA,SAAO;AACT;","names":[]}
@@ -150,7 +150,7 @@ export declare class RealtimeSession extends llm.RealtimeSession {
150
150
  private emitError;
151
151
  private resampleAudio;
152
152
  private createResponse;
153
- private emitGenerationEvent;
153
+ private resolveGeneration;
154
154
  }
155
155
  export {};
156
156
  //# sourceMappingURL=realtime_model.d.ts.map
@@ -150,7 +150,7 @@ export declare class RealtimeSession extends llm.RealtimeSession {
150
150
  private emitError;
151
151
  private resampleAudio;
152
152
  private createResponse;
153
- private emitGenerationEvent;
153
+ private resolveGeneration;
154
154
  }
155
155
  export {};
156
156
  //# sourceMappingURL=realtime_model.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"realtime_model.d.ts","sourceRoot":"","sources":["../../src/realtime/realtime_model.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"realtime_model.d.ts","sourceRoot":"","sources":["../../src/realtime/realtime_model.ts"],"names":[],"mappings":"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"}
@@ -733,6 +733,7 @@ class RealtimeSession extends llm.RealtimeSession {
733
733
  });
734
734
  }
735
735
  handleResponseCreated(event) {
736
+ var _a;
736
737
  if (!event.response.id) {
737
738
  throw new Error("response.id is missing");
738
739
  }
@@ -743,17 +744,23 @@ class RealtimeSession extends llm.RealtimeSession {
743
744
  _doneFut: new Future(),
744
745
  _createdTimestamp: Date.now()
745
746
  };
746
- if (!event.response.metadata || !event.response.metadata.client_event_id) return;
747
- const handle = this.responseCreatedFutures[event.response.metadata.client_event_id];
748
- if (handle) {
749
- delete this.responseCreatedFutures[event.response.metadata.client_event_id];
750
- this.responseCreatedFutures[event.response.id] = handle;
751
- }
752
- this.emit("generation_created", {
747
+ const generationEv = {
753
748
  messageStream: this.currentGeneration.messageChannel.stream(),
754
749
  functionStream: this.currentGeneration.functionChannel.stream(),
755
750
  userInitiated: false
756
- });
751
+ };
752
+ const clientEventId = (_a = event.response.metadata) == null ? void 0 : _a.client_event_id;
753
+ if (clientEventId) {
754
+ const handle = this.responseCreatedFutures[clientEventId];
755
+ if (handle) {
756
+ delete this.responseCreatedFutures[clientEventId];
757
+ generationEv.userInitiated = true;
758
+ if (!handle.doneFut.done) {
759
+ handle.doneFut.resolve(generationEv);
760
+ }
761
+ }
762
+ }
763
+ this.emit("generation_created", generationEv);
757
764
  }
758
765
  handleResponseOutputItemAdded(event) {
759
766
  if (!this.currentGeneration) {
@@ -768,7 +775,7 @@ class RealtimeSession extends llm.RealtimeSession {
768
775
  const itemType = event.item.type;
769
776
  const responseId = event.response_id;
770
777
  if (itemType !== "message") {
771
- this.emitGenerationEvent(responseId);
778
+ this.resolveGeneration(responseId);
772
779
  this.textModeRecoveryRetries = 0;
773
780
  return;
774
781
  }
@@ -834,7 +841,7 @@ class RealtimeSession extends llm.RealtimeSession {
834
841
  const itemType = event.part.type;
835
842
  const responseId = event.response_id;
836
843
  if (itemType === "audio") {
837
- this.emitGenerationEvent(responseId);
844
+ this.resolveGeneration(responseId);
838
845
  if (this.textModeRecoveryRetries > 0) {
839
846
  this.#logger.info(
840
847
  { retries: this.textModeRecoveryRetries },
@@ -1054,7 +1061,7 @@ class RealtimeSession extends llm.RealtimeSession {
1054
1061
  });
1055
1062
  return handle;
1056
1063
  }
1057
- emitGenerationEvent(responseId) {
1064
+ resolveGeneration(responseId) {
1058
1065
  if (!this.currentGeneration) {
1059
1066
  throw new Error("currentGeneration is not set");
1060
1067
  }
@@ -1073,8 +1080,6 @@ class RealtimeSession extends llm.RealtimeSession {
1073
1080
  handle.doneFut.resolve(generation_ev);
1074
1081
  }
1075
1082
  }
1076
- this.#logger.debug({ responseId }, "Emitting generation_created event");
1077
- this.emit("generation_created", generation_ev);
1078
1083
  }
1079
1084
  }
1080
1085
  function livekitItemToOpenAIItem(item) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/realtime/realtime_model.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { metrics } from '@livekit/agents';\nimport {\n type APIConnectOptions,\n APIConnectionError,\n APIError,\n AudioByteStream,\n DEFAULT_API_CONNECT_OPTIONS,\n Future,\n Queue,\n Task,\n cancelAndWait,\n delay,\n isAPIError,\n llm,\n log,\n shortuuid,\n stream,\n} from '@livekit/agents';\nimport { Mutex } from '@livekit/mutex';\nimport type { AudioResampler } from '@livekit/rtc-node';\nimport { AudioFrame, combineAudioFrames } from '@livekit/rtc-node';\nimport type { GenerationCreatedEvent } from 'agents/dist/llm/realtime.js';\nimport { type MessageEvent, WebSocket } from 'ws';\nimport * as api_proto from './api_proto.js';\n\nconst SAMPLE_RATE = 24000;\nconst NUM_CHANNELS = 1;\nconst BASE_URL = 'https://api.openai.com/v1';\n\nconst MOCK_AUDIO_ID_PREFIX = 'lk_mock_audio_item_';\n\ninterface RealtimeOptions {\n model: api_proto.Model;\n voice: api_proto.Voice;\n temperature: number;\n toolChoice?: llm.ToolChoice;\n inputAudioTranscription?: api_proto.InputAudioTranscription | null;\n // TODO(shubhra): add inputAudioNoiseReduction\n turnDetection?: api_proto.TurnDetectionType | null;\n maxResponseOutputTokens?: number | 'inf';\n speed?: number;\n // TODO(shubhra): add openai tracing options\n apiKey?: string;\n baseURL: string;\n isAzure: boolean;\n azureDeployment?: string;\n entraToken?: string;\n apiVersion?: string;\n maxSessionDuration: number;\n // reset the connection after this many seconds if provided\n connOptions: APIConnectOptions;\n}\n\ninterface MessageGeneration {\n messageId: string;\n textChannel: stream.StreamChannel<string>;\n audioChannel: stream.StreamChannel<AudioFrame>;\n audioTranscript: string;\n}\n\ninterface ResponseGeneration {\n messageChannel: stream.StreamChannel<llm.MessageGeneration>;\n functionChannel: stream.StreamChannel<llm.FunctionCall>;\n messages: Map<string, MessageGeneration>;\n\n /** @internal */\n _doneFut: Future;\n /** @internal */\n _createdTimestamp: number;\n /** @internal */\n _firstTokenTimestamp?: number;\n}\n\nclass CreateResponseHandle {\n instructions?: string;\n doneFut: Future<llm.GenerationCreatedEvent>;\n // TODO(shubhra): add timeout\n constructor({ instructions }: { instructions?: string }) {\n this.instructions = instructions;\n this.doneFut = new Future();\n }\n}\n\n// default values got from a \"default\" session from their API\nconst DEFAULT_FIRST_RETRY_INTERVAL_MS = 100;\nconst DEFAULT_TEMPERATURE = 0.8;\nconst DEFAULT_TURN_DETECTION: api_proto.TurnDetectionType = {\n type: 'server_vad',\n threshold: 0.5,\n prefix_padding_ms: 300,\n silence_duration_ms: 200,\n create_response: true,\n interrupt_response: true,\n};\nconst DEFAULT_INPUT_AUDIO_TRANSCRIPTION: api_proto.InputAudioTranscription = {\n model: 'gpt-4o-mini-transcribe',\n};\nconst DEFAULT_TOOL_CHOICE: llm.ToolChoice = 'auto';\nconst DEFAULT_MAX_RESPONSE_OUTPUT_TOKENS: number | 'inf' = 'inf';\n\nconst AZURE_DEFAULT_INPUT_AUDIO_TRANSCRIPTION: api_proto.InputAudioTranscription = {\n model: 'whisper-1',\n};\n\nconst AZURE_DEFAULT_TURN_DETECTION: api_proto.TurnDetectionType = {\n type: 'server_vad',\n threshold: 0.5,\n prefix_padding_ms: 300,\n silence_duration_ms: 200,\n create_response: true,\n};\n\nconst DEFAULT_MAX_SESSION_DURATION = 20 * 60 * 1000; // 20 minutes\n\nconst DEFAULT_REALTIME_MODEL_OPTIONS = {\n model: 'gpt-realtime',\n voice: 'marin',\n temperature: DEFAULT_TEMPERATURE,\n inputAudioTranscription: DEFAULT_INPUT_AUDIO_TRANSCRIPTION,\n turnDetection: DEFAULT_TURN_DETECTION,\n toolChoice: DEFAULT_TOOL_CHOICE,\n maxResponseOutputTokens: DEFAULT_MAX_RESPONSE_OUTPUT_TOKENS,\n maxSessionDuration: DEFAULT_MAX_SESSION_DURATION,\n connOptions: DEFAULT_API_CONNECT_OPTIONS,\n};\nexport class RealtimeModel extends llm.RealtimeModel {\n sampleRate = api_proto.SAMPLE_RATE;\n numChannels = api_proto.NUM_CHANNELS;\n inFrameSize = api_proto.IN_FRAME_SIZE;\n outFrameSize = api_proto.OUT_FRAME_SIZE;\n\n /* @internal */\n _options: RealtimeOptions;\n\n constructor(\n options: {\n model?: string;\n voice?: string;\n temperature?: number;\n toolChoice?: llm.ToolChoice;\n baseURL?: string;\n inputAudioTranscription?: api_proto.InputAudioTranscription | null;\n // TODO(shubhra): add inputAudioNoiseReduction\n turnDetection?: api_proto.TurnDetectionType | null;\n speed?: number;\n // TODO(shubhra): add openai tracing options\n azureDeployment?: string;\n apiKey?: string;\n entraToken?: string;\n apiVersion?: string;\n maxSessionDuration?: number;\n connOptions?: APIConnectOptions;\n } = {},\n ) {\n super({\n messageTruncation: true,\n turnDetection: options.turnDetection !== null,\n userTranscription: options.inputAudioTranscription !== null,\n autoToolReplyGeneration: false,\n });\n\n const isAzure = !!(options.apiVersion || options.entraToken || options.azureDeployment);\n\n if (options.apiKey === '' && !isAzure) {\n throw new Error(\n 'OpenAI API key is required, either using the argument or by setting the OPENAI_API_KEY environment variable',\n );\n }\n\n const apiKey = options.apiKey || process.env.OPENAI_API_KEY;\n\n if (!apiKey && !isAzure) {\n throw new Error(\n 'OpenAI API key is required, either using the argument or by setting the OPENAI_API_KEY environment variable',\n );\n }\n\n if (!options.baseURL && isAzure) {\n const azureEndpoint = process.env.AZURE_OPENAI_ENDPOINT;\n if (!azureEndpoint) {\n throw new Error(\n 'Missing Azure endpoint. Please pass base_url or set AZURE_OPENAI_ENDPOINT environment variable.',\n );\n }\n options.baseURL = `${azureEndpoint.replace(/\\/$/, '')}/openai`;\n }\n\n this._options = {\n ...DEFAULT_REALTIME_MODEL_OPTIONS,\n ...options,\n baseURL: options.baseURL || BASE_URL,\n apiKey,\n isAzure,\n model: options.model || DEFAULT_REALTIME_MODEL_OPTIONS.model,\n };\n }\n\n /**\n * Create a RealtimeModel instance configured for Azure OpenAI Service.\n *\n * @param azureDeployment - The name of your Azure OpenAI deployment.\n * @param azureEndpoint - The endpoint URL for your Azure OpenAI resource. If undefined, will attempt to read from the environment variable AZURE_OPENAI_ENDPOINT.\n * @param apiVersion - API version to use with Azure OpenAI Service. If undefined, will attempt to read from the environment variable OPENAI_API_VERSION.\n * @param apiKey - Azure OpenAI API key. If undefined, will attempt to read from the environment variable AZURE_OPENAI_API_KEY.\n * @param entraToken - Azure Entra authentication token. Required if not using API key authentication.\n * @param baseURL - Base URL for the API endpoint. If undefined, constructed from the azure_endpoint.\n * @param voice - Voice setting for audio outputs. Defaults to \"alloy\".\n * @param inputAudioTranscription - Options for transcribing input audio. Defaults to @see DEFAULT_INPUT_AUDIO_TRANSCRIPTION.\n * @param turnDetection - Options for server-based voice activity detection (VAD). Defaults to @see DEFAULT_SERVER_VAD_OPTIONS.\n * @param temperature - Sampling temperature for response generation. Defaults to @see DEFAULT_TEMPERATURE.\n * @param speed - Speed of the audio output. Defaults to 1.0.\n * @param maxResponseOutputTokens - Maximum number of tokens in the response. Defaults to @see DEFAULT_MAX_RESPONSE_OUTPUT_TOKENS.\n * @param maxSessionDuration - Maximum duration of the session in milliseconds. Defaults to @see DEFAULT_MAX_SESSION_DURATION.\n *\n * @returns A RealtimeModel instance configured for Azure OpenAI Service.\n *\n * @throws Error if required Azure parameters are missing or invalid.\n */\n static withAzure({\n azureDeployment,\n azureEndpoint,\n apiVersion,\n apiKey,\n entraToken,\n baseURL,\n voice = 'alloy',\n inputAudioTranscription = AZURE_DEFAULT_INPUT_AUDIO_TRANSCRIPTION,\n turnDetection = AZURE_DEFAULT_TURN_DETECTION,\n temperature = 0.8,\n speed,\n }: {\n azureDeployment: string;\n azureEndpoint?: string;\n apiVersion?: string;\n apiKey?: string;\n entraToken?: string;\n baseURL?: string;\n voice?: string;\n inputAudioTranscription?: api_proto.InputAudioTranscription;\n // TODO(shubhra): add inputAudioNoiseReduction\n turnDetection?: api_proto.TurnDetectionType;\n temperature?: number;\n speed?: number;\n }) {\n apiKey = apiKey || process.env.AZURE_OPENAI_API_KEY;\n if (!apiKey && !entraToken) {\n throw new Error(\n 'Missing credentials. Please pass one of `apiKey`, `entraToken`, or the `AZURE_OPENAI_API_KEY` environment variable.',\n );\n }\n\n apiVersion = apiVersion || process.env.OPENAI_API_VERSION;\n if (!apiVersion) {\n throw new Error(\n 'Must provide either the `apiVersion` argument or the `OPENAI_API_VERSION` environment variable',\n );\n }\n\n if (!baseURL) {\n azureEndpoint = azureEndpoint || process.env.AZURE_OPENAI_ENDPOINT;\n if (!azureEndpoint) {\n throw new Error(\n 'Missing Azure endpoint. Please pass the `azure_endpoint` parameter or set the `AZURE_OPENAI_ENDPOINT` environment variable.',\n );\n }\n baseURL = `${azureEndpoint.replace(/\\/$/, '')}/openai`;\n }\n\n return new RealtimeModel({\n voice,\n inputAudioTranscription,\n turnDetection,\n temperature,\n speed,\n apiKey,\n azureDeployment,\n apiVersion,\n entraToken,\n baseURL,\n });\n }\n\n session() {\n return new RealtimeSession(this);\n }\n\n async close() {\n return;\n }\n}\n\nfunction processBaseURL({\n baseURL,\n model,\n isAzure = false,\n azureDeployment,\n apiVersion,\n}: {\n baseURL: string;\n model: string;\n isAzure: boolean;\n azureDeployment?: string;\n apiVersion?: string;\n}): string {\n const url = new URL([baseURL, 'realtime'].join('/'));\n\n if (url.protocol === 'https:') {\n url.protocol = 'wss:';\n }\n\n // ensure \"/realtime\" is added if the path is empty OR \"/v1\"\n if (!url.pathname || ['', '/v1', '/openai'].includes(url.pathname.replace(/\\/$/, ''))) {\n url.pathname = url.pathname.replace(/\\/$/, '') + '/realtime';\n } else {\n url.pathname = url.pathname.replace(/\\/$/, '');\n }\n\n const queryParams: Record<string, string> = {};\n if (isAzure) {\n if (apiVersion) {\n queryParams['api-version'] = apiVersion;\n }\n if (azureDeployment) {\n queryParams['deployment'] = azureDeployment;\n }\n } else {\n queryParams['model'] = model;\n }\n\n for (const [key, value] of Object.entries(queryParams)) {\n url.searchParams.set(key, value);\n }\n\n return url.toString();\n}\n\n/**\n * A session for the OpenAI Realtime API.\n *\n * This class is used to interact with the OpenAI Realtime API.\n * It is responsible for sending events to the OpenAI Realtime API and receiving events from it.\n *\n * It exposes two more events:\n * - openai_server_event_received: expose the raw server events from the OpenAI Realtime API\n * - openai_client_event_queued: expose the raw client events sent to the OpenAI Realtime API\n */\nexport class RealtimeSession extends llm.RealtimeSession {\n private _tools: llm.ToolContext = {};\n private remoteChatCtx: llm.RemoteChatContext = new llm.RemoteChatContext();\n private messageChannel = new Queue<api_proto.ClientEvent>();\n private inputResampler?: AudioResampler;\n private instructions?: string;\n private oaiRealtimeModel: RealtimeModel;\n private currentGeneration?: ResponseGeneration;\n private responseCreatedFutures: { [id: string]: CreateResponseHandle } = {};\n\n private textModeRecoveryRetries: number = 0;\n\n private itemCreateFutures: { [id: string]: Future } = {};\n private itemDeleteFutures: { [id: string]: Future } = {};\n\n private updateChatCtxLock = new Mutex();\n private updateFuncCtxLock = new Mutex();\n\n // 100ms chunks\n private bstream = new AudioByteStream(SAMPLE_RATE, NUM_CHANNELS, SAMPLE_RATE / 10);\n\n private pushedDurationMs: number = 0;\n\n #logger = log();\n #task: Task<void>;\n #closed = false;\n\n constructor(realtimeModel: RealtimeModel) {\n super(realtimeModel);\n\n this.oaiRealtimeModel = realtimeModel;\n\n this.#task = Task.from(({ signal }) => this.#mainTask(signal));\n\n this.sendEvent(this.createSessionUpdateEvent());\n }\n\n sendEvent(command: api_proto.ClientEvent): void {\n this.messageChannel.put(command);\n }\n\n private createSessionUpdateEvent(): api_proto.SessionUpdateEvent {\n return {\n type: 'session.update',\n session: {\n model: this.oaiRealtimeModel._options.model,\n voice: this.oaiRealtimeModel._options.voice,\n input_audio_format: 'pcm16',\n output_audio_format: 'pcm16',\n modalities: ['text', 'audio'],\n turn_detection: this.oaiRealtimeModel._options.turnDetection,\n input_audio_transcription: this.oaiRealtimeModel._options.inputAudioTranscription,\n // TODO(shubhra): add inputAudioNoiseReduction\n temperature: this.oaiRealtimeModel._options.temperature,\n tool_choice: toOaiToolChoice(this.oaiRealtimeModel._options.toolChoice),\n max_response_output_tokens:\n this.oaiRealtimeModel._options.maxResponseOutputTokens === Infinity\n ? 'inf'\n : this.oaiRealtimeModel._options.maxResponseOutputTokens,\n // TODO(shubhra): add tracing options\n instructions: this.instructions,\n speed: this.oaiRealtimeModel._options.speed,\n },\n };\n }\n\n get chatCtx() {\n return this.remoteChatCtx.toChatCtx();\n }\n\n get tools() {\n return { ...this._tools } as llm.ToolContext;\n }\n\n async updateChatCtx(_chatCtx: llm.ChatContext): Promise<void> {\n const unlock = await this.updateChatCtxLock.lock();\n const events = this.createChatCtxUpdateEvents(_chatCtx);\n const futures: Future<void>[] = [];\n\n for (const event of events) {\n const future = new Future<void>();\n futures.push(future);\n\n if (event.type === 'conversation.item.create') {\n this.itemCreateFutures[event.item.id] = future;\n } else if (event.type == 'conversation.item.delete') {\n this.itemDeleteFutures[event.item_id] = future;\n }\n\n this.sendEvent(event);\n }\n\n if (futures.length === 0) {\n unlock();\n return;\n }\n\n try {\n // wait for futures to resolve or timeout\n await Promise.race([\n Promise.all(futures),\n delay(5000).then(() => {\n throw new Error('Chat ctx update events timed out');\n }),\n ]);\n } catch (e) {\n this.#logger.error((e as Error).message);\n throw e;\n } finally {\n unlock();\n }\n }\n\n private createChatCtxUpdateEvents(\n chatCtx: llm.ChatContext,\n addMockAudio: boolean = false,\n ): (api_proto.ConversationItemCreateEvent | api_proto.ConversationItemDeleteEvent)[] {\n const newChatCtx = chatCtx.copy();\n if (addMockAudio) {\n newChatCtx.items.push(createMockAudioItem());\n } else {\n // clean up existing mock audio items\n newChatCtx.items = newChatCtx.items.filter(\n (item) => !item.id.startsWith(MOCK_AUDIO_ID_PREFIX),\n );\n }\n\n const events: (\n | api_proto.ConversationItemCreateEvent\n | api_proto.ConversationItemDeleteEvent\n )[] = [];\n\n const diffOps = llm.computeChatCtxDiff(this.chatCtx, newChatCtx);\n for (const op of diffOps.toRemove) {\n events.push({\n type: 'conversation.item.delete',\n item_id: op,\n event_id: shortuuid('chat_ctx_delete_'),\n } as api_proto.ConversationItemDeleteEvent);\n }\n\n for (const [previousId, id] of diffOps.toCreate) {\n const chatItem = newChatCtx.getById(id);\n if (!chatItem) {\n throw new Error(`Chat item ${id} not found`);\n }\n events.push({\n type: 'conversation.item.create',\n item: livekitItemToOpenAIItem(chatItem),\n previous_item_id: previousId ?? undefined,\n event_id: shortuuid('chat_ctx_create_'),\n } as api_proto.ConversationItemCreateEvent);\n }\n return events;\n }\n\n async updateTools(_tools: llm.ToolContext): Promise<void> {\n const unlock = await this.updateFuncCtxLock.lock();\n const ev = this.createToolsUpdateEvent(_tools);\n this.sendEvent(ev);\n\n if (!ev.session.tools) {\n throw new Error('Tools are missing in the session update event');\n }\n\n // TODO(brian): these logics below are noops I think, leaving it here to keep\n // parity with the python but we should remove them later\n const retainedToolNames = new Set(ev.session.tools.map((tool) => tool.name));\n const retainedTools = Object.fromEntries(\n Object.entries(_tools).filter(\n ([name, tool]) => llm.isFunctionTool(tool) && retainedToolNames.has(name),\n ),\n );\n\n this._tools = retainedTools as llm.ToolContext;\n\n unlock();\n }\n\n private createToolsUpdateEvent(_tools: llm.ToolContext): api_proto.SessionUpdateEvent {\n const oaiTools: api_proto.Tool[] = [];\n\n for (const [name, tool] of Object.entries(_tools)) {\n if (!llm.isFunctionTool(tool)) {\n this.#logger.error({ name, tool }, \"OpenAI Realtime API doesn't support this tool type\");\n continue;\n }\n\n const { parameters: toolParameters, description } = tool;\n try {\n const parameters = llm.toJsonSchema(\n toolParameters,\n ) as unknown as api_proto.Tool['parameters'];\n\n oaiTools.push({\n name,\n description,\n parameters: parameters,\n type: 'function',\n });\n } catch (e) {\n this.#logger.error({ name, tool }, \"OpenAI Realtime API doesn't support this tool type\");\n continue;\n }\n }\n\n return {\n type: 'session.update',\n session: {\n model: this.oaiRealtimeModel._options.model,\n tools: oaiTools,\n },\n event_id: shortuuid('tools_update_'),\n };\n }\n\n async updateInstructions(_instructions: string): Promise<void> {\n const eventId = shortuuid('instructions_update_');\n this.sendEvent({\n type: 'session.update',\n session: {\n instructions: _instructions,\n },\n event_id: eventId,\n } as api_proto.SessionUpdateEvent);\n this.instructions = _instructions;\n }\n\n updateOptions({ toolChoice }: { toolChoice?: llm.ToolChoice }): void {\n const options: api_proto.SessionUpdateEvent['session'] = {};\n\n this.oaiRealtimeModel._options.toolChoice = toolChoice;\n options.tool_choice = toOaiToolChoice(toolChoice);\n\n // TODO(brian): add other options here\n\n this.sendEvent({\n type: 'session.update',\n session: options,\n event_id: shortuuid('options_update_'),\n });\n }\n\n pushAudio(frame: AudioFrame): void {\n for (const f of this.resampleAudio(frame)) {\n for (const nf of this.bstream.write(f.data.buffer)) {\n this.sendEvent({\n type: 'input_audio_buffer.append',\n audio: Buffer.from(nf.data.buffer).toString('base64'),\n } as api_proto.InputAudioBufferAppendEvent);\n // TODO(AJS-102): use frame.durationMs once available in rtc-node\n this.pushedDurationMs += (nf.samplesPerChannel / nf.sampleRate) * 1000;\n }\n }\n }\n\n async commitAudio(): Promise<void> {\n if (this.pushedDurationMs > 100) {\n // OpenAI requires at least 100ms of audio\n this.sendEvent({\n type: 'input_audio_buffer.commit',\n } as api_proto.InputAudioBufferCommitEvent);\n this.pushedDurationMs = 0;\n }\n }\n\n async clearAudio(): Promise<void> {\n this.sendEvent({\n type: 'input_audio_buffer.clear',\n } as api_proto.InputAudioBufferClearEvent);\n this.pushedDurationMs = 0;\n }\n\n async generateReply(instructions?: string): Promise<llm.GenerationCreatedEvent> {\n const handle = this.createResponse({ instructions, userInitiated: true });\n this.textModeRecoveryRetries = 0;\n return handle.doneFut.await;\n }\n\n async interrupt(): Promise<void> {\n this.sendEvent({\n type: 'response.cancel',\n } as api_proto.ResponseCancelEvent);\n }\n\n async truncate(_options: { messageId: string; audioEndMs: number }): Promise<void> {\n this.sendEvent({\n type: 'conversation.item.truncate',\n content_index: 0,\n item_id: _options.messageId,\n audio_end_ms: _options.audioEndMs,\n } as api_proto.ConversationItemTruncateEvent);\n }\n\n /// Truncates the data field of the event to the specified maxLength to avoid overwhelming logs\n /// with large amounts of base64 audio data.\n #loggableEvent(\n event: api_proto.ClientEvent | api_proto.ServerEvent,\n maxLength: number = 30,\n ): Record<string, unknown> {\n const untypedEvent: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(event)) {\n if (value !== undefined) {\n untypedEvent[key] = value;\n }\n }\n\n if (untypedEvent.audio && typeof untypedEvent.audio === 'string') {\n const truncatedData =\n untypedEvent.audio.slice(0, maxLength) + (untypedEvent.audio.length > maxLength ? '…' : '');\n return { ...untypedEvent, audio: truncatedData };\n }\n if (\n untypedEvent.delta &&\n typeof untypedEvent.delta === 'string' &&\n event.type === 'response.audio.delta'\n ) {\n const truncatedDelta =\n untypedEvent.delta.slice(0, maxLength) + (untypedEvent.delta.length > maxLength ? '…' : '');\n return { ...untypedEvent, delta: truncatedDelta };\n }\n return untypedEvent;\n }\n\n private async createWsConn(): Promise<WebSocket> {\n const headers: Record<string, string> = {\n 'User-Agent': 'LiveKit-Agents-JS',\n };\n\n if (this.oaiRealtimeModel._options.isAzure) {\n // Microsoft API has two ways of authentication\n // 1. Entra token set as `Bearer` token\n // 2. API key set as `api_key` header (also accepts query string)\n if (this.oaiRealtimeModel._options.entraToken) {\n headers.Authorization = `Bearer ${this.oaiRealtimeModel._options.entraToken}`;\n } else if (this.oaiRealtimeModel._options.apiKey) {\n headers['api-key'] = this.oaiRealtimeModel._options.apiKey;\n } else {\n throw new Error('Microsoft API key or entraToken is required');\n }\n } else {\n headers.Authorization = `Bearer ${this.oaiRealtimeModel._options.apiKey}`;\n headers['OpenAI-Beta'] = 'realtime=v1';\n }\n\n const url = processBaseURL({\n baseURL: this.oaiRealtimeModel._options.baseURL,\n model: this.oaiRealtimeModel._options.model,\n isAzure: this.oaiRealtimeModel._options.isAzure,\n apiVersion: this.oaiRealtimeModel._options.apiVersion,\n azureDeployment: this.oaiRealtimeModel._options.azureDeployment,\n });\n\n this.#logger.debug(`Connecting to OpenAI Realtime API at ${url}`);\n\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(url, { headers });\n let waiting = true;\n\n const timeout = setTimeout(() => {\n ws.close();\n reject(new Error('WebSocket connection timeout'));\n }, this.oaiRealtimeModel._options.connOptions.timeoutMs);\n\n ws.once('open', () => {\n if (!waiting) return;\n waiting = false;\n clearTimeout(timeout);\n resolve(ws);\n });\n\n ws.once('close', () => {\n if (!waiting) return;\n waiting = false;\n clearTimeout(timeout);\n reject(new Error('OpenAI Realtime API connection closed'));\n });\n });\n }\n\n async #mainTask(signal: AbortSignal): Promise<void> {\n let reconnecting = false;\n let numRetries = 0;\n let wsConn: WebSocket | null = null;\n const maxRetries = this.oaiRealtimeModel._options.connOptions.maxRetry;\n\n const reconnect = async () => {\n this.#logger.debug(\n {\n maxSessionDuration: this.oaiRealtimeModel._options.maxSessionDuration,\n },\n 'Reconnecting to OpenAI Realtime API',\n );\n\n const events: api_proto.ClientEvent[] = [];\n\n // options and instructions\n events.push(this.createSessionUpdateEvent());\n\n // tools\n if (Object.keys(this._tools).length > 0) {\n events.push(this.createToolsUpdateEvent(this._tools));\n }\n\n // chat context\n const chatCtx = this.chatCtx.copy({\n excludeFunctionCall: true,\n excludeInstructions: true,\n excludeEmptyMessage: true,\n });\n\n const oldChatCtx = this.remoteChatCtx;\n this.remoteChatCtx = new llm.RemoteChatContext();\n events.push(...this.createChatCtxUpdateEvents(chatCtx));\n\n try {\n for (const ev of events) {\n this.emit('openai_client_event_queued', ev);\n wsConn!.send(JSON.stringify(ev));\n }\n } catch (error) {\n this.remoteChatCtx = oldChatCtx;\n throw new APIConnectionError({\n message: 'Failed to send message to OpenAI Realtime API during session re-connection',\n });\n }\n\n this.#logger.debug('Reconnected to OpenAI Realtime API');\n\n this.emit('session_reconnected', {} as llm.RealtimeSessionReconnectedEvent);\n };\n\n reconnecting = false;\n while (!this.#closed && !signal.aborted) {\n this.#logger.debug('Creating WebSocket connection to OpenAI Realtime API');\n wsConn = await this.createWsConn();\n if (signal.aborted) break;\n\n try {\n if (reconnecting) {\n await reconnect();\n if (signal.aborted) break;\n numRetries = 0;\n }\n\n await this.runWs(wsConn);\n if (signal.aborted) break;\n } catch (error) {\n if (!isAPIError(error)) {\n this.emitError({ error: error as Error, recoverable: false });\n throw error;\n }\n\n if (maxRetries === 0 || !error.retryable) {\n this.emitError({ error: error as Error, recoverable: false });\n throw error;\n }\n\n if (numRetries === maxRetries) {\n this.emitError({ error: error as Error, recoverable: false });\n throw new APIConnectionError({\n message: `OpenAI Realtime API connection failed after ${numRetries} attempts`,\n options: {\n body: error,\n retryable: false,\n },\n });\n }\n\n this.emitError({ error: error as Error, recoverable: true });\n const retryInterval =\n numRetries === 0\n ? DEFAULT_FIRST_RETRY_INTERVAL_MS\n : this.oaiRealtimeModel._options.connOptions.retryIntervalMs;\n this.#logger.warn(\n {\n attempt: numRetries,\n maxRetries,\n error,\n },\n `OpenAI Realtime API connection failed, retrying in ${retryInterval / 1000}s`,\n );\n\n await delay(retryInterval);\n numRetries++;\n }\n\n reconnecting = true;\n }\n }\n\n private async runWs(wsConn: WebSocket): Promise<void> {\n const forwardEvents = async (signal: AbortSignal): Promise<void> => {\n const abortFuture = new Future<void>();\n signal.addEventListener('abort', () => abortFuture.resolve());\n\n while (!this.#closed && wsConn.readyState === WebSocket.OPEN && !signal.aborted) {\n try {\n const event = await Promise.race([this.messageChannel.get(), abortFuture.await]);\n if (signal.aborted || abortFuture.done || event === undefined) {\n break;\n }\n\n if (event.type !== 'input_audio_buffer.append') {\n this.#logger.debug(`(client) -> ${JSON.stringify(this.#loggableEvent(event))}`);\n }\n\n this.emit('openai_client_event_queued', event);\n wsConn.send(JSON.stringify(event));\n } catch (error) {\n break;\n }\n }\n\n wsConn.close();\n };\n\n const wsCloseFuture = new Future<void | Error>();\n\n wsConn.onerror = (error) => {\n wsCloseFuture.resolve(new APIConnectionError({ message: error.message }));\n };\n wsConn.onclose = () => {\n wsCloseFuture.resolve();\n };\n\n wsConn.onmessage = (message: MessageEvent) => {\n const event: api_proto.ServerEvent = JSON.parse(message.data as string);\n\n this.emit('openai_server_event_received', event);\n this.#logger.debug(`(server) <- ${JSON.stringify(this.#loggableEvent(event))}`);\n\n switch (event.type) {\n case 'input_audio_buffer.speech_started':\n this.handleInputAudioBufferSpeechStarted(event);\n break;\n case 'input_audio_buffer.speech_stopped':\n this.handleInputAudioBufferSpeechStopped(event);\n break;\n case 'response.created':\n this.handleResponseCreated(event);\n break;\n case 'response.output_item.added':\n this.handleResponseOutputItemAdded(event);\n break;\n case 'conversation.item.created':\n this.handleConversationItemCreated(event);\n break;\n case 'conversation.item.deleted':\n this.handleConversationItemDeleted(event);\n break;\n case 'conversation.item.input_audio_transcription.completed':\n this.handleConversationItemInputAudioTranscriptionCompleted(event);\n break;\n case 'conversation.item.input_audio_transcription.failed':\n this.handleConversationItemInputAudioTranscriptionFailed(event);\n break;\n case 'response.content_part.added':\n this.handleResponseContentPartAdded(event);\n break;\n case 'response.content_part.done':\n this.handleResponseContentPartDone(event);\n break;\n case 'response.audio_transcript.delta':\n this.handleResponseAudioTranscriptDelta(event);\n break;\n case 'response.audio.delta':\n this.handleResponseAudioDelta(event);\n break;\n case 'response.audio_transcript.done':\n this.handleResponseAudioTranscriptDone(event);\n break;\n case 'response.audio.done':\n this.handleResponseAudioDone(event);\n break;\n case 'response.output_item.done':\n this.handleResponseOutputItemDone(event);\n break;\n case 'response.done':\n this.handleResponseDone(event);\n break;\n case 'error':\n this.handleError(event);\n break;\n default:\n this.#logger.debug(`unhandled event: ${event.type}`);\n break;\n }\n };\n\n const sendTask = Task.from(({ signal }) => forwardEvents(signal));\n\n const wsTask = Task.from(({ signal }) => {\n const abortPromise = new Promise<void>((resolve) => {\n signal.addEventListener('abort', () => {\n resolve();\n });\n });\n\n return Promise.race([wsCloseFuture.await, abortPromise]);\n });\n\n const waitReconnectTask = Task.from(async ({ signal }) => {\n await delay(this.oaiRealtimeModel._options.maxSessionDuration, { signal });\n return new APIConnectionError({\n message: 'OpenAI Realtime API connection timeout',\n });\n });\n\n try {\n const result = await Promise.race([wsTask.result, sendTask.result, waitReconnectTask.result]);\n\n if (waitReconnectTask.done && this.currentGeneration) {\n await this.currentGeneration._doneFut.await;\n }\n\n if (result instanceof Error) {\n throw result;\n }\n } finally {\n await cancelAndWait([wsTask, sendTask, waitReconnectTask], 2000);\n wsConn.close();\n }\n }\n\n async close() {\n super.close();\n this.#closed = true;\n await this.#task;\n }\n\n private handleInputAudioBufferSpeechStarted(\n _event: api_proto.InputAudioBufferSpeechStartedEvent,\n ): void {\n this.emit('input_speech_started', {} as llm.InputSpeechStartedEvent);\n }\n\n private handleInputAudioBufferSpeechStopped(\n _event: api_proto.InputAudioBufferSpeechStoppedEvent,\n ): void {\n this.emit('input_speech_stopped', {\n userTranscriptionEnabled: this.oaiRealtimeModel._options.inputAudioTranscription !== null,\n } as llm.InputSpeechStoppedEvent);\n }\n\n private handleResponseCreated(event: api_proto.ResponseCreatedEvent): void {\n if (!event.response.id) {\n throw new Error('response.id is missing');\n }\n\n this.currentGeneration = {\n messageChannel: stream.createStreamChannel<llm.MessageGeneration>(),\n functionChannel: stream.createStreamChannel<llm.FunctionCall>(),\n messages: new Map(),\n _doneFut: new Future(),\n _createdTimestamp: Date.now(),\n };\n\n if (!event.response.metadata || !event.response.metadata.client_event_id) return;\n\n const handle = this.responseCreatedFutures[event.response.metadata.client_event_id];\n if (handle) {\n delete this.responseCreatedFutures[event.response.metadata.client_event_id];\n\n // set key to the response id\n this.responseCreatedFutures[event.response.id] = handle;\n }\n\n // the generation_created event is emitted when\n // 1. the response is not a message on response.output_item.added event\n // 2. the content is audio on response.content_part.added event\n // will try to recover from text response on response.content_part.done event\n this.emit('generation_created', {\n messageStream: this.currentGeneration.messageChannel.stream(),\n functionStream: this.currentGeneration.functionChannel.stream(),\n userInitiated: false,\n } as GenerationCreatedEvent);\n }\n\n private handleResponseOutputItemAdded(event: api_proto.ResponseOutputItemAddedEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n if (!event.item.type) {\n throw new Error('item.type is not set');\n }\n\n if (!event.response_id) {\n throw new Error('response_id is not set');\n }\n\n const itemType = event.item.type;\n const responseId = event.response_id;\n\n if (itemType !== 'message') {\n // emit immediately if it's not a message, otherwise wait response.content_part.added\n this.emitGenerationEvent(responseId);\n this.textModeRecoveryRetries = 0;\n return;\n }\n }\n\n private handleConversationItemCreated(event: api_proto.ConversationItemCreatedEvent): void {\n if (!event.item.id) {\n throw new Error('item.id is not set');\n }\n\n try {\n this.remoteChatCtx.insert(event.previous_item_id, openAIItemToLivekitItem(event.item));\n } catch (error) {\n this.#logger.error({ error, itemId: event.item.id }, 'failed to insert conversation item');\n }\n\n const fut = this.itemCreateFutures[event.item.id];\n if (fut) {\n fut.resolve();\n delete this.itemCreateFutures[event.item.id];\n }\n }\n\n private handleConversationItemDeleted(event: api_proto.ConversationItemDeletedEvent): void {\n if (!event.item_id) {\n throw new Error('item_id is not set');\n }\n\n try {\n this.remoteChatCtx.delete(event.item_id);\n } catch (error) {\n this.#logger.error({ error, itemId: event.item_id }, 'failed to delete conversation item');\n }\n\n const fut = this.itemDeleteFutures[event.item_id];\n if (fut) {\n fut.resolve();\n delete this.itemDeleteFutures[event.item_id];\n }\n }\n\n private handleConversationItemInputAudioTranscriptionCompleted(\n event: api_proto.ConversationItemInputAudioTranscriptionCompletedEvent,\n ): void {\n const remoteItem = this.remoteChatCtx.get(event.item_id);\n if (!remoteItem) {\n return;\n }\n\n const item = remoteItem.item;\n if (item instanceof llm.ChatMessage) {\n item.content.push(event.transcript);\n } else {\n throw new Error('item is not a chat message');\n }\n\n this.emit('input_audio_transcription_completed', {\n itemId: event.item_id,\n transcript: event.transcript,\n isFinal: true,\n } as llm.InputTranscriptionCompleted);\n }\n\n private handleConversationItemInputAudioTranscriptionFailed(\n event: api_proto.ConversationItemInputAudioTranscriptionFailedEvent,\n ): void {\n this.#logger.error(\n { error: event.error },\n 'OpenAI Realtime API failed to transcribe input audio',\n );\n }\n\n private handleResponseContentPartAdded(event: api_proto.ResponseContentPartAddedEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemId = event.item_id;\n const itemType = event.part.type;\n const responseId = event.response_id;\n\n if (itemType === 'audio') {\n this.emitGenerationEvent(responseId);\n if (this.textModeRecoveryRetries > 0) {\n this.#logger.info(\n { retries: this.textModeRecoveryRetries },\n 'recovered from text-only response',\n );\n this.textModeRecoveryRetries = 0;\n }\n\n const itemGeneration: MessageGeneration = {\n messageId: itemId,\n textChannel: stream.createStreamChannel<string>(),\n audioChannel: stream.createStreamChannel<AudioFrame>(),\n audioTranscript: '',\n };\n\n this.currentGeneration.messageChannel.write({\n messageId: itemId,\n textStream: itemGeneration.textChannel.stream(),\n audioStream: itemGeneration.audioChannel.stream(),\n });\n\n this.currentGeneration.messages.set(itemId, itemGeneration);\n this.currentGeneration._firstTokenTimestamp = Date.now();\n return;\n } else {\n this.interrupt();\n if (this.textModeRecoveryRetries === 0) {\n this.#logger.warn({ responseId }, 'received text-only response from OpenAI Realtime API');\n }\n }\n }\n\n private handleResponseContentPartDone(event: api_proto.ResponseContentPartDoneEvent): void {\n if (event.part.type !== 'text') {\n return;\n }\n\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n // TODO(shubhra): handle text mode recovery\n }\n\n private handleResponseAudioTranscriptDelta(\n event: api_proto.ResponseAudioTranscriptDeltaEvent,\n ): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemId = event.item_id;\n const delta = event.delta;\n\n // TODO (shubhra): add timed string support\n\n const itemGeneration = this.currentGeneration.messages.get(itemId);\n if (!itemGeneration) {\n throw new Error('itemGeneration is not set');\n } else {\n itemGeneration.textChannel.write(delta);\n itemGeneration.audioTranscript += delta;\n }\n }\n\n private handleResponseAudioDelta(event: api_proto.ResponseAudioDeltaEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemGeneration = this.currentGeneration.messages.get(event.item_id);\n if (!itemGeneration) {\n throw new Error('itemGeneration is not set');\n }\n\n const binaryString = atob(event.delta);\n const len = binaryString.length;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n itemGeneration.audioChannel.write(\n new AudioFrame(\n new Int16Array(bytes.buffer),\n api_proto.SAMPLE_RATE,\n api_proto.NUM_CHANNELS,\n bytes.length / 2,\n ),\n );\n }\n\n private handleResponseAudioTranscriptDone(\n _event: api_proto.ResponseAudioTranscriptDoneEvent,\n ): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n }\n\n private handleResponseAudioDone(_event: api_proto.ResponseAudioDoneEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n }\n\n private handleResponseOutputItemDone(event: api_proto.ResponseOutputItemDoneEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemId = event.item.id;\n const itemType = event.item.type;\n\n if (itemType === 'function_call') {\n const item = event.item;\n if (!item.call_id || !item.name || !item.arguments) {\n throw new Error('item is not a function call');\n }\n this.currentGeneration.functionChannel.write({\n callId: item.call_id,\n name: item.name,\n args: item.arguments,\n } as llm.FunctionCall);\n } else if (itemType === 'message') {\n const itemGeneration = this.currentGeneration.messages.get(itemId);\n if (!itemGeneration) {\n return;\n }\n // text response doesn't have itemGeneration\n itemGeneration.textChannel.close();\n itemGeneration.audioChannel.close();\n }\n }\n\n private handleResponseDone(_event: api_proto.ResponseDoneEvent): void {\n if (!this.currentGeneration) {\n // OpenAI has a race condition where we could receive response.done without any\n // previous response.created (This happens generally during interruption)\n return;\n }\n\n const createdTimestamp = this.currentGeneration._createdTimestamp;\n const firstTokenTimestamp = this.currentGeneration._firstTokenTimestamp;\n\n this.#logger.debug(\n {\n messageCount: this.currentGeneration.messages.size,\n },\n 'Closing generation channels in handleResponseDone',\n );\n\n for (const generation of this.currentGeneration.messages.values()) {\n generation.textChannel.close();\n generation.audioChannel.close();\n }\n\n this.currentGeneration.functionChannel.close();\n this.currentGeneration.messageChannel.close();\n\n for (const itemId of this.currentGeneration.messages.keys()) {\n const remoteItem = this.remoteChatCtx.get(itemId);\n if (remoteItem && remoteItem.item instanceof llm.ChatMessage) {\n remoteItem.item.content.push(this.currentGeneration.messages.get(itemId)!.audioTranscript);\n }\n }\n\n this.currentGeneration._doneFut.resolve();\n this.currentGeneration = undefined;\n\n // Calculate and emit metrics\n const usage = _event.response.usage;\n const ttft = firstTokenTimestamp ? firstTokenTimestamp - createdTimestamp : -1;\n const duration = (Date.now() - createdTimestamp) / 1000; // Convert to seconds\n\n const realtimeMetrics: metrics.RealtimeModelMetrics = {\n type: 'realtime_model_metrics',\n timestamp: createdTimestamp / 1000, // Convert to seconds\n requestId: _event.response.id || '',\n ttft,\n duration,\n cancelled: _event.response.status === 'cancelled',\n label: 'openai_realtime',\n inputTokens: usage?.input_tokens ?? 0,\n outputTokens: usage?.output_tokens ?? 0,\n totalTokens: usage?.total_tokens ?? 0,\n tokensPerSecond: duration > 0 ? (usage?.output_tokens ?? 0) / duration : 0,\n inputTokenDetails: {\n audioTokens: usage?.input_token_details?.audio_tokens ?? 0,\n textTokens: usage?.input_token_details?.text_tokens ?? 0,\n imageTokens: 0, // Not supported yet\n cachedTokens: usage?.input_token_details?.cached_tokens ?? 0,\n cachedTokensDetails: usage?.input_token_details?.cached_tokens_details\n ? {\n audioTokens: usage?.input_token_details?.cached_tokens_details?.audio_tokens ?? 0,\n textTokens: usage?.input_token_details?.cached_tokens_details?.text_tokens ?? 0,\n imageTokens: usage?.input_token_details?.cached_tokens_details?.image_tokens ?? 0,\n }\n : undefined,\n },\n outputTokenDetails: {\n textTokens: usage?.output_token_details?.text_tokens ?? 0,\n audioTokens: usage?.output_token_details?.audio_tokens ?? 0,\n imageTokens: 0,\n },\n };\n\n this.emit('metrics_collected', realtimeMetrics);\n // TODO(brian): handle response done but not complete\n }\n\n private handleError(event: api_proto.ErrorEvent): void {\n if (event.error.message.startsWith('Cancellation failed')) {\n return;\n }\n\n this.#logger.error({ error: event.error }, 'OpenAI Realtime API returned an error');\n this.emitError({\n error: new APIError(event.error.message, {\n body: event.error,\n retryable: true,\n }),\n recoverable: true,\n });\n\n // TODO(brian): set error for response future if it exists\n }\n\n private emitError({ error, recoverable }: { error: Error; recoverable: boolean }): void {\n // IMPORTANT: only emit error if there are listeners; otherwise emit will throw an error\n this.emit('error', {\n timestamp: Date.now(),\n // TODO(brian): add label\n label: '',\n error,\n recoverable,\n } as llm.RealtimeModelError);\n }\n\n private *resampleAudio(frame: AudioFrame): Generator<AudioFrame> {\n yield frame;\n }\n\n private createResponse({\n userInitiated,\n instructions,\n oldHandle,\n }: {\n userInitiated: boolean;\n instructions?: string;\n oldHandle?: CreateResponseHandle;\n }): CreateResponseHandle {\n const handle = oldHandle || new CreateResponseHandle({ instructions });\n if (oldHandle && instructions) {\n handle.instructions = instructions;\n }\n\n const eventId = shortuuid('response_create_');\n if (userInitiated) {\n this.responseCreatedFutures[eventId] = handle;\n }\n\n const response: api_proto.ResponseCreateEvent['response'] = {};\n if (instructions) response.instructions = instructions;\n if (userInitiated) response.metadata = { client_event_id: eventId };\n\n this.sendEvent({\n type: 'response.create',\n event_id: eventId,\n response: Object.keys(response).length > 0 ? response : undefined,\n });\n\n return handle;\n }\n\n private emitGenerationEvent(responseId: string): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const generation_ev: llm.GenerationCreatedEvent = {\n messageStream: this.currentGeneration.messageChannel.stream(),\n functionStream: this.currentGeneration.functionChannel.stream(),\n userInitiated: false,\n };\n\n const handle = this.responseCreatedFutures[responseId];\n if (handle) {\n delete this.responseCreatedFutures[responseId];\n generation_ev.userInitiated = true;\n if (handle.doneFut.done) {\n this.#logger.warn({ responseId }, 'response received after timeout');\n } else {\n handle.doneFut.resolve(generation_ev);\n }\n }\n\n this.#logger.debug({ responseId }, 'Emitting generation_created event');\n this.emit('generation_created', generation_ev);\n }\n}\n\nfunction livekitItemToOpenAIItem(item: llm.ChatItem): api_proto.ItemResource {\n switch (item.type) {\n case 'function_call':\n return {\n id: item.id,\n type: 'function_call',\n call_id: item.callId,\n name: item.name,\n arguments: item.args,\n } as api_proto.FunctionCallItem;\n case 'function_call_output':\n return {\n id: item.id,\n type: 'function_call_output',\n call_id: item.callId,\n output: item.output,\n } as api_proto.FunctionCallOutputItem;\n case 'message':\n const role = item.role === 'developer' ? 'system' : item.role;\n const contentList: api_proto.Content[] = [];\n for (const c of item.content) {\n if (typeof c === 'string') {\n contentList.push({\n type: role === 'assistant' ? 'text' : 'input_text',\n text: c,\n } as api_proto.InputTextContent);\n } else if (c.type === 'image_content') {\n // not supported for now\n continue;\n } else if (c.type === 'audio_content') {\n if (role === 'user') {\n const encodedAudio = Buffer.from(combineAudioFrames(c.frame).data).toString('base64');\n contentList.push({\n type: 'input_audio',\n audio: encodedAudio,\n } as api_proto.InputAudioContent);\n }\n }\n }\n return {\n id: item.id,\n type: 'message',\n role,\n content: contentList,\n } as api_proto.UserItem;\n }\n}\n\nfunction openAIItemToLivekitItem(item: api_proto.ItemResource): llm.ChatItem {\n if (!item.id) {\n throw new Error('item.id is not set');\n }\n\n switch (item.type) {\n case 'function_call':\n return llm.FunctionCall.create({\n id: item.id,\n callId: item.call_id,\n name: item.name,\n args: item.arguments,\n });\n case 'function_call_output':\n return llm.FunctionCallOutput.create({\n id: item.id,\n callId: item.call_id,\n output: item.output,\n isError: false,\n });\n case 'message':\n const content: llm.ChatContent[] = [];\n // item.content can be a single object or an array; normalize to array\n const contents = Array.isArray(item.content) ? item.content : [item.content];\n for (const c of contents) {\n if (c.type === 'text' || c.type === 'input_text') {\n content.push(c.text);\n }\n }\n return llm.ChatMessage.create({\n id: item.id,\n role: item.role,\n content,\n });\n }\n}\n\nfunction createMockAudioItem(durationSeconds: number = 2): llm.ChatMessage {\n const audioData = Buffer.alloc(durationSeconds * SAMPLE_RATE);\n return llm.ChatMessage.create({\n id: shortuuid(MOCK_AUDIO_ID_PREFIX),\n role: 'user',\n content: [\n {\n type: 'audio_content',\n frame: [\n new AudioFrame(\n new Int16Array(audioData.buffer),\n SAMPLE_RATE,\n NUM_CHANNELS,\n audioData.length / 2,\n ),\n ],\n } as llm.AudioContent,\n ],\n });\n}\n\nfunction toOaiToolChoice(toolChoice?: llm.ToolChoice): api_proto.ToolChoice {\n if (typeof toolChoice === 'string') {\n return toolChoice;\n }\n\n if (toolChoice?.type === 'function') {\n return toolChoice.function.name;\n }\n\n return 'auto';\n}\n"],"mappings":"AAIA;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAa;AAEtB,SAAS,YAAY,0BAA0B;AAE/C,SAA4B,iBAAiB;AAC7C,YAAY,eAAe;AAE3B,MAAM,cAAc;AACpB,MAAM,eAAe;AACrB,MAAM,WAAW;AAEjB,MAAM,uBAAuB;AA4C7B,MAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA;AAAA,EAEA,YAAY,EAAE,aAAa,GAA8B;AACvD,SAAK,eAAe;AACpB,SAAK,UAAU,IAAI,OAAO;AAAA,EAC5B;AACF;AAGA,MAAM,kCAAkC;AACxC,MAAM,sBAAsB;AAC5B,MAAM,yBAAsD;AAAA,EAC1D,MAAM;AAAA,EACN,WAAW;AAAA,EACX,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,oBAAoB;AACtB;AACA,MAAM,oCAAuE;AAAA,EAC3E,OAAO;AACT;AACA,MAAM,sBAAsC;AAC5C,MAAM,qCAAqD;AAE3D,MAAM,0CAA6E;AAAA,EACjF,OAAO;AACT;AAEA,MAAM,+BAA4D;AAAA,EAChE,MAAM;AAAA,EACN,WAAW;AAAA,EACX,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,iBAAiB;AACnB;AAEA,MAAM,+BAA+B,KAAK,KAAK;AAE/C,MAAM,iCAAiC;AAAA,EACrC,OAAO;AAAA,EACP,OAAO;AAAA,EACP,aAAa;AAAA,EACb,yBAAyB;AAAA,EACzB,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,yBAAyB;AAAA,EACzB,oBAAoB;AAAA,EACpB,aAAa;AACf;AACO,MAAM,sBAAsB,IAAI,cAAc;AAAA,EACnD,aAAa,UAAU;AAAA,EACvB,cAAc,UAAU;AAAA,EACxB,cAAc,UAAU;AAAA,EACxB,eAAe,UAAU;AAAA;AAAA,EAGzB;AAAA,EAEA,YACE,UAiBI,CAAC,GACL;AACA,UAAM;AAAA,MACJ,mBAAmB;AAAA,MACnB,eAAe,QAAQ,kBAAkB;AAAA,MACzC,mBAAmB,QAAQ,4BAA4B;AAAA,MACvD,yBAAyB;AAAA,IAC3B,CAAC;AAED,UAAM,UAAU,CAAC,EAAE,QAAQ,cAAc,QAAQ,cAAc,QAAQ;AAEvE,QAAI,QAAQ,WAAW,MAAM,CAAC,SAAS;AACrC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,SAAS,QAAQ,UAAU,QAAQ,IAAI;AAE7C,QAAI,CAAC,UAAU,CAAC,SAAS;AACvB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,QAAQ,WAAW,SAAS;AAC/B,YAAM,gBAAgB,QAAQ,IAAI;AAClC,UAAI,CAAC,eAAe;AAClB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,cAAQ,UAAU,GAAG,cAAc,QAAQ,OAAO,EAAE,CAAC;AAAA,IACvD;AAEA,SAAK,WAAW;AAAA,MACd,GAAG;AAAA,MACH,GAAG;AAAA,MACH,SAAS,QAAQ,WAAW;AAAA,MAC5B;AAAA,MACA;AAAA,MACA,OAAO,QAAQ,SAAS,+BAA+B;AAAA,IACzD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,OAAO,UAAU;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR,0BAA0B;AAAA,IAC1B,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd;AAAA,EACF,GAaG;AACD,aAAS,UAAU,QAAQ,IAAI;AAC/B,QAAI,CAAC,UAAU,CAAC,YAAY;AAC1B,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,iBAAa,cAAc,QAAQ,IAAI;AACvC,QAAI,CAAC,YAAY;AACf,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,SAAS;AACZ,sBAAgB,iBAAiB,QAAQ,IAAI;AAC7C,UAAI,CAAC,eAAe;AAClB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,gBAAU,GAAG,cAAc,QAAQ,OAAO,EAAE,CAAC;AAAA,IAC/C;AAEA,WAAO,IAAI,cAAc;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,UAAU;AACR,WAAO,IAAI,gBAAgB,IAAI;AAAA,EACjC;AAAA,EAEA,MAAM,QAAQ;AACZ;AAAA,EACF;AACF;AAEA,SAAS,eAAe;AAAA,EACtB;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AACF,GAMW;AACT,QAAM,MAAM,IAAI,IAAI,CAAC,SAAS,UAAU,EAAE,KAAK,GAAG,CAAC;AAEnD,MAAI,IAAI,aAAa,UAAU;AAC7B,QAAI,WAAW;AAAA,EACjB;AAGA,MAAI,CAAC,IAAI,YAAY,CAAC,IAAI,OAAO,SAAS,EAAE,SAAS,IAAI,SAAS,QAAQ,OAAO,EAAE,CAAC,GAAG;AACrF,QAAI,WAAW,IAAI,SAAS,QAAQ,OAAO,EAAE,IAAI;AAAA,EACnD,OAAO;AACL,QAAI,WAAW,IAAI,SAAS,QAAQ,OAAO,EAAE;AAAA,EAC/C;AAEA,QAAM,cAAsC,CAAC;AAC7C,MAAI,SAAS;AACX,QAAI,YAAY;AACd,kBAAY,aAAa,IAAI;AAAA,IAC/B;AACA,QAAI,iBAAiB;AACnB,kBAAY,YAAY,IAAI;AAAA,IAC9B;AAAA,EACF,OAAO;AACL,gBAAY,OAAO,IAAI;AAAA,EACzB;AAEA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,WAAW,GAAG;AACtD,QAAI,aAAa,IAAI,KAAK,KAAK;AAAA,EACjC;AAEA,SAAO,IAAI,SAAS;AACtB;AAYO,MAAM,wBAAwB,IAAI,gBAAgB;AAAA,EAC/C,SAA0B,CAAC;AAAA,EAC3B,gBAAuC,IAAI,IAAI,kBAAkB;AAAA,EACjE,iBAAiB,IAAI,MAA6B;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,yBAAiE,CAAC;AAAA,EAElE,0BAAkC;AAAA,EAElC,oBAA8C,CAAC;AAAA,EAC/C,oBAA8C,CAAC;AAAA,EAE/C,oBAAoB,IAAI,MAAM;AAAA,EAC9B,oBAAoB,IAAI,MAAM;AAAA;AAAA,EAG9B,UAAU,IAAI,gBAAgB,aAAa,cAAc,cAAc,EAAE;AAAA,EAEzE,mBAA2B;AAAA,EAEnC,UAAU,IAAI;AAAA,EACd;AAAA,EACA,UAAU;AAAA,EAEV,YAAY,eAA8B;AACxC,UAAM,aAAa;AAEnB,SAAK,mBAAmB;AAExB,SAAK,QAAQ,KAAK,KAAK,CAAC,EAAE,OAAO,MAAM,KAAK,UAAU,MAAM,CAAC;AAE7D,SAAK,UAAU,KAAK,yBAAyB,CAAC;AAAA,EAChD;AAAA,EAEA,UAAU,SAAsC;AAC9C,SAAK,eAAe,IAAI,OAAO;AAAA,EACjC;AAAA,EAEQ,2BAAyD;AAC/D,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,QACP,OAAO,KAAK,iBAAiB,SAAS;AAAA,QACtC,OAAO,KAAK,iBAAiB,SAAS;AAAA,QACtC,oBAAoB;AAAA,QACpB,qBAAqB;AAAA,QACrB,YAAY,CAAC,QAAQ,OAAO;AAAA,QAC5B,gBAAgB,KAAK,iBAAiB,SAAS;AAAA,QAC/C,2BAA2B,KAAK,iBAAiB,SAAS;AAAA;AAAA,QAE1D,aAAa,KAAK,iBAAiB,SAAS;AAAA,QAC5C,aAAa,gBAAgB,KAAK,iBAAiB,SAAS,UAAU;AAAA,QACtE,4BACE,KAAK,iBAAiB,SAAS,4BAA4B,WACvD,QACA,KAAK,iBAAiB,SAAS;AAAA;AAAA,QAErC,cAAc,KAAK;AAAA,QACnB,OAAO,KAAK,iBAAiB,SAAS;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,IAAI,UAAU;AACZ,WAAO,KAAK,cAAc,UAAU;AAAA,EACtC;AAAA,EAEA,IAAI,QAAQ;AACV,WAAO,EAAE,GAAG,KAAK,OAAO;AAAA,EAC1B;AAAA,EAEA,MAAM,cAAc,UAA0C;AAC5D,UAAM,SAAS,MAAM,KAAK,kBAAkB,KAAK;AACjD,UAAM,SAAS,KAAK,0BAA0B,QAAQ;AACtD,UAAM,UAA0B,CAAC;AAEjC,eAAW,SAAS,QAAQ;AAC1B,YAAM,SAAS,IAAI,OAAa;AAChC,cAAQ,KAAK,MAAM;AAEnB,UAAI,MAAM,SAAS,4BAA4B;AAC7C,aAAK,kBAAkB,MAAM,KAAK,EAAE,IAAI;AAAA,MAC1C,WAAW,MAAM,QAAQ,4BAA4B;AACnD,aAAK,kBAAkB,MAAM,OAAO,IAAI;AAAA,MAC1C;AAEA,WAAK,UAAU,KAAK;AAAA,IACtB;AAEA,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO;AACP;AAAA,IACF;AAEA,QAAI;AAEF,YAAM,QAAQ,KAAK;AAAA,QACjB,QAAQ,IAAI,OAAO;AAAA,QACnB,MAAM,GAAI,EAAE,KAAK,MAAM;AACrB,gBAAM,IAAI,MAAM,kCAAkC;AAAA,QACpD,CAAC;AAAA,MACH,CAAC;AAAA,IACH,SAAS,GAAG;AACV,WAAK,QAAQ,MAAO,EAAY,OAAO;AACvC,YAAM;AAAA,IACR,UAAE;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,0BACN,SACA,eAAwB,OAC2D;AACnF,UAAM,aAAa,QAAQ,KAAK;AAChC,QAAI,cAAc;AAChB,iBAAW,MAAM,KAAK,oBAAoB,CAAC;AAAA,IAC7C,OAAO;AAEL,iBAAW,QAAQ,WAAW,MAAM;AAAA,QAClC,CAAC,SAAS,CAAC,KAAK,GAAG,WAAW,oBAAoB;AAAA,MACpD;AAAA,IACF;AAEA,UAAM,SAGA,CAAC;AAEP,UAAM,UAAU,IAAI,mBAAmB,KAAK,SAAS,UAAU;AAC/D,eAAW,MAAM,QAAQ,UAAU;AACjC,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SAAS;AAAA,QACT,UAAU,UAAU,kBAAkB;AAAA,MACxC,CAA0C;AAAA,IAC5C;AAEA,eAAW,CAAC,YAAY,EAAE,KAAK,QAAQ,UAAU;AAC/C,YAAM,WAAW,WAAW,QAAQ,EAAE;AACtC,UAAI,CAAC,UAAU;AACb,cAAM,IAAI,MAAM,aAAa,EAAE,YAAY;AAAA,MAC7C;AACA,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,MAAM,wBAAwB,QAAQ;AAAA,QACtC,kBAAkB,cAAc;AAAA,QAChC,UAAU,UAAU,kBAAkB;AAAA,MACxC,CAA0C;AAAA,IAC5C;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY,QAAwC;AACxD,UAAM,SAAS,MAAM,KAAK,kBAAkB,KAAK;AACjD,UAAM,KAAK,KAAK,uBAAuB,MAAM;AAC7C,SAAK,UAAU,EAAE;AAEjB,QAAI,CAAC,GAAG,QAAQ,OAAO;AACrB,YAAM,IAAI,MAAM,+CAA+C;AAAA,IACjE;AAIA,UAAM,oBAAoB,IAAI,IAAI,GAAG,QAAQ,MAAM,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC;AAC3E,UAAM,gBAAgB,OAAO;AAAA,MAC3B,OAAO,QAAQ,MAAM,EAAE;AAAA,QACrB,CAAC,CAAC,MAAM,IAAI,MAAM,IAAI,eAAe,IAAI,KAAK,kBAAkB,IAAI,IAAI;AAAA,MAC1E;AAAA,IACF;AAEA,SAAK,SAAS;AAEd,WAAO;AAAA,EACT;AAAA,EAEQ,uBAAuB,QAAuD;AACpF,UAAM,WAA6B,CAAC;AAEpC,eAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,UAAI,CAAC,IAAI,eAAe,IAAI,GAAG;AAC7B,aAAK,QAAQ,MAAM,EAAE,MAAM,KAAK,GAAG,oDAAoD;AACvF;AAAA,MACF;AAEA,YAAM,EAAE,YAAY,gBAAgB,YAAY,IAAI;AACpD,UAAI;AACF,cAAM,aAAa,IAAI;AAAA,UACrB;AAAA,QACF;AAEA,iBAAS,KAAK;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,UACA,MAAM;AAAA,QACR,CAAC;AAAA,MACH,SAAS,GAAG;AACV,aAAK,QAAQ,MAAM,EAAE,MAAM,KAAK,GAAG,oDAAoD;AACvF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,QACP,OAAO,KAAK,iBAAiB,SAAS;AAAA,QACtC,OAAO;AAAA,MACT;AAAA,MACA,UAAU,UAAU,eAAe;AAAA,IACrC;AAAA,EACF;AAAA,EAEA,MAAM,mBAAmB,eAAsC;AAC7D,UAAM,UAAU,UAAU,sBAAsB;AAChD,SAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,QACP,cAAc;AAAA,MAChB;AAAA,MACA,UAAU;AAAA,IACZ,CAAiC;AACjC,SAAK,eAAe;AAAA,EACtB;AAAA,EAEA,cAAc,EAAE,WAAW,GAA0C;AACnE,UAAM,UAAmD,CAAC;AAE1D,SAAK,iBAAiB,SAAS,aAAa;AAC5C,YAAQ,cAAc,gBAAgB,UAAU;AAIhD,SAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,MACT,UAAU,UAAU,iBAAiB;AAAA,IACvC,CAAC;AAAA,EACH;AAAA,EAEA,UAAU,OAAyB;AACjC,eAAW,KAAK,KAAK,cAAc,KAAK,GAAG;AACzC,iBAAW,MAAM,KAAK,QAAQ,MAAM,EAAE,KAAK,MAAM,GAAG;AAClD,aAAK,UAAU;AAAA,UACb,MAAM;AAAA,UACN,OAAO,OAAO,KAAK,GAAG,KAAK,MAAM,EAAE,SAAS,QAAQ;AAAA,QACtD,CAA0C;AAE1C,aAAK,oBAAqB,GAAG,oBAAoB,GAAG,aAAc;AAAA,MACpE;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,cAA6B;AACjC,QAAI,KAAK,mBAAmB,KAAK;AAE/B,WAAK,UAAU;AAAA,QACb,MAAM;AAAA,MACR,CAA0C;AAC1C,WAAK,mBAAmB;AAAA,IAC1B;AAAA,EACF;AAAA,EAEA,MAAM,aAA4B;AAChC,SAAK,UAAU;AAAA,MACb,MAAM;AAAA,IACR,CAAyC;AACzC,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEA,MAAM,cAAc,cAA4D;AAC9E,UAAM,SAAS,KAAK,eAAe,EAAE,cAAc,eAAe,KAAK,CAAC;AACxE,SAAK,0BAA0B;AAC/B,WAAO,OAAO,QAAQ;AAAA,EACxB;AAAA,EAEA,MAAM,YAA2B;AAC/B,SAAK,UAAU;AAAA,MACb,MAAM;AAAA,IACR,CAAkC;AAAA,EACpC;AAAA,EAEA,MAAM,SAAS,UAAoE;AACjF,SAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN,eAAe;AAAA,MACf,SAAS,SAAS;AAAA,MAClB,cAAc,SAAS;AAAA,IACzB,CAA4C;AAAA,EAC9C;AAAA;AAAA;AAAA,EAIA,eACE,OACA,YAAoB,IACK;AACzB,UAAM,eAAwC,CAAC;AAC/C,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,UAAI,UAAU,QAAW;AACvB,qBAAa,GAAG,IAAI;AAAA,MACtB;AAAA,IACF;AAEA,QAAI,aAAa,SAAS,OAAO,aAAa,UAAU,UAAU;AAChE,YAAM,gBACJ,aAAa,MAAM,MAAM,GAAG,SAAS,KAAK,aAAa,MAAM,SAAS,YAAY,WAAM;AAC1F,aAAO,EAAE,GAAG,cAAc,OAAO,cAAc;AAAA,IACjD;AACA,QACE,aAAa,SACb,OAAO,aAAa,UAAU,YAC9B,MAAM,SAAS,wBACf;AACA,YAAM,iBACJ,aAAa,MAAM,MAAM,GAAG,SAAS,KAAK,aAAa,MAAM,SAAS,YAAY,WAAM;AAC1F,aAAO,EAAE,GAAG,cAAc,OAAO,eAAe;AAAA,IAClD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,eAAmC;AAC/C,UAAM,UAAkC;AAAA,MACtC,cAAc;AAAA,IAChB;AAEA,QAAI,KAAK,iBAAiB,SAAS,SAAS;AAI1C,UAAI,KAAK,iBAAiB,SAAS,YAAY;AAC7C,gBAAQ,gBAAgB,UAAU,KAAK,iBAAiB,SAAS,UAAU;AAAA,MAC7E,WAAW,KAAK,iBAAiB,SAAS,QAAQ;AAChD,gBAAQ,SAAS,IAAI,KAAK,iBAAiB,SAAS;AAAA,MACtD,OAAO;AACL,cAAM,IAAI,MAAM,6CAA6C;AAAA,MAC/D;AAAA,IACF,OAAO;AACL,cAAQ,gBAAgB,UAAU,KAAK,iBAAiB,SAAS,MAAM;AACvE,cAAQ,aAAa,IAAI;AAAA,IAC3B;AAEA,UAAM,MAAM,eAAe;AAAA,MACzB,SAAS,KAAK,iBAAiB,SAAS;AAAA,MACxC,OAAO,KAAK,iBAAiB,SAAS;AAAA,MACtC,SAAS,KAAK,iBAAiB,SAAS;AAAA,MACxC,YAAY,KAAK,iBAAiB,SAAS;AAAA,MAC3C,iBAAiB,KAAK,iBAAiB,SAAS;AAAA,IAClD,CAAC;AAED,SAAK,QAAQ,MAAM,wCAAwC,GAAG,EAAE;AAEhE,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,KAAK,IAAI,UAAU,KAAK,EAAE,QAAQ,CAAC;AACzC,UAAI,UAAU;AAEd,YAAM,UAAU,WAAW,MAAM;AAC/B,WAAG,MAAM;AACT,eAAO,IAAI,MAAM,8BAA8B,CAAC;AAAA,MAClD,GAAG,KAAK,iBAAiB,SAAS,YAAY,SAAS;AAEvD,SAAG,KAAK,QAAQ,MAAM;AACpB,YAAI,CAAC,QAAS;AACd,kBAAU;AACV,qBAAa,OAAO;AACpB,gBAAQ,EAAE;AAAA,MACZ,CAAC;AAED,SAAG,KAAK,SAAS,MAAM;AACrB,YAAI,CAAC,QAAS;AACd,kBAAU;AACV,qBAAa,OAAO;AACpB,eAAO,IAAI,MAAM,uCAAuC,CAAC;AAAA,MAC3D,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,UAAU,QAAoC;AAClD,QAAI,eAAe;AACnB,QAAI,aAAa;AACjB,QAAI,SAA2B;AAC/B,UAAM,aAAa,KAAK,iBAAiB,SAAS,YAAY;AAE9D,UAAM,YAAY,YAAY;AAC5B,WAAK,QAAQ;AAAA,QACX;AAAA,UACE,oBAAoB,KAAK,iBAAiB,SAAS;AAAA,QACrD;AAAA,QACA;AAAA,MACF;AAEA,YAAM,SAAkC,CAAC;AAGzC,aAAO,KAAK,KAAK,yBAAyB,CAAC;AAG3C,UAAI,OAAO,KAAK,KAAK,MAAM,EAAE,SAAS,GAAG;AACvC,eAAO,KAAK,KAAK,uBAAuB,KAAK,MAAM,CAAC;AAAA,MACtD;AAGA,YAAM,UAAU,KAAK,QAAQ,KAAK;AAAA,QAChC,qBAAqB;AAAA,QACrB,qBAAqB;AAAA,QACrB,qBAAqB;AAAA,MACvB,CAAC;AAED,YAAM,aAAa,KAAK;AACxB,WAAK,gBAAgB,IAAI,IAAI,kBAAkB;AAC/C,aAAO,KAAK,GAAG,KAAK,0BAA0B,OAAO,CAAC;AAEtD,UAAI;AACF,mBAAW,MAAM,QAAQ;AACvB,eAAK,KAAK,8BAA8B,EAAE;AAC1C,iBAAQ,KAAK,KAAK,UAAU,EAAE,CAAC;AAAA,QACjC;AAAA,MACF,SAAS,OAAO;AACd,aAAK,gBAAgB;AACrB,cAAM,IAAI,mBAAmB;AAAA,UAC3B,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AAEA,WAAK,QAAQ,MAAM,oCAAoC;AAEvD,WAAK,KAAK,uBAAuB,CAAC,CAAwC;AAAA,IAC5E;AAEA,mBAAe;AACf,WAAO,CAAC,KAAK,WAAW,CAAC,OAAO,SAAS;AACvC,WAAK,QAAQ,MAAM,sDAAsD;AACzE,eAAS,MAAM,KAAK,aAAa;AACjC,UAAI,OAAO,QAAS;AAEpB,UAAI;AACF,YAAI,cAAc;AAChB,gBAAM,UAAU;AAChB,cAAI,OAAO,QAAS;AACpB,uBAAa;AAAA,QACf;AAEA,cAAM,KAAK,MAAM,MAAM;AACvB,YAAI,OAAO,QAAS;AAAA,MACtB,SAAS,OAAO;AACd,YAAI,CAAC,WAAW,KAAK,GAAG;AACtB,eAAK,UAAU,EAAE,OAAuB,aAAa,MAAM,CAAC;AAC5D,gBAAM;AAAA,QACR;AAEA,YAAI,eAAe,KAAK,CAAC,MAAM,WAAW;AACxC,eAAK,UAAU,EAAE,OAAuB,aAAa,MAAM,CAAC;AAC5D,gBAAM;AAAA,QACR;AAEA,YAAI,eAAe,YAAY;AAC7B,eAAK,UAAU,EAAE,OAAuB,aAAa,MAAM,CAAC;AAC5D,gBAAM,IAAI,mBAAmB;AAAA,YAC3B,SAAS,+CAA+C,UAAU;AAAA,YAClE,SAAS;AAAA,cACP,MAAM;AAAA,cACN,WAAW;AAAA,YACb;AAAA,UACF,CAAC;AAAA,QACH;AAEA,aAAK,UAAU,EAAE,OAAuB,aAAa,KAAK,CAAC;AAC3D,cAAM,gBACJ,eAAe,IACX,kCACA,KAAK,iBAAiB,SAAS,YAAY;AACjD,aAAK,QAAQ;AAAA,UACX;AAAA,YACE,SAAS;AAAA,YACT;AAAA,YACA;AAAA,UACF;AAAA,UACA,sDAAsD,gBAAgB,GAAI;AAAA,QAC5E;AAEA,cAAM,MAAM,aAAa;AACzB;AAAA,MACF;AAEA,qBAAe;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,MAAc,MAAM,QAAkC;AACpD,UAAM,gBAAgB,OAAO,WAAuC;AAClE,YAAM,cAAc,IAAI,OAAa;AACrC,aAAO,iBAAiB,SAAS,MAAM,YAAY,QAAQ,CAAC;AAE5D,aAAO,CAAC,KAAK,WAAW,OAAO,eAAe,UAAU,QAAQ,CAAC,OAAO,SAAS;AAC/E,YAAI;AACF,gBAAM,QAAQ,MAAM,QAAQ,KAAK,CAAC,KAAK,eAAe,IAAI,GAAG,YAAY,KAAK,CAAC;AAC/E,cAAI,OAAO,WAAW,YAAY,QAAQ,UAAU,QAAW;AAC7D;AAAA,UACF;AAEA,cAAI,MAAM,SAAS,6BAA6B;AAC9C,iBAAK,QAAQ,MAAM,eAAe,KAAK,UAAU,KAAK,eAAe,KAAK,CAAC,CAAC,EAAE;AAAA,UAChF;AAEA,eAAK,KAAK,8BAA8B,KAAK;AAC7C,iBAAO,KAAK,KAAK,UAAU,KAAK,CAAC;AAAA,QACnC,SAAS,OAAO;AACd;AAAA,QACF;AAAA,MACF;AAEA,aAAO,MAAM;AAAA,IACf;AAEA,UAAM,gBAAgB,IAAI,OAAqB;AAE/C,WAAO,UAAU,CAAC,UAAU;AAC1B,oBAAc,QAAQ,IAAI,mBAAmB,EAAE,SAAS,MAAM,QAAQ,CAAC,CAAC;AAAA,IAC1E;AACA,WAAO,UAAU,MAAM;AACrB,oBAAc,QAAQ;AAAA,IACxB;AAEA,WAAO,YAAY,CAAC,YAA0B;AAC5C,YAAM,QAA+B,KAAK,MAAM,QAAQ,IAAc;AAEtE,WAAK,KAAK,gCAAgC,KAAK;AAC/C,WAAK,QAAQ,MAAM,eAAe,KAAK,UAAU,KAAK,eAAe,KAAK,CAAC,CAAC,EAAE;AAE9E,cAAQ,MAAM,MAAM;AAAA,QAClB,KAAK;AACH,eAAK,oCAAoC,KAAK;AAC9C;AAAA,QACF,KAAK;AACH,eAAK,oCAAoC,KAAK;AAC9C;AAAA,QACF,KAAK;AACH,eAAK,sBAAsB,KAAK;AAChC;AAAA,QACF,KAAK;AACH,eAAK,8BAA8B,KAAK;AACxC;AAAA,QACF,KAAK;AACH,eAAK,8BAA8B,KAAK;AACxC;AAAA,QACF,KAAK;AACH,eAAK,8BAA8B,KAAK;AACxC;AAAA,QACF,KAAK;AACH,eAAK,uDAAuD,KAAK;AACjE;AAAA,QACF,KAAK;AACH,eAAK,oDAAoD,KAAK;AAC9D;AAAA,QACF,KAAK;AACH,eAAK,+BAA+B,KAAK;AACzC;AAAA,QACF,KAAK;AACH,eAAK,8BAA8B,KAAK;AACxC;AAAA,QACF,KAAK;AACH,eAAK,mCAAmC,KAAK;AAC7C;AAAA,QACF,KAAK;AACH,eAAK,yBAAyB,KAAK;AACnC;AAAA,QACF,KAAK;AACH,eAAK,kCAAkC,KAAK;AAC5C;AAAA,QACF,KAAK;AACH,eAAK,wBAAwB,KAAK;AAClC;AAAA,QACF,KAAK;AACH,eAAK,6BAA6B,KAAK;AACvC;AAAA,QACF,KAAK;AACH,eAAK,mBAAmB,KAAK;AAC7B;AAAA,QACF,KAAK;AACH,eAAK,YAAY,KAAK;AACtB;AAAA,QACF;AACE,eAAK,QAAQ,MAAM,oBAAoB,MAAM,IAAI,EAAE;AACnD;AAAA,MACJ;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,KAAK,CAAC,EAAE,OAAO,MAAM,cAAc,MAAM,CAAC;AAEhE,UAAM,SAAS,KAAK,KAAK,CAAC,EAAE,OAAO,MAAM;AACvC,YAAM,eAAe,IAAI,QAAc,CAAC,YAAY;AAClD,eAAO,iBAAiB,SAAS,MAAM;AACrC,kBAAQ;AAAA,QACV,CAAC;AAAA,MACH,CAAC;AAED,aAAO,QAAQ,KAAK,CAAC,cAAc,OAAO,YAAY,CAAC;AAAA,IACzD,CAAC;AAED,UAAM,oBAAoB,KAAK,KAAK,OAAO,EAAE,OAAO,MAAM;AACxD,YAAM,MAAM,KAAK,iBAAiB,SAAS,oBAAoB,EAAE,OAAO,CAAC;AACzE,aAAO,IAAI,mBAAmB;AAAA,QAC5B,SAAS;AAAA,MACX,CAAC;AAAA,IACH,CAAC;AAED,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,KAAK,CAAC,OAAO,QAAQ,SAAS,QAAQ,kBAAkB,MAAM,CAAC;AAE5F,UAAI,kBAAkB,QAAQ,KAAK,mBAAmB;AACpD,cAAM,KAAK,kBAAkB,SAAS;AAAA,MACxC;AAEA,UAAI,kBAAkB,OAAO;AAC3B,cAAM;AAAA,MACR;AAAA,IACF,UAAE;AACA,YAAM,cAAc,CAAC,QAAQ,UAAU,iBAAiB,GAAG,GAAI;AAC/D,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ;AACZ,UAAM,MAAM;AACZ,SAAK,UAAU;AACf,UAAM,KAAK;AAAA,EACb;AAAA,EAEQ,oCACN,QACM;AACN,SAAK,KAAK,wBAAwB,CAAC,CAAgC;AAAA,EACrE;AAAA,EAEQ,oCACN,QACM;AACN,SAAK,KAAK,wBAAwB;AAAA,MAChC,0BAA0B,KAAK,iBAAiB,SAAS,4BAA4B;AAAA,IACvF,CAAgC;AAAA,EAClC;AAAA,EAEQ,sBAAsB,OAA6C;AACzE,QAAI,CAAC,MAAM,SAAS,IAAI;AACtB,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAEA,SAAK,oBAAoB;AAAA,MACvB,gBAAgB,OAAO,oBAA2C;AAAA,MAClE,iBAAiB,OAAO,oBAAsC;AAAA,MAC9D,UAAU,oBAAI,IAAI;AAAA,MAClB,UAAU,IAAI,OAAO;AAAA,MACrB,mBAAmB,KAAK,IAAI;AAAA,IAC9B;AAEA,QAAI,CAAC,MAAM,SAAS,YAAY,CAAC,MAAM,SAAS,SAAS,gBAAiB;AAE1E,UAAM,SAAS,KAAK,uBAAuB,MAAM,SAAS,SAAS,eAAe;AAClF,QAAI,QAAQ;AACV,aAAO,KAAK,uBAAuB,MAAM,SAAS,SAAS,eAAe;AAG1E,WAAK,uBAAuB,MAAM,SAAS,EAAE,IAAI;AAAA,IACnD;AAMA,SAAK,KAAK,sBAAsB;AAAA,MAC9B,eAAe,KAAK,kBAAkB,eAAe,OAAO;AAAA,MAC5D,gBAAgB,KAAK,kBAAkB,gBAAgB,OAAO;AAAA,MAC9D,eAAe;AAAA,IACjB,CAA2B;AAAA,EAC7B;AAAA,EAEQ,8BAA8B,OAAqD;AACzF,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,QAAI,CAAC,MAAM,KAAK,MAAM;AACpB,YAAM,IAAI,MAAM,sBAAsB;AAAA,IACxC;AAEA,QAAI,CAAC,MAAM,aAAa;AACtB,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAEA,UAAM,WAAW,MAAM,KAAK;AAC5B,UAAM,aAAa,MAAM;AAEzB,QAAI,aAAa,WAAW;AAE1B,WAAK,oBAAoB,UAAU;AACnC,WAAK,0BAA0B;AAC/B;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,8BAA8B,OAAqD;AACzF,QAAI,CAAC,MAAM,KAAK,IAAI;AAClB,YAAM,IAAI,MAAM,oBAAoB;AAAA,IACtC;AAEA,QAAI;AACF,WAAK,cAAc,OAAO,MAAM,kBAAkB,wBAAwB,MAAM,IAAI,CAAC;AAAA,IACvF,SAAS,OAAO;AACd,WAAK,QAAQ,MAAM,EAAE,OAAO,QAAQ,MAAM,KAAK,GAAG,GAAG,oCAAoC;AAAA,IAC3F;AAEA,UAAM,MAAM,KAAK,kBAAkB,MAAM,KAAK,EAAE;AAChD,QAAI,KAAK;AACP,UAAI,QAAQ;AACZ,aAAO,KAAK,kBAAkB,MAAM,KAAK,EAAE;AAAA,IAC7C;AAAA,EACF;AAAA,EAEQ,8BAA8B,OAAqD;AACzF,QAAI,CAAC,MAAM,SAAS;AAClB,YAAM,IAAI,MAAM,oBAAoB;AAAA,IACtC;AAEA,QAAI;AACF,WAAK,cAAc,OAAO,MAAM,OAAO;AAAA,IACzC,SAAS,OAAO;AACd,WAAK,QAAQ,MAAM,EAAE,OAAO,QAAQ,MAAM,QAAQ,GAAG,oCAAoC;AAAA,IAC3F;AAEA,UAAM,MAAM,KAAK,kBAAkB,MAAM,OAAO;AAChD,QAAI,KAAK;AACP,UAAI,QAAQ;AACZ,aAAO,KAAK,kBAAkB,MAAM,OAAO;AAAA,IAC7C;AAAA,EACF;AAAA,EAEQ,uDACN,OACM;AACN,UAAM,aAAa,KAAK,cAAc,IAAI,MAAM,OAAO;AACvD,QAAI,CAAC,YAAY;AACf;AAAA,IACF;AAEA,UAAM,OAAO,WAAW;AACxB,QAAI,gBAAgB,IAAI,aAAa;AACnC,WAAK,QAAQ,KAAK,MAAM,UAAU;AAAA,IACpC,OAAO;AACL,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAEA,SAAK,KAAK,uCAAuC;AAAA,MAC/C,QAAQ,MAAM;AAAA,MACd,YAAY,MAAM;AAAA,MAClB,SAAS;AAAA,IACX,CAAoC;AAAA,EACtC;AAAA,EAEQ,oDACN,OACM;AACN,SAAK,QAAQ;AAAA,MACX,EAAE,OAAO,MAAM,MAAM;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,+BAA+B,OAAsD;AAC3F,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,UAAM,SAAS,MAAM;AACrB,UAAM,WAAW,MAAM,KAAK;AAC5B,UAAM,aAAa,MAAM;AAEzB,QAAI,aAAa,SAAS;AACxB,WAAK,oBAAoB,UAAU;AACnC,UAAI,KAAK,0BAA0B,GAAG;AACpC,aAAK,QAAQ;AAAA,UACX,EAAE,SAAS,KAAK,wBAAwB;AAAA,UACxC;AAAA,QACF;AACA,aAAK,0BAA0B;AAAA,MACjC;AAEA,YAAM,iBAAoC;AAAA,QACxC,WAAW;AAAA,QACX,aAAa,OAAO,oBAA4B;AAAA,QAChD,cAAc,OAAO,oBAAgC;AAAA,QACrD,iBAAiB;AAAA,MACnB;AAEA,WAAK,kBAAkB,eAAe,MAAM;AAAA,QAC1C,WAAW;AAAA,QACX,YAAY,eAAe,YAAY,OAAO;AAAA,QAC9C,aAAa,eAAe,aAAa,OAAO;AAAA,MAClD,CAAC;AAED,WAAK,kBAAkB,SAAS,IAAI,QAAQ,cAAc;AAC1D,WAAK,kBAAkB,uBAAuB,KAAK,IAAI;AACvD;AAAA,IACF,OAAO;AACL,WAAK,UAAU;AACf,UAAI,KAAK,4BAA4B,GAAG;AACtC,aAAK,QAAQ,KAAK,EAAE,WAAW,GAAG,sDAAsD;AAAA,MAC1F;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,8BAA8B,OAAqD;AACzF,QAAI,MAAM,KAAK,SAAS,QAAQ;AAC9B;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAAA,EAGF;AAAA,EAEQ,mCACN,OACM;AACN,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,UAAM,SAAS,MAAM;AACrB,UAAM,QAAQ,MAAM;AAIpB,UAAM,iBAAiB,KAAK,kBAAkB,SAAS,IAAI,MAAM;AACjE,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAI,MAAM,2BAA2B;AAAA,IAC7C,OAAO;AACL,qBAAe,YAAY,MAAM,KAAK;AACtC,qBAAe,mBAAmB;AAAA,IACpC;AAAA,EACF;AAAA,EAEQ,yBAAyB,OAAgD;AAC/E,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,UAAM,iBAAiB,KAAK,kBAAkB,SAAS,IAAI,MAAM,OAAO;AACxE,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAI,MAAM,2BAA2B;AAAA,IAC7C;AAEA,UAAM,eAAe,KAAK,MAAM,KAAK;AACrC,UAAM,MAAM,aAAa;AACzB,UAAM,QAAQ,IAAI,WAAW,GAAG;AAChC,aAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,YAAM,CAAC,IAAI,aAAa,WAAW,CAAC;AAAA,IACtC;AAEA,mBAAe,aAAa;AAAA,MAC1B,IAAI;AAAA,QACF,IAAI,WAAW,MAAM,MAAM;AAAA,QAC3B,UAAU;AAAA,QACV,UAAU;AAAA,QACV,MAAM,SAAS;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,kCACN,QACM;AACN,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAAA,EACF;AAAA,EAEQ,wBAAwB,QAAgD;AAC9E,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAAA,EACF;AAAA,EAEQ,6BAA6B,OAAoD;AACvF,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,UAAM,SAAS,MAAM,KAAK;AAC1B,UAAM,WAAW,MAAM,KAAK;AAE5B,QAAI,aAAa,iBAAiB;AAChC,YAAM,OAAO,MAAM;AACnB,UAAI,CAAC,KAAK,WAAW,CAAC,KAAK,QAAQ,CAAC,KAAK,WAAW;AAClD,cAAM,IAAI,MAAM,6BAA6B;AAAA,MAC/C;AACA,WAAK,kBAAkB,gBAAgB,MAAM;AAAA,QAC3C,QAAQ,KAAK;AAAA,QACb,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,MACb,CAAqB;AAAA,IACvB,WAAW,aAAa,WAAW;AACjC,YAAM,iBAAiB,KAAK,kBAAkB,SAAS,IAAI,MAAM;AACjE,UAAI,CAAC,gBAAgB;AACnB;AAAA,MACF;AAEA,qBAAe,YAAY,MAAM;AACjC,qBAAe,aAAa,MAAM;AAAA,IACpC;AAAA,EACF;AAAA,EAEQ,mBAAmB,QAA2C;AAjvCxE;AAkvCI,QAAI,CAAC,KAAK,mBAAmB;AAG3B;AAAA,IACF;AAEA,UAAM,mBAAmB,KAAK,kBAAkB;AAChD,UAAM,sBAAsB,KAAK,kBAAkB;AAEnD,SAAK,QAAQ;AAAA,MACX;AAAA,QACE,cAAc,KAAK,kBAAkB,SAAS;AAAA,MAChD;AAAA,MACA;AAAA,IACF;AAEA,eAAW,cAAc,KAAK,kBAAkB,SAAS,OAAO,GAAG;AACjE,iBAAW,YAAY,MAAM;AAC7B,iBAAW,aAAa,MAAM;AAAA,IAChC;AAEA,SAAK,kBAAkB,gBAAgB,MAAM;AAC7C,SAAK,kBAAkB,eAAe,MAAM;AAE5C,eAAW,UAAU,KAAK,kBAAkB,SAAS,KAAK,GAAG;AAC3D,YAAM,aAAa,KAAK,cAAc,IAAI,MAAM;AAChD,UAAI,cAAc,WAAW,gBAAgB,IAAI,aAAa;AAC5D,mBAAW,KAAK,QAAQ,KAAK,KAAK,kBAAkB,SAAS,IAAI,MAAM,EAAG,eAAe;AAAA,MAC3F;AAAA,IACF;AAEA,SAAK,kBAAkB,SAAS,QAAQ;AACxC,SAAK,oBAAoB;AAGzB,UAAM,QAAQ,OAAO,SAAS;AAC9B,UAAM,OAAO,sBAAsB,sBAAsB,mBAAmB;AAC5E,UAAM,YAAY,KAAK,IAAI,IAAI,oBAAoB;AAEnD,UAAM,kBAAgD;AAAA,MACpD,MAAM;AAAA,MACN,WAAW,mBAAmB;AAAA;AAAA,MAC9B,WAAW,OAAO,SAAS,MAAM;AAAA,MACjC;AAAA,MACA;AAAA,MACA,WAAW,OAAO,SAAS,WAAW;AAAA,MACtC,OAAO;AAAA,MACP,cAAa,+BAAO,iBAAgB;AAAA,MACpC,eAAc,+BAAO,kBAAiB;AAAA,MACtC,cAAa,+BAAO,iBAAgB;AAAA,MACpC,iBAAiB,WAAW,MAAK,+BAAO,kBAAiB,KAAK,WAAW;AAAA,MACzE,mBAAmB;AAAA,QACjB,eAAa,oCAAO,wBAAP,mBAA4B,iBAAgB;AAAA,QACzD,cAAY,oCAAO,wBAAP,mBAA4B,gBAAe;AAAA,QACvD,aAAa;AAAA;AAAA,QACb,gBAAc,oCAAO,wBAAP,mBAA4B,kBAAiB;AAAA,QAC3D,uBAAqB,oCAAO,wBAAP,mBAA4B,yBAC7C;AAAA,UACE,eAAa,0CAAO,wBAAP,mBAA4B,0BAA5B,mBAAmD,iBAAgB;AAAA,UAChF,cAAY,0CAAO,wBAAP,mBAA4B,0BAA5B,mBAAmD,gBAAe;AAAA,UAC9E,eAAa,0CAAO,wBAAP,mBAA4B,0BAA5B,mBAAmD,iBAAgB;AAAA,QAClF,IACA;AAAA,MACN;AAAA,MACA,oBAAoB;AAAA,QAClB,cAAY,oCAAO,yBAAP,mBAA6B,gBAAe;AAAA,QACxD,eAAa,oCAAO,yBAAP,mBAA6B,iBAAgB;AAAA,QAC1D,aAAa;AAAA,MACf;AAAA,IACF;AAEA,SAAK,KAAK,qBAAqB,eAAe;AAAA,EAEhD;AAAA,EAEQ,YAAY,OAAmC;AACrD,QAAI,MAAM,MAAM,QAAQ,WAAW,qBAAqB,GAAG;AACzD;AAAA,IACF;AAEA,SAAK,QAAQ,MAAM,EAAE,OAAO,MAAM,MAAM,GAAG,uCAAuC;AAClF,SAAK,UAAU;AAAA,MACb,OAAO,IAAI,SAAS,MAAM,MAAM,SAAS;AAAA,QACvC,MAAM,MAAM;AAAA,QACZ,WAAW;AAAA,MACb,CAAC;AAAA,MACD,aAAa;AAAA,IACf,CAAC;AAAA,EAGH;AAAA,EAEQ,UAAU,EAAE,OAAO,YAAY,GAAiD;AAEtF,SAAK,KAAK,SAAS;AAAA,MACjB,WAAW,KAAK,IAAI;AAAA;AAAA,MAEpB,OAAO;AAAA,MACP;AAAA,MACA;AAAA,IACF,CAA2B;AAAA,EAC7B;AAAA,EAEA,CAAS,cAAc,OAA0C;AAC/D,UAAM;AAAA,EACR;AAAA,EAEQ,eAAe;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAIyB;AACvB,UAAM,SAAS,aAAa,IAAI,qBAAqB,EAAE,aAAa,CAAC;AACrE,QAAI,aAAa,cAAc;AAC7B,aAAO,eAAe;AAAA,IACxB;AAEA,UAAM,UAAU,UAAU,kBAAkB;AAC5C,QAAI,eAAe;AACjB,WAAK,uBAAuB,OAAO,IAAI;AAAA,IACzC;AAEA,UAAM,WAAsD,CAAC;AAC7D,QAAI,aAAc,UAAS,eAAe;AAC1C,QAAI,cAAe,UAAS,WAAW,EAAE,iBAAiB,QAAQ;AAElE,SAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN,UAAU;AAAA,MACV,UAAU,OAAO,KAAK,QAAQ,EAAE,SAAS,IAAI,WAAW;AAAA,IAC1D,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAEQ,oBAAoB,YAA0B;AACpD,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,UAAM,gBAA4C;AAAA,MAChD,eAAe,KAAK,kBAAkB,eAAe,OAAO;AAAA,MAC5D,gBAAgB,KAAK,kBAAkB,gBAAgB,OAAO;AAAA,MAC9D,eAAe;AAAA,IACjB;AAEA,UAAM,SAAS,KAAK,uBAAuB,UAAU;AACrD,QAAI,QAAQ;AACV,aAAO,KAAK,uBAAuB,UAAU;AAC7C,oBAAc,gBAAgB;AAC9B,UAAI,OAAO,QAAQ,MAAM;AACvB,aAAK,QAAQ,KAAK,EAAE,WAAW,GAAG,iCAAiC;AAAA,MACrE,OAAO;AACL,eAAO,QAAQ,QAAQ,aAAa;AAAA,MACtC;AAAA,IACF;AAEA,SAAK,QAAQ,MAAM,EAAE,WAAW,GAAG,mCAAmC;AACtE,SAAK,KAAK,sBAAsB,aAAa;AAAA,EAC/C;AACF;AAEA,SAAS,wBAAwB,MAA4C;AAC3E,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO;AAAA,QACL,IAAI,KAAK;AAAA,QACT,MAAM;AAAA,QACN,SAAS,KAAK;AAAA,QACd,MAAM,KAAK;AAAA,QACX,WAAW,KAAK;AAAA,MAClB;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,IAAI,KAAK;AAAA,QACT,MAAM;AAAA,QACN,SAAS,KAAK;AAAA,QACd,QAAQ,KAAK;AAAA,MACf;AAAA,IACF,KAAK;AACH,YAAM,OAAO,KAAK,SAAS,cAAc,WAAW,KAAK;AACzD,YAAM,cAAmC,CAAC;AAC1C,iBAAW,KAAK,KAAK,SAAS;AAC5B,YAAI,OAAO,MAAM,UAAU;AACzB,sBAAY,KAAK;AAAA,YACf,MAAM,SAAS,cAAc,SAAS;AAAA,YACtC,MAAM;AAAA,UACR,CAA+B;AAAA,QACjC,WAAW,EAAE,SAAS,iBAAiB;AAErC;AAAA,QACF,WAAW,EAAE,SAAS,iBAAiB;AACrC,cAAI,SAAS,QAAQ;AACnB,kBAAM,eAAe,OAAO,KAAK,mBAAmB,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,QAAQ;AACpF,wBAAY,KAAK;AAAA,cACf,MAAM;AAAA,cACN,OAAO;AAAA,YACT,CAAgC;AAAA,UAClC;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,QACL,IAAI,KAAK;AAAA,QACT,MAAM;AAAA,QACN;AAAA,QACA,SAAS;AAAA,MACX;AAAA,EACJ;AACF;AAEA,SAAS,wBAAwB,MAA4C;AAC3E,MAAI,CAAC,KAAK,IAAI;AACZ,UAAM,IAAI,MAAM,oBAAoB;AAAA,EACtC;AAEA,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO,IAAI,aAAa,OAAO;AAAA,QAC7B,IAAI,KAAK;AAAA,QACT,QAAQ,KAAK;AAAA,QACb,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,MACb,CAAC;AAAA,IACH,KAAK;AACH,aAAO,IAAI,mBAAmB,OAAO;AAAA,QACnC,IAAI,KAAK;AAAA,QACT,QAAQ,KAAK;AAAA,QACb,QAAQ,KAAK;AAAA,QACb,SAAS;AAAA,MACX,CAAC;AAAA,IACH,KAAK;AACH,YAAM,UAA6B,CAAC;AAEpC,YAAM,WAAW,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,UAAU,CAAC,KAAK,OAAO;AAC3E,iBAAW,KAAK,UAAU;AACxB,YAAI,EAAE,SAAS,UAAU,EAAE,SAAS,cAAc;AAChD,kBAAQ,KAAK,EAAE,IAAI;AAAA,QACrB;AAAA,MACF;AACA,aAAO,IAAI,YAAY,OAAO;AAAA,QAC5B,IAAI,KAAK;AAAA,QACT,MAAM,KAAK;AAAA,QACX;AAAA,MACF,CAAC;AAAA,EACL;AACF;AAEA,SAAS,oBAAoB,kBAA0B,GAAoB;AACzE,QAAM,YAAY,OAAO,MAAM,kBAAkB,WAAW;AAC5D,SAAO,IAAI,YAAY,OAAO;AAAA,IAC5B,IAAI,UAAU,oBAAoB;AAAA,IAClC,MAAM;AAAA,IACN,SAAS;AAAA,MACP;AAAA,QACE,MAAM;AAAA,QACN,OAAO;AAAA,UACL,IAAI;AAAA,YACF,IAAI,WAAW,UAAU,MAAM;AAAA,YAC/B;AAAA,YACA;AAAA,YACA,UAAU,SAAS;AAAA,UACrB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,SAAS,gBAAgB,YAAmD;AAC1E,MAAI,OAAO,eAAe,UAAU;AAClC,WAAO;AAAA,EACT;AAEA,OAAI,yCAAY,UAAS,YAAY;AACnC,WAAO,WAAW,SAAS;AAAA,EAC7B;AAEA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/realtime/realtime_model.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { metrics } from '@livekit/agents';\nimport {\n type APIConnectOptions,\n APIConnectionError,\n APIError,\n AudioByteStream,\n DEFAULT_API_CONNECT_OPTIONS,\n Future,\n Queue,\n Task,\n cancelAndWait,\n delay,\n isAPIError,\n llm,\n log,\n shortuuid,\n stream,\n} from '@livekit/agents';\nimport { Mutex } from '@livekit/mutex';\nimport type { AudioResampler } from '@livekit/rtc-node';\nimport { AudioFrame, combineAudioFrames } from '@livekit/rtc-node';\nimport type { GenerationCreatedEvent } from 'agents/dist/llm/realtime.js';\nimport { type MessageEvent, WebSocket } from 'ws';\nimport * as api_proto from './api_proto.js';\n\nconst SAMPLE_RATE = 24000;\nconst NUM_CHANNELS = 1;\nconst BASE_URL = 'https://api.openai.com/v1';\n\nconst MOCK_AUDIO_ID_PREFIX = 'lk_mock_audio_item_';\n\ninterface RealtimeOptions {\n model: api_proto.Model;\n voice: api_proto.Voice;\n temperature: number;\n toolChoice?: llm.ToolChoice;\n inputAudioTranscription?: api_proto.InputAudioTranscription | null;\n // TODO(shubhra): add inputAudioNoiseReduction\n turnDetection?: api_proto.TurnDetectionType | null;\n maxResponseOutputTokens?: number | 'inf';\n speed?: number;\n // TODO(shubhra): add openai tracing options\n apiKey?: string;\n baseURL: string;\n isAzure: boolean;\n azureDeployment?: string;\n entraToken?: string;\n apiVersion?: string;\n maxSessionDuration: number;\n // reset the connection after this many seconds if provided\n connOptions: APIConnectOptions;\n}\n\ninterface MessageGeneration {\n messageId: string;\n textChannel: stream.StreamChannel<string>;\n audioChannel: stream.StreamChannel<AudioFrame>;\n audioTranscript: string;\n}\n\ninterface ResponseGeneration {\n messageChannel: stream.StreamChannel<llm.MessageGeneration>;\n functionChannel: stream.StreamChannel<llm.FunctionCall>;\n messages: Map<string, MessageGeneration>;\n\n /** @internal */\n _doneFut: Future;\n /** @internal */\n _createdTimestamp: number;\n /** @internal */\n _firstTokenTimestamp?: number;\n}\n\nclass CreateResponseHandle {\n instructions?: string;\n doneFut: Future<llm.GenerationCreatedEvent>;\n // TODO(shubhra): add timeout\n constructor({ instructions }: { instructions?: string }) {\n this.instructions = instructions;\n this.doneFut = new Future();\n }\n}\n\n// default values got from a \"default\" session from their API\nconst DEFAULT_FIRST_RETRY_INTERVAL_MS = 100;\nconst DEFAULT_TEMPERATURE = 0.8;\nconst DEFAULT_TURN_DETECTION: api_proto.TurnDetectionType = {\n type: 'server_vad',\n threshold: 0.5,\n prefix_padding_ms: 300,\n silence_duration_ms: 200,\n create_response: true,\n interrupt_response: true,\n};\nconst DEFAULT_INPUT_AUDIO_TRANSCRIPTION: api_proto.InputAudioTranscription = {\n model: 'gpt-4o-mini-transcribe',\n};\nconst DEFAULT_TOOL_CHOICE: llm.ToolChoice = 'auto';\nconst DEFAULT_MAX_RESPONSE_OUTPUT_TOKENS: number | 'inf' = 'inf';\n\nconst AZURE_DEFAULT_INPUT_AUDIO_TRANSCRIPTION: api_proto.InputAudioTranscription = {\n model: 'whisper-1',\n};\n\nconst AZURE_DEFAULT_TURN_DETECTION: api_proto.TurnDetectionType = {\n type: 'server_vad',\n threshold: 0.5,\n prefix_padding_ms: 300,\n silence_duration_ms: 200,\n create_response: true,\n};\n\nconst DEFAULT_MAX_SESSION_DURATION = 20 * 60 * 1000; // 20 minutes\n\nconst DEFAULT_REALTIME_MODEL_OPTIONS = {\n model: 'gpt-realtime',\n voice: 'marin',\n temperature: DEFAULT_TEMPERATURE,\n inputAudioTranscription: DEFAULT_INPUT_AUDIO_TRANSCRIPTION,\n turnDetection: DEFAULT_TURN_DETECTION,\n toolChoice: DEFAULT_TOOL_CHOICE,\n maxResponseOutputTokens: DEFAULT_MAX_RESPONSE_OUTPUT_TOKENS,\n maxSessionDuration: DEFAULT_MAX_SESSION_DURATION,\n connOptions: DEFAULT_API_CONNECT_OPTIONS,\n};\nexport class RealtimeModel extends llm.RealtimeModel {\n sampleRate = api_proto.SAMPLE_RATE;\n numChannels = api_proto.NUM_CHANNELS;\n inFrameSize = api_proto.IN_FRAME_SIZE;\n outFrameSize = api_proto.OUT_FRAME_SIZE;\n\n /* @internal */\n _options: RealtimeOptions;\n\n constructor(\n options: {\n model?: string;\n voice?: string;\n temperature?: number;\n toolChoice?: llm.ToolChoice;\n baseURL?: string;\n inputAudioTranscription?: api_proto.InputAudioTranscription | null;\n // TODO(shubhra): add inputAudioNoiseReduction\n turnDetection?: api_proto.TurnDetectionType | null;\n speed?: number;\n // TODO(shubhra): add openai tracing options\n azureDeployment?: string;\n apiKey?: string;\n entraToken?: string;\n apiVersion?: string;\n maxSessionDuration?: number;\n connOptions?: APIConnectOptions;\n } = {},\n ) {\n super({\n messageTruncation: true,\n turnDetection: options.turnDetection !== null,\n userTranscription: options.inputAudioTranscription !== null,\n autoToolReplyGeneration: false,\n });\n\n const isAzure = !!(options.apiVersion || options.entraToken || options.azureDeployment);\n\n if (options.apiKey === '' && !isAzure) {\n throw new Error(\n 'OpenAI API key is required, either using the argument or by setting the OPENAI_API_KEY environment variable',\n );\n }\n\n const apiKey = options.apiKey || process.env.OPENAI_API_KEY;\n\n if (!apiKey && !isAzure) {\n throw new Error(\n 'OpenAI API key is required, either using the argument or by setting the OPENAI_API_KEY environment variable',\n );\n }\n\n if (!options.baseURL && isAzure) {\n const azureEndpoint = process.env.AZURE_OPENAI_ENDPOINT;\n if (!azureEndpoint) {\n throw new Error(\n 'Missing Azure endpoint. Please pass base_url or set AZURE_OPENAI_ENDPOINT environment variable.',\n );\n }\n options.baseURL = `${azureEndpoint.replace(/\\/$/, '')}/openai`;\n }\n\n this._options = {\n ...DEFAULT_REALTIME_MODEL_OPTIONS,\n ...options,\n baseURL: options.baseURL || BASE_URL,\n apiKey,\n isAzure,\n model: options.model || DEFAULT_REALTIME_MODEL_OPTIONS.model,\n };\n }\n\n /**\n * Create a RealtimeModel instance configured for Azure OpenAI Service.\n *\n * @param azureDeployment - The name of your Azure OpenAI deployment.\n * @param azureEndpoint - The endpoint URL for your Azure OpenAI resource. If undefined, will attempt to read from the environment variable AZURE_OPENAI_ENDPOINT.\n * @param apiVersion - API version to use with Azure OpenAI Service. If undefined, will attempt to read from the environment variable OPENAI_API_VERSION.\n * @param apiKey - Azure OpenAI API key. If undefined, will attempt to read from the environment variable AZURE_OPENAI_API_KEY.\n * @param entraToken - Azure Entra authentication token. Required if not using API key authentication.\n * @param baseURL - Base URL for the API endpoint. If undefined, constructed from the azure_endpoint.\n * @param voice - Voice setting for audio outputs. Defaults to \"alloy\".\n * @param inputAudioTranscription - Options for transcribing input audio. Defaults to @see DEFAULT_INPUT_AUDIO_TRANSCRIPTION.\n * @param turnDetection - Options for server-based voice activity detection (VAD). Defaults to @see DEFAULT_SERVER_VAD_OPTIONS.\n * @param temperature - Sampling temperature for response generation. Defaults to @see DEFAULT_TEMPERATURE.\n * @param speed - Speed of the audio output. Defaults to 1.0.\n * @param maxResponseOutputTokens - Maximum number of tokens in the response. Defaults to @see DEFAULT_MAX_RESPONSE_OUTPUT_TOKENS.\n * @param maxSessionDuration - Maximum duration of the session in milliseconds. Defaults to @see DEFAULT_MAX_SESSION_DURATION.\n *\n * @returns A RealtimeModel instance configured for Azure OpenAI Service.\n *\n * @throws Error if required Azure parameters are missing or invalid.\n */\n static withAzure({\n azureDeployment,\n azureEndpoint,\n apiVersion,\n apiKey,\n entraToken,\n baseURL,\n voice = 'alloy',\n inputAudioTranscription = AZURE_DEFAULT_INPUT_AUDIO_TRANSCRIPTION,\n turnDetection = AZURE_DEFAULT_TURN_DETECTION,\n temperature = 0.8,\n speed,\n }: {\n azureDeployment: string;\n azureEndpoint?: string;\n apiVersion?: string;\n apiKey?: string;\n entraToken?: string;\n baseURL?: string;\n voice?: string;\n inputAudioTranscription?: api_proto.InputAudioTranscription;\n // TODO(shubhra): add inputAudioNoiseReduction\n turnDetection?: api_proto.TurnDetectionType;\n temperature?: number;\n speed?: number;\n }) {\n apiKey = apiKey || process.env.AZURE_OPENAI_API_KEY;\n if (!apiKey && !entraToken) {\n throw new Error(\n 'Missing credentials. Please pass one of `apiKey`, `entraToken`, or the `AZURE_OPENAI_API_KEY` environment variable.',\n );\n }\n\n apiVersion = apiVersion || process.env.OPENAI_API_VERSION;\n if (!apiVersion) {\n throw new Error(\n 'Must provide either the `apiVersion` argument or the `OPENAI_API_VERSION` environment variable',\n );\n }\n\n if (!baseURL) {\n azureEndpoint = azureEndpoint || process.env.AZURE_OPENAI_ENDPOINT;\n if (!azureEndpoint) {\n throw new Error(\n 'Missing Azure endpoint. Please pass the `azure_endpoint` parameter or set the `AZURE_OPENAI_ENDPOINT` environment variable.',\n );\n }\n baseURL = `${azureEndpoint.replace(/\\/$/, '')}/openai`;\n }\n\n return new RealtimeModel({\n voice,\n inputAudioTranscription,\n turnDetection,\n temperature,\n speed,\n apiKey,\n azureDeployment,\n apiVersion,\n entraToken,\n baseURL,\n });\n }\n\n session() {\n return new RealtimeSession(this);\n }\n\n async close() {\n return;\n }\n}\n\nfunction processBaseURL({\n baseURL,\n model,\n isAzure = false,\n azureDeployment,\n apiVersion,\n}: {\n baseURL: string;\n model: string;\n isAzure: boolean;\n azureDeployment?: string;\n apiVersion?: string;\n}): string {\n const url = new URL([baseURL, 'realtime'].join('/'));\n\n if (url.protocol === 'https:') {\n url.protocol = 'wss:';\n }\n\n // ensure \"/realtime\" is added if the path is empty OR \"/v1\"\n if (!url.pathname || ['', '/v1', '/openai'].includes(url.pathname.replace(/\\/$/, ''))) {\n url.pathname = url.pathname.replace(/\\/$/, '') + '/realtime';\n } else {\n url.pathname = url.pathname.replace(/\\/$/, '');\n }\n\n const queryParams: Record<string, string> = {};\n if (isAzure) {\n if (apiVersion) {\n queryParams['api-version'] = apiVersion;\n }\n if (azureDeployment) {\n queryParams['deployment'] = azureDeployment;\n }\n } else {\n queryParams['model'] = model;\n }\n\n for (const [key, value] of Object.entries(queryParams)) {\n url.searchParams.set(key, value);\n }\n\n return url.toString();\n}\n\n/**\n * A session for the OpenAI Realtime API.\n *\n * This class is used to interact with the OpenAI Realtime API.\n * It is responsible for sending events to the OpenAI Realtime API and receiving events from it.\n *\n * It exposes two more events:\n * - openai_server_event_received: expose the raw server events from the OpenAI Realtime API\n * - openai_client_event_queued: expose the raw client events sent to the OpenAI Realtime API\n */\nexport class RealtimeSession extends llm.RealtimeSession {\n private _tools: llm.ToolContext = {};\n private remoteChatCtx: llm.RemoteChatContext = new llm.RemoteChatContext();\n private messageChannel = new Queue<api_proto.ClientEvent>();\n private inputResampler?: AudioResampler;\n private instructions?: string;\n private oaiRealtimeModel: RealtimeModel;\n private currentGeneration?: ResponseGeneration;\n private responseCreatedFutures: { [id: string]: CreateResponseHandle } = {};\n\n private textModeRecoveryRetries: number = 0;\n\n private itemCreateFutures: { [id: string]: Future } = {};\n private itemDeleteFutures: { [id: string]: Future } = {};\n\n private updateChatCtxLock = new Mutex();\n private updateFuncCtxLock = new Mutex();\n\n // 100ms chunks\n private bstream = new AudioByteStream(SAMPLE_RATE, NUM_CHANNELS, SAMPLE_RATE / 10);\n\n private pushedDurationMs: number = 0;\n\n #logger = log();\n #task: Task<void>;\n #closed = false;\n\n constructor(realtimeModel: RealtimeModel) {\n super(realtimeModel);\n\n this.oaiRealtimeModel = realtimeModel;\n\n this.#task = Task.from(({ signal }) => this.#mainTask(signal));\n\n this.sendEvent(this.createSessionUpdateEvent());\n }\n\n sendEvent(command: api_proto.ClientEvent): void {\n this.messageChannel.put(command);\n }\n\n private createSessionUpdateEvent(): api_proto.SessionUpdateEvent {\n return {\n type: 'session.update',\n session: {\n model: this.oaiRealtimeModel._options.model,\n voice: this.oaiRealtimeModel._options.voice,\n input_audio_format: 'pcm16',\n output_audio_format: 'pcm16',\n modalities: ['text', 'audio'],\n turn_detection: this.oaiRealtimeModel._options.turnDetection,\n input_audio_transcription: this.oaiRealtimeModel._options.inputAudioTranscription,\n // TODO(shubhra): add inputAudioNoiseReduction\n temperature: this.oaiRealtimeModel._options.temperature,\n tool_choice: toOaiToolChoice(this.oaiRealtimeModel._options.toolChoice),\n max_response_output_tokens:\n this.oaiRealtimeModel._options.maxResponseOutputTokens === Infinity\n ? 'inf'\n : this.oaiRealtimeModel._options.maxResponseOutputTokens,\n // TODO(shubhra): add tracing options\n instructions: this.instructions,\n speed: this.oaiRealtimeModel._options.speed,\n },\n };\n }\n\n get chatCtx() {\n return this.remoteChatCtx.toChatCtx();\n }\n\n get tools() {\n return { ...this._tools } as llm.ToolContext;\n }\n\n async updateChatCtx(_chatCtx: llm.ChatContext): Promise<void> {\n const unlock = await this.updateChatCtxLock.lock();\n const events = this.createChatCtxUpdateEvents(_chatCtx);\n const futures: Future<void>[] = [];\n\n for (const event of events) {\n const future = new Future<void>();\n futures.push(future);\n\n if (event.type === 'conversation.item.create') {\n this.itemCreateFutures[event.item.id] = future;\n } else if (event.type == 'conversation.item.delete') {\n this.itemDeleteFutures[event.item_id] = future;\n }\n\n this.sendEvent(event);\n }\n\n if (futures.length === 0) {\n unlock();\n return;\n }\n\n try {\n // wait for futures to resolve or timeout\n await Promise.race([\n Promise.all(futures),\n delay(5000).then(() => {\n throw new Error('Chat ctx update events timed out');\n }),\n ]);\n } catch (e) {\n this.#logger.error((e as Error).message);\n throw e;\n } finally {\n unlock();\n }\n }\n\n private createChatCtxUpdateEvents(\n chatCtx: llm.ChatContext,\n addMockAudio: boolean = false,\n ): (api_proto.ConversationItemCreateEvent | api_proto.ConversationItemDeleteEvent)[] {\n const newChatCtx = chatCtx.copy();\n if (addMockAudio) {\n newChatCtx.items.push(createMockAudioItem());\n } else {\n // clean up existing mock audio items\n newChatCtx.items = newChatCtx.items.filter(\n (item) => !item.id.startsWith(MOCK_AUDIO_ID_PREFIX),\n );\n }\n\n const events: (\n | api_proto.ConversationItemCreateEvent\n | api_proto.ConversationItemDeleteEvent\n )[] = [];\n\n const diffOps = llm.computeChatCtxDiff(this.chatCtx, newChatCtx);\n for (const op of diffOps.toRemove) {\n events.push({\n type: 'conversation.item.delete',\n item_id: op,\n event_id: shortuuid('chat_ctx_delete_'),\n } as api_proto.ConversationItemDeleteEvent);\n }\n\n for (const [previousId, id] of diffOps.toCreate) {\n const chatItem = newChatCtx.getById(id);\n if (!chatItem) {\n throw new Error(`Chat item ${id} not found`);\n }\n events.push({\n type: 'conversation.item.create',\n item: livekitItemToOpenAIItem(chatItem),\n previous_item_id: previousId ?? undefined,\n event_id: shortuuid('chat_ctx_create_'),\n } as api_proto.ConversationItemCreateEvent);\n }\n return events;\n }\n\n async updateTools(_tools: llm.ToolContext): Promise<void> {\n const unlock = await this.updateFuncCtxLock.lock();\n const ev = this.createToolsUpdateEvent(_tools);\n this.sendEvent(ev);\n\n if (!ev.session.tools) {\n throw new Error('Tools are missing in the session update event');\n }\n\n // TODO(brian): these logics below are noops I think, leaving it here to keep\n // parity with the python but we should remove them later\n const retainedToolNames = new Set(ev.session.tools.map((tool) => tool.name));\n const retainedTools = Object.fromEntries(\n Object.entries(_tools).filter(\n ([name, tool]) => llm.isFunctionTool(tool) && retainedToolNames.has(name),\n ),\n );\n\n this._tools = retainedTools as llm.ToolContext;\n\n unlock();\n }\n\n private createToolsUpdateEvent(_tools: llm.ToolContext): api_proto.SessionUpdateEvent {\n const oaiTools: api_proto.Tool[] = [];\n\n for (const [name, tool] of Object.entries(_tools)) {\n if (!llm.isFunctionTool(tool)) {\n this.#logger.error({ name, tool }, \"OpenAI Realtime API doesn't support this tool type\");\n continue;\n }\n\n const { parameters: toolParameters, description } = tool;\n try {\n const parameters = llm.toJsonSchema(\n toolParameters,\n ) as unknown as api_proto.Tool['parameters'];\n\n oaiTools.push({\n name,\n description,\n parameters: parameters,\n type: 'function',\n });\n } catch (e) {\n this.#logger.error({ name, tool }, \"OpenAI Realtime API doesn't support this tool type\");\n continue;\n }\n }\n\n return {\n type: 'session.update',\n session: {\n model: this.oaiRealtimeModel._options.model,\n tools: oaiTools,\n },\n event_id: shortuuid('tools_update_'),\n };\n }\n\n async updateInstructions(_instructions: string): Promise<void> {\n const eventId = shortuuid('instructions_update_');\n this.sendEvent({\n type: 'session.update',\n session: {\n instructions: _instructions,\n },\n event_id: eventId,\n } as api_proto.SessionUpdateEvent);\n this.instructions = _instructions;\n }\n\n updateOptions({ toolChoice }: { toolChoice?: llm.ToolChoice }): void {\n const options: api_proto.SessionUpdateEvent['session'] = {};\n\n this.oaiRealtimeModel._options.toolChoice = toolChoice;\n options.tool_choice = toOaiToolChoice(toolChoice);\n\n // TODO(brian): add other options here\n\n this.sendEvent({\n type: 'session.update',\n session: options,\n event_id: shortuuid('options_update_'),\n });\n }\n\n pushAudio(frame: AudioFrame): void {\n for (const f of this.resampleAudio(frame)) {\n for (const nf of this.bstream.write(f.data.buffer)) {\n this.sendEvent({\n type: 'input_audio_buffer.append',\n audio: Buffer.from(nf.data.buffer).toString('base64'),\n } as api_proto.InputAudioBufferAppendEvent);\n // TODO(AJS-102): use frame.durationMs once available in rtc-node\n this.pushedDurationMs += (nf.samplesPerChannel / nf.sampleRate) * 1000;\n }\n }\n }\n\n async commitAudio(): Promise<void> {\n if (this.pushedDurationMs > 100) {\n // OpenAI requires at least 100ms of audio\n this.sendEvent({\n type: 'input_audio_buffer.commit',\n } as api_proto.InputAudioBufferCommitEvent);\n this.pushedDurationMs = 0;\n }\n }\n\n async clearAudio(): Promise<void> {\n this.sendEvent({\n type: 'input_audio_buffer.clear',\n } as api_proto.InputAudioBufferClearEvent);\n this.pushedDurationMs = 0;\n }\n\n async generateReply(instructions?: string): Promise<llm.GenerationCreatedEvent> {\n const handle = this.createResponse({ instructions, userInitiated: true });\n this.textModeRecoveryRetries = 0;\n return handle.doneFut.await;\n }\n\n async interrupt(): Promise<void> {\n this.sendEvent({\n type: 'response.cancel',\n } as api_proto.ResponseCancelEvent);\n }\n\n async truncate(_options: { messageId: string; audioEndMs: number }): Promise<void> {\n this.sendEvent({\n type: 'conversation.item.truncate',\n content_index: 0,\n item_id: _options.messageId,\n audio_end_ms: _options.audioEndMs,\n } as api_proto.ConversationItemTruncateEvent);\n }\n\n /// Truncates the data field of the event to the specified maxLength to avoid overwhelming logs\n /// with large amounts of base64 audio data.\n #loggableEvent(\n event: api_proto.ClientEvent | api_proto.ServerEvent,\n maxLength: number = 30,\n ): Record<string, unknown> {\n const untypedEvent: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(event)) {\n if (value !== undefined) {\n untypedEvent[key] = value;\n }\n }\n\n if (untypedEvent.audio && typeof untypedEvent.audio === 'string') {\n const truncatedData =\n untypedEvent.audio.slice(0, maxLength) + (untypedEvent.audio.length > maxLength ? '…' : '');\n return { ...untypedEvent, audio: truncatedData };\n }\n if (\n untypedEvent.delta &&\n typeof untypedEvent.delta === 'string' &&\n event.type === 'response.audio.delta'\n ) {\n const truncatedDelta =\n untypedEvent.delta.slice(0, maxLength) + (untypedEvent.delta.length > maxLength ? '…' : '');\n return { ...untypedEvent, delta: truncatedDelta };\n }\n return untypedEvent;\n }\n\n private async createWsConn(): Promise<WebSocket> {\n const headers: Record<string, string> = {\n 'User-Agent': 'LiveKit-Agents-JS',\n };\n\n if (this.oaiRealtimeModel._options.isAzure) {\n // Microsoft API has two ways of authentication\n // 1. Entra token set as `Bearer` token\n // 2. API key set as `api_key` header (also accepts query string)\n if (this.oaiRealtimeModel._options.entraToken) {\n headers.Authorization = `Bearer ${this.oaiRealtimeModel._options.entraToken}`;\n } else if (this.oaiRealtimeModel._options.apiKey) {\n headers['api-key'] = this.oaiRealtimeModel._options.apiKey;\n } else {\n throw new Error('Microsoft API key or entraToken is required');\n }\n } else {\n headers.Authorization = `Bearer ${this.oaiRealtimeModel._options.apiKey}`;\n headers['OpenAI-Beta'] = 'realtime=v1';\n }\n\n const url = processBaseURL({\n baseURL: this.oaiRealtimeModel._options.baseURL,\n model: this.oaiRealtimeModel._options.model,\n isAzure: this.oaiRealtimeModel._options.isAzure,\n apiVersion: this.oaiRealtimeModel._options.apiVersion,\n azureDeployment: this.oaiRealtimeModel._options.azureDeployment,\n });\n\n this.#logger.debug(`Connecting to OpenAI Realtime API at ${url}`);\n\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(url, { headers });\n let waiting = true;\n\n const timeout = setTimeout(() => {\n ws.close();\n reject(new Error('WebSocket connection timeout'));\n }, this.oaiRealtimeModel._options.connOptions.timeoutMs);\n\n ws.once('open', () => {\n if (!waiting) return;\n waiting = false;\n clearTimeout(timeout);\n resolve(ws);\n });\n\n ws.once('close', () => {\n if (!waiting) return;\n waiting = false;\n clearTimeout(timeout);\n reject(new Error('OpenAI Realtime API connection closed'));\n });\n });\n }\n\n async #mainTask(signal: AbortSignal): Promise<void> {\n let reconnecting = false;\n let numRetries = 0;\n let wsConn: WebSocket | null = null;\n const maxRetries = this.oaiRealtimeModel._options.connOptions.maxRetry;\n\n const reconnect = async () => {\n this.#logger.debug(\n {\n maxSessionDuration: this.oaiRealtimeModel._options.maxSessionDuration,\n },\n 'Reconnecting to OpenAI Realtime API',\n );\n\n const events: api_proto.ClientEvent[] = [];\n\n // options and instructions\n events.push(this.createSessionUpdateEvent());\n\n // tools\n if (Object.keys(this._tools).length > 0) {\n events.push(this.createToolsUpdateEvent(this._tools));\n }\n\n // chat context\n const chatCtx = this.chatCtx.copy({\n excludeFunctionCall: true,\n excludeInstructions: true,\n excludeEmptyMessage: true,\n });\n\n const oldChatCtx = this.remoteChatCtx;\n this.remoteChatCtx = new llm.RemoteChatContext();\n events.push(...this.createChatCtxUpdateEvents(chatCtx));\n\n try {\n for (const ev of events) {\n this.emit('openai_client_event_queued', ev);\n wsConn!.send(JSON.stringify(ev));\n }\n } catch (error) {\n this.remoteChatCtx = oldChatCtx;\n throw new APIConnectionError({\n message: 'Failed to send message to OpenAI Realtime API during session re-connection',\n });\n }\n\n this.#logger.debug('Reconnected to OpenAI Realtime API');\n\n this.emit('session_reconnected', {} as llm.RealtimeSessionReconnectedEvent);\n };\n\n reconnecting = false;\n while (!this.#closed && !signal.aborted) {\n this.#logger.debug('Creating WebSocket connection to OpenAI Realtime API');\n wsConn = await this.createWsConn();\n if (signal.aborted) break;\n\n try {\n if (reconnecting) {\n await reconnect();\n if (signal.aborted) break;\n numRetries = 0;\n }\n\n await this.runWs(wsConn);\n if (signal.aborted) break;\n } catch (error) {\n if (!isAPIError(error)) {\n this.emitError({ error: error as Error, recoverable: false });\n throw error;\n }\n\n if (maxRetries === 0 || !error.retryable) {\n this.emitError({ error: error as Error, recoverable: false });\n throw error;\n }\n\n if (numRetries === maxRetries) {\n this.emitError({ error: error as Error, recoverable: false });\n throw new APIConnectionError({\n message: `OpenAI Realtime API connection failed after ${numRetries} attempts`,\n options: {\n body: error,\n retryable: false,\n },\n });\n }\n\n this.emitError({ error: error as Error, recoverable: true });\n const retryInterval =\n numRetries === 0\n ? DEFAULT_FIRST_RETRY_INTERVAL_MS\n : this.oaiRealtimeModel._options.connOptions.retryIntervalMs;\n this.#logger.warn(\n {\n attempt: numRetries,\n maxRetries,\n error,\n },\n `OpenAI Realtime API connection failed, retrying in ${retryInterval / 1000}s`,\n );\n\n await delay(retryInterval);\n numRetries++;\n }\n\n reconnecting = true;\n }\n }\n\n private async runWs(wsConn: WebSocket): Promise<void> {\n const forwardEvents = async (signal: AbortSignal): Promise<void> => {\n const abortFuture = new Future<void>();\n signal.addEventListener('abort', () => abortFuture.resolve());\n\n while (!this.#closed && wsConn.readyState === WebSocket.OPEN && !signal.aborted) {\n try {\n const event = await Promise.race([this.messageChannel.get(), abortFuture.await]);\n if (signal.aborted || abortFuture.done || event === undefined) {\n break;\n }\n\n if (event.type !== 'input_audio_buffer.append') {\n this.#logger.debug(`(client) -> ${JSON.stringify(this.#loggableEvent(event))}`);\n }\n\n this.emit('openai_client_event_queued', event);\n wsConn.send(JSON.stringify(event));\n } catch (error) {\n break;\n }\n }\n\n wsConn.close();\n };\n\n const wsCloseFuture = new Future<void | Error>();\n\n wsConn.onerror = (error) => {\n wsCloseFuture.resolve(new APIConnectionError({ message: error.message }));\n };\n wsConn.onclose = () => {\n wsCloseFuture.resolve();\n };\n\n wsConn.onmessage = (message: MessageEvent) => {\n const event: api_proto.ServerEvent = JSON.parse(message.data as string);\n\n this.emit('openai_server_event_received', event);\n this.#logger.debug(`(server) <- ${JSON.stringify(this.#loggableEvent(event))}`);\n\n switch (event.type) {\n case 'input_audio_buffer.speech_started':\n this.handleInputAudioBufferSpeechStarted(event);\n break;\n case 'input_audio_buffer.speech_stopped':\n this.handleInputAudioBufferSpeechStopped(event);\n break;\n case 'response.created':\n this.handleResponseCreated(event);\n break;\n case 'response.output_item.added':\n this.handleResponseOutputItemAdded(event);\n break;\n case 'conversation.item.created':\n this.handleConversationItemCreated(event);\n break;\n case 'conversation.item.deleted':\n this.handleConversationItemDeleted(event);\n break;\n case 'conversation.item.input_audio_transcription.completed':\n this.handleConversationItemInputAudioTranscriptionCompleted(event);\n break;\n case 'conversation.item.input_audio_transcription.failed':\n this.handleConversationItemInputAudioTranscriptionFailed(event);\n break;\n case 'response.content_part.added':\n this.handleResponseContentPartAdded(event);\n break;\n case 'response.content_part.done':\n this.handleResponseContentPartDone(event);\n break;\n case 'response.audio_transcript.delta':\n this.handleResponseAudioTranscriptDelta(event);\n break;\n case 'response.audio.delta':\n this.handleResponseAudioDelta(event);\n break;\n case 'response.audio_transcript.done':\n this.handleResponseAudioTranscriptDone(event);\n break;\n case 'response.audio.done':\n this.handleResponseAudioDone(event);\n break;\n case 'response.output_item.done':\n this.handleResponseOutputItemDone(event);\n break;\n case 'response.done':\n this.handleResponseDone(event);\n break;\n case 'error':\n this.handleError(event);\n break;\n default:\n this.#logger.debug(`unhandled event: ${event.type}`);\n break;\n }\n };\n\n const sendTask = Task.from(({ signal }) => forwardEvents(signal));\n\n const wsTask = Task.from(({ signal }) => {\n const abortPromise = new Promise<void>((resolve) => {\n signal.addEventListener('abort', () => {\n resolve();\n });\n });\n\n return Promise.race([wsCloseFuture.await, abortPromise]);\n });\n\n const waitReconnectTask = Task.from(async ({ signal }) => {\n await delay(this.oaiRealtimeModel._options.maxSessionDuration, { signal });\n return new APIConnectionError({\n message: 'OpenAI Realtime API connection timeout',\n });\n });\n\n try {\n const result = await Promise.race([wsTask.result, sendTask.result, waitReconnectTask.result]);\n\n if (waitReconnectTask.done && this.currentGeneration) {\n await this.currentGeneration._doneFut.await;\n }\n\n if (result instanceof Error) {\n throw result;\n }\n } finally {\n await cancelAndWait([wsTask, sendTask, waitReconnectTask], 2000);\n wsConn.close();\n }\n }\n\n async close() {\n super.close();\n this.#closed = true;\n await this.#task;\n }\n\n private handleInputAudioBufferSpeechStarted(\n _event: api_proto.InputAudioBufferSpeechStartedEvent,\n ): void {\n this.emit('input_speech_started', {} as llm.InputSpeechStartedEvent);\n }\n\n private handleInputAudioBufferSpeechStopped(\n _event: api_proto.InputAudioBufferSpeechStoppedEvent,\n ): void {\n this.emit('input_speech_stopped', {\n userTranscriptionEnabled: this.oaiRealtimeModel._options.inputAudioTranscription !== null,\n } as llm.InputSpeechStoppedEvent);\n }\n\n private handleResponseCreated(event: api_proto.ResponseCreatedEvent): void {\n if (!event.response.id) {\n throw new Error('response.id is missing');\n }\n\n this.currentGeneration = {\n messageChannel: stream.createStreamChannel<llm.MessageGeneration>(),\n functionChannel: stream.createStreamChannel<llm.FunctionCall>(),\n messages: new Map(),\n _doneFut: new Future(),\n _createdTimestamp: Date.now(),\n };\n\n // Build generation event and resolve client future (if any) before emitting,\n // matching Python behavior.\n const generationEv = {\n messageStream: this.currentGeneration.messageChannel.stream(),\n functionStream: this.currentGeneration.functionChannel.stream(),\n userInitiated: false,\n } as GenerationCreatedEvent;\n\n const clientEventId = event.response.metadata?.client_event_id;\n if (clientEventId) {\n const handle = this.responseCreatedFutures[clientEventId];\n if (handle) {\n delete this.responseCreatedFutures[clientEventId];\n generationEv.userInitiated = true;\n if (!handle.doneFut.done) {\n handle.doneFut.resolve(generationEv);\n }\n }\n }\n\n this.emit('generation_created', generationEv);\n }\n\n private handleResponseOutputItemAdded(event: api_proto.ResponseOutputItemAddedEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n if (!event.item.type) {\n throw new Error('item.type is not set');\n }\n\n if (!event.response_id) {\n throw new Error('response_id is not set');\n }\n\n const itemType = event.item.type;\n const responseId = event.response_id;\n\n if (itemType !== 'message') {\n // emit immediately if it's not a message, otherwise wait response.content_part.added\n this.resolveGeneration(responseId);\n this.textModeRecoveryRetries = 0;\n return;\n }\n }\n\n private handleConversationItemCreated(event: api_proto.ConversationItemCreatedEvent): void {\n if (!event.item.id) {\n throw new Error('item.id is not set');\n }\n\n try {\n this.remoteChatCtx.insert(event.previous_item_id, openAIItemToLivekitItem(event.item));\n } catch (error) {\n this.#logger.error({ error, itemId: event.item.id }, 'failed to insert conversation item');\n }\n\n const fut = this.itemCreateFutures[event.item.id];\n if (fut) {\n fut.resolve();\n delete this.itemCreateFutures[event.item.id];\n }\n }\n\n private handleConversationItemDeleted(event: api_proto.ConversationItemDeletedEvent): void {\n if (!event.item_id) {\n throw new Error('item_id is not set');\n }\n\n try {\n this.remoteChatCtx.delete(event.item_id);\n } catch (error) {\n this.#logger.error({ error, itemId: event.item_id }, 'failed to delete conversation item');\n }\n\n const fut = this.itemDeleteFutures[event.item_id];\n if (fut) {\n fut.resolve();\n delete this.itemDeleteFutures[event.item_id];\n }\n }\n\n private handleConversationItemInputAudioTranscriptionCompleted(\n event: api_proto.ConversationItemInputAudioTranscriptionCompletedEvent,\n ): void {\n const remoteItem = this.remoteChatCtx.get(event.item_id);\n if (!remoteItem) {\n return;\n }\n\n const item = remoteItem.item;\n if (item instanceof llm.ChatMessage) {\n item.content.push(event.transcript);\n } else {\n throw new Error('item is not a chat message');\n }\n\n this.emit('input_audio_transcription_completed', {\n itemId: event.item_id,\n transcript: event.transcript,\n isFinal: true,\n } as llm.InputTranscriptionCompleted);\n }\n\n private handleConversationItemInputAudioTranscriptionFailed(\n event: api_proto.ConversationItemInputAudioTranscriptionFailedEvent,\n ): void {\n this.#logger.error(\n { error: event.error },\n 'OpenAI Realtime API failed to transcribe input audio',\n );\n }\n\n private handleResponseContentPartAdded(event: api_proto.ResponseContentPartAddedEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemId = event.item_id;\n const itemType = event.part.type;\n const responseId = event.response_id;\n\n if (itemType === 'audio') {\n this.resolveGeneration(responseId);\n if (this.textModeRecoveryRetries > 0) {\n this.#logger.info(\n { retries: this.textModeRecoveryRetries },\n 'recovered from text-only response',\n );\n this.textModeRecoveryRetries = 0;\n }\n\n const itemGeneration: MessageGeneration = {\n messageId: itemId,\n textChannel: stream.createStreamChannel<string>(),\n audioChannel: stream.createStreamChannel<AudioFrame>(),\n audioTranscript: '',\n };\n\n this.currentGeneration.messageChannel.write({\n messageId: itemId,\n textStream: itemGeneration.textChannel.stream(),\n audioStream: itemGeneration.audioChannel.stream(),\n });\n\n this.currentGeneration.messages.set(itemId, itemGeneration);\n this.currentGeneration._firstTokenTimestamp = Date.now();\n return;\n } else {\n this.interrupt();\n if (this.textModeRecoveryRetries === 0) {\n this.#logger.warn({ responseId }, 'received text-only response from OpenAI Realtime API');\n }\n }\n }\n\n private handleResponseContentPartDone(event: api_proto.ResponseContentPartDoneEvent): void {\n if (event.part.type !== 'text') {\n return;\n }\n\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n // TODO(shubhra): handle text mode recovery\n }\n\n private handleResponseAudioTranscriptDelta(\n event: api_proto.ResponseAudioTranscriptDeltaEvent,\n ): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemId = event.item_id;\n const delta = event.delta;\n\n // TODO (shubhra): add timed string support\n\n const itemGeneration = this.currentGeneration.messages.get(itemId);\n if (!itemGeneration) {\n throw new Error('itemGeneration is not set');\n } else {\n itemGeneration.textChannel.write(delta);\n itemGeneration.audioTranscript += delta;\n }\n }\n\n private handleResponseAudioDelta(event: api_proto.ResponseAudioDeltaEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemGeneration = this.currentGeneration.messages.get(event.item_id);\n if (!itemGeneration) {\n throw new Error('itemGeneration is not set');\n }\n\n const binaryString = atob(event.delta);\n const len = binaryString.length;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n itemGeneration.audioChannel.write(\n new AudioFrame(\n new Int16Array(bytes.buffer),\n api_proto.SAMPLE_RATE,\n api_proto.NUM_CHANNELS,\n bytes.length / 2,\n ),\n );\n }\n\n private handleResponseAudioTranscriptDone(\n _event: api_proto.ResponseAudioTranscriptDoneEvent,\n ): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n }\n\n private handleResponseAudioDone(_event: api_proto.ResponseAudioDoneEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n }\n\n private handleResponseOutputItemDone(event: api_proto.ResponseOutputItemDoneEvent): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const itemId = event.item.id;\n const itemType = event.item.type;\n\n if (itemType === 'function_call') {\n const item = event.item;\n if (!item.call_id || !item.name || !item.arguments) {\n throw new Error('item is not a function call');\n }\n this.currentGeneration.functionChannel.write({\n callId: item.call_id,\n name: item.name,\n args: item.arguments,\n } as llm.FunctionCall);\n } else if (itemType === 'message') {\n const itemGeneration = this.currentGeneration.messages.get(itemId);\n if (!itemGeneration) {\n return;\n }\n // text response doesn't have itemGeneration\n itemGeneration.textChannel.close();\n itemGeneration.audioChannel.close();\n }\n }\n\n private handleResponseDone(_event: api_proto.ResponseDoneEvent): void {\n if (!this.currentGeneration) {\n // OpenAI has a race condition where we could receive response.done without any\n // previous response.created (This happens generally during interruption)\n return;\n }\n\n const createdTimestamp = this.currentGeneration._createdTimestamp;\n const firstTokenTimestamp = this.currentGeneration._firstTokenTimestamp;\n\n this.#logger.debug(\n {\n messageCount: this.currentGeneration.messages.size,\n },\n 'Closing generation channels in handleResponseDone',\n );\n\n for (const generation of this.currentGeneration.messages.values()) {\n generation.textChannel.close();\n generation.audioChannel.close();\n }\n\n this.currentGeneration.functionChannel.close();\n this.currentGeneration.messageChannel.close();\n\n for (const itemId of this.currentGeneration.messages.keys()) {\n const remoteItem = this.remoteChatCtx.get(itemId);\n if (remoteItem && remoteItem.item instanceof llm.ChatMessage) {\n remoteItem.item.content.push(this.currentGeneration.messages.get(itemId)!.audioTranscript);\n }\n }\n\n this.currentGeneration._doneFut.resolve();\n this.currentGeneration = undefined;\n\n // Calculate and emit metrics\n const usage = _event.response.usage;\n const ttft = firstTokenTimestamp ? firstTokenTimestamp - createdTimestamp : -1;\n const duration = (Date.now() - createdTimestamp) / 1000; // Convert to seconds\n\n const realtimeMetrics: metrics.RealtimeModelMetrics = {\n type: 'realtime_model_metrics',\n timestamp: createdTimestamp / 1000, // Convert to seconds\n requestId: _event.response.id || '',\n ttft,\n duration,\n cancelled: _event.response.status === 'cancelled',\n label: 'openai_realtime',\n inputTokens: usage?.input_tokens ?? 0,\n outputTokens: usage?.output_tokens ?? 0,\n totalTokens: usage?.total_tokens ?? 0,\n tokensPerSecond: duration > 0 ? (usage?.output_tokens ?? 0) / duration : 0,\n inputTokenDetails: {\n audioTokens: usage?.input_token_details?.audio_tokens ?? 0,\n textTokens: usage?.input_token_details?.text_tokens ?? 0,\n imageTokens: 0, // Not supported yet\n cachedTokens: usage?.input_token_details?.cached_tokens ?? 0,\n cachedTokensDetails: usage?.input_token_details?.cached_tokens_details\n ? {\n audioTokens: usage?.input_token_details?.cached_tokens_details?.audio_tokens ?? 0,\n textTokens: usage?.input_token_details?.cached_tokens_details?.text_tokens ?? 0,\n imageTokens: usage?.input_token_details?.cached_tokens_details?.image_tokens ?? 0,\n }\n : undefined,\n },\n outputTokenDetails: {\n textTokens: usage?.output_token_details?.text_tokens ?? 0,\n audioTokens: usage?.output_token_details?.audio_tokens ?? 0,\n imageTokens: 0,\n },\n };\n\n this.emit('metrics_collected', realtimeMetrics);\n // TODO(brian): handle response done but not complete\n }\n\n private handleError(event: api_proto.ErrorEvent): void {\n if (event.error.message.startsWith('Cancellation failed')) {\n return;\n }\n\n this.#logger.error({ error: event.error }, 'OpenAI Realtime API returned an error');\n this.emitError({\n error: new APIError(event.error.message, {\n body: event.error,\n retryable: true,\n }),\n recoverable: true,\n });\n\n // TODO(brian): set error for response future if it exists\n }\n\n private emitError({ error, recoverable }: { error: Error; recoverable: boolean }): void {\n // IMPORTANT: only emit error if there are listeners; otherwise emit will throw an error\n this.emit('error', {\n timestamp: Date.now(),\n // TODO(brian): add label\n label: '',\n error,\n recoverable,\n } as llm.RealtimeModelError);\n }\n\n private *resampleAudio(frame: AudioFrame): Generator<AudioFrame> {\n yield frame;\n }\n\n private createResponse({\n userInitiated,\n instructions,\n oldHandle,\n }: {\n userInitiated: boolean;\n instructions?: string;\n oldHandle?: CreateResponseHandle;\n }): CreateResponseHandle {\n const handle = oldHandle || new CreateResponseHandle({ instructions });\n if (oldHandle && instructions) {\n handle.instructions = instructions;\n }\n\n const eventId = shortuuid('response_create_');\n if (userInitiated) {\n this.responseCreatedFutures[eventId] = handle;\n }\n\n const response: api_proto.ResponseCreateEvent['response'] = {};\n if (instructions) response.instructions = instructions;\n if (userInitiated) response.metadata = { client_event_id: eventId };\n\n this.sendEvent({\n type: 'response.create',\n event_id: eventId,\n response: Object.keys(response).length > 0 ? response : undefined,\n });\n\n return handle;\n }\n\n private resolveGeneration(responseId: string): void {\n if (!this.currentGeneration) {\n throw new Error('currentGeneration is not set');\n }\n\n const generation_ev = {\n messageStream: this.currentGeneration.messageChannel.stream(),\n functionStream: this.currentGeneration.functionChannel.stream(),\n userInitiated: false,\n } as GenerationCreatedEvent;\n\n const handle = this.responseCreatedFutures[responseId];\n if (handle) {\n delete this.responseCreatedFutures[responseId];\n generation_ev.userInitiated = true;\n if (handle.doneFut.done) {\n this.#logger.warn({ responseId }, 'response received after timeout');\n } else {\n handle.doneFut.resolve(generation_ev);\n }\n }\n }\n}\n\nfunction livekitItemToOpenAIItem(item: llm.ChatItem): api_proto.ItemResource {\n switch (item.type) {\n case 'function_call':\n return {\n id: item.id,\n type: 'function_call',\n call_id: item.callId,\n name: item.name,\n arguments: item.args,\n } as api_proto.FunctionCallItem;\n case 'function_call_output':\n return {\n id: item.id,\n type: 'function_call_output',\n call_id: item.callId,\n output: item.output,\n } as api_proto.FunctionCallOutputItem;\n case 'message':\n const role = item.role === 'developer' ? 'system' : item.role;\n const contentList: api_proto.Content[] = [];\n for (const c of item.content) {\n if (typeof c === 'string') {\n contentList.push({\n type: role === 'assistant' ? 'text' : 'input_text',\n text: c,\n } as api_proto.InputTextContent);\n } else if (c.type === 'image_content') {\n // not supported for now\n continue;\n } else if (c.type === 'audio_content') {\n if (role === 'user') {\n const encodedAudio = Buffer.from(combineAudioFrames(c.frame).data).toString('base64');\n contentList.push({\n type: 'input_audio',\n audio: encodedAudio,\n } as api_proto.InputAudioContent);\n }\n }\n }\n return {\n id: item.id,\n type: 'message',\n role,\n content: contentList,\n } as api_proto.UserItem;\n }\n}\n\nfunction openAIItemToLivekitItem(item: api_proto.ItemResource): llm.ChatItem {\n if (!item.id) {\n throw new Error('item.id is not set');\n }\n\n switch (item.type) {\n case 'function_call':\n return llm.FunctionCall.create({\n id: item.id,\n callId: item.call_id,\n name: item.name,\n args: item.arguments,\n });\n case 'function_call_output':\n return llm.FunctionCallOutput.create({\n id: item.id,\n callId: item.call_id,\n output: item.output,\n isError: false,\n });\n case 'message':\n const content: llm.ChatContent[] = [];\n // item.content can be a single object or an array; normalize to array\n const contents = Array.isArray(item.content) ? item.content : [item.content];\n for (const c of contents) {\n if (c.type === 'text' || c.type === 'input_text') {\n content.push(c.text);\n }\n }\n return llm.ChatMessage.create({\n id: item.id,\n role: item.role,\n content,\n });\n }\n}\n\nfunction createMockAudioItem(durationSeconds: number = 2): llm.ChatMessage {\n const audioData = Buffer.alloc(durationSeconds * SAMPLE_RATE);\n return llm.ChatMessage.create({\n id: shortuuid(MOCK_AUDIO_ID_PREFIX),\n role: 'user',\n content: [\n {\n type: 'audio_content',\n frame: [\n new AudioFrame(\n new Int16Array(audioData.buffer),\n SAMPLE_RATE,\n NUM_CHANNELS,\n audioData.length / 2,\n ),\n ],\n } as llm.AudioContent,\n ],\n });\n}\n\nfunction toOaiToolChoice(toolChoice?: llm.ToolChoice): api_proto.ToolChoice {\n if (typeof toolChoice === 'string') {\n return toolChoice;\n }\n\n if (toolChoice?.type === 'function') {\n return toolChoice.function.name;\n }\n\n return 'auto';\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livekit/agents-plugin-openai",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "OpenAI plugin for LiveKit Node Agents",
5
5
  "main": "dist/index.js",
6
6
  "require": "dist/index.cjs",
@@ -30,9 +30,9 @@
30
30
  "@types/ws": "^8.5.10",
31
31
  "tsup": "^8.3.5",
32
32
  "typescript": "^5.0.0",
33
- "@livekit/agents": "1.0.2",
34
- "@livekit/agents-plugin-silero": "1.0.2",
35
- "@livekit/agents-plugins-test": "1.0.2"
33
+ "@livekit/agents": "1.0.3",
34
+ "@livekit/agents-plugin-silero": "1.0.3",
35
+ "@livekit/agents-plugins-test": "1.0.3"
36
36
  },
37
37
  "dependencies": {
38
38
  "@livekit/mutex": "^1.1.1",
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@livekit/rtc-node": "^0.13.12",
44
- "@livekit/agents": "1.0.2"
44
+ "@livekit/agents": "1.0.3"
45
45
  },
46
46
  "scripts": {
47
47
  "build": "tsup --onSuccess \"pnpm build:types\"",
@@ -1005,25 +1005,27 @@ export class RealtimeSession extends llm.RealtimeSession {
1005
1005
  _createdTimestamp: Date.now(),
1006
1006
  };
1007
1007
 
1008
- if (!event.response.metadata || !event.response.metadata.client_event_id) return;
1009
-
1010
- const handle = this.responseCreatedFutures[event.response.metadata.client_event_id];
1011
- if (handle) {
1012
- delete this.responseCreatedFutures[event.response.metadata.client_event_id];
1013
-
1014
- // set key to the response id
1015
- this.responseCreatedFutures[event.response.id] = handle;
1016
- }
1017
-
1018
- // the generation_created event is emitted when
1019
- // 1. the response is not a message on response.output_item.added event
1020
- // 2. the content is audio on response.content_part.added event
1021
- // will try to recover from text response on response.content_part.done event
1022
- this.emit('generation_created', {
1008
+ // Build generation event and resolve client future (if any) before emitting,
1009
+ // matching Python behavior.
1010
+ const generationEv = {
1023
1011
  messageStream: this.currentGeneration.messageChannel.stream(),
1024
1012
  functionStream: this.currentGeneration.functionChannel.stream(),
1025
1013
  userInitiated: false,
1026
- } as GenerationCreatedEvent);
1014
+ } as GenerationCreatedEvent;
1015
+
1016
+ const clientEventId = event.response.metadata?.client_event_id;
1017
+ if (clientEventId) {
1018
+ const handle = this.responseCreatedFutures[clientEventId];
1019
+ if (handle) {
1020
+ delete this.responseCreatedFutures[clientEventId];
1021
+ generationEv.userInitiated = true;
1022
+ if (!handle.doneFut.done) {
1023
+ handle.doneFut.resolve(generationEv);
1024
+ }
1025
+ }
1026
+ }
1027
+
1028
+ this.emit('generation_created', generationEv);
1027
1029
  }
1028
1030
 
1029
1031
  private handleResponseOutputItemAdded(event: api_proto.ResponseOutputItemAddedEvent): void {
@@ -1044,7 +1046,7 @@ export class RealtimeSession extends llm.RealtimeSession {
1044
1046
 
1045
1047
  if (itemType !== 'message') {
1046
1048
  // emit immediately if it's not a message, otherwise wait response.content_part.added
1047
- this.emitGenerationEvent(responseId);
1049
+ this.resolveGeneration(responseId);
1048
1050
  this.textModeRecoveryRetries = 0;
1049
1051
  return;
1050
1052
  }
@@ -1127,7 +1129,7 @@ export class RealtimeSession extends llm.RealtimeSession {
1127
1129
  const responseId = event.response_id;
1128
1130
 
1129
1131
  if (itemType === 'audio') {
1130
- this.emitGenerationEvent(responseId);
1132
+ this.resolveGeneration(responseId);
1131
1133
  if (this.textModeRecoveryRetries > 0) {
1132
1134
  this.#logger.info(
1133
1135
  { retries: this.textModeRecoveryRetries },
@@ -1403,16 +1405,16 @@ export class RealtimeSession extends llm.RealtimeSession {
1403
1405
  return handle;
1404
1406
  }
1405
1407
 
1406
- private emitGenerationEvent(responseId: string): void {
1408
+ private resolveGeneration(responseId: string): void {
1407
1409
  if (!this.currentGeneration) {
1408
1410
  throw new Error('currentGeneration is not set');
1409
1411
  }
1410
1412
 
1411
- const generation_ev: llm.GenerationCreatedEvent = {
1413
+ const generation_ev = {
1412
1414
  messageStream: this.currentGeneration.messageChannel.stream(),
1413
1415
  functionStream: this.currentGeneration.functionChannel.stream(),
1414
1416
  userInitiated: false,
1415
- };
1417
+ } as GenerationCreatedEvent;
1416
1418
 
1417
1419
  const handle = this.responseCreatedFutures[responseId];
1418
1420
  if (handle) {
@@ -1424,9 +1426,6 @@ export class RealtimeSession extends llm.RealtimeSession {
1424
1426
  handle.doneFut.resolve(generation_ev);
1425
1427
  }
1426
1428
  }
1427
-
1428
- this.#logger.debug({ responseId }, 'Emitting generation_created event');
1429
- this.emit('generation_created', generation_ev);
1430
1429
  }
1431
1430
  }
1432
1431