@pipecat-ai/client-react 1.1.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 +152 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1136 -2
- package/dist/index.js.map +1 -1
- package/dist/index.module.js +1131 -4
- package/dist/index.module.js.map +1 -1
- package/package.json +38 -2
package/dist/index.module.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {jsx as $h9lXz$jsx, Fragment as $h9lXz$Fragment} from "react/jsx-runtime";
|
|
2
2
|
import {RTVIEvent as $h9lXz$RTVIEvent, setAboutClient as $h9lXz$setAboutClient} from "@pipecat-ai/client-js";
|
|
3
|
-
import $h9lXz$react, {useRef as $h9lXz$useRef, useEffect as $h9lXz$useEffect, useCallback as $h9lXz$useCallback, useContext as $h9lXz$useContext, createContext as $h9lXz$createContext, useState as $h9lXz$useState, forwardRef as $h9lXz$forwardRef} from "react";
|
|
3
|
+
import $h9lXz$react, {useRef as $h9lXz$useRef, useEffect as $h9lXz$useEffect, useCallback as $h9lXz$useCallback, useContext as $h9lXz$useContext, createContext as $h9lXz$createContext, useState as $h9lXz$useState, forwardRef as $h9lXz$forwardRef, useId as $h9lXz$useId, useMemo as $h9lXz$useMemo} from "react";
|
|
4
4
|
import {atom as $h9lXz$atom, useAtomValue as $h9lXz$useAtomValue, createStore as $h9lXz$createStore} from "jotai";
|
|
5
5
|
import {atomFamily as $h9lXz$atomFamily, useAtomCallback as $h9lXz$useAtomCallback} from "jotai/utils";
|
|
6
6
|
import {Provider as $h9lXz$Provider} from "jotai/react";
|
|
@@ -31,13 +31,784 @@ 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.1
|
|
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
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Copyright (c) 2024, Daily.
|
|
43
|
+
*
|
|
44
|
+
* SPDX-License-Identifier: BSD-2-Clause
|
|
45
|
+
*/ /**
|
|
46
|
+
* Copyright (c) 2024, Daily.
|
|
47
|
+
*
|
|
48
|
+
* SPDX-License-Identifier: BSD-2-Clause
|
|
49
|
+
*/ const $5989c1f88db60f73$export$7d7fdcf7579a70b2 = (text)=>{
|
|
50
|
+
return text.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, "");
|
|
51
|
+
};
|
|
52
|
+
const $5989c1f88db60f73$var$skipWhitespace = (text, start)=>{
|
|
53
|
+
let i = start;
|
|
54
|
+
while(i < text.length && /\s/.test(text[i]))i++;
|
|
55
|
+
return i;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Finds where `spoken` appears in `unspoken`, starting from `startPosition`.
|
|
59
|
+
* - Best-effort sequential word matching (normalized, punctuation-stripped)
|
|
60
|
+
* - Returns the original start position on mismatch (no advancement)
|
|
61
|
+
*/ const $5989c1f88db60f73$var$findSpokenPositionInUnspoken = (spoken, unspoken, startPosition)=>{
|
|
62
|
+
if (!spoken || !unspoken || startPosition >= unspoken.length) return startPosition;
|
|
63
|
+
// If spoken text includes a leading separator space, skip leading whitespace in unspoken.
|
|
64
|
+
let actualStart = startPosition;
|
|
65
|
+
let spokenForMatching = spoken;
|
|
66
|
+
if (spoken.startsWith(" ") && startPosition < unspoken.length) {
|
|
67
|
+
actualStart = $5989c1f88db60f73$var$skipWhitespace(unspoken, startPosition);
|
|
68
|
+
spokenForMatching = spoken.trimStart();
|
|
69
|
+
} else if (startPosition === 0 && startPosition < unspoken.length) // If we're at the start, also skip leading whitespace (e.g. newlines)
|
|
70
|
+
actualStart = $5989c1f88db60f73$var$skipWhitespace(unspoken, 0);
|
|
71
|
+
const remainder = unspoken.slice(actualStart);
|
|
72
|
+
// Sentence-level: if spoken exactly matches the remainder (normalized), consume the whole part
|
|
73
|
+
// so we never leave a word unspoken due to word-matching edge cases.
|
|
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
|
+
if (spokenWords.length === 0) return actualStart;
|
|
77
|
+
const unspokenWords = $5989c1f88db60f73$export$7d7fdcf7579a70b2(unspoken.slice(actualStart)).split(/\s+/).filter(Boolean);
|
|
78
|
+
// Sequential match, allowing prefix match for contractions (e.g. "I" vs "I'm")
|
|
79
|
+
// and limited skipping of mismatched unspoken words (e.g. punctuation artifacts).
|
|
80
|
+
let matchedWords = 0;
|
|
81
|
+
let consecutiveSkips = 0;
|
|
82
|
+
const MAX_CONSECUTIVE_SKIPS = 2;
|
|
83
|
+
for(let i = 0; i < unspokenWords.length && matchedWords < spokenWords.length; i++){
|
|
84
|
+
const target = spokenWords[matchedWords];
|
|
85
|
+
const candidate = unspokenWords[i];
|
|
86
|
+
if (candidate === target || candidate.startsWith(target)) {
|
|
87
|
+
matchedWords++;
|
|
88
|
+
consecutiveSkips = 0;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
consecutiveSkips++;
|
|
92
|
+
if (consecutiveSkips > MAX_CONSECUTIVE_SKIPS) return actualStart;
|
|
93
|
+
// Skip this unspoken word and try matching the next one
|
|
94
|
+
}
|
|
95
|
+
if (matchedWords < spokenWords.length) return actualStart;
|
|
96
|
+
// Convert word matches back into a character position in the original unspoken string.
|
|
97
|
+
const isWordChar = (char)=>/[\p{L}\p{N}]/u.test(char);
|
|
98
|
+
let wordCount = 0;
|
|
99
|
+
let i = actualStart;
|
|
100
|
+
let inWord = false;
|
|
101
|
+
while(i < unspoken.length){
|
|
102
|
+
const charIsWord = isWordChar(unspoken[i]);
|
|
103
|
+
if (charIsWord && !inWord) {
|
|
104
|
+
inWord = true;
|
|
105
|
+
wordCount++;
|
|
106
|
+
if (wordCount === matchedWords) {
|
|
107
|
+
// Consume the rest of this word
|
|
108
|
+
i++;
|
|
109
|
+
while(i < unspoken.length && isWordChar(unspoken[i]))i++;
|
|
110
|
+
// Include any punctuation after the word until the next space, then include the space
|
|
111
|
+
while(i < unspoken.length){
|
|
112
|
+
if (unspoken[i] === " ") {
|
|
113
|
+
i++;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
i++;
|
|
117
|
+
}
|
|
118
|
+
return i;
|
|
119
|
+
}
|
|
120
|
+
} else if (!charIsWord && inWord) inWord = false;
|
|
121
|
+
i++;
|
|
122
|
+
}
|
|
123
|
+
return unspoken.length;
|
|
124
|
+
};
|
|
125
|
+
function $5989c1f88db60f73$export$802e9b947136afbf(cursor, parts) {
|
|
126
|
+
if (parts.length === 0) return false;
|
|
127
|
+
for(let i = 0; i < parts.length; i++){
|
|
128
|
+
if (typeof parts[i]?.text !== "string") continue;
|
|
129
|
+
if (!cursor.partFinalFlags[i]) return true;
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
function $5989c1f88db60f73$export$9b39fd8438081d6d(cursor, parts, spokenText) {
|
|
134
|
+
if (parts.length === 0) return false;
|
|
135
|
+
// Find the next part that should be spoken (skip parts already marked final/skipped)
|
|
136
|
+
let partToMatch = cursor.currentPartIndex;
|
|
137
|
+
while(partToMatch < parts.length && cursor.partFinalFlags[partToMatch])partToMatch++;
|
|
138
|
+
if (partToMatch >= parts.length) return false;
|
|
139
|
+
if (partToMatch > cursor.currentPartIndex) {
|
|
140
|
+
cursor.currentPartIndex = partToMatch;
|
|
141
|
+
cursor.currentCharIndex = 0;
|
|
142
|
+
}
|
|
143
|
+
const currentPart = parts[cursor.currentPartIndex];
|
|
144
|
+
if (typeof currentPart.text !== "string") return false;
|
|
145
|
+
const partText = currentPart.text;
|
|
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;
|
|
151
|
+
const newPosition = $5989c1f88db60f73$var$findSpokenPositionInUnspoken(spokenText, partText, startChar);
|
|
152
|
+
const whitespaceEnd = $5989c1f88db60f73$var$skipWhitespace(partText, startChar);
|
|
153
|
+
if (newPosition > whitespaceEnd) {
|
|
154
|
+
cursor.currentCharIndex = newPosition;
|
|
155
|
+
if (newPosition >= partText.length) {
|
|
156
|
+
cursor.partFinalFlags[cursor.currentPartIndex] = true;
|
|
157
|
+
if (cursor.currentPartIndex < parts.length - 1) {
|
|
158
|
+
cursor.currentPartIndex++;
|
|
159
|
+
cursor.currentCharIndex = 0;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
// Intra-part scan-ahead recovery: if matching failed at the current position,
|
|
165
|
+
// scan forward word-by-word within the same part. This prevents the cursor
|
|
166
|
+
// from getting permanently stuck mid-part when a single word mismatch occurs
|
|
167
|
+
// (e.g. TTS variation, punctuation boundary like `apexes."Sometimes`).
|
|
168
|
+
if (startChar > 0) {
|
|
169
|
+
const MAX_SCAN_WORDS = 8;
|
|
170
|
+
let scanPos = startChar;
|
|
171
|
+
for(let scan = 0; scan < MAX_SCAN_WORDS; scan++){
|
|
172
|
+
// Advance past current word
|
|
173
|
+
while(scanPos < partText.length && !/\s/.test(partText[scanPos]))scanPos++;
|
|
174
|
+
// Advance past whitespace to next word
|
|
175
|
+
while(scanPos < partText.length && /\s/.test(partText[scanPos]))scanPos++;
|
|
176
|
+
if (scanPos >= partText.length) break;
|
|
177
|
+
const retryPos = $5989c1f88db60f73$var$findSpokenPositionInUnspoken(spokenText, partText, scanPos);
|
|
178
|
+
const scanWsEnd = $5989c1f88db60f73$var$skipWhitespace(partText, scanPos);
|
|
179
|
+
if (retryPos > scanWsEnd) {
|
|
180
|
+
cursor.currentCharIndex = retryPos;
|
|
181
|
+
if (retryPos >= partText.length) {
|
|
182
|
+
cursor.partFinalFlags[cursor.currentPartIndex] = true;
|
|
183
|
+
if (cursor.currentPartIndex < parts.length - 1) {
|
|
184
|
+
cursor.currentPartIndex++;
|
|
185
|
+
cursor.currentCharIndex = 0;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Mismatch recovery: try to find the spoken text in a later part.
|
|
193
|
+
for(let nextPartIdx = cursor.currentPartIndex + 1; nextPartIdx < parts.length; nextPartIdx++){
|
|
194
|
+
const nextPart = parts[nextPartIdx];
|
|
195
|
+
if (typeof nextPart.text !== "string") continue;
|
|
196
|
+
const match = $5989c1f88db60f73$var$findSpokenPositionInUnspoken(spokenText, nextPart.text, 0);
|
|
197
|
+
const nextWhitespaceEnd = $5989c1f88db60f73$var$skipWhitespace(nextPart.text, 0);
|
|
198
|
+
if (match > nextWhitespaceEnd) {
|
|
199
|
+
// Mark skipped parts as final and jump to the matched part
|
|
200
|
+
for(let i = cursor.currentPartIndex; i < nextPartIdx; i++)cursor.partFinalFlags[i] = true;
|
|
201
|
+
cursor.currentPartIndex = nextPartIdx;
|
|
202
|
+
cursor.currentCharIndex = match;
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// If we're stuck at the start, mark the current part as skipped to avoid deadlock.
|
|
207
|
+
if (startChar === 0 && cursor.currentPartIndex < parts.length - 1) {
|
|
208
|
+
cursor.partFinalFlags[cursor.currentPartIndex] = true;
|
|
209
|
+
cursor.currentPartIndex++;
|
|
210
|
+
cursor.currentCharIndex = 0;
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Copyright (c) 2024, Daily.
|
|
219
|
+
*
|
|
220
|
+
* SPDX-License-Identifier: BSD-2-Clause
|
|
221
|
+
*/
|
|
222
|
+
const $7b7579093c9adf9b$export$92a4076839a978d0 = (0, $h9lXz$atom)([]);
|
|
223
|
+
const $7b7579093c9adf9b$export$bd01e11bc95333a1 = (0, $h9lXz$atom)(new Map());
|
|
224
|
+
const $7b7579093c9adf9b$export$b0cd27059c88d0ab = (0, $h9lXz$atom)(new Map());
|
|
225
|
+
const $7b7579093c9adf9b$export$57b1a1df06a7e728 = (0, $h9lXz$atom)(null);
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Copyright (c) 2024, Daily.
|
|
230
|
+
*
|
|
231
|
+
* SPDX-License-Identifier: BSD-2-Clause
|
|
232
|
+
*/ // ES2020-compatible polyfills for findLast / findLastIndex
|
|
233
|
+
function $603acc766b2aa439$export$8855a8be7bd3e9f8(arr, predicate) {
|
|
234
|
+
for(let i = arr.length - 1; i >= 0; i--){
|
|
235
|
+
if (predicate(arr[i], i, arr)) return i;
|
|
236
|
+
}
|
|
237
|
+
return -1;
|
|
238
|
+
}
|
|
239
|
+
function $603acc766b2aa439$export$296de88ccac4bedb(arr, predicate) {
|
|
240
|
+
const idx = $603acc766b2aa439$export$8855a8be7bd3e9f8(arr, predicate);
|
|
241
|
+
return idx === -1 ? undefined : arr[idx];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
|
|
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
|
+
}
|
|
267
|
+
const $38ede2d92c123540$export$cbefc5865f8cbd89 = (a, b)=>{
|
|
268
|
+
return a.createdAt < b.createdAt ? -1 : a.createdAt > b.createdAt ? 1 : 0;
|
|
269
|
+
};
|
|
270
|
+
const $38ede2d92c123540$export$f4610a88b89838b = (message)=>{
|
|
271
|
+
if (message.role === "function_call") return false;
|
|
272
|
+
const parts = message.parts || [];
|
|
273
|
+
if (parts.length === 0) return true;
|
|
274
|
+
return parts.every((p)=>{
|
|
275
|
+
if (typeof p.text === "string") return p.text.trim().length === 0;
|
|
276
|
+
// Check BotOutputText objects
|
|
277
|
+
if (typeof p.text === "object" && p.text !== null && "spoken" in p.text && "unspoken" in p.text) {
|
|
278
|
+
const botText = p.text;
|
|
279
|
+
return botText.spoken.trim().length === 0 && botText.unspoken.trim().length === 0;
|
|
280
|
+
}
|
|
281
|
+
// For ReactNode, consider it non-empty
|
|
282
|
+
return false;
|
|
283
|
+
});
|
|
284
|
+
};
|
|
285
|
+
const $38ede2d92c123540$export$3d3951a55577516 = (messages)=>{
|
|
286
|
+
return messages.filter((message, index, array)=>{
|
|
287
|
+
if (!$38ede2d92c123540$export$f4610a88b89838b(message)) return true;
|
|
288
|
+
// For empty messages, keep only if no following non-empty message with same role
|
|
289
|
+
const nextMessageWithSameRole = array.slice(index + 1).find((m)=>m.role === message.role && !$38ede2d92c123540$export$f4610a88b89838b(m));
|
|
290
|
+
return !nextMessageWithSameRole;
|
|
291
|
+
});
|
|
292
|
+
};
|
|
293
|
+
const $38ede2d92c123540$export$23a96e868837e158 = (messages)=>{
|
|
294
|
+
const mergedMessages = [];
|
|
295
|
+
for(let i = 0; i < messages.length; i++){
|
|
296
|
+
const currentMessage = messages[i];
|
|
297
|
+
const lastMerged = mergedMessages[mergedMessages.length - 1];
|
|
298
|
+
const timeDiff = lastMerged ? Math.abs(new Date(currentMessage.createdAt).getTime() - new Date(lastMerged.createdAt).getTime()) : Infinity;
|
|
299
|
+
const shouldMerge = lastMerged && lastMerged.role === currentMessage.role && currentMessage.role !== "system" && currentMessage.role !== "function_call" && timeDiff < $38ede2d92c123540$var$MERGE_WINDOW_MS;
|
|
300
|
+
if (shouldMerge) mergedMessages[mergedMessages.length - 1] = {
|
|
301
|
+
...lastMerged,
|
|
302
|
+
parts: [
|
|
303
|
+
...lastMerged.parts || [],
|
|
304
|
+
...currentMessage.parts || []
|
|
305
|
+
],
|
|
306
|
+
updatedAt: currentMessage.updatedAt || currentMessage.createdAt,
|
|
307
|
+
final: currentMessage.final !== false
|
|
308
|
+
};
|
|
309
|
+
else mergedMessages.push(currentMessage);
|
|
310
|
+
}
|
|
311
|
+
return mergedMessages;
|
|
312
|
+
};
|
|
313
|
+
const $38ede2d92c123540$var$statusPriority = {
|
|
314
|
+
started: 0,
|
|
315
|
+
in_progress: 1,
|
|
316
|
+
completed: 2
|
|
317
|
+
};
|
|
318
|
+
const $38ede2d92c123540$export$dfa183f99573a822 = (messages)=>{
|
|
319
|
+
const bestByToolCallId = new Map();
|
|
320
|
+
const toRemove = new Set();
|
|
321
|
+
for(let i = 0; i < messages.length; i++){
|
|
322
|
+
const msg = messages[i];
|
|
323
|
+
const tcid = msg.functionCall?.tool_call_id;
|
|
324
|
+
if (msg.role !== "function_call" || !tcid) continue;
|
|
325
|
+
const existingIdx = bestByToolCallId.get(tcid);
|
|
326
|
+
if (existingIdx !== undefined) {
|
|
327
|
+
const existingPriority = $38ede2d92c123540$var$statusPriority[messages[existingIdx].functionCall.status] ?? 0;
|
|
328
|
+
const currentPriority = $38ede2d92c123540$var$statusPriority[msg.functionCall.status] ?? 0;
|
|
329
|
+
if (currentPriority >= existingPriority) {
|
|
330
|
+
toRemove.add(existingIdx);
|
|
331
|
+
bestByToolCallId.set(tcid, i);
|
|
332
|
+
} else toRemove.add(i);
|
|
333
|
+
} else bestByToolCallId.set(tcid, i);
|
|
334
|
+
}
|
|
335
|
+
if (toRemove.size === 0) return messages;
|
|
336
|
+
return messages.filter((_, i)=>!toRemove.has(i));
|
|
337
|
+
};
|
|
338
|
+
const $38ede2d92c123540$var$normalizeMessagesForUI = (messages)=>{
|
|
339
|
+
return $38ede2d92c123540$export$23a96e868837e158($38ede2d92c123540$export$dfa183f99573a822($38ede2d92c123540$export$3d3951a55577516([
|
|
340
|
+
...messages
|
|
341
|
+
].sort($38ede2d92c123540$export$cbefc5865f8cbd89))));
|
|
342
|
+
};
|
|
343
|
+
// ---------------------------------------------------------------------------
|
|
344
|
+
// Internal helpers
|
|
345
|
+
// ---------------------------------------------------------------------------
|
|
346
|
+
const $38ede2d92c123540$var$callCallbacks = (callbacksMap, type, message)=>{
|
|
347
|
+
callbacksMap.forEach((callbacks)=>{
|
|
348
|
+
try {
|
|
349
|
+
callbacks[type]?.(message);
|
|
350
|
+
} catch (error) {
|
|
351
|
+
console.error(`Error in ${type} callback:`, error);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
};
|
|
355
|
+
function $38ede2d92c123540$export$f2820c5e040afe17(get, set, id, callbacks) {
|
|
356
|
+
const map = new Map(get((0, $7b7579093c9adf9b$export$b0cd27059c88d0ab)));
|
|
357
|
+
map.set(id, callbacks);
|
|
358
|
+
set((0, $7b7579093c9adf9b$export$b0cd27059c88d0ab), map);
|
|
359
|
+
}
|
|
360
|
+
function $38ede2d92c123540$export$46ae1ba121fdc956(get, set, id) {
|
|
361
|
+
const map = new Map(get((0, $7b7579093c9adf9b$export$b0cd27059c88d0ab)));
|
|
362
|
+
map.delete(id);
|
|
363
|
+
set((0, $7b7579093c9adf9b$export$b0cd27059c88d0ab), map);
|
|
364
|
+
}
|
|
365
|
+
function $38ede2d92c123540$export$f2434643f2abff11(_get, set) {
|
|
366
|
+
set((0, $7b7579093c9adf9b$export$92a4076839a978d0), []);
|
|
367
|
+
set((0, $7b7579093c9adf9b$export$bd01e11bc95333a1), new Map());
|
|
368
|
+
}
|
|
369
|
+
function $38ede2d92c123540$export$16fdb433d434f08(get, set, messageData) {
|
|
370
|
+
const now = new Date();
|
|
371
|
+
const message = {
|
|
372
|
+
...messageData,
|
|
373
|
+
createdAt: now.toISOString(),
|
|
374
|
+
updatedAt: now.toISOString()
|
|
375
|
+
};
|
|
376
|
+
const current = get((0, $7b7579093c9adf9b$export$92a4076839a978d0));
|
|
377
|
+
const updatedMessages = [
|
|
378
|
+
...current,
|
|
379
|
+
message
|
|
380
|
+
];
|
|
381
|
+
const processedMessages = $38ede2d92c123540$var$normalizeMessagesForUI(updatedMessages);
|
|
382
|
+
$38ede2d92c123540$var$callCallbacks(get((0, $7b7579093c9adf9b$export$b0cd27059c88d0ab)), "onMessageCreated", message);
|
|
383
|
+
set((0, $7b7579093c9adf9b$export$92a4076839a978d0), processedMessages);
|
|
384
|
+
}
|
|
385
|
+
function $38ede2d92c123540$export$a05f96a2aa40873e(get, set, role, updates) {
|
|
386
|
+
const messages = [
|
|
387
|
+
...get((0, $7b7579093c9adf9b$export$92a4076839a978d0))
|
|
388
|
+
];
|
|
389
|
+
const lastMessageIndex = (0, $603acc766b2aa439$export$8855a8be7bd3e9f8)(messages, (msg)=>msg.role === role);
|
|
390
|
+
if (lastMessageIndex === -1) return;
|
|
391
|
+
const existing = messages[lastMessageIndex];
|
|
392
|
+
const updatedMessage = {
|
|
393
|
+
...existing,
|
|
394
|
+
...updates,
|
|
395
|
+
updatedAt: new Date().toISOString()
|
|
396
|
+
};
|
|
397
|
+
messages[lastMessageIndex] = updatedMessage;
|
|
398
|
+
const processedMessages = $38ede2d92c123540$var$normalizeMessagesForUI(messages);
|
|
399
|
+
$38ede2d92c123540$var$callCallbacks(get((0, $7b7579093c9adf9b$export$b0cd27059c88d0ab)), "onMessageUpdated", updatedMessage);
|
|
400
|
+
set((0, $7b7579093c9adf9b$export$92a4076839a978d0), processedMessages);
|
|
401
|
+
}
|
|
402
|
+
function $38ede2d92c123540$export$78bd074993ab081f(get, set, role) {
|
|
403
|
+
const messages = [
|
|
404
|
+
...get((0, $7b7579093c9adf9b$export$92a4076839a978d0))
|
|
405
|
+
];
|
|
406
|
+
const lastMessageIndex = (0, $603acc766b2aa439$export$8855a8be7bd3e9f8)(messages, (msg)=>msg.role === role);
|
|
407
|
+
if (lastMessageIndex === -1) return;
|
|
408
|
+
const lastMessage = messages[lastMessageIndex];
|
|
409
|
+
// Check if message is empty
|
|
410
|
+
if ($38ede2d92c123540$export$f4610a88b89838b(lastMessage)) // Remove empty message only if it has no text in streams either
|
|
411
|
+
messages.splice(lastMessageIndex, 1);
|
|
412
|
+
else {
|
|
413
|
+
// Finalize message and its last part
|
|
414
|
+
const parts = [
|
|
415
|
+
...lastMessage.parts || []
|
|
416
|
+
];
|
|
417
|
+
if (parts.length > 0) parts[parts.length - 1] = {
|
|
418
|
+
...parts[parts.length - 1],
|
|
419
|
+
final: true
|
|
420
|
+
};
|
|
421
|
+
messages[lastMessageIndex] = {
|
|
422
|
+
...lastMessage,
|
|
423
|
+
parts: parts,
|
|
424
|
+
final: true,
|
|
425
|
+
updatedAt: new Date().toISOString()
|
|
426
|
+
};
|
|
427
|
+
$38ede2d92c123540$var$callCallbacks(get((0, $7b7579093c9adf9b$export$b0cd27059c88d0ab)), "onMessageUpdated", messages[lastMessageIndex]);
|
|
428
|
+
}
|
|
429
|
+
const processedMessages = $38ede2d92c123540$var$normalizeMessagesForUI(messages);
|
|
430
|
+
set((0, $7b7579093c9adf9b$export$92a4076839a978d0), processedMessages);
|
|
431
|
+
}
|
|
432
|
+
function $38ede2d92c123540$export$cd74f809c89ea10c(get, set, role) {
|
|
433
|
+
const messages = [
|
|
434
|
+
...get((0, $7b7579093c9adf9b$export$92a4076839a978d0))
|
|
435
|
+
];
|
|
436
|
+
const lastMessageIndex = (0, $603acc766b2aa439$export$8855a8be7bd3e9f8)(messages, (msg)=>msg.role === role);
|
|
437
|
+
if (lastMessageIndex === -1) return;
|
|
438
|
+
const lastMessage = messages[lastMessageIndex];
|
|
439
|
+
if ($38ede2d92c123540$export$f4610a88b89838b(lastMessage)) {
|
|
440
|
+
messages.splice(lastMessageIndex, 1);
|
|
441
|
+
const processedMessages = $38ede2d92c123540$var$normalizeMessagesForUI(messages);
|
|
442
|
+
set((0, $7b7579093c9adf9b$export$92a4076839a978d0), processedMessages);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
function $38ede2d92c123540$export$c3a007efc2f315a0(get, set, messageData) {
|
|
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();
|
|
459
|
+
const message = {
|
|
460
|
+
role: messageData.role,
|
|
461
|
+
final: true,
|
|
462
|
+
parts: [
|
|
463
|
+
...messageData.parts
|
|
464
|
+
],
|
|
465
|
+
createdAt: timestamp,
|
|
466
|
+
updatedAt: now.toISOString()
|
|
467
|
+
};
|
|
468
|
+
const updatedMessages = [
|
|
469
|
+
...current,
|
|
470
|
+
message
|
|
471
|
+
];
|
|
472
|
+
const processedMessages = $38ede2d92c123540$var$normalizeMessagesForUI(updatedMessages);
|
|
473
|
+
$38ede2d92c123540$var$callCallbacks(get((0, $7b7579093c9adf9b$export$b0cd27059c88d0ab)), "onMessageCreated", message);
|
|
474
|
+
set((0, $7b7579093c9adf9b$export$92a4076839a978d0), processedMessages);
|
|
475
|
+
}
|
|
476
|
+
function $38ede2d92c123540$export$57de01211e3df548(get, set, text, final) {
|
|
477
|
+
const now = new Date();
|
|
478
|
+
const messages = [
|
|
479
|
+
...get((0, $7b7579093c9adf9b$export$92a4076839a978d0))
|
|
480
|
+
];
|
|
481
|
+
// Find last user message
|
|
482
|
+
const lastUserIndex = (0, $603acc766b2aa439$export$8855a8be7bd3e9f8)(messages, (m)=>m.role === "user");
|
|
483
|
+
if (lastUserIndex !== -1 && !messages[lastUserIndex].final) {
|
|
484
|
+
// Update existing user message
|
|
485
|
+
const target = {
|
|
486
|
+
...messages[lastUserIndex]
|
|
487
|
+
};
|
|
488
|
+
const parts = Array.isArray(target.parts) ? [
|
|
489
|
+
...target.parts
|
|
490
|
+
] : [];
|
|
491
|
+
const lastPart = parts[parts.length - 1];
|
|
492
|
+
if (!lastPart || lastPart.final) // Start a new part
|
|
493
|
+
parts.push({
|
|
494
|
+
text: text,
|
|
495
|
+
final: final,
|
|
496
|
+
createdAt: now.toISOString()
|
|
497
|
+
});
|
|
498
|
+
else // Update in-progress part
|
|
499
|
+
parts[parts.length - 1] = {
|
|
500
|
+
...lastPart,
|
|
501
|
+
text: text,
|
|
502
|
+
final: final
|
|
503
|
+
};
|
|
504
|
+
const updatedMessage = {
|
|
505
|
+
...target,
|
|
506
|
+
parts: parts,
|
|
507
|
+
updatedAt: now.toISOString()
|
|
508
|
+
};
|
|
509
|
+
messages[lastUserIndex] = updatedMessage;
|
|
510
|
+
const processedMessages = $38ede2d92c123540$var$normalizeMessagesForUI(messages);
|
|
511
|
+
$38ede2d92c123540$var$callCallbacks(get((0, $7b7579093c9adf9b$export$b0cd27059c88d0ab)), "onMessageUpdated", updatedMessage);
|
|
512
|
+
set((0, $7b7579093c9adf9b$export$92a4076839a978d0), processedMessages);
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
// Create a new user message initialized with this transcript
|
|
516
|
+
const newMessage = {
|
|
517
|
+
role: "user",
|
|
518
|
+
final: false,
|
|
519
|
+
parts: [
|
|
520
|
+
{
|
|
521
|
+
text: text,
|
|
522
|
+
final: final,
|
|
523
|
+
createdAt: now.toISOString()
|
|
524
|
+
}
|
|
525
|
+
],
|
|
526
|
+
createdAt: now.toISOString(),
|
|
527
|
+
updatedAt: now.toISOString()
|
|
528
|
+
};
|
|
529
|
+
const updatedMessages = [
|
|
530
|
+
...messages,
|
|
531
|
+
newMessage
|
|
532
|
+
];
|
|
533
|
+
const processedMessages = $38ede2d92c123540$var$normalizeMessagesForUI(updatedMessages);
|
|
534
|
+
$38ede2d92c123540$var$callCallbacks(get((0, $7b7579093c9adf9b$export$b0cd27059c88d0ab)), "onMessageCreated", newMessage);
|
|
535
|
+
set((0, $7b7579093c9adf9b$export$92a4076839a978d0), processedMessages);
|
|
536
|
+
}
|
|
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;
|
|
542
|
+
const now = new Date();
|
|
543
|
+
const messages = [
|
|
544
|
+
...get((0, $7b7579093c9adf9b$export$92a4076839a978d0))
|
|
545
|
+
];
|
|
546
|
+
const botOutputMessageState = new Map(get((0, $7b7579093c9adf9b$export$bd01e11bc95333a1)));
|
|
547
|
+
const lastAssistantIndex = (0, $603acc766b2aa439$export$8855a8be7bd3e9f8)(messages, (msg)=>msg.role === "assistant");
|
|
548
|
+
let messageId;
|
|
549
|
+
let isNewMessage = false;
|
|
550
|
+
if (lastAssistantIndex === -1) {
|
|
551
|
+
// Create new assistant message
|
|
552
|
+
isNewMessage = true;
|
|
553
|
+
messageId = now.toISOString();
|
|
554
|
+
const newMessage = {
|
|
555
|
+
role: "assistant",
|
|
556
|
+
final: final,
|
|
557
|
+
parts: [],
|
|
558
|
+
createdAt: messageId,
|
|
559
|
+
updatedAt: messageId
|
|
560
|
+
};
|
|
561
|
+
messages.push(newMessage);
|
|
562
|
+
// Initialize message state
|
|
563
|
+
botOutputMessageState.set(messageId, {
|
|
564
|
+
currentPartIndex: 0,
|
|
565
|
+
currentCharIndex: 0,
|
|
566
|
+
partFinalFlags: []
|
|
567
|
+
});
|
|
568
|
+
} else {
|
|
569
|
+
// Update existing assistant message
|
|
570
|
+
const lastMessage = messages[lastAssistantIndex];
|
|
571
|
+
messageId = lastMessage.createdAt;
|
|
572
|
+
messages[lastAssistantIndex] = {
|
|
573
|
+
...lastMessage,
|
|
574
|
+
final: final ? true : lastMessage.final,
|
|
575
|
+
updatedAt: now.toISOString()
|
|
576
|
+
};
|
|
577
|
+
// Ensure message state exists
|
|
578
|
+
if (!botOutputMessageState.has(messageId)) botOutputMessageState.set(messageId, {
|
|
579
|
+
currentPartIndex: 0,
|
|
580
|
+
currentCharIndex: 0,
|
|
581
|
+
partFinalFlags: []
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
const messageState = botOutputMessageState.get(messageId);
|
|
585
|
+
const message = messages[lastAssistantIndex === -1 ? messages.length - 1 : lastAssistantIndex];
|
|
586
|
+
const parts = [
|
|
587
|
+
...message.parts || []
|
|
588
|
+
];
|
|
589
|
+
if (!spoken) {
|
|
590
|
+
// UNSPOKEN EVENT: Create/update message parts immediately
|
|
591
|
+
// Only append when both current and last part are word-level; sentence-level
|
|
592
|
+
// and other types each get their own part so spoken events can match 1:1.
|
|
593
|
+
const isDefaultType = aggregatedBy === "sentence" || aggregatedBy === "word" || !aggregatedBy;
|
|
594
|
+
const lastPart = parts[parts.length - 1];
|
|
595
|
+
const shouldAppend = lastPart && aggregatedBy === "word" && lastPart.aggregatedBy === "word" && typeof lastPart.text === "string";
|
|
596
|
+
if (shouldAppend) {
|
|
597
|
+
// Append to last part (word-level only)
|
|
598
|
+
const lastPartText = lastPart.text;
|
|
599
|
+
const separator = lastPartText && !lastPartText.endsWith(" ") && !text.startsWith(" ") ? " " : "";
|
|
600
|
+
parts[parts.length - 1] = {
|
|
601
|
+
...lastPart,
|
|
602
|
+
text: lastPartText + separator + text
|
|
603
|
+
};
|
|
604
|
+
} else {
|
|
605
|
+
// Create new part (sentence-level, custom types, or first word chunk)
|
|
606
|
+
// Default to inline; custom types get displayMode from metadata in the hook
|
|
607
|
+
const defaultDisplayMode = isDefaultType ? "inline" : undefined;
|
|
608
|
+
const newPart = {
|
|
609
|
+
text: text,
|
|
610
|
+
final: false,
|
|
611
|
+
createdAt: now.toISOString(),
|
|
612
|
+
aggregatedBy: aggregatedBy,
|
|
613
|
+
displayMode: defaultDisplayMode
|
|
614
|
+
};
|
|
615
|
+
parts.push(newPart);
|
|
616
|
+
// Extend partFinalFlags array
|
|
617
|
+
messageState.partFinalFlags.push(false);
|
|
618
|
+
}
|
|
619
|
+
// Update message with new parts
|
|
620
|
+
messages[lastAssistantIndex === -1 ? messages.length - 1 : lastAssistantIndex] = {
|
|
621
|
+
...message,
|
|
622
|
+
parts: parts
|
|
623
|
+
};
|
|
624
|
+
} else {
|
|
625
|
+
// SPOKEN EVENT: advance cursor into existing text, or add as new part if
|
|
626
|
+
// there is none (bots that only send spoken: true, never unspoken).
|
|
627
|
+
const advanced = parts.length > 0 && (0, $5989c1f88db60f73$export$9b39fd8438081d6d)(messageState, parts, text);
|
|
628
|
+
if (!advanced) {
|
|
629
|
+
// No unspoken content to advance: add this text as a part already fully spoken
|
|
630
|
+
const isDefaultType = aggregatedBy === "sentence" || aggregatedBy === "word" || !aggregatedBy;
|
|
631
|
+
const defaultDisplayMode = isDefaultType ? "inline" : undefined;
|
|
632
|
+
const newPart = {
|
|
633
|
+
text: text,
|
|
634
|
+
final: false,
|
|
635
|
+
createdAt: now.toISOString(),
|
|
636
|
+
aggregatedBy: aggregatedBy,
|
|
637
|
+
displayMode: defaultDisplayMode
|
|
638
|
+
};
|
|
639
|
+
parts.push(newPart);
|
|
640
|
+
messageState.partFinalFlags.push(true);
|
|
641
|
+
messageState.currentPartIndex = parts.length - 1;
|
|
642
|
+
messageState.currentCharIndex = text.length;
|
|
643
|
+
messages[lastAssistantIndex === -1 ? messages.length - 1 : lastAssistantIndex] = {
|
|
644
|
+
...message,
|
|
645
|
+
parts: parts
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
const processedMessages = $38ede2d92c123540$var$normalizeMessagesForUI(messages);
|
|
650
|
+
const cbs = get((0, $7b7579093c9adf9b$export$b0cd27059c88d0ab));
|
|
651
|
+
const updatedMsg = messages[lastAssistantIndex === -1 ? messages.length - 1 : lastAssistantIndex];
|
|
652
|
+
if (isNewMessage) $38ede2d92c123540$var$callCallbacks(cbs, "onMessageCreated", updatedMsg);
|
|
653
|
+
else $38ede2d92c123540$var$callCallbacks(cbs, "onMessageUpdated", updatedMsg);
|
|
654
|
+
set((0, $7b7579093c9adf9b$export$92a4076839a978d0), processedMessages);
|
|
655
|
+
set((0, $7b7579093c9adf9b$export$bd01e11bc95333a1), botOutputMessageState);
|
|
656
|
+
}
|
|
657
|
+
function $38ede2d92c123540$export$e450805545c7dd86(get, set, data) {
|
|
658
|
+
const messages = get((0, $7b7579093c9adf9b$export$92a4076839a978d0));
|
|
659
|
+
// If a tool_call_id is provided, check for an existing entry to avoid duplicates
|
|
660
|
+
if (data.tool_call_id) {
|
|
661
|
+
const existingIndex = (0, $603acc766b2aa439$export$8855a8be7bd3e9f8)(messages, (msg)=>msg.role === "function_call" && msg.functionCall?.tool_call_id === data.tool_call_id);
|
|
662
|
+
if (existingIndex !== -1) return;
|
|
663
|
+
}
|
|
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();
|
|
675
|
+
const message = {
|
|
676
|
+
role: "function_call",
|
|
677
|
+
final: false,
|
|
678
|
+
parts: [],
|
|
679
|
+
createdAt: timestamp,
|
|
680
|
+
updatedAt: now.toISOString(),
|
|
681
|
+
functionCall: {
|
|
682
|
+
function_name: data.function_name,
|
|
683
|
+
tool_call_id: data.tool_call_id,
|
|
684
|
+
args: data.args,
|
|
685
|
+
status: "started"
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
const updatedMessages = [
|
|
689
|
+
...messages,
|
|
690
|
+
message
|
|
691
|
+
];
|
|
692
|
+
const processedMessages = $38ede2d92c123540$var$normalizeMessagesForUI(updatedMessages);
|
|
693
|
+
$38ede2d92c123540$var$callCallbacks(get((0, $7b7579093c9adf9b$export$b0cd27059c88d0ab)), "onMessageCreated", message);
|
|
694
|
+
set((0, $7b7579093c9adf9b$export$92a4076839a978d0), processedMessages);
|
|
695
|
+
}
|
|
696
|
+
function $38ede2d92c123540$export$1908264de3a8afe4(get, set, tool_call_id, updates) {
|
|
697
|
+
const messages = [
|
|
698
|
+
...get((0, $7b7579093c9adf9b$export$92a4076839a978d0))
|
|
699
|
+
];
|
|
700
|
+
const index = (0, $603acc766b2aa439$export$8855a8be7bd3e9f8)(messages, (msg)=>msg.role === "function_call" && msg.functionCall?.tool_call_id === tool_call_id);
|
|
701
|
+
if (index === -1) return false;
|
|
702
|
+
const existing = messages[index];
|
|
703
|
+
const updated = {
|
|
704
|
+
...existing,
|
|
705
|
+
updatedAt: new Date().toISOString(),
|
|
706
|
+
final: updates.status === "completed" ? true : existing.final,
|
|
707
|
+
functionCall: {
|
|
708
|
+
...existing.functionCall,
|
|
709
|
+
...updates
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
messages[index] = updated;
|
|
713
|
+
const processedMessages = $38ede2d92c123540$var$normalizeMessagesForUI(messages);
|
|
714
|
+
$38ede2d92c123540$var$callCallbacks(get((0, $7b7579093c9adf9b$export$b0cd27059c88d0ab)), "onMessageUpdated", updated);
|
|
715
|
+
set((0, $7b7579093c9adf9b$export$92a4076839a978d0), processedMessages);
|
|
716
|
+
return true;
|
|
717
|
+
}
|
|
718
|
+
function $38ede2d92c123540$export$54b3765e641b1813(get, set, updates) {
|
|
719
|
+
const messages = [
|
|
720
|
+
...get((0, $7b7579093c9adf9b$export$92a4076839a978d0))
|
|
721
|
+
];
|
|
722
|
+
const index = (0, $603acc766b2aa439$export$8855a8be7bd3e9f8)(messages, (msg)=>msg.role === "function_call" && msg.functionCall?.status === "started" && !msg.functionCall?.tool_call_id);
|
|
723
|
+
if (index === -1) return false;
|
|
724
|
+
const existing = messages[index];
|
|
725
|
+
const updated = {
|
|
726
|
+
...existing,
|
|
727
|
+
updatedAt: new Date().toISOString(),
|
|
728
|
+
functionCall: {
|
|
729
|
+
...existing.functionCall,
|
|
730
|
+
...updates
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
messages[index] = updated;
|
|
734
|
+
const processedMessages = $38ede2d92c123540$var$normalizeMessagesForUI(messages);
|
|
735
|
+
$38ede2d92c123540$var$callCallbacks(get((0, $7b7579093c9adf9b$export$b0cd27059c88d0ab)), "onMessageUpdated", updated);
|
|
736
|
+
set((0, $7b7579093c9adf9b$export$92a4076839a978d0), processedMessages);
|
|
737
|
+
return true;
|
|
738
|
+
}
|
|
739
|
+
function $38ede2d92c123540$export$809efdca1a10761a(get, set, data) {
|
|
740
|
+
const messages = get((0, $7b7579093c9adf9b$export$92a4076839a978d0));
|
|
741
|
+
const lastFc = (0, $603acc766b2aa439$export$296de88ccac4bedb)(messages, (m)=>m.role === "function_call");
|
|
742
|
+
// Check if InProgress already created an entry (events arrived out of order).
|
|
743
|
+
if (lastFc?.functionCall && lastFc.functionCall.status !== "started" && Date.now() - new Date(lastFc.updatedAt ?? lastFc.createdAt).getTime() < 2000) {
|
|
744
|
+
if (data.function_name && !lastFc.functionCall.function_name && lastFc.functionCall.tool_call_id) $38ede2d92c123540$export$1908264de3a8afe4(get, set, lastFc.functionCall.tool_call_id, {
|
|
745
|
+
function_name: data.function_name
|
|
746
|
+
});
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
$38ede2d92c123540$export$e450805545c7dd86(get, set, {
|
|
750
|
+
function_name: data.function_name
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
function $38ede2d92c123540$export$568cde5f4aa4dcba(get, set, data) {
|
|
754
|
+
// Tier 1: Try to update the last "started" entry (from LLMFunctionCallStarted)
|
|
755
|
+
const updated = $38ede2d92c123540$export$54b3765e641b1813(get, set, {
|
|
756
|
+
function_name: data.function_name,
|
|
757
|
+
tool_call_id: data.tool_call_id,
|
|
758
|
+
args: data.args,
|
|
759
|
+
status: "in_progress"
|
|
760
|
+
});
|
|
761
|
+
if (!updated) {
|
|
762
|
+
// Tier 2: Try updating an existing entry by tool_call_id
|
|
763
|
+
const found = $38ede2d92c123540$export$1908264de3a8afe4(get, set, data.tool_call_id, {
|
|
764
|
+
function_name: data.function_name,
|
|
765
|
+
args: data.args,
|
|
766
|
+
status: "in_progress"
|
|
767
|
+
});
|
|
768
|
+
if (!found) {
|
|
769
|
+
// Tier 3: No existing entry at all; create a new one as in_progress
|
|
770
|
+
$38ede2d92c123540$export$e450805545c7dd86(get, set, {
|
|
771
|
+
function_name: data.function_name,
|
|
772
|
+
tool_call_id: data.tool_call_id,
|
|
773
|
+
args: data.args
|
|
774
|
+
});
|
|
775
|
+
$38ede2d92c123540$export$1908264de3a8afe4(get, set, data.tool_call_id, {
|
|
776
|
+
status: "in_progress"
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
function $38ede2d92c123540$export$a3cea3f7508e820b(get, set, data) {
|
|
782
|
+
// Tier 1: Try updating by tool_call_id
|
|
783
|
+
const found = $38ede2d92c123540$export$1908264de3a8afe4(get, set, data.tool_call_id, {
|
|
784
|
+
function_name: data.function_name,
|
|
785
|
+
status: "completed",
|
|
786
|
+
result: data.result,
|
|
787
|
+
cancelled: data.cancelled
|
|
788
|
+
});
|
|
789
|
+
if (!found) {
|
|
790
|
+
// Tier 2: No match by tool_call_id (e.g. InProgress was skipped).
|
|
791
|
+
const matched = $38ede2d92c123540$export$54b3765e641b1813(get, set, {
|
|
792
|
+
function_name: data.function_name,
|
|
793
|
+
tool_call_id: data.tool_call_id
|
|
794
|
+
});
|
|
795
|
+
if (matched) $38ede2d92c123540$export$1908264de3a8afe4(get, set, data.tool_call_id, {
|
|
796
|
+
status: "completed",
|
|
797
|
+
result: data.result,
|
|
798
|
+
cancelled: data.cancelled
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Copyright (c) 2024, Daily.
|
|
807
|
+
*
|
|
808
|
+
* SPDX-License-Identifier: BSD-2-Clause
|
|
809
|
+
*/
|
|
810
|
+
|
|
811
|
+
|
|
41
812
|
/**
|
|
42
813
|
* Copyright (c) 2024, Daily.
|
|
43
814
|
*
|
|
@@ -66,6 +837,239 @@ const $824ea64b5f757259$export$33a6ac53b8f02625 = (event, handler)=>{
|
|
|
66
837
|
};
|
|
67
838
|
|
|
68
839
|
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* Checks if a version meets a minimum version requirement.
|
|
846
|
+
* Inlined to avoid adding a `semver` dependency.
|
|
847
|
+
*/ function $d1596d1cb01c59d1$var$isMinVersion(currentVersion, minVersion) {
|
|
848
|
+
// Strip pre-release suffix (e.g. "1.1.0-beta.1" -> "1.1.0")
|
|
849
|
+
const parts = currentVersion.split("-")[0].split(".").map(Number);
|
|
850
|
+
for(let i = 0; i < 3; i++){
|
|
851
|
+
if ((parts[i] || 0) > minVersion[i]) return true;
|
|
852
|
+
if ((parts[i] || 0) < minVersion[i]) return false;
|
|
853
|
+
}
|
|
854
|
+
return true; // equal
|
|
855
|
+
}
|
|
856
|
+
/** Delay (ms) before finalizing the assistant message after bot stops speaking. */ const $d1596d1cb01c59d1$var$BOT_STOPPED_FINALIZE_DELAY_MS = 2500;
|
|
857
|
+
function $d1596d1cb01c59d1$export$52680e26621bab39() {
|
|
858
|
+
const userStoppedTimeout = (0, $h9lXz$useRef)(undefined);
|
|
859
|
+
const botStoppedSpeakingTimeoutRef = (0, $h9lXz$useRef)(undefined);
|
|
860
|
+
const assistantStreamResetRef = (0, $h9lXz$useRef)(0);
|
|
861
|
+
const botOutputLastChunkRef = (0, $h9lXz$useRef)({
|
|
862
|
+
spoken: "",
|
|
863
|
+
unspoken: ""
|
|
864
|
+
});
|
|
865
|
+
// Clean up pending timeouts on unmount
|
|
866
|
+
(0, $h9lXz$useEffect)(()=>{
|
|
867
|
+
return ()=>{
|
|
868
|
+
clearTimeout(userStoppedTimeout.current);
|
|
869
|
+
clearTimeout(botStoppedSpeakingTimeoutRef.current);
|
|
870
|
+
};
|
|
871
|
+
}, []);
|
|
872
|
+
// -- helpers ---------------------------------------------------------------
|
|
873
|
+
const finalizeLastAssistantMessageIfPending = (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((get, set)=>{
|
|
874
|
+
clearTimeout(botStoppedSpeakingTimeoutRef.current);
|
|
875
|
+
botStoppedSpeakingTimeoutRef.current = undefined;
|
|
876
|
+
const messages = get((0, $7b7579093c9adf9b$export$92a4076839a978d0));
|
|
877
|
+
const lastAssistant = (0, $603acc766b2aa439$export$296de88ccac4bedb)(messages, (m)=>m.role === "assistant");
|
|
878
|
+
if (lastAssistant && !lastAssistant.final) (0, $38ede2d92c123540$export$78bd074993ab081f)(get, set, "assistant");
|
|
879
|
+
}, []));
|
|
880
|
+
const ensureAssistantMessage = (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((get, set)=>{
|
|
881
|
+
const messages = get((0, $7b7579093c9adf9b$export$92a4076839a978d0));
|
|
882
|
+
const lastAssistantIndex = (0, $603acc766b2aa439$export$8855a8be7bd3e9f8)(messages, (msg)=>msg.role === "assistant");
|
|
883
|
+
const lastAssistant = lastAssistantIndex !== -1 ? messages[lastAssistantIndex] : undefined;
|
|
884
|
+
if (!lastAssistant || lastAssistant.final) {
|
|
885
|
+
// If the message was finalized but still has unspoken content, it was
|
|
886
|
+
// finalized prematurely (e.g. BotStoppedSpeaking timer fired during a
|
|
887
|
+
// TTS pause mid-response). Un-finalize it instead of creating a new
|
|
888
|
+
// message bubble — but only when no user message followed.
|
|
889
|
+
if (lastAssistant?.final && lastAssistantIndex === messages.length - 1) {
|
|
890
|
+
const messageId = lastAssistant.createdAt;
|
|
891
|
+
const botOutputState = get((0, $7b7579093c9adf9b$export$bd01e11bc95333a1));
|
|
892
|
+
const cursor = botOutputState.get(messageId);
|
|
893
|
+
if (cursor && (0, $5989c1f88db60f73$export$802e9b947136afbf)(cursor, lastAssistant.parts || [])) {
|
|
894
|
+
(0, $38ede2d92c123540$export$a05f96a2aa40873e)(get, set, "assistant", {
|
|
895
|
+
final: false
|
|
896
|
+
});
|
|
897
|
+
return false;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
(0, $38ede2d92c123540$export$16fdb433d434f08)(get, set, {
|
|
901
|
+
role: "assistant",
|
|
902
|
+
final: false,
|
|
903
|
+
parts: []
|
|
904
|
+
});
|
|
905
|
+
assistantStreamResetRef.current += 1;
|
|
906
|
+
return true;
|
|
907
|
+
}
|
|
908
|
+
return false;
|
|
909
|
+
}, []));
|
|
910
|
+
// -- event handlers --------------------------------------------------------
|
|
911
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).Connected, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((get, set)=>{
|
|
912
|
+
(0, $38ede2d92c123540$export$f2434643f2abff11)(get, set);
|
|
913
|
+
set((0, $7b7579093c9adf9b$export$57b1a1df06a7e728), null);
|
|
914
|
+
clearTimeout(botStoppedSpeakingTimeoutRef.current);
|
|
915
|
+
botStoppedSpeakingTimeoutRef.current = undefined;
|
|
916
|
+
botOutputLastChunkRef.current = {
|
|
917
|
+
spoken: "",
|
|
918
|
+
unspoken: ""
|
|
919
|
+
};
|
|
920
|
+
}, [])));
|
|
921
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).BotReady, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((_get, set, botData)=>{
|
|
922
|
+
const rtviVersion = botData.version;
|
|
923
|
+
const supportsBotOutput = $d1596d1cb01c59d1$var$isMinVersion(rtviVersion, [
|
|
924
|
+
1,
|
|
925
|
+
1,
|
|
926
|
+
0
|
|
927
|
+
]);
|
|
928
|
+
set((0, $7b7579093c9adf9b$export$57b1a1df06a7e728), supportsBotOutput);
|
|
929
|
+
}, [])));
|
|
930
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).BotOutput, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((get, set, data)=>{
|
|
931
|
+
// A BotOutput event means the response is still active; cancel any
|
|
932
|
+
// pending finalize timer from BotStoppedSpeaking.
|
|
933
|
+
clearTimeout(botStoppedSpeakingTimeoutRef.current);
|
|
934
|
+
botStoppedSpeakingTimeoutRef.current = undefined;
|
|
935
|
+
ensureAssistantMessage();
|
|
936
|
+
// Handle spacing for BotOutput chunks
|
|
937
|
+
let textToAdd = data.text;
|
|
938
|
+
const lastChunk = data.spoken ? botOutputLastChunkRef.current.spoken : botOutputLastChunkRef.current.unspoken;
|
|
939
|
+
// Add space separator if needed between BotOutput chunks
|
|
940
|
+
if (lastChunk) textToAdd = " " + textToAdd;
|
|
941
|
+
// Update the appropriate last chunk tracker
|
|
942
|
+
if (data.spoken) botOutputLastChunkRef.current.spoken = textToAdd;
|
|
943
|
+
else botOutputLastChunkRef.current.unspoken = textToAdd;
|
|
944
|
+
// Update both spoken and unspoken text streams
|
|
945
|
+
const isFinal = data.aggregated_by === "sentence";
|
|
946
|
+
(0, $38ede2d92c123540$export$d78810e35f742a16)(get, set, textToAdd, isFinal, data.spoken, data.aggregated_by);
|
|
947
|
+
}, [
|
|
948
|
+
ensureAssistantMessage
|
|
949
|
+
])));
|
|
950
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).BotStoppedSpeaking, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((get, set)=>{
|
|
951
|
+
// Don't finalize immediately; start a timer. Bot may start speaking again (pause).
|
|
952
|
+
clearTimeout(botStoppedSpeakingTimeoutRef.current);
|
|
953
|
+
const messages = get((0, $7b7579093c9adf9b$export$92a4076839a978d0));
|
|
954
|
+
const lastAssistant = (0, $603acc766b2aa439$export$296de88ccac4bedb)(messages, (m)=>m.role === "assistant");
|
|
955
|
+
if (!lastAssistant || lastAssistant.final) return;
|
|
956
|
+
botStoppedSpeakingTimeoutRef.current = setTimeout(()=>{
|
|
957
|
+
botStoppedSpeakingTimeoutRef.current = undefined;
|
|
958
|
+
// Snap the speech-progress cursor to the end of all parts.
|
|
959
|
+
// The bot finished speaking normally (not interrupted), so all
|
|
960
|
+
// text should render as "spoken". Without this, text from the
|
|
961
|
+
// last sentence can remain grey if the spoken BotOutput event
|
|
962
|
+
// didn't match the unspoken text exactly.
|
|
963
|
+
const msgs = get((0, $7b7579093c9adf9b$export$92a4076839a978d0));
|
|
964
|
+
const cursorMap = new Map(get((0, $7b7579093c9adf9b$export$bd01e11bc95333a1)));
|
|
965
|
+
const last = (0, $603acc766b2aa439$export$296de88ccac4bedb)(msgs, (m)=>m.role === "assistant");
|
|
966
|
+
if (last) {
|
|
967
|
+
const cursor = cursorMap.get(last.createdAt);
|
|
968
|
+
if (cursor && last.parts && last.parts.length > 0) {
|
|
969
|
+
const lastPartIdx = last.parts.length - 1;
|
|
970
|
+
const lastPartText = last.parts[lastPartIdx]?.text;
|
|
971
|
+
cursor.currentPartIndex = lastPartIdx;
|
|
972
|
+
cursor.currentCharIndex = typeof lastPartText === "string" ? lastPartText.length : 0;
|
|
973
|
+
for(let i = 0; i <= lastPartIdx; i++)cursor.partFinalFlags[i] = true;
|
|
974
|
+
set((0, $7b7579093c9adf9b$export$bd01e11bc95333a1), cursorMap);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
(0, $38ede2d92c123540$export$78bd074993ab081f)(get, set, "assistant");
|
|
978
|
+
}, $d1596d1cb01c59d1$var$BOT_STOPPED_FINALIZE_DELAY_MS);
|
|
979
|
+
}, [])));
|
|
980
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).BotStartedSpeaking, (0, $h9lXz$useCallback)(()=>{
|
|
981
|
+
// Bot is speaking again; reset the finalize timer (bot was just pausing).
|
|
982
|
+
clearTimeout(botStoppedSpeakingTimeoutRef.current);
|
|
983
|
+
botStoppedSpeakingTimeoutRef.current = undefined;
|
|
984
|
+
}, []));
|
|
985
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).UserStartedSpeaking, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((get, set)=>{
|
|
986
|
+
// User started a new turn; bot's turn is done. Fast-forward: finalize immediately.
|
|
987
|
+
finalizeLastAssistantMessageIfPending();
|
|
988
|
+
clearTimeout(userStoppedTimeout.current);
|
|
989
|
+
// Only finalize the previous user message if the bot has responded since
|
|
990
|
+
// the user last spoke. This prevents finalizing during VAD gaps (brief
|
|
991
|
+
// breathing pauses within the same user turn where UserStoppedSpeaking/
|
|
992
|
+
// UserStartedSpeaking fire without an actual turn change).
|
|
993
|
+
const messages = get((0, $7b7579093c9adf9b$export$92a4076839a978d0));
|
|
994
|
+
const lastUserIdx = (0, $603acc766b2aa439$export$8855a8be7bd3e9f8)(messages, (m)=>m.role === "user");
|
|
995
|
+
if (lastUserIdx !== -1 && !messages[lastUserIdx].final) {
|
|
996
|
+
const hasBotActivityAfterUser = messages.slice(lastUserIdx + 1).some((m)=>m.role === "assistant");
|
|
997
|
+
if (hasBotActivityAfterUser) (0, $38ede2d92c123540$export$78bd074993ab081f)(get, set, "user");
|
|
998
|
+
}
|
|
999
|
+
}, [
|
|
1000
|
+
finalizeLastAssistantMessageIfPending
|
|
1001
|
+
])));
|
|
1002
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).UserTranscript, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((get, set, data)=>{
|
|
1003
|
+
const text = data.text ?? "";
|
|
1004
|
+
const final = Boolean(data.final);
|
|
1005
|
+
(0, $38ede2d92c123540$export$57de01211e3df548)(get, set, text, final);
|
|
1006
|
+
// If we got any transcript, cancel pending cleanup
|
|
1007
|
+
clearTimeout(userStoppedTimeout.current);
|
|
1008
|
+
}, [])));
|
|
1009
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).UserStoppedSpeaking, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((get, set)=>{
|
|
1010
|
+
clearTimeout(userStoppedTimeout.current);
|
|
1011
|
+
// If no transcript ends up arriving, ensure any accidental empty placeholder is removed.
|
|
1012
|
+
userStoppedTimeout.current = setTimeout(()=>{
|
|
1013
|
+
// Re-read state at timeout time
|
|
1014
|
+
const messages = get((0, $7b7579093c9adf9b$export$92a4076839a978d0));
|
|
1015
|
+
const lastUser = (0, $603acc766b2aa439$export$296de88ccac4bedb)(messages, (m)=>m.role === "user");
|
|
1016
|
+
const hasParts = Array.isArray(lastUser?.parts) && lastUser.parts.length > 0;
|
|
1017
|
+
if (!lastUser || !hasParts) (0, $38ede2d92c123540$export$cd74f809c89ea10c)(get, set, "user");
|
|
1018
|
+
else if (!lastUser.final) (0, $38ede2d92c123540$export$78bd074993ab081f)(get, set, "user");
|
|
1019
|
+
}, 3000);
|
|
1020
|
+
}, [])));
|
|
1021
|
+
// LLM Function Call lifecycle events
|
|
1022
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).LLMFunctionCallStarted, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((get, set, data)=>{
|
|
1023
|
+
(0, $38ede2d92c123540$export$809efdca1a10761a)(get, set, {
|
|
1024
|
+
function_name: data.function_name
|
|
1025
|
+
});
|
|
1026
|
+
}, [])));
|
|
1027
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).LLMFunctionCallInProgress, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((get, set, data)=>{
|
|
1028
|
+
(0, $38ede2d92c123540$export$568cde5f4aa4dcba)(get, set, {
|
|
1029
|
+
function_name: data.function_name,
|
|
1030
|
+
tool_call_id: data.tool_call_id,
|
|
1031
|
+
args: data.arguments
|
|
1032
|
+
});
|
|
1033
|
+
}, [])));
|
|
1034
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).LLMFunctionCallStopped, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((get, set, data)=>{
|
|
1035
|
+
(0, $38ede2d92c123540$export$a3cea3f7508e820b)(get, set, {
|
|
1036
|
+
function_name: data.function_name,
|
|
1037
|
+
tool_call_id: data.tool_call_id,
|
|
1038
|
+
result: data.result,
|
|
1039
|
+
cancelled: data.cancelled
|
|
1040
|
+
});
|
|
1041
|
+
}, [])));
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
|
|
1045
|
+
const $b5a460b32af71766$export$4d68d6035e4164b1 = /*#__PURE__*/ (0, $h9lXz$createContext)(null);
|
|
1046
|
+
const $b5a460b32af71766$export$e372a2172e7d18e8 = ({ children: children })=>{
|
|
1047
|
+
(0, $d1596d1cb01c59d1$export$52680e26621bab39)();
|
|
1048
|
+
const injectMessage = (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((get, set, message)=>{
|
|
1049
|
+
(0, $38ede2d92c123540$export$c3a007efc2f315a0)(get, set, message);
|
|
1050
|
+
}, []));
|
|
1051
|
+
const botOutputSupported = (0, $h9lXz$useAtomValue)((0, $7b7579093c9adf9b$export$57b1a1df06a7e728));
|
|
1052
|
+
return (0, $h9lXz$jsx)($b5a460b32af71766$export$4d68d6035e4164b1.Provider, {
|
|
1053
|
+
value: {
|
|
1054
|
+
injectMessage: injectMessage,
|
|
1055
|
+
botOutputSupported: botOutputSupported
|
|
1056
|
+
},
|
|
1057
|
+
children: children
|
|
1058
|
+
});
|
|
1059
|
+
};
|
|
1060
|
+
$b5a460b32af71766$export$e372a2172e7d18e8.displayName = "PipecatConversationProvider";
|
|
1061
|
+
const $b5a460b32af71766$export$8eec679bf8249822 = ()=>{
|
|
1062
|
+
const context = (0, $h9lXz$useContext)($b5a460b32af71766$export$4d68d6035e4164b1);
|
|
1063
|
+
if (!context) throw new Error("useConversationContext must be used within a PipecatClientProvider");
|
|
1064
|
+
return context;
|
|
1065
|
+
};
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
|
|
1071
|
+
|
|
1072
|
+
|
|
69
1073
|
const $a90aa7250c094218$export$d6bdcccacef16204 = /*#__PURE__*/ (0, $h9lXz$createContext)({
|
|
70
1074
|
enableCam: ()=>{
|
|
71
1075
|
throw new Error("PipecatClientCamStateContext: enableCam() called outside of provider");
|
|
@@ -200,7 +1204,9 @@ const $d2e362c5a07ee3c5$export$bb43666ced7a20d0 = ({ children: children, client:
|
|
|
200
1204
|
off: off
|
|
201
1205
|
},
|
|
202
1206
|
children: (0, $h9lXz$jsx)((0, $a90aa7250c094218$export$4777554fda61c378), {
|
|
203
|
-
children:
|
|
1207
|
+
children: (0, $h9lXz$jsx)((0, $b5a460b32af71766$export$e372a2172e7d18e8), {
|
|
1208
|
+
children: children
|
|
1209
|
+
})
|
|
204
1210
|
})
|
|
205
1211
|
})
|
|
206
1212
|
})
|
|
@@ -846,5 +1852,126 @@ $993a744193844a95$export$59bf27bd43679db6.displayName = "VoiceVisualizer";
|
|
|
846
1852
|
|
|
847
1853
|
|
|
848
1854
|
|
|
849
|
-
|
|
1855
|
+
/**
|
|
1856
|
+
* Copyright (c) 2024, Daily.
|
|
1857
|
+
*
|
|
1858
|
+
* SPDX-License-Identifier: BSD-2-Clause
|
|
1859
|
+
*/
|
|
1860
|
+
|
|
1861
|
+
|
|
1862
|
+
|
|
1863
|
+
|
|
1864
|
+
|
|
1865
|
+
const $894023a10d0bd040$export$acb832b064cb6c09 = ({ onMessageCreated: onMessageCreated, onMessageUpdated: onMessageUpdated, onMessageAdded: onMessageAdded, aggregationMetadata: aggregationMetadata } = {})=>{
|
|
1866
|
+
const { injectMessage: injectMessage } = (0, $b5a460b32af71766$export$8eec679bf8249822)();
|
|
1867
|
+
// Generate a unique ID for this hook instance
|
|
1868
|
+
const callbackId = (0, $h9lXz$useId)();
|
|
1869
|
+
// Resolve deprecated onMessageAdded → onMessageCreated
|
|
1870
|
+
const resolvedCreated = onMessageCreated ?? onMessageAdded;
|
|
1871
|
+
// Register and unregister the callbacks
|
|
1872
|
+
const doRegister = (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((get, set)=>{
|
|
1873
|
+
(0, $38ede2d92c123540$export$f2820c5e040afe17)(get, set, callbackId, {
|
|
1874
|
+
onMessageCreated: resolvedCreated,
|
|
1875
|
+
onMessageUpdated: onMessageUpdated
|
|
1876
|
+
});
|
|
1877
|
+
}, [
|
|
1878
|
+
callbackId,
|
|
1879
|
+
resolvedCreated,
|
|
1880
|
+
onMessageUpdated
|
|
1881
|
+
]));
|
|
1882
|
+
const doUnregister = (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((get, set)=>{
|
|
1883
|
+
(0, $38ede2d92c123540$export$46ae1ba121fdc956)(get, set, callbackId);
|
|
1884
|
+
}, [
|
|
1885
|
+
callbackId
|
|
1886
|
+
]));
|
|
1887
|
+
(0, $h9lXz$useEffect)(()=>{
|
|
1888
|
+
doRegister();
|
|
1889
|
+
return ()=>{
|
|
1890
|
+
doUnregister();
|
|
1891
|
+
};
|
|
1892
|
+
}, [
|
|
1893
|
+
doRegister,
|
|
1894
|
+
doUnregister
|
|
1895
|
+
]);
|
|
1896
|
+
// Get the raw state from atoms
|
|
1897
|
+
const messages = (0, $h9lXz$useAtomValue)((0, $7b7579093c9adf9b$export$92a4076839a978d0));
|
|
1898
|
+
const botOutputMessageState = (0, $h9lXz$useAtomValue)((0, $7b7579093c9adf9b$export$bd01e11bc95333a1));
|
|
1899
|
+
// Memoize the filtered messages to prevent infinite loops
|
|
1900
|
+
const filteredMessages = (0, $h9lXz$useMemo)(()=>{
|
|
1901
|
+
const getMetadata = (part)=>{
|
|
1902
|
+
return part.aggregatedBy ? aggregationMetadata?.[part.aggregatedBy] : undefined;
|
|
1903
|
+
};
|
|
1904
|
+
// Process messages: convert string parts to BotOutputText based on position state
|
|
1905
|
+
const processedMessages = messages.map((message)=>{
|
|
1906
|
+
if (message.role === "assistant") {
|
|
1907
|
+
const messageId = message.createdAt;
|
|
1908
|
+
const messageState = botOutputMessageState.get(messageId);
|
|
1909
|
+
if (!messageState) // No state yet, return message as-is
|
|
1910
|
+
return message;
|
|
1911
|
+
const parts = message.parts || [];
|
|
1912
|
+
// Find the actual current part index (skip parts that aren't meant to be spoken)
|
|
1913
|
+
let actualCurrentPartIndex = messageState.currentPartIndex;
|
|
1914
|
+
while(actualCurrentPartIndex < parts.length){
|
|
1915
|
+
const part = parts[actualCurrentPartIndex];
|
|
1916
|
+
if (typeof part?.text !== "string") break;
|
|
1917
|
+
const isSpoken = getMetadata(part)?.isSpoken !== false;
|
|
1918
|
+
if (isSpoken) break;
|
|
1919
|
+
actualCurrentPartIndex++;
|
|
1920
|
+
}
|
|
1921
|
+
if (parts.length > 0 && actualCurrentPartIndex >= parts.length) actualCurrentPartIndex = parts.length - 1;
|
|
1922
|
+
// Convert parts to BotOutputText format based on position state
|
|
1923
|
+
const processedParts = parts.map((part, partIndex)=>{
|
|
1924
|
+
// If part text is not a string, it's already processed (e.g., ReactNode)
|
|
1925
|
+
if (typeof part.text !== "string") return part;
|
|
1926
|
+
const metadata = getMetadata(part);
|
|
1927
|
+
const displayMode = part.displayMode ?? metadata?.displayMode ?? "inline";
|
|
1928
|
+
const isSpoken = metadata?.isSpoken !== false;
|
|
1929
|
+
const partText = displayMode === "block" && !isSpoken ? part.text.trim() : part.text;
|
|
1930
|
+
if (!isSpoken) return {
|
|
1931
|
+
...part,
|
|
1932
|
+
displayMode: displayMode,
|
|
1933
|
+
text: {
|
|
1934
|
+
spoken: "",
|
|
1935
|
+
unspoken: partText
|
|
1936
|
+
}
|
|
1937
|
+
};
|
|
1938
|
+
// Use cursor split for the part at actualCurrentPartIndex for every message,
|
|
1939
|
+
// so previous (e.g. interrupted) messages keep partially spoken state.
|
|
1940
|
+
const isPartAtCursor = partIndex === actualCurrentPartIndex;
|
|
1941
|
+
const currentCharIndex = messageState.currentCharIndex;
|
|
1942
|
+
const spokenText = isPartAtCursor ? partText.slice(0, currentCharIndex) : partIndex < actualCurrentPartIndex ? partText : "";
|
|
1943
|
+
const unspokenText = isPartAtCursor ? partText.slice(currentCharIndex) : partIndex < actualCurrentPartIndex ? "" : partText;
|
|
1944
|
+
return {
|
|
1945
|
+
...part,
|
|
1946
|
+
displayMode: displayMode,
|
|
1947
|
+
text: {
|
|
1948
|
+
spoken: spokenText,
|
|
1949
|
+
unspoken: unspokenText
|
|
1950
|
+
}
|
|
1951
|
+
};
|
|
1952
|
+
});
|
|
1953
|
+
return {
|
|
1954
|
+
...message,
|
|
1955
|
+
parts: processedParts
|
|
1956
|
+
};
|
|
1957
|
+
}
|
|
1958
|
+
return message;
|
|
1959
|
+
});
|
|
1960
|
+
// Messages are already normalized (sorted, filtered, deduped, merged) on write.
|
|
1961
|
+
return processedMessages;
|
|
1962
|
+
}, [
|
|
1963
|
+
messages,
|
|
1964
|
+
botOutputMessageState,
|
|
1965
|
+
aggregationMetadata
|
|
1966
|
+
]);
|
|
1967
|
+
return {
|
|
1968
|
+
messages: filteredMessages,
|
|
1969
|
+
injectMessage: injectMessage
|
|
1970
|
+
};
|
|
1971
|
+
};
|
|
1972
|
+
|
|
1973
|
+
|
|
1974
|
+
|
|
1975
|
+
|
|
1976
|
+
export {$f209aa7ddb77dcb2$export$b52250cb73ff4de1 as PipecatClientAudio, $7cb2ce2c4cbfb401$export$dc9a029eeca8213f as PipecatClientCamToggle, $2984fdfc31bad375$export$bc8133b69ff660a2 as PipecatClientMicToggle, $d2e362c5a07ee3c5$export$bb43666ced7a20d0 as PipecatClientProvider, $d6dc7e80e6e976a0$export$93764714dfab3a46 as PipecatClientScreenShareToggle, $6a65deb8615a2ad7$export$85974db6d0cc43b3 as PipecatClientVideo, $034a56e7ee1b7bed$export$777fa8498be78705 as usePipecatClient, $e76ee2f021b54325$export$3ea2601427f0430f as usePipecatClientCamControl, $f934f1f8b10aaf19$export$642bc4d2d2a376f1 as usePipecatClientMediaDevices, $4b4b9099cdb5b776$export$9813dcd2d0c26814 as usePipecatClientMediaTrack, $5905c001b0dc8d25$export$388e706586309ef0 as usePipecatClientMicControl, $d39c0bc3744ea030$export$be63b19bd7f7d4f5 as usePipecatClientScreenShareControl, $33f3729bbe9f09df$export$30aee278309a867b as usePipecatClientTransportState, $824ea64b5f757259$export$33a6ac53b8f02625 as useRTVIClientEvent, $993a744193844a95$export$59bf27bd43679db6 as VoiceVisualizer, $b5a460b32af71766$export$8eec679bf8249822 as useConversationContext, $894023a10d0bd040$export$acb832b064cb6c09 as usePipecatConversation, $38ede2d92c123540$export$dfa183f99573a822 as deduplicateFunctionCalls, $38ede2d92c123540$export$3d3951a55577516 as filterEmptyMessages, $38ede2d92c123540$export$f4610a88b89838b as isMessageEmpty, $38ede2d92c123540$export$23a96e868837e158 as mergeMessages, $38ede2d92c123540$export$cbefc5865f8cbd89 as sortByCreatedAt};
|
|
850
1977
|
//# sourceMappingURL=index.module.js.map
|