@pipecat-ai/client-react 1.2.0 → 1.2.1

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.
@@ -31,7 +31,7 @@ import {Provider as $h9lXz$Provider} from "jotai/react";
31
31
 
32
32
 
33
33
  var $ad20387e24e513d4$exports = {};
34
- $ad20387e24e513d4$exports = JSON.parse("{\"name\":\"@pipecat-ai/client-react\",\"version\":\"1.2.0\",\"license\":\"BSD-2-Clause\",\"main\":\"dist/index.js\",\"module\":\"dist/index.module.js\",\"types\":\"dist/index.d.ts\",\"source\":\"src/index.ts\",\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/pipecat-ai/pipecat-client-web.git\"},\"exports\":{\".\":{\"types\":\"./dist/index.d.ts\",\"import\":\"./dist/index.module.js\",\"require\":\"./dist/index.js\"}},\"files\":[\"dist\",\"package.json\",\"README.md\"],\"scripts\":{\"build\":\"parcel build --no-cache\",\"dev\":\"parcel watch\",\"lint\":\"eslint . --report-unused-disable-directives --max-warnings 0 --ignore-pattern 'dist/'\",\"test\":\"jest\"},\"jest\":{\"preset\":\"ts-jest\",\"testEnvironment\":\"jsdom\",\"moduleNameMapper\":{\"^@/(.*)$\":\"<rootDir>/src/$1\"},\"transform\":{\"^.+\\\\.tsx?$\":[\"ts-jest\",{\"tsconfig\":{\"jsx\":\"react-jsx\",\"module\":\"commonjs\",\"moduleResolution\":\"node\",\"esModuleInterop\":true,\"allowImportingTsExtensions\":false,\"noUnusedLocals\":false,\"noUnusedParameters\":false}}]}},\"devDependencies\":{\"@jest/globals\":\"^30.2.0\",\"@pipecat-ai/client-js\":\"*\",\"@types/jest\":\"^30.0.0\",\"@types/react\":\"^18.3.3\",\"@types/react-dom\":\"^18.3.0\",\"@typescript-eslint/eslint-plugin\":\"^8.32.0\",\"eslint\":\"^9.11.1\",\"eslint-config-prettier\":\"^9.1.0\",\"eslint-plugin-react-hooks\":\"^5.2.0\",\"eslint-plugin-simple-import-sort\":\"^12.1.1\",\"jest\":\"^30.2.0\",\"jest-environment-jsdom\":\"^30.2.0\",\"parcel\":\"^2.12.0\",\"react\":\"^18.3.1\",\"react-dom\":\"^18.3.1\",\"ts-jest\":\"^29.4.6\",\"typescript\":\"^5.2.2\"},\"peerDependencies\":{\"@pipecat-ai/client-js\":\"*\",\"react\":\">=18\",\"react-dom\":\">=18\"},\"dependencies\":{\"jotai\":\"^2.9.0\"}}");
34
+ $ad20387e24e513d4$exports = JSON.parse("{\"name\":\"@pipecat-ai/client-react\",\"version\":\"1.2.1\",\"license\":\"BSD-2-Clause\",\"main\":\"dist/index.js\",\"module\":\"dist/index.module.js\",\"types\":\"dist/index.d.ts\",\"source\":\"src/index.ts\",\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/pipecat-ai/pipecat-client-web.git\"},\"exports\":{\".\":{\"types\":\"./dist/index.d.ts\",\"import\":\"./dist/index.module.js\",\"require\":\"./dist/index.js\"}},\"files\":[\"dist\",\"package.json\",\"README.md\"],\"scripts\":{\"build\":\"parcel build --no-cache\",\"dev\":\"parcel watch\",\"lint\":\"eslint . --report-unused-disable-directives --max-warnings 0 --ignore-pattern 'dist/'\",\"test\":\"jest\"},\"jest\":{\"preset\":\"ts-jest\",\"testEnvironment\":\"jsdom\",\"moduleNameMapper\":{\"^@/(.*)$\":\"<rootDir>/src/$1\"},\"transform\":{\"^.+\\\\.tsx?$\":[\"ts-jest\",{\"tsconfig\":{\"jsx\":\"react-jsx\",\"module\":\"commonjs\",\"moduleResolution\":\"node\",\"esModuleInterop\":true,\"allowImportingTsExtensions\":false,\"noUnusedLocals\":false,\"noUnusedParameters\":false}}]}},\"devDependencies\":{\"@jest/globals\":\"^30.2.0\",\"@pipecat-ai/client-js\":\"*\",\"@types/jest\":\"^30.0.0\",\"@types/react\":\"^18.3.3\",\"@types/react-dom\":\"^18.3.0\",\"@typescript-eslint/eslint-plugin\":\"^8.32.0\",\"eslint\":\"^9.11.1\",\"eslint-config-prettier\":\"^9.1.0\",\"eslint-plugin-react-hooks\":\"^5.2.0\",\"eslint-plugin-simple-import-sort\":\"^12.1.1\",\"jest\":\"^30.2.0\",\"jest-environment-jsdom\":\"^30.2.0\",\"parcel\":\"^2.12.0\",\"react\":\"^18.3.1\",\"react-dom\":\"^18.3.1\",\"ts-jest\":\"^29.4.6\",\"typescript\":\"^5.2.2\"},\"peerDependencies\":{\"@pipecat-ai/client-js\":\"*\",\"react\":\">=18\",\"react-dom\":\">=18\"},\"dependencies\":{\"jotai\":\"^2.9.0\"}}");
35
35
 
36
36
 
37
37
 
@@ -46,8 +46,8 @@ $ad20387e24e513d4$exports = JSON.parse("{\"name\":\"@pipecat-ai/client-react\",\
46
46
  * Copyright (c) 2024, Daily.
47
47
  *
48
48
  * SPDX-License-Identifier: BSD-2-Clause
49
- */ const $5989c1f88db60f73$var$normalizeForMatching = (text)=>{
50
- return text.toLowerCase().replace(/[^\w\s]/g, "");
49
+ */ const $5989c1f88db60f73$export$7d7fdcf7579a70b2 = (text)=>{
50
+ return text.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, "");
51
51
  };
52
52
  const $5989c1f88db60f73$var$skipWhitespace = (text, start)=>{
53
53
  let i = start;
@@ -71,10 +71,10 @@ const $5989c1f88db60f73$var$skipWhitespace = (text, start)=>{
71
71
  const remainder = unspoken.slice(actualStart);
72
72
  // Sentence-level: if spoken exactly matches the remainder (normalized), consume the whole part
73
73
  // so we never leave a word unspoken due to word-matching edge cases.
74
- if ($5989c1f88db60f73$var$normalizeForMatching(spokenForMatching).trim() === $5989c1f88db60f73$var$normalizeForMatching(remainder).trim()) return unspoken.length;
75
- const spokenWords = $5989c1f88db60f73$var$normalizeForMatching(spokenForMatching).split(/\s+/).filter(Boolean);
74
+ if ($5989c1f88db60f73$export$7d7fdcf7579a70b2(spokenForMatching).trim() === $5989c1f88db60f73$export$7d7fdcf7579a70b2(remainder).trim()) return unspoken.length;
75
+ const spokenWords = $5989c1f88db60f73$export$7d7fdcf7579a70b2(spokenForMatching).split(/\s+/).filter(Boolean);
76
76
  if (spokenWords.length === 0) return actualStart;
77
- const unspokenWords = $5989c1f88db60f73$var$normalizeForMatching(unspoken.slice(actualStart)).split(/\s+/).filter(Boolean);
77
+ const unspokenWords = $5989c1f88db60f73$export$7d7fdcf7579a70b2(unspoken.slice(actualStart)).split(/\s+/).filter(Boolean);
78
78
  // Sequential match, allowing prefix match for contractions (e.g. "I" vs "I'm")
79
79
  // and limited skipping of mismatched unspoken words (e.g. punctuation artifacts).
80
80
  let matchedWords = 0;
@@ -94,7 +94,7 @@ const $5989c1f88db60f73$var$skipWhitespace = (text, start)=>{
94
94
  }
95
95
  if (matchedWords < spokenWords.length) return actualStart;
96
96
  // Convert word matches back into a character position in the original unspoken string.
97
- const isWordChar = (char)=>/[a-zA-Z0-9]/.test(char);
97
+ const isWordChar = (char)=>/[\p{L}\p{N}]/u.test(char);
98
98
  let wordCount = 0;
99
99
  let i = actualStart;
100
100
  let inWord = false;
@@ -144,6 +144,10 @@ function $5989c1f88db60f73$export$9b39fd8438081d6d(cursor, parts, spokenText) {
144
144
  if (typeof currentPart.text !== "string") return false;
145
145
  const partText = currentPart.text;
146
146
  const startChar = cursor.currentCharIndex;
147
+ // If the spoken text is pure punctuation (e.g. an em dash "—"), normalization
148
+ // strips it to empty and word-matching would fail. Treat it as successfully
149
+ // consumed so the cursor stays put and subsequent words continue matching.
150
+ if ($5989c1f88db60f73$export$7d7fdcf7579a70b2(spokenText).trim().length === 0) return true;
147
151
  const newPosition = $5989c1f88db60f73$var$findSpokenPositionInUnspoken(spokenText, partText, startChar);
148
152
  const whitespaceEnd = $5989c1f88db60f73$var$skipWhitespace(partText, startChar);
149
153
  if (newPosition > whitespaceEnd) {
@@ -239,6 +243,27 @@ function $603acc766b2aa439$export$296de88ccac4bedb(arr, predicate) {
239
243
 
240
244
 
241
245
  /** Max time gap (ms) between consecutive same-role messages for merging. */ const $38ede2d92c123540$var$MERGE_WINDOW_MS = 30000;
246
+ /**
247
+ * Unicode characters used by `FilterIncompleteTurns` on the server to mark
248
+ * turn completion status. The LLM emits one of these as the very first
249
+ * character of every response:
250
+ *
251
+ * ✓ (U+2713) — complete turn
252
+ * ○ (U+25CB) — incomplete short
253
+ * ◐ (U+25D0) — incomplete long
254
+ *
255
+ * They must be stripped before the text reaches conversation state.
256
+ */ const $38ede2d92c123540$var$TURN_COMPLETION_MARKERS = new Set([
257
+ "\u2713",
258
+ "\u25CB",
259
+ "\u25D0"
260
+ ]);
261
+ function $38ede2d92c123540$export$146b18879fa8cfd7(text) {
262
+ if (text.length === 0) return text;
263
+ if ($38ede2d92c123540$var$TURN_COMPLETION_MARKERS.has(text[0])) // Remove marker and optional single trailing space
264
+ return text[1] === " " ? text.slice(2) : text.slice(1);
265
+ return text;
266
+ }
242
267
  const $38ede2d92c123540$export$cbefc5865f8cbd89 = (a, b)=>{
243
268
  return a.createdAt < b.createdAt ? -1 : a.createdAt > b.createdAt ? 1 : 0;
244
269
  };
@@ -419,16 +444,27 @@ function $38ede2d92c123540$export$cd74f809c89ea10c(get, set, role) {
419
444
  }
420
445
  function $38ede2d92c123540$export$c3a007efc2f315a0(get, set, messageData) {
421
446
  const now = new Date();
447
+ const current = get((0, $7b7579093c9adf9b$export$92a4076839a978d0));
448
+ // If the most recent message is an active (non-final) assistant message,
449
+ // backdate the injected message so it sorts before the assistant's createdAt.
450
+ // This prevents the injected message from splitting the bot's response across
451
+ // two bubbles and breaking the karaoke cursor.
452
+ const lastMessage = current[current.length - 1];
453
+ const lastAssistant = lastMessage?.role === "assistant" ? lastMessage : undefined;
454
+ let timestamp;
455
+ if (lastAssistant && lastAssistant.final === false && messageData.role === "system") {
456
+ const assistantTime = new Date(lastAssistant.createdAt);
457
+ timestamp = new Date(assistantTime.getTime() - 1).toISOString();
458
+ } else timestamp = now.toISOString();
422
459
  const message = {
423
460
  role: messageData.role,
424
461
  final: true,
425
462
  parts: [
426
463
  ...messageData.parts
427
464
  ],
428
- createdAt: now.toISOString(),
465
+ createdAt: timestamp,
429
466
  updatedAt: now.toISOString()
430
467
  };
431
- const current = get((0, $7b7579093c9adf9b$export$92a4076839a978d0));
432
468
  const updatedMessages = [
433
469
  ...current,
434
470
  message
@@ -499,6 +535,10 @@ function $38ede2d92c123540$export$57de01211e3df548(get, set, text, final) {
499
535
  set((0, $7b7579093c9adf9b$export$92a4076839a978d0), processedMessages);
500
536
  }
501
537
  function $38ede2d92c123540$export$d78810e35f742a16(get, set, text, final, spoken, aggregatedBy) {
538
+ // Strip turn-completion markers emitted by FilterIncompleteTurns before
539
+ // the text enters conversation state.
540
+ text = $38ede2d92c123540$export$146b18879fa8cfd7(text);
541
+ if (text.length === 0) return;
502
542
  const now = new Date();
503
543
  const messages = [
504
544
  ...get((0, $7b7579093c9adf9b$export$92a4076839a978d0))
@@ -622,11 +662,21 @@ function $38ede2d92c123540$export$e450805545c7dd86(get, set, data) {
622
662
  if (existingIndex !== -1) return;
623
663
  }
624
664
  const now = new Date();
665
+ // If the most recent message is an active (non-final) assistant message,
666
+ // backdate the function call so it sorts before the assistant's createdAt.
667
+ // This prevents the function call from splitting the bot's response across
668
+ // two bubbles and breaking the karaoke cursor.
669
+ const lastMessage = messages[messages.length - 1];
670
+ let timestamp;
671
+ if (lastMessage?.role === "assistant" && lastMessage.final === false) {
672
+ const assistantTime = new Date(lastMessage.createdAt);
673
+ timestamp = new Date(assistantTime.getTime() - 1).toISOString();
674
+ } else timestamp = now.toISOString();
625
675
  const message = {
626
676
  role: "function_call",
627
677
  final: false,
628
678
  parts: [],
629
- createdAt: now.toISOString(),
679
+ createdAt: timestamp,
630
680
  updatedAt: now.toISOString(),
631
681
  functionCall: {
632
682
  function_name: data.function_name,
@@ -690,7 +740,7 @@ function $38ede2d92c123540$export$809efdca1a10761a(get, set, data) {
690
740
  const messages = get((0, $7b7579093c9adf9b$export$92a4076839a978d0));
691
741
  const lastFc = (0, $603acc766b2aa439$export$296de88ccac4bedb)(messages, (m)=>m.role === "function_call");
692
742
  // Check if InProgress already created an entry (events arrived out of order).
693
- if (lastFc?.functionCall && lastFc.functionCall.status !== "started" && Date.now() - new Date(lastFc.createdAt).getTime() < 2000) {
743
+ if (lastFc?.functionCall && lastFc.functionCall.status !== "started" && Date.now() - new Date(lastFc.updatedAt ?? lastFc.createdAt).getTime() < 2000) {
694
744
  if (data.function_name && !lastFc.functionCall.function_name && lastFc.functionCall.tool_call_id) $38ede2d92c123540$export$1908264de3a8afe4(get, set, lastFc.functionCall.tool_call_id, {
695
745
  function_name: data.function_name
696
746
  });