@livekit/agents-plugin-phonic 1.0.46

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.
@@ -0,0 +1,156 @@
1
+ import type { APIConnectOptions } from '@livekit/agents';
2
+ import { llm } from '@livekit/agents';
3
+ import { AudioFrame } from '@livekit/rtc-node';
4
+ import type { Phonic } from 'phonic';
5
+ import type { Voice } from './api_proto.js';
6
+ export interface RealtimeModelOptions {
7
+ apiKey: string;
8
+ model: string;
9
+ phonicAgent?: string;
10
+ voice?: Voice | string;
11
+ welcomeMessage?: string;
12
+ generateWelcomeMessage?: boolean;
13
+ project?: string;
14
+ connOptions: APIConnectOptions;
15
+ baseUrl?: string;
16
+ languages?: string[];
17
+ audioSpeed?: number;
18
+ phonicTools?: string[];
19
+ boostedKeywords?: string[];
20
+ generateNoInputPokeText?: boolean;
21
+ noInputPokeSec?: number;
22
+ noInputPokeText?: string;
23
+ noInputEndConversationSec?: number;
24
+ /** Set by `updateInstructions` via `voice.Agent` rather than the RealtimeModel constructor */
25
+ instructions?: string;
26
+ }
27
+ export declare class RealtimeModel extends llm.RealtimeModel {
28
+ /** @internal */
29
+ _options: RealtimeModelOptions;
30
+ get model(): string;
31
+ constructor(options?: {
32
+ /**
33
+ * Phonic API key. If not provided, will attempt to read from PHONIC_API_KEY environment variable
34
+ */
35
+ apiKey?: string;
36
+ /**
37
+ * The name of the model to use. Defaults to 'merritt'
38
+ */
39
+ model?: Phonic.ConfigPayload['model'] | string;
40
+ /**
41
+ * Phonic agent to use for the conversation. Options explicitly set here will override the agent settings.
42
+ */
43
+ phonicAgent?: string;
44
+ /**
45
+ * Voice ID for agent outputs
46
+ */
47
+ voice?: Voice;
48
+ /**
49
+ * Welcome message for the agent to say when the conversation starts. Ignored when generateWelcomeMessage is true
50
+ */
51
+ welcomeMessage?: string;
52
+ /**
53
+ * When true, the welcome message will be automatically generated and welcomeMessage will be ignored
54
+ */
55
+ generateWelcomeMessage?: boolean;
56
+ /**
57
+ * Project name to use for the conversation. Defaults to `main`
58
+ */
59
+ project?: string;
60
+ /**
61
+ * ISO 639-1 language codes the agent should recognize and speak
62
+ */
63
+ languages?: string[];
64
+ /**
65
+ * Audio playback speed
66
+ */
67
+ audioSpeed?: number;
68
+ /**
69
+ * Phonic tool names available to the assistant
70
+ */
71
+ phonicTools?: string[];
72
+ /**
73
+ * Keywords to boost in speech recognition
74
+ */
75
+ boostedKeywords?: string[];
76
+ /**
77
+ * Auto-generate poke text when user is silent
78
+ */
79
+ generateNoInputPokeText?: boolean;
80
+ /**
81
+ * Seconds of silence before sending poke message
82
+ */
83
+ noInputPokeSec?: number;
84
+ /**
85
+ * Poke message text (ignored when generateNoInputPokeText is true)
86
+ */
87
+ noInputPokeText?: string;
88
+ /**
89
+ * Seconds of silence before ending conversation
90
+ */
91
+ noInputEndConversationSec?: number;
92
+ /**
93
+ * Connection options for the API connection
94
+ */
95
+ connOptions?: APIConnectOptions;
96
+ baseUrl?: string;
97
+ });
98
+ /**
99
+ * Create a new realtime session
100
+ */
101
+ session(): RealtimeSession;
102
+ close(): Promise<void>;
103
+ }
104
+ /**
105
+ * Realtime session for Phonic (https://docs.phonic.co/)
106
+ */
107
+ export declare class RealtimeSession extends llm.RealtimeSession {
108
+ private _tools;
109
+ private _chatCtx;
110
+ private options;
111
+ private bstream;
112
+ private inputResampler?;
113
+ private inputResamplerInputRate?;
114
+ private currentGeneration?;
115
+ private conversationId?;
116
+ private client;
117
+ private socket?;
118
+ private logger;
119
+ private closed;
120
+ private configSent;
121
+ private instructionsReady;
122
+ private resolveInstructionsReady;
123
+ private connectTask;
124
+ constructor(realtimeModel: RealtimeModel);
125
+ get chatCtx(): llm.ChatContext;
126
+ get tools(): llm.ToolContext;
127
+ updateInstructions(instructions: string): Promise<void>;
128
+ updateChatCtx(_chatCtx: llm.ChatContext): Promise<void>;
129
+ updateTools(tools: llm.ToolContext): Promise<void>;
130
+ updateOptions(_options: {
131
+ toolChoice?: llm.ToolChoice | null;
132
+ }): void;
133
+ pushAudio(frame: AudioFrame): void;
134
+ generateReply(_instructions?: string): Promise<llm.GenerationCreatedEvent>;
135
+ commitAudio(): Promise<void>;
136
+ clearAudio(): Promise<void>;
137
+ interrupt(): Promise<void>;
138
+ truncate(_options: {
139
+ messageId: string;
140
+ audioEndMs: number;
141
+ audioTranscript?: string;
142
+ }): Promise<void>;
143
+ close(): Promise<void>;
144
+ private connect;
145
+ private handleServerMessage;
146
+ private handleAudioChunk;
147
+ private handleInputText;
148
+ private handleInputSpeechStarted;
149
+ private handleInputSpeechStopped;
150
+ private startNewAssistantTurn;
151
+ private finishAssistantTurn;
152
+ private closeCurrentGeneration;
153
+ private emitError;
154
+ private resampleAudio;
155
+ }
156
+ //# sourceMappingURL=realtime_model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime_model.d.ts","sourceRoot":"","sources":["../../src/realtime/realtime_model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,413 @@
1
+ import {
2
+ AudioByteStream,
3
+ DEFAULT_API_CONNECT_OPTIONS,
4
+ llm,
5
+ log,
6
+ shortuuid,
7
+ stream
8
+ } from "@livekit/agents";
9
+ import { AudioFrame, AudioResampler } from "@livekit/rtc-node";
10
+ import { PhonicClient } from "phonic";
11
+ const PHONIC_INPUT_SAMPLE_RATE = 44100;
12
+ const PHONIC_OUTPUT_SAMPLE_RATE = 44100;
13
+ const PHONIC_NUM_CHANNELS = 1;
14
+ const PHONIC_INPUT_FRAME_MS = 20;
15
+ const DEFAULT_MODEL = "merritt";
16
+ const WS_CLOSE_NORMAL = 1e3;
17
+ class RealtimeModel extends llm.RealtimeModel {
18
+ /** @internal */
19
+ _options;
20
+ get model() {
21
+ return this._options.model;
22
+ }
23
+ constructor(options = {}) {
24
+ super({
25
+ messageTruncation: false,
26
+ turnDetection: true,
27
+ userTranscription: true,
28
+ // TODO @Phonic-Co: Implement tool support
29
+ // Phonic has automatic tool reply generation, but tools are not supported with LiveKit Agents yet.
30
+ autoToolReplyGeneration: true,
31
+ audioOutput: true
32
+ });
33
+ const apiKey = options.apiKey || process.env.PHONIC_API_KEY;
34
+ if (!apiKey) {
35
+ throw new Error("Phonic API key is required. Provide apiKey or set PHONIC_API_KEY.");
36
+ }
37
+ this._options = {
38
+ apiKey,
39
+ voice: options.voice,
40
+ phonicAgent: options.phonicAgent,
41
+ project: options.project,
42
+ welcomeMessage: options.welcomeMessage,
43
+ generateWelcomeMessage: options.generateWelcomeMessage,
44
+ languages: options.languages,
45
+ audioSpeed: options.audioSpeed,
46
+ phonicTools: options.phonicTools,
47
+ boostedKeywords: options.boostedKeywords,
48
+ generateNoInputPokeText: options.generateNoInputPokeText,
49
+ noInputPokeSec: options.noInputPokeSec,
50
+ noInputPokeText: options.noInputPokeText,
51
+ noInputEndConversationSec: options.noInputEndConversationSec,
52
+ connOptions: options.connOptions ?? DEFAULT_API_CONNECT_OPTIONS,
53
+ model: options.model ?? DEFAULT_MODEL,
54
+ baseUrl: options.baseUrl
55
+ };
56
+ }
57
+ /**
58
+ * Create a new realtime session
59
+ */
60
+ session() {
61
+ return new RealtimeSession(this);
62
+ }
63
+ async close() {
64
+ }
65
+ }
66
+ class RealtimeSession extends llm.RealtimeSession {
67
+ _tools = {};
68
+ _chatCtx = llm.ChatContext.empty();
69
+ options;
70
+ bstream;
71
+ inputResampler;
72
+ inputResamplerInputRate;
73
+ currentGeneration;
74
+ conversationId;
75
+ client;
76
+ socket;
77
+ logger = log();
78
+ closed = false;
79
+ configSent = false;
80
+ instructionsReady;
81
+ resolveInstructionsReady;
82
+ connectTask;
83
+ constructor(realtimeModel) {
84
+ super(realtimeModel);
85
+ this.options = realtimeModel._options;
86
+ this.resolveInstructionsReady = () => {
87
+ };
88
+ this.instructionsReady = new Promise((resolve) => {
89
+ this.resolveInstructionsReady = resolve;
90
+ });
91
+ this.client = new PhonicClient({
92
+ apiKey: this.options.apiKey,
93
+ baseUrl: this.options.baseUrl
94
+ });
95
+ this.bstream = new AudioByteStream(
96
+ PHONIC_INPUT_SAMPLE_RATE,
97
+ PHONIC_NUM_CHANNELS,
98
+ PHONIC_INPUT_SAMPLE_RATE * PHONIC_INPUT_FRAME_MS / 1e3
99
+ );
100
+ this.connectTask = this.connect().catch((error) => {
101
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
102
+ this.emitError(normalizedError, false);
103
+ });
104
+ }
105
+ get chatCtx() {
106
+ return this._chatCtx.copy();
107
+ }
108
+ get tools() {
109
+ return { ...this._tools };
110
+ }
111
+ async updateInstructions(instructions) {
112
+ if (this.configSent) {
113
+ this.logger.warn(
114
+ "updateInstructions called after config was already sent. Phonic does not support updating instructions mid-session."
115
+ );
116
+ return;
117
+ }
118
+ this.options.instructions = instructions;
119
+ this.resolveInstructionsReady();
120
+ }
121
+ async updateChatCtx(_chatCtx) {
122
+ this.logger.warn("updateChatCtx is not supported by the Phonic realtime model.");
123
+ }
124
+ async updateTools(tools) {
125
+ if (Object.keys(tools).length > 0) {
126
+ this.logger.warn("Tool use is not supported by the Phonic realtime model.");
127
+ }
128
+ }
129
+ updateOptions(_options) {
130
+ this.logger.warn("updateOptions is not supported by the Phonic realtime model.");
131
+ }
132
+ pushAudio(frame) {
133
+ if (this.closed) {
134
+ return;
135
+ }
136
+ for (const resampledFrame of this.resampleAudio(frame)) {
137
+ for (const chunk of this.bstream.write(resampledFrame.data.buffer)) {
138
+ const bytes = Buffer.from(chunk.data.buffer, chunk.data.byteOffset, chunk.data.byteLength);
139
+ const payload = {
140
+ type: "audio_chunk",
141
+ audio: bytes.toString("base64")
142
+ };
143
+ if (!this.socket) {
144
+ continue;
145
+ }
146
+ this.socket.sendAudioChunk(payload);
147
+ }
148
+ }
149
+ }
150
+ // TODO @Phonic-Co: Implement generateReply
151
+ async generateReply(_instructions) {
152
+ throw new Error(
153
+ "generateReply is not yet supported by the Phonic realtime model. Consider using `welcomeMessage` instead."
154
+ );
155
+ }
156
+ async commitAudio() {
157
+ this.logger.warn("commitAudio is not supported by the Phonic realtime model.");
158
+ }
159
+ async clearAudio() {
160
+ this.logger.warn("clearAudio is not supported by the Phonic realtime model.");
161
+ }
162
+ async interrupt() {
163
+ this.logger.warn("interrupt is not supported by the Phonic realtime model.");
164
+ }
165
+ async truncate(_options) {
166
+ this.logger.warn("truncate is not supported by the Phonic realtime model.");
167
+ }
168
+ async close() {
169
+ var _a;
170
+ this.closed = true;
171
+ this.resolveInstructionsReady();
172
+ this.closeCurrentGeneration({ interrupted: false });
173
+ (_a = this.socket) == null ? void 0 : _a.close();
174
+ await this.connectTask;
175
+ await super.close();
176
+ }
177
+ async connect() {
178
+ this.socket = await this.client.conversations.connect({
179
+ reconnectAttempts: this.options.connOptions.maxRetry
180
+ });
181
+ if (this.closed) {
182
+ this.socket.close();
183
+ return;
184
+ }
185
+ this.socket.on(
186
+ "message",
187
+ (message) => this.handleServerMessage(message)
188
+ );
189
+ this.socket.on("error", (error) => this.emitError(error, false));
190
+ this.socket.on("close", (event) => {
191
+ this.closeCurrentGeneration({ interrupted: false });
192
+ if (!this.closed && event.code !== WS_CLOSE_NORMAL) {
193
+ this.emitError(new Error(`Phonic STS socket closed with code ${event.code ?? -1}`), false);
194
+ }
195
+ });
196
+ await this.socket.waitForOpen();
197
+ await this.instructionsReady;
198
+ if (this.closed) return;
199
+ this.configSent = true;
200
+ this.socket.sendConfig({
201
+ type: "config",
202
+ model: this.options.model,
203
+ agent: this.options.phonicAgent,
204
+ project: this.options.project,
205
+ welcome_message: this.options.welcomeMessage,
206
+ generate_welcome_message: this.options.generateWelcomeMessage,
207
+ system_prompt: this.options.instructions,
208
+ voice_id: this.options.voice,
209
+ input_format: "pcm_44100",
210
+ output_format: "pcm_44100",
211
+ recognized_languages: this.options.languages,
212
+ audio_speed: this.options.audioSpeed,
213
+ tools: this.options.phonicTools,
214
+ boosted_keywords: this.options.boostedKeywords,
215
+ generate_no_input_poke_text: this.options.generateNoInputPokeText,
216
+ no_input_poke_sec: this.options.noInputPokeSec,
217
+ no_input_poke_text: this.options.noInputPokeText,
218
+ no_input_end_conversation_sec: this.options.noInputEndConversationSec
219
+ });
220
+ }
221
+ handleServerMessage(message) {
222
+ if (this.closed) {
223
+ return;
224
+ }
225
+ switch (message.type) {
226
+ case "assistant_started_speaking":
227
+ this.startNewAssistantTurn();
228
+ break;
229
+ case "assistant_finished_speaking":
230
+ this.finishAssistantTurn();
231
+ break;
232
+ case "audio_chunk":
233
+ this.handleAudioChunk(message);
234
+ break;
235
+ case "input_text":
236
+ this.handleInputText(message);
237
+ break;
238
+ case "user_started_speaking":
239
+ this.handleInputSpeechStarted();
240
+ break;
241
+ case "user_finished_speaking":
242
+ this.handleInputSpeechStopped();
243
+ break;
244
+ case "error":
245
+ this.emitError(new Error(message.error.message), false);
246
+ break;
247
+ case "tool_call":
248
+ this.emitError(
249
+ new Error(
250
+ `WebSocket tool calls are not yet supported by the Phonic realtime model with LiveKit Agents.`
251
+ ),
252
+ false
253
+ );
254
+ break;
255
+ case "assistant_ended_conversation":
256
+ this.emitError(
257
+ new Error(
258
+ "assistant_ended_conversation is not supported by the Phonic realtime model with LiveKit Agents."
259
+ ),
260
+ false
261
+ );
262
+ break;
263
+ case "conversation_created":
264
+ this.conversationId = message.conversation_id;
265
+ this.logger.info(`Phonic Conversation began with ID: ${this.conversationId}`);
266
+ break;
267
+ case "assistant_chose_not_to_respond":
268
+ case "ready_to_start_conversation":
269
+ case "input_cancelled":
270
+ case "tool_call_output_processed":
271
+ case "tool_call_interrupted":
272
+ case "dtmf":
273
+ default:
274
+ break;
275
+ }
276
+ }
277
+ handleAudioChunk(message) {
278
+ const gen = this.currentGeneration;
279
+ if (!gen) return;
280
+ if (message.text) {
281
+ gen.outputText += message.text;
282
+ gen.textChannel.write(message.text);
283
+ }
284
+ if (message.audio) {
285
+ const bytes = Buffer.from(message.audio, "base64");
286
+ const sampleCount = Math.floor(bytes.byteLength / Int16Array.BYTES_PER_ELEMENT);
287
+ if (sampleCount > 0) {
288
+ const pcm = new Int16Array(
289
+ bytes.buffer.slice(
290
+ bytes.byteOffset,
291
+ bytes.byteOffset + sampleCount * Int16Array.BYTES_PER_ELEMENT
292
+ )
293
+ );
294
+ const frame = new AudioFrame(
295
+ pcm,
296
+ PHONIC_OUTPUT_SAMPLE_RATE,
297
+ PHONIC_NUM_CHANNELS,
298
+ sampleCount / PHONIC_NUM_CHANNELS
299
+ );
300
+ gen.audioChannel.write(frame);
301
+ }
302
+ }
303
+ }
304
+ handleInputText(message) {
305
+ const itemId = shortuuid("PI_");
306
+ this.emit("input_audio_transcription_completed", {
307
+ itemId,
308
+ transcript: message.text,
309
+ isFinal: true
310
+ });
311
+ this._chatCtx.addMessage({
312
+ role: "user",
313
+ content: message.text,
314
+ id: itemId
315
+ });
316
+ }
317
+ handleInputSpeechStarted() {
318
+ this.emit("input_speech_started", {});
319
+ this.closeCurrentGeneration({ interrupted: true });
320
+ }
321
+ handleInputSpeechStopped() {
322
+ this.emit("input_speech_stopped", {
323
+ userTranscriptionEnabled: true
324
+ });
325
+ }
326
+ startNewAssistantTurn() {
327
+ if (this.currentGeneration) {
328
+ this.closeCurrentGeneration({ interrupted: true });
329
+ }
330
+ const responseId = shortuuid("PS_");
331
+ const textChannel = stream.createStreamChannel();
332
+ const audioChannel = stream.createStreamChannel();
333
+ const functionChannel = stream.createStreamChannel();
334
+ const messageChannel = stream.createStreamChannel();
335
+ messageChannel.write({
336
+ messageId: responseId,
337
+ textStream: textChannel.stream(),
338
+ audioStream: audioChannel.stream(),
339
+ modalities: Promise.resolve(["audio", "text"])
340
+ });
341
+ this.currentGeneration = {
342
+ responseId,
343
+ messageChannel,
344
+ functionChannel,
345
+ textChannel,
346
+ audioChannel,
347
+ outputText: ""
348
+ };
349
+ this.emit("generation_created", {
350
+ messageStream: messageChannel.stream(),
351
+ functionStream: functionChannel.stream(),
352
+ userInitiated: false,
353
+ responseId
354
+ });
355
+ }
356
+ finishAssistantTurn() {
357
+ this.closeCurrentGeneration({ interrupted: false });
358
+ }
359
+ closeCurrentGeneration({ interrupted }) {
360
+ const gen = this.currentGeneration;
361
+ if (!gen) return;
362
+ if (gen.outputText) {
363
+ this._chatCtx.addMessage({
364
+ role: "assistant",
365
+ content: gen.outputText,
366
+ id: gen.responseId,
367
+ interrupted
368
+ });
369
+ }
370
+ gen.textChannel.close();
371
+ gen.audioChannel.close();
372
+ gen.functionChannel.close();
373
+ gen.messageChannel.close();
374
+ this.currentGeneration = void 0;
375
+ }
376
+ emitError(error, recoverable) {
377
+ this.emit("error", {
378
+ timestamp: Date.now(),
379
+ label: "phonic_realtime",
380
+ type: "realtime_model_error",
381
+ error,
382
+ recoverable
383
+ });
384
+ }
385
+ *resampleAudio(frame) {
386
+ if (this.inputResampler) {
387
+ if (frame.sampleRate !== this.inputResamplerInputRate) {
388
+ this.inputResampler = void 0;
389
+ this.inputResamplerInputRate = void 0;
390
+ }
391
+ }
392
+ if (this.inputResampler === void 0 && (frame.sampleRate !== PHONIC_INPUT_SAMPLE_RATE || frame.channels !== PHONIC_NUM_CHANNELS)) {
393
+ this.inputResampler = new AudioResampler(
394
+ frame.sampleRate,
395
+ PHONIC_INPUT_SAMPLE_RATE,
396
+ PHONIC_NUM_CHANNELS
397
+ );
398
+ this.inputResamplerInputRate = frame.sampleRate;
399
+ }
400
+ if (this.inputResampler) {
401
+ for (const resampledFrame of this.inputResampler.push(frame)) {
402
+ yield resampledFrame;
403
+ }
404
+ } else {
405
+ yield frame;
406
+ }
407
+ }
408
+ }
409
+ export {
410
+ RealtimeModel,
411
+ RealtimeSession
412
+ };
413
+ //# sourceMappingURL=realtime_model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/realtime/realtime_model.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { APIConnectOptions } from '@livekit/agents';\nimport {\n AudioByteStream,\n DEFAULT_API_CONNECT_OPTIONS,\n llm,\n log,\n shortuuid,\n stream,\n} from '@livekit/agents';\nimport { AudioFrame, AudioResampler } from '@livekit/rtc-node';\nimport type { Phonic } from 'phonic';\nimport { PhonicClient } from 'phonic';\nimport type { ServerEvent, Voice } from './api_proto.js';\n\nconst PHONIC_INPUT_SAMPLE_RATE = 44100;\nconst PHONIC_OUTPUT_SAMPLE_RATE = 44100;\nconst PHONIC_NUM_CHANNELS = 1;\nconst PHONIC_INPUT_FRAME_MS = 20;\nconst DEFAULT_MODEL = 'merritt';\nconst WS_CLOSE_NORMAL = 1000;\n\nexport interface RealtimeModelOptions {\n apiKey: string;\n model: string;\n phonicAgent?: string;\n voice?: Voice | string;\n welcomeMessage?: string;\n generateWelcomeMessage?: boolean;\n project?: string;\n connOptions: APIConnectOptions;\n baseUrl?: string;\n languages?: string[];\n audioSpeed?: number;\n phonicTools?: string[];\n boostedKeywords?: string[];\n generateNoInputPokeText?: boolean;\n noInputPokeSec?: number;\n noInputPokeText?: string;\n noInputEndConversationSec?: number;\n /** Set by `updateInstructions` via `voice.Agent` rather than the RealtimeModel constructor */\n instructions?: string;\n}\n\nexport class RealtimeModel extends llm.RealtimeModel {\n /** @internal */\n _options: RealtimeModelOptions;\n\n get model(): string {\n return this._options.model;\n }\n\n constructor(\n options: {\n /**\n * Phonic API key. If not provided, will attempt to read from PHONIC_API_KEY environment variable\n */\n apiKey?: string;\n /**\n * The name of the model to use. Defaults to 'merritt'\n */\n model?: Phonic.ConfigPayload['model'] | string;\n /**\n * Phonic agent to use for the conversation. Options explicitly set here will override the agent settings.\n */\n phonicAgent?: string;\n /**\n * Voice ID for agent outputs\n */\n voice?: Voice;\n /**\n * Welcome message for the agent to say when the conversation starts. Ignored when generateWelcomeMessage is true\n */\n welcomeMessage?: string;\n /**\n * When true, the welcome message will be automatically generated and welcomeMessage will be ignored\n */\n generateWelcomeMessage?: boolean;\n /**\n * Project name to use for the conversation. Defaults to `main`\n */\n project?: string;\n /**\n * ISO 639-1 language codes the agent should recognize and speak\n */\n languages?: string[];\n /**\n * Audio playback speed\n */\n audioSpeed?: number;\n /**\n * Phonic tool names available to the assistant\n */\n phonicTools?: string[];\n /**\n * Keywords to boost in speech recognition\n */\n boostedKeywords?: string[];\n /**\n * Auto-generate poke text when user is silent\n */\n generateNoInputPokeText?: boolean;\n /**\n * Seconds of silence before sending poke message\n */\n noInputPokeSec?: number;\n /**\n * Poke message text (ignored when generateNoInputPokeText is true)\n */\n noInputPokeText?: string;\n /**\n * Seconds of silence before ending conversation\n */\n noInputEndConversationSec?: number;\n /**\n * Connection options for the API connection\n */\n connOptions?: APIConnectOptions;\n baseUrl?: string;\n } = {},\n ) {\n super({\n messageTruncation: false,\n turnDetection: true,\n userTranscription: true,\n // TODO @Phonic-Co: Implement tool support\n // Phonic has automatic tool reply generation, but tools are not supported with LiveKit Agents yet.\n autoToolReplyGeneration: true,\n audioOutput: true,\n });\n\n const apiKey = options.apiKey || process.env.PHONIC_API_KEY;\n if (!apiKey) {\n throw new Error('Phonic API key is required. Provide apiKey or set PHONIC_API_KEY.');\n }\n\n this._options = {\n apiKey,\n voice: options.voice,\n phonicAgent: options.phonicAgent,\n project: options.project,\n welcomeMessage: options.welcomeMessage,\n generateWelcomeMessage: options.generateWelcomeMessage,\n languages: options.languages,\n audioSpeed: options.audioSpeed,\n phonicTools: options.phonicTools,\n boostedKeywords: options.boostedKeywords,\n generateNoInputPokeText: options.generateNoInputPokeText,\n noInputPokeSec: options.noInputPokeSec,\n noInputPokeText: options.noInputPokeText,\n noInputEndConversationSec: options.noInputEndConversationSec,\n connOptions: options.connOptions ?? DEFAULT_API_CONNECT_OPTIONS,\n model: options.model ?? DEFAULT_MODEL,\n baseUrl: options.baseUrl,\n };\n }\n\n /**\n * Create a new realtime session\n */\n session(): RealtimeSession {\n return new RealtimeSession(this);\n }\n\n async close(): Promise<void> {}\n}\n\ninterface GenerationState {\n responseId: string;\n messageChannel: stream.StreamChannel<llm.MessageGeneration>;\n functionChannel: stream.StreamChannel<llm.FunctionCall>;\n textChannel: stream.StreamChannel<string>;\n audioChannel: stream.StreamChannel<AudioFrame>;\n outputText: string;\n}\n\n/**\n * Realtime session for Phonic (https://docs.phonic.co/)\n */\nexport class RealtimeSession extends llm.RealtimeSession {\n private _tools: llm.ToolContext = {};\n private _chatCtx = llm.ChatContext.empty();\n\n private options: RealtimeModelOptions;\n private bstream: AudioByteStream;\n private inputResampler?: AudioResampler;\n private inputResamplerInputRate?: number;\n\n private currentGeneration?: GenerationState;\n private conversationId?: string;\n\n private client: PhonicClient;\n private socket?: Awaited<ReturnType<PhonicClient['conversations']['connect']>>;\n private logger = log();\n private closed = false;\n private configSent = false;\n private instructionsReady: Promise<void>;\n private resolveInstructionsReady: () => void;\n private connectTask: Promise<void>;\n\n constructor(realtimeModel: RealtimeModel) {\n super(realtimeModel);\n this.options = realtimeModel._options;\n\n this.resolveInstructionsReady = () => {};\n this.instructionsReady = new Promise<void>((resolve) => {\n this.resolveInstructionsReady = resolve;\n });\n\n this.client = new PhonicClient({\n apiKey: this.options.apiKey,\n baseUrl: this.options.baseUrl,\n });\n this.bstream = new AudioByteStream(\n PHONIC_INPUT_SAMPLE_RATE,\n PHONIC_NUM_CHANNELS,\n (PHONIC_INPUT_SAMPLE_RATE * PHONIC_INPUT_FRAME_MS) / 1000,\n );\n this.connectTask = this.connect().catch((error: unknown) => {\n const normalizedError = error instanceof Error ? error : new Error(String(error));\n this.emitError(normalizedError, false);\n });\n }\n\n get chatCtx(): llm.ChatContext {\n return this._chatCtx.copy();\n }\n\n get tools(): llm.ToolContext {\n return { ...this._tools };\n }\n\n async updateInstructions(instructions: string): Promise<void> {\n if (this.configSent) {\n this.logger.warn(\n 'updateInstructions called after config was already sent. Phonic does not support updating instructions mid-session.',\n );\n return;\n }\n this.options.instructions = instructions;\n this.resolveInstructionsReady();\n }\n\n async updateChatCtx(_chatCtx: llm.ChatContext): Promise<void> {\n this.logger.warn('updateChatCtx is not supported by the Phonic realtime model.');\n }\n\n async updateTools(tools: llm.ToolContext): Promise<void> {\n if (Object.keys(tools).length > 0) {\n this.logger.warn('Tool use is not supported by the Phonic realtime model.');\n }\n }\n\n updateOptions(_options: { toolChoice?: llm.ToolChoice | null }): void {\n this.logger.warn('updateOptions is not supported by the Phonic realtime model.');\n }\n\n pushAudio(frame: AudioFrame): void {\n if (this.closed) {\n return;\n }\n\n for (const resampledFrame of this.resampleAudio(frame)) {\n for (const chunk of this.bstream.write(resampledFrame.data.buffer as ArrayBuffer)) {\n const bytes = Buffer.from(chunk.data.buffer, chunk.data.byteOffset, chunk.data.byteLength);\n const payload: Phonic.AudioChunkPayload = {\n type: 'audio_chunk',\n audio: bytes.toString('base64'),\n };\n\n if (!this.socket) {\n continue;\n }\n this.socket.sendAudioChunk(payload);\n }\n }\n }\n\n // TODO @Phonic-Co: Implement generateReply\n async generateReply(_instructions?: string): Promise<llm.GenerationCreatedEvent> {\n throw new Error(\n 'generateReply is not yet supported by the Phonic realtime model. Consider using `welcomeMessage` instead.',\n );\n }\n\n async commitAudio(): Promise<void> {\n this.logger.warn('commitAudio is not supported by the Phonic realtime model.');\n }\n async clearAudio(): Promise<void> {\n this.logger.warn('clearAudio is not supported by the Phonic realtime model.');\n }\n\n async interrupt(): Promise<void> {\n this.logger.warn('interrupt is not supported by the Phonic realtime model.');\n }\n\n async truncate(_options: { messageId: string; audioEndMs: number; audioTranscript?: string }) {\n this.logger.warn('truncate is not supported by the Phonic realtime model.');\n }\n\n async close(): Promise<void> {\n this.closed = true;\n this.resolveInstructionsReady();\n this.closeCurrentGeneration({ interrupted: false });\n this.socket?.close();\n await this.connectTask;\n await super.close();\n }\n\n private async connect(): Promise<void> {\n this.socket = await this.client.conversations.connect({\n reconnectAttempts: this.options.connOptions.maxRetry,\n });\n\n if (this.closed) {\n this.socket.close();\n return;\n }\n\n this.socket.on('message', (message: unknown) =>\n this.handleServerMessage(message as ServerEvent),\n );\n this.socket.on('error', (error: Error) => this.emitError(error, false));\n this.socket.on('close', (event: { code?: number }) => {\n this.closeCurrentGeneration({ interrupted: false });\n if (!this.closed && event.code !== WS_CLOSE_NORMAL) {\n this.emitError(new Error(`Phonic STS socket closed with code ${event.code ?? -1}`), false);\n }\n });\n\n await this.socket.waitForOpen();\n await this.instructionsReady;\n if (this.closed) return;\n this.configSent = true;\n this.socket.sendConfig({\n type: 'config',\n model: this.options.model as Phonic.ConfigPayload['model'],\n agent: this.options.phonicAgent,\n project: this.options.project,\n welcome_message: this.options.welcomeMessage,\n generate_welcome_message: this.options.generateWelcomeMessage,\n system_prompt: this.options.instructions,\n voice_id: this.options.voice,\n input_format: 'pcm_44100',\n output_format: 'pcm_44100',\n recognized_languages: this.options.languages,\n audio_speed: this.options.audioSpeed,\n tools: this.options.phonicTools,\n boosted_keywords: this.options.boostedKeywords,\n generate_no_input_poke_text: this.options.generateNoInputPokeText,\n no_input_poke_sec: this.options.noInputPokeSec,\n no_input_poke_text: this.options.noInputPokeText,\n no_input_end_conversation_sec: this.options.noInputEndConversationSec,\n });\n }\n\n private handleServerMessage(message: ServerEvent): void {\n if (this.closed) {\n return;\n }\n\n switch (message.type) {\n case 'assistant_started_speaking':\n this.startNewAssistantTurn();\n break;\n case 'assistant_finished_speaking':\n this.finishAssistantTurn();\n break;\n case 'audio_chunk':\n this.handleAudioChunk(message);\n break;\n case 'input_text':\n this.handleInputText(message);\n break;\n case 'user_started_speaking':\n this.handleInputSpeechStarted();\n break;\n case 'user_finished_speaking':\n this.handleInputSpeechStopped();\n break;\n case 'error':\n this.emitError(new Error(message.error.message), false);\n break;\n case 'tool_call':\n this.emitError(\n new Error(\n `WebSocket tool calls are not yet supported by the Phonic realtime model with LiveKit Agents.`,\n ),\n false,\n );\n break;\n case 'assistant_ended_conversation':\n this.emitError(\n new Error(\n 'assistant_ended_conversation is not supported by the Phonic realtime model with LiveKit Agents.',\n ),\n false,\n );\n break;\n case 'conversation_created':\n this.conversationId = message.conversation_id;\n this.logger.info(`Phonic Conversation began with ID: ${this.conversationId}`);\n break;\n case 'assistant_chose_not_to_respond':\n case 'ready_to_start_conversation':\n case 'input_cancelled':\n case 'tool_call_output_processed':\n case 'tool_call_interrupted':\n case 'dtmf':\n default:\n break;\n }\n }\n\n private handleAudioChunk(message: Phonic.AudioChunkResponsePayload): void {\n /**\n * Although Phonic sends audio chunks when the assistant is not speaking (i.e. containing silence or background noise),\n * we only process the chunks when the assistant is speaking to align with the generations model, whereby new streams are created for each turn.\n */\n const gen = this.currentGeneration;\n if (!gen) return;\n\n if (message.text) {\n gen.outputText += message.text;\n gen.textChannel.write(message.text);\n }\n\n if (message.audio) {\n const bytes = Buffer.from(message.audio, 'base64');\n const sampleCount = Math.floor(bytes.byteLength / Int16Array.BYTES_PER_ELEMENT);\n if (sampleCount > 0) {\n const pcm = new Int16Array(\n bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + sampleCount * Int16Array.BYTES_PER_ELEMENT,\n ),\n );\n const frame = new AudioFrame(\n pcm,\n PHONIC_OUTPUT_SAMPLE_RATE,\n PHONIC_NUM_CHANNELS,\n sampleCount / PHONIC_NUM_CHANNELS,\n );\n gen.audioChannel.write(frame);\n }\n }\n }\n\n private handleInputText(message: Phonic.InputTextPayload): void {\n const itemId = shortuuid('PI_');\n this.emit('input_audio_transcription_completed', {\n itemId,\n transcript: message.text,\n isFinal: true,\n });\n\n this._chatCtx.addMessage({\n role: 'user',\n content: message.text,\n id: itemId,\n });\n }\n\n private handleInputSpeechStarted(): void {\n this.emit('input_speech_started', {});\n this.closeCurrentGeneration({ interrupted: true });\n }\n\n private handleInputSpeechStopped(): void {\n this.emit('input_speech_stopped', {\n userTranscriptionEnabled: true,\n });\n }\n\n private startNewAssistantTurn(): void {\n if (this.currentGeneration) {\n this.closeCurrentGeneration({ interrupted: true });\n }\n\n const responseId = shortuuid('PS_');\n\n const textChannel = stream.createStreamChannel<string>();\n const audioChannel = stream.createStreamChannel<AudioFrame>();\n const functionChannel = stream.createStreamChannel<llm.FunctionCall>();\n const messageChannel = stream.createStreamChannel<llm.MessageGeneration>();\n\n messageChannel.write({\n messageId: responseId,\n textStream: textChannel.stream(),\n audioStream: audioChannel.stream(),\n modalities: Promise.resolve(['audio', 'text']),\n });\n\n this.currentGeneration = {\n responseId,\n messageChannel,\n functionChannel,\n textChannel,\n audioChannel,\n outputText: '',\n };\n\n this.emit('generation_created', {\n messageStream: messageChannel.stream(),\n functionStream: functionChannel.stream(),\n userInitiated: false,\n responseId,\n });\n }\n\n private finishAssistantTurn(): void {\n this.closeCurrentGeneration({ interrupted: false });\n }\n\n private closeCurrentGeneration({ interrupted }: { interrupted: boolean }): void {\n const gen = this.currentGeneration;\n if (!gen) return;\n\n if (gen.outputText) {\n this._chatCtx.addMessage({\n role: 'assistant',\n content: gen.outputText,\n id: gen.responseId,\n interrupted,\n });\n }\n\n gen.textChannel.close();\n gen.audioChannel.close();\n gen.functionChannel.close();\n gen.messageChannel.close();\n this.currentGeneration = undefined;\n }\n\n private emitError(error: Error, recoverable: boolean): void {\n this.emit('error', {\n timestamp: Date.now(),\n label: 'phonic_realtime',\n type: 'realtime_model_error',\n error,\n recoverable,\n } satisfies llm.RealtimeModelError);\n }\n\n private *resampleAudio(frame: AudioFrame): Generator<AudioFrame> {\n if (this.inputResampler) {\n if (frame.sampleRate !== this.inputResamplerInputRate) {\n this.inputResampler = undefined;\n this.inputResamplerInputRate = undefined;\n }\n }\n\n if (\n this.inputResampler === undefined &&\n (frame.sampleRate !== PHONIC_INPUT_SAMPLE_RATE || frame.channels !== PHONIC_NUM_CHANNELS)\n ) {\n this.inputResampler = new AudioResampler(\n frame.sampleRate,\n PHONIC_INPUT_SAMPLE_RATE,\n PHONIC_NUM_CHANNELS,\n );\n this.inputResamplerInputRate = frame.sampleRate;\n }\n\n if (this.inputResampler) {\n for (const resampledFrame of this.inputResampler.push(frame)) {\n yield resampledFrame;\n }\n } else {\n yield frame;\n }\n }\n}\n"],"mappings":"AAIA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,YAAY,sBAAsB;AAE3C,SAAS,oBAAoB;AAG7B,MAAM,2BAA2B;AACjC,MAAM,4BAA4B;AAClC,MAAM,sBAAsB;AAC5B,MAAM,wBAAwB;AAC9B,MAAM,gBAAgB;AACtB,MAAM,kBAAkB;AAwBjB,MAAM,sBAAsB,IAAI,cAAc;AAAA;AAAA,EAEnD;AAAA,EAEA,IAAI,QAAgB;AAClB,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA,EAEA,YACE,UAkEI,CAAC,GACL;AACA,UAAM;AAAA,MACJ,mBAAmB;AAAA,MACnB,eAAe;AAAA,MACf,mBAAmB;AAAA;AAAA;AAAA,MAGnB,yBAAyB;AAAA,MACzB,aAAa;AAAA,IACf,CAAC;AAED,UAAM,SAAS,QAAQ,UAAU,QAAQ,IAAI;AAC7C,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,mEAAmE;AAAA,IACrF;AAEA,SAAK,WAAW;AAAA,MACd;AAAA,MACA,OAAO,QAAQ;AAAA,MACf,aAAa,QAAQ;AAAA,MACrB,SAAS,QAAQ;AAAA,MACjB,gBAAgB,QAAQ;AAAA,MACxB,wBAAwB,QAAQ;AAAA,MAChC,WAAW,QAAQ;AAAA,MACnB,YAAY,QAAQ;AAAA,MACpB,aAAa,QAAQ;AAAA,MACrB,iBAAiB,QAAQ;AAAA,MACzB,yBAAyB,QAAQ;AAAA,MACjC,gBAAgB,QAAQ;AAAA,MACxB,iBAAiB,QAAQ;AAAA,MACzB,2BAA2B,QAAQ;AAAA,MACnC,aAAa,QAAQ,eAAe;AAAA,MACpC,OAAO,QAAQ,SAAS;AAAA,MACxB,SAAS,QAAQ;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAA2B;AACzB,WAAO,IAAI,gBAAgB,IAAI;AAAA,EACjC;AAAA,EAEA,MAAM,QAAuB;AAAA,EAAC;AAChC;AAcO,MAAM,wBAAwB,IAAI,gBAAgB;AAAA,EAC/C,SAA0B,CAAC;AAAA,EAC3B,WAAW,IAAI,YAAY,MAAM;AAAA,EAEjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA,SAAS,IAAI;AAAA,EACb,SAAS;AAAA,EACT,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,eAA8B;AACxC,UAAM,aAAa;AACnB,SAAK,UAAU,cAAc;AAE7B,SAAK,2BAA2B,MAAM;AAAA,IAAC;AACvC,SAAK,oBAAoB,IAAI,QAAc,CAAC,YAAY;AACtD,WAAK,2BAA2B;AAAA,IAClC,CAAC;AAED,SAAK,SAAS,IAAI,aAAa;AAAA,MAC7B,QAAQ,KAAK,QAAQ;AAAA,MACrB,SAAS,KAAK,QAAQ;AAAA,IACxB,CAAC;AACD,SAAK,UAAU,IAAI;AAAA,MACjB;AAAA,MACA;AAAA,MACC,2BAA2B,wBAAyB;AAAA,IACvD;AACA,SAAK,cAAc,KAAK,QAAQ,EAAE,MAAM,CAAC,UAAmB;AAC1D,YAAM,kBAAkB,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAChF,WAAK,UAAU,iBAAiB,KAAK;AAAA,IACvC,CAAC;AAAA,EACH;AAAA,EAEA,IAAI,UAA2B;AAC7B,WAAO,KAAK,SAAS,KAAK;AAAA,EAC5B;AAAA,EAEA,IAAI,QAAyB;AAC3B,WAAO,EAAE,GAAG,KAAK,OAAO;AAAA,EAC1B;AAAA,EAEA,MAAM,mBAAmB,cAAqC;AAC5D,QAAI,KAAK,YAAY;AACnB,WAAK,OAAO;AAAA,QACV;AAAA,MACF;AACA;AAAA,IACF;AACA,SAAK,QAAQ,eAAe;AAC5B,SAAK,yBAAyB;AAAA,EAChC;AAAA,EAEA,MAAM,cAAc,UAA0C;AAC5D,SAAK,OAAO,KAAK,8DAA8D;AAAA,EACjF;AAAA,EAEA,MAAM,YAAY,OAAuC;AACvD,QAAI,OAAO,KAAK,KAAK,EAAE,SAAS,GAAG;AACjC,WAAK,OAAO,KAAK,yDAAyD;AAAA,IAC5E;AAAA,EACF;AAAA,EAEA,cAAc,UAAwD;AACpE,SAAK,OAAO,KAAK,8DAA8D;AAAA,EACjF;AAAA,EAEA,UAAU,OAAyB;AACjC,QAAI,KAAK,QAAQ;AACf;AAAA,IACF;AAEA,eAAW,kBAAkB,KAAK,cAAc,KAAK,GAAG;AACtD,iBAAW,SAAS,KAAK,QAAQ,MAAM,eAAe,KAAK,MAAqB,GAAG;AACjF,cAAM,QAAQ,OAAO,KAAK,MAAM,KAAK,QAAQ,MAAM,KAAK,YAAY,MAAM,KAAK,UAAU;AACzF,cAAM,UAAoC;AAAA,UACxC,MAAM;AAAA,UACN,OAAO,MAAM,SAAS,QAAQ;AAAA,QAChC;AAEA,YAAI,CAAC,KAAK,QAAQ;AAChB;AAAA,QACF;AACA,aAAK,OAAO,eAAe,OAAO;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,cAAc,eAA6D;AAC/E,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,cAA6B;AACjC,SAAK,OAAO,KAAK,4DAA4D;AAAA,EAC/E;AAAA,EACA,MAAM,aAA4B;AAChC,SAAK,OAAO,KAAK,2DAA2D;AAAA,EAC9E;AAAA,EAEA,MAAM,YAA2B;AAC/B,SAAK,OAAO,KAAK,0DAA0D;AAAA,EAC7E;AAAA,EAEA,MAAM,SAAS,UAA+E;AAC5F,SAAK,OAAO,KAAK,yDAAyD;AAAA,EAC5E;AAAA,EAEA,MAAM,QAAuB;AA9S/B;AA+SI,SAAK,SAAS;AACd,SAAK,yBAAyB;AAC9B,SAAK,uBAAuB,EAAE,aAAa,MAAM,CAAC;AAClD,eAAK,WAAL,mBAAa;AACb,UAAM,KAAK;AACX,UAAM,MAAM,MAAM;AAAA,EACpB;AAAA,EAEA,MAAc,UAAyB;AACrC,SAAK,SAAS,MAAM,KAAK,OAAO,cAAc,QAAQ;AAAA,MACpD,mBAAmB,KAAK,QAAQ,YAAY;AAAA,IAC9C,CAAC;AAED,QAAI,KAAK,QAAQ;AACf,WAAK,OAAO,MAAM;AAClB;AAAA,IACF;AAEA,SAAK,OAAO;AAAA,MAAG;AAAA,MAAW,CAAC,YACzB,KAAK,oBAAoB,OAAsB;AAAA,IACjD;AACA,SAAK,OAAO,GAAG,SAAS,CAAC,UAAiB,KAAK,UAAU,OAAO,KAAK,CAAC;AACtE,SAAK,OAAO,GAAG,SAAS,CAAC,UAA6B;AACpD,WAAK,uBAAuB,EAAE,aAAa,MAAM,CAAC;AAClD,UAAI,CAAC,KAAK,UAAU,MAAM,SAAS,iBAAiB;AAClD,aAAK,UAAU,IAAI,MAAM,sCAAsC,MAAM,QAAQ,EAAE,EAAE,GAAG,KAAK;AAAA,MAC3F;AAAA,IACF,CAAC;AAED,UAAM,KAAK,OAAO,YAAY;AAC9B,UAAM,KAAK;AACX,QAAI,KAAK,OAAQ;AACjB,SAAK,aAAa;AAClB,SAAK,OAAO,WAAW;AAAA,MACrB,MAAM;AAAA,MACN,OAAO,KAAK,QAAQ;AAAA,MACpB,OAAO,KAAK,QAAQ;AAAA,MACpB,SAAS,KAAK,QAAQ;AAAA,MACtB,iBAAiB,KAAK,QAAQ;AAAA,MAC9B,0BAA0B,KAAK,QAAQ;AAAA,MACvC,eAAe,KAAK,QAAQ;AAAA,MAC5B,UAAU,KAAK,QAAQ;AAAA,MACvB,cAAc;AAAA,MACd,eAAe;AAAA,MACf,sBAAsB,KAAK,QAAQ;AAAA,MACnC,aAAa,KAAK,QAAQ;AAAA,MAC1B,OAAO,KAAK,QAAQ;AAAA,MACpB,kBAAkB,KAAK,QAAQ;AAAA,MAC/B,6BAA6B,KAAK,QAAQ;AAAA,MAC1C,mBAAmB,KAAK,QAAQ;AAAA,MAChC,oBAAoB,KAAK,QAAQ;AAAA,MACjC,+BAA+B,KAAK,QAAQ;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA,EAEQ,oBAAoB,SAA4B;AACtD,QAAI,KAAK,QAAQ;AACf;AAAA,IACF;AAEA,YAAQ,QAAQ,MAAM;AAAA,MACpB,KAAK;AACH,aAAK,sBAAsB;AAC3B;AAAA,MACF,KAAK;AACH,aAAK,oBAAoB;AACzB;AAAA,MACF,KAAK;AACH,aAAK,iBAAiB,OAAO;AAC7B;AAAA,MACF,KAAK;AACH,aAAK,gBAAgB,OAAO;AAC5B;AAAA,MACF,KAAK;AACH,aAAK,yBAAyB;AAC9B;AAAA,MACF,KAAK;AACH,aAAK,yBAAyB;AAC9B;AAAA,MACF,KAAK;AACH,aAAK,UAAU,IAAI,MAAM,QAAQ,MAAM,OAAO,GAAG,KAAK;AACtD;AAAA,MACF,KAAK;AACH,aAAK;AAAA,UACH,IAAI;AAAA,YACF;AAAA,UACF;AAAA,UACA;AAAA,QACF;AACA;AAAA,MACF,KAAK;AACH,aAAK;AAAA,UACH,IAAI;AAAA,YACF;AAAA,UACF;AAAA,UACA;AAAA,QACF;AACA;AAAA,MACF,KAAK;AACH,aAAK,iBAAiB,QAAQ;AAC9B,aAAK,OAAO,KAAK,sCAAsC,KAAK,cAAc,EAAE;AAC5E;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL;AACE;AAAA,IACJ;AAAA,EACF;AAAA,EAEQ,iBAAiB,SAAiD;AAKxE,UAAM,MAAM,KAAK;AACjB,QAAI,CAAC,IAAK;AAEV,QAAI,QAAQ,MAAM;AAChB,UAAI,cAAc,QAAQ;AAC1B,UAAI,YAAY,MAAM,QAAQ,IAAI;AAAA,IACpC;AAEA,QAAI,QAAQ,OAAO;AACjB,YAAM,QAAQ,OAAO,KAAK,QAAQ,OAAO,QAAQ;AACjD,YAAM,cAAc,KAAK,MAAM,MAAM,aAAa,WAAW,iBAAiB;AAC9E,UAAI,cAAc,GAAG;AACnB,cAAM,MAAM,IAAI;AAAA,UACd,MAAM,OAAO;AAAA,YACX,MAAM;AAAA,YACN,MAAM,aAAa,cAAc,WAAW;AAAA,UAC9C;AAAA,QACF;AACA,cAAM,QAAQ,IAAI;AAAA,UAChB;AAAA,UACA;AAAA,UACA;AAAA,UACA,cAAc;AAAA,QAChB;AACA,YAAI,aAAa,MAAM,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,gBAAgB,SAAwC;AAC9D,UAAM,SAAS,UAAU,KAAK;AAC9B,SAAK,KAAK,uCAAuC;AAAA,MAC/C;AAAA,MACA,YAAY,QAAQ;AAAA,MACpB,SAAS;AAAA,IACX,CAAC;AAED,SAAK,SAAS,WAAW;AAAA,MACvB,MAAM;AAAA,MACN,SAAS,QAAQ;AAAA,MACjB,IAAI;AAAA,IACN,CAAC;AAAA,EACH;AAAA,EAEQ,2BAAiC;AACvC,SAAK,KAAK,wBAAwB,CAAC,CAAC;AACpC,SAAK,uBAAuB,EAAE,aAAa,KAAK,CAAC;AAAA,EACnD;AAAA,EAEQ,2BAAiC;AACvC,SAAK,KAAK,wBAAwB;AAAA,MAChC,0BAA0B;AAAA,IAC5B,CAAC;AAAA,EACH;AAAA,EAEQ,wBAA8B;AACpC,QAAI,KAAK,mBAAmB;AAC1B,WAAK,uBAAuB,EAAE,aAAa,KAAK,CAAC;AAAA,IACnD;AAEA,UAAM,aAAa,UAAU,KAAK;AAElC,UAAM,cAAc,OAAO,oBAA4B;AACvD,UAAM,eAAe,OAAO,oBAAgC;AAC5D,UAAM,kBAAkB,OAAO,oBAAsC;AACrE,UAAM,iBAAiB,OAAO,oBAA2C;AAEzE,mBAAe,MAAM;AAAA,MACnB,WAAW;AAAA,MACX,YAAY,YAAY,OAAO;AAAA,MAC/B,aAAa,aAAa,OAAO;AAAA,MACjC,YAAY,QAAQ,QAAQ,CAAC,SAAS,MAAM,CAAC;AAAA,IAC/C,CAAC;AAED,SAAK,oBAAoB;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AAAA,IACd;AAEA,SAAK,KAAK,sBAAsB;AAAA,MAC9B,eAAe,eAAe,OAAO;AAAA,MACrC,gBAAgB,gBAAgB,OAAO;AAAA,MACvC,eAAe;AAAA,MACf;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,sBAA4B;AAClC,SAAK,uBAAuB,EAAE,aAAa,MAAM,CAAC;AAAA,EACpD;AAAA,EAEQ,uBAAuB,EAAE,YAAY,GAAmC;AAC9E,UAAM,MAAM,KAAK;AACjB,QAAI,CAAC,IAAK;AAEV,QAAI,IAAI,YAAY;AAClB,WAAK,SAAS,WAAW;AAAA,QACvB,MAAM;AAAA,QACN,SAAS,IAAI;AAAA,QACb,IAAI,IAAI;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI,YAAY,MAAM;AACtB,QAAI,aAAa,MAAM;AACvB,QAAI,gBAAgB,MAAM;AAC1B,QAAI,eAAe,MAAM;AACzB,SAAK,oBAAoB;AAAA,EAC3B;AAAA,EAEQ,UAAU,OAAc,aAA4B;AAC1D,SAAK,KAAK,SAAS;AAAA,MACjB,WAAW,KAAK,IAAI;AAAA,MACpB,OAAO;AAAA,MACP,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF,CAAkC;AAAA,EACpC;AAAA,EAEA,CAAS,cAAc,OAA0C;AAC/D,QAAI,KAAK,gBAAgB;AACvB,UAAI,MAAM,eAAe,KAAK,yBAAyB;AACrD,aAAK,iBAAiB;AACtB,aAAK,0BAA0B;AAAA,MACjC;AAAA,IACF;AAEA,QACE,KAAK,mBAAmB,WACvB,MAAM,eAAe,4BAA4B,MAAM,aAAa,sBACrE;AACA,WAAK,iBAAiB,IAAI;AAAA,QACxB,MAAM;AAAA,QACN;AAAA,QACA;AAAA,MACF;AACA,WAAK,0BAA0B,MAAM;AAAA,IACvC;AAEA,QAAI,KAAK,gBAAgB;AACvB,iBAAW,kBAAkB,KAAK,eAAe,KAAK,KAAK,GAAG;AAC5D,cAAM;AAAA,MACR;AAAA,IACF,OAAO;AACL,YAAM;AAAA,IACR;AAAA,EACF;AACF;","names":[]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@livekit/agents-plugin-phonic",
3
+ "version": "1.0.46",
4
+ "description": "Phonic STS plugin for LiveKit Node Agents",
5
+ "main": "dist/index.js",
6
+ "require": "dist/index.cjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ "import": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "require": {
14
+ "types": "./dist/index.d.cts",
15
+ "default": "./dist/index.cjs"
16
+ }
17
+ },
18
+ "author": "LiveKit",
19
+ "type": "module",
20
+ "repository": "git@github.com:livekit/agents-js.git",
21
+ "license": "Apache-2.0",
22
+ "files": [
23
+ "dist",
24
+ "src",
25
+ "README.md"
26
+ ],
27
+ "devDependencies": {
28
+ "@livekit/rtc-node": "^0.13.24",
29
+ "@microsoft/api-extractor": "^7.35.0",
30
+ "tsup": "^8.3.5",
31
+ "typescript": "^5.0.0",
32
+ "@livekit/agents": "1.0.46"
33
+ },
34
+ "dependencies": {
35
+ "phonic": "^0.30.37"
36
+ },
37
+ "peerDependencies": {
38
+ "@livekit/rtc-node": "^0.13.24",
39
+ "@livekit/agents": "1.0.46"
40
+ },
41
+ "scripts": {
42
+ "build": "tsup --onSuccess \"pnpm build:types\"",
43
+ "build:types": "tsc --declaration --emitDeclarationOnly && node ../../scripts/copyDeclarationOutput.js",
44
+ "clean": "rm -rf dist",
45
+ "clean:build": "pnpm clean && pnpm build",
46
+ "lint": "eslint -f unix \"src/**/*.{ts,js}\"",
47
+ "api:check": "api-extractor run --typescript-compiler-folder ../../node_modules/typescript",
48
+ "api:update": "api-extractor run --local --typescript-compiler-folder ../../node_modules/typescript --verbose"
49
+ }
50
+ }