@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.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +61 -11
- package/dist/index.js.map +1 -1
- package/dist/index.module.js +61 -11
- package/dist/index.module.js.map +1 -1
- package/package.json +1 -1
package/dist/index.module.js
CHANGED
|
@@ -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.
|
|
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$
|
|
50
|
-
return text.toLowerCase().replace(/[^\
|
|
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$
|
|
75
|
-
const spokenWords = $5989c1f88db60f73$
|
|
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$
|
|
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)=>/[
|
|
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:
|
|
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:
|
|
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
|
});
|