@livekit/agents-plugin-openai 1.2.3 → 1.2.5

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.
Files changed (39) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.js +1 -1
  3. package/dist/realtime/realtime_model.cjs +73 -9
  4. package/dist/realtime/realtime_model.cjs.map +1 -1
  5. package/dist/realtime/realtime_model.d.ts.map +1 -1
  6. package/dist/realtime/realtime_model.js +73 -9
  7. package/dist/realtime/realtime_model.js.map +1 -1
  8. package/dist/realtime/realtime_model_beta.cjs +5 -1
  9. package/dist/realtime/realtime_model_beta.cjs.map +1 -1
  10. package/dist/realtime/realtime_model_beta.d.ts.map +1 -1
  11. package/dist/realtime/realtime_model_beta.js +5 -1
  12. package/dist/realtime/realtime_model_beta.js.map +1 -1
  13. package/dist/responses/llm.cjs +5 -1
  14. package/dist/responses/llm.cjs.map +1 -1
  15. package/dist/responses/llm.d.cts +2 -0
  16. package/dist/responses/llm.d.ts +2 -0
  17. package/dist/responses/llm.d.ts.map +1 -1
  18. package/dist/responses/llm.js +5 -1
  19. package/dist/responses/llm.js.map +1 -1
  20. package/dist/ws/llm.cjs +5 -1
  21. package/dist/ws/llm.cjs.map +1 -1
  22. package/dist/ws/llm.d.cts +2 -0
  23. package/dist/ws/llm.d.ts +2 -0
  24. package/dist/ws/llm.d.ts.map +1 -1
  25. package/dist/ws/llm.js +5 -1
  26. package/dist/ws/llm.js.map +1 -1
  27. package/dist/ws/types.cjs +1 -0
  28. package/dist/ws/types.cjs.map +1 -1
  29. package/dist/ws/types.d.cts +2 -0
  30. package/dist/ws/types.d.ts +2 -0
  31. package/dist/ws/types.d.ts.map +1 -1
  32. package/dist/ws/types.js +1 -0
  33. package/dist/ws/types.js.map +1 -1
  34. package/package.json +7 -7
  35. package/src/realtime/realtime_model.ts +80 -10
  36. package/src/realtime/realtime_model_beta.ts +4 -0
  37. package/src/responses/llm.ts +7 -0
  38. package/src/ws/llm.ts +7 -0
  39. package/src/ws/types.ts +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livekit/agents-plugin-openai",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "OpenAI plugin for LiveKit Node Agents",
5
5
  "main": "dist/index.js",
6
6
  "require": "dist/index.cjs",
@@ -25,14 +25,14 @@
25
25
  "README.md"
26
26
  ],
27
27
  "devDependencies": {
28
- "@livekit/rtc-node": "^0.13.24",
28
+ "@livekit/rtc-node": "^0.13.25",
29
29
  "@microsoft/api-extractor": "^7.35.0",
30
30
  "@types/ws": "^8.5.10",
31
31
  "tsup": "^8.3.5",
32
32
  "typescript": "^5.0.0",
33
- "@livekit/agents": "1.2.3",
34
- "@livekit/agents-plugin-silero": "1.2.3",
35
- "@livekit/agents-plugins-test": "1.2.3"
33
+ "@livekit/agents": "1.2.5",
34
+ "@livekit/agents-plugin-silero": "1.2.5",
35
+ "@livekit/agents-plugins-test": "1.2.5"
36
36
  },
37
37
  "dependencies": {
38
38
  "@livekit/mutex": "^1.1.1",
@@ -40,9 +40,9 @@
40
40
  "ws": "^8.18.0"
41
41
  },
42
42
  "peerDependencies": {
43
- "@livekit/rtc-node": "^0.13.24",
43
+ "@livekit/rtc-node": "^0.13.25",
44
44
  "zod": "^3.25.76 || ^4.1.8",
45
- "@livekit/agents": "1.2.3"
45
+ "@livekit/agents": "1.2.5"
46
46
  },
47
47
  "scripts": {
48
48
  "build": "tsup --onSuccess \"pnpm build:types\"",
@@ -185,6 +185,10 @@ export class RealtimeModel extends llm.RealtimeModel {
185
185
  autoToolReplyGeneration: false,
186
186
  audioOutput: modalities.includes('audio'),
187
187
  manualFunctionCalls: true,
188
+ midSessionChatCtxUpdate: true,
189
+ midSessionInstructionsUpdate: true,
190
+ midSessionToolsUpdate: true,
191
+ perResponseToolChoice: true,
188
192
  });
189
193
 
190
194
  const isAzure = !!(options.apiVersion || options.entraToken || options.azureDeployment);
@@ -477,17 +481,75 @@ export class RealtimeSession extends llm.RealtimeSession {
477
481
  async updateChatCtx(_chatCtx: llm.ChatContext): Promise<void> {
478
482
  const unlock = await this.updateChatCtxLock.lock();
479
483
  try {
484
+ const validation = llm.validateChatContextStructure(_chatCtx);
485
+ const blockingErrors = validation.issues.filter(
486
+ (issue: llm.ChatContextValidationIssue) =>
487
+ issue.severity === 'error' && issue.code !== 'timestamp_order',
488
+ );
489
+ const timestampOrderIssue = validation.issues.find(
490
+ (issue: llm.ChatContextValidationIssue) => issue.code === 'timestamp_order',
491
+ );
492
+ if (blockingErrors.length > 0) {
493
+ this.#logger.error(
494
+ { issues: validation.issues, blockingErrors },
495
+ 'Invalid chat context supplied to updateChatCtx',
496
+ );
497
+ throw new Error(
498
+ `Invalid chat context: ${validation.errors} errors, ${validation.warnings} warnings`,
499
+ );
500
+ }
501
+ if (timestampOrderIssue) {
502
+ this.#logger.warn(
503
+ { timestampOrderIssue },
504
+ 'Proceeding with non-monotonic createdAt ordering in realtime chat context',
505
+ );
506
+ }
507
+ if (lkOaiDebug > 0 && validation.warnings > 0) {
508
+ this.#logger.debug(
509
+ {
510
+ warnings: validation.warnings,
511
+ issues: validation.issues,
512
+ },
513
+ 'Chat context warnings detected before realtime update',
514
+ );
515
+ }
516
+
480
517
  const events = await this.createChatCtxUpdateEvents(_chatCtx);
481
518
  const futures: Future<void>[] = [];
519
+ const ownedCreateFutures: { [id: string]: Future<void> } = {};
520
+ const ownedDeleteFutures: { [id: string]: Future<void> } = {};
521
+
522
+ const cleanupTimedOutFutures = () => {
523
+ // remove timed-out entries so late server acks
524
+ // don't resolve stale futures from a previous updateChatCtx call.
525
+ for (const [itemId, future] of Object.entries(ownedDeleteFutures)) {
526
+ if (this.itemDeleteFutures[itemId] === future) {
527
+ delete this.itemDeleteFutures[itemId];
528
+ }
529
+ }
530
+ for (const [itemId, future] of Object.entries(ownedCreateFutures)) {
531
+ if (this.itemCreateFutures[itemId] === future) {
532
+ delete this.itemCreateFutures[itemId];
533
+ }
534
+ }
535
+ };
482
536
 
483
537
  for (const event of events) {
484
- const future = new Future<void>();
485
- futures.push(future);
486
-
487
538
  if (event.type === 'conversation.item.create') {
539
+ const future = new Future<void>();
540
+ futures.push(future);
488
541
  this.itemCreateFutures[event.item.id] = future;
542
+ ownedCreateFutures[event.item.id] = future;
489
543
  } else if (event.type == 'conversation.item.delete') {
544
+ const existingDeleteFuture = this.itemDeleteFutures[event.item_id];
545
+ if (existingDeleteFuture) {
546
+ futures.push(existingDeleteFuture);
547
+ continue;
548
+ }
549
+ const future = new Future<void>();
550
+ futures.push(future);
490
551
  this.itemDeleteFutures[event.item_id] = future;
552
+ ownedDeleteFutures[event.item_id] = future;
491
553
  }
492
554
 
493
555
  this.sendEvent(event);
@@ -497,13 +559,21 @@ export class RealtimeSession extends llm.RealtimeSession {
497
559
  return;
498
560
  }
499
561
 
500
- // wait for futures to resolve or timeout
501
- await Promise.race([
502
- Promise.all(futures),
503
- delay(5000).then(() => {
504
- throw new Error('Chat ctx update events timed out');
505
- }),
506
- ]);
562
+ // wait for futures to resolve or timeout.
563
+ // Cancel the timeout branch once futures resolve to avoid stale cleanup.
564
+ const timeoutController = new AbortController();
565
+ const timeoutPromise = delay(5000, { signal: timeoutController.signal }).then(() => {
566
+ cleanupTimedOutFutures();
567
+ throw new Error('Chat ctx update events timed out');
568
+ });
569
+
570
+ try {
571
+ await Promise.race([Promise.all(futures), timeoutPromise]);
572
+ } finally {
573
+ if (!timeoutController.signal.aborted) {
574
+ timeoutController.abort();
575
+ }
576
+ }
507
577
  } catch (e) {
508
578
  this.#logger.error((e as Error).message);
509
579
  throw e;
@@ -177,6 +177,10 @@ export class RealtimeModel extends llm.RealtimeModel {
177
177
  autoToolReplyGeneration: false,
178
178
  audioOutput: modalities.includes('audio'),
179
179
  manualFunctionCalls: true,
180
+ midSessionChatCtxUpdate: true,
181
+ midSessionInstructionsUpdate: true,
182
+ midSessionToolsUpdate: true,
183
+ perResponseToolChoice: true,
180
184
  });
181
185
 
182
186
  const isAzure = !!(options.apiVersion || options.entraToken || options.azureDeployment);
@@ -26,6 +26,8 @@ export interface LLMOptions {
26
26
  store?: boolean;
27
27
  metadata?: Record<string, string>;
28
28
  strictToolSchema?: boolean;
29
+ /** Specifies the processing tier (e.g. 'auto', 'default', 'priority', 'flex'). */
30
+ serviceTier?: string;
29
31
 
30
32
  /**
31
33
  * Whether to use the WebSocket API.
@@ -114,6 +116,10 @@ class ResponsesHttpLLM extends llm.LLM {
114
116
  modelOptions.metadata = this.#opts.metadata;
115
117
  }
116
118
 
119
+ if (this.#opts.serviceTier) {
120
+ modelOptions.service_tier = this.#opts.serviceTier;
121
+ }
122
+
117
123
  return new ResponsesHttpLLMStream(this, {
118
124
  model: this.#opts.model,
119
125
  client: this.#client,
@@ -333,6 +339,7 @@ class ResponsesHttpLLMStream extends llm.LLMStream {
333
339
  promptTokens: event.response.usage.input_tokens,
334
340
  promptCachedTokens: event.response.usage.input_tokens_details.cached_tokens,
335
341
  totalTokens: event.response.usage.total_tokens,
342
+ serviceTier: event.response.service_tier ?? undefined,
336
343
  },
337
344
  };
338
345
  }
package/src/ws/llm.ts CHANGED
@@ -141,6 +141,8 @@ export interface WSLLMOptions {
141
141
  store?: boolean;
142
142
  metadata?: Record<string, string>;
143
143
  strictToolSchema?: boolean;
144
+ /** Specifies the processing tier (e.g. 'auto', 'default', 'priority', 'flex'). */
145
+ serviceTier?: string;
144
146
  }
145
147
 
146
148
  const defaultLLMOptions: WSLLMOptions = {
@@ -266,6 +268,10 @@ export class WSLLM extends llm.LLM {
266
268
  modelOptions.metadata = this.#opts.metadata;
267
269
  }
268
270
 
271
+ if (this.#opts.serviceTier) {
272
+ modelOptions.service_tier = this.#opts.serviceTier;
273
+ }
274
+
269
275
  let inputChatCtx = chatCtx;
270
276
  let prevResponseId: string | undefined;
271
277
  const canUseStoredResponse = modelOptions.store !== false;
@@ -584,6 +590,7 @@ export class WSLLMStream extends llm.LLMStream {
584
590
  promptTokens: event.response.usage.input_tokens,
585
591
  promptCachedTokens: event.response.usage.input_tokens_details.cached_tokens,
586
592
  totalTokens: event.response.usage.total_tokens,
593
+ serviceTier: event.response.service_tier ?? undefined,
587
594
  },
588
595
  };
589
596
  }
package/src/ws/types.ts CHANGED
@@ -66,6 +66,7 @@ export const wsResponseCompletedEventSchema = z.object({
66
66
  response: z
67
67
  .object({
68
68
  id: z.string(),
69
+ service_tier: z.string().nullable().optional(),
69
70
  usage: z
70
71
  .object({
71
72
  output_tokens: z.number(),