@paymanai/payman-typescript-ask-sdk 1.2.11 → 2.0.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.mts +136 -108
- package/dist/index.d.ts +136 -108
- package/dist/index.js +589 -1203
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +588 -1204
- package/dist/index.mjs.map +1 -1
- package/dist/index.native.js +591 -1205
- package/dist/index.native.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var react = require('react');
|
|
4
4
|
|
|
5
|
-
// src/hooks/
|
|
5
|
+
// src/hooks/useChatV2.ts
|
|
6
6
|
|
|
7
7
|
// src/utils/generateId.ts
|
|
8
8
|
function generateId() {
|
|
@@ -147,13 +147,13 @@ function getEventMessage(event) {
|
|
|
147
147
|
case "USER_ACTION_REQUIRED":
|
|
148
148
|
return "Waiting for verification...";
|
|
149
149
|
case "USER_ACTION_SUCCESS":
|
|
150
|
-
return "Verification
|
|
150
|
+
return "Verification approved";
|
|
151
151
|
case "USER_ACTION_EXPIRED":
|
|
152
152
|
return "Verification expired";
|
|
153
153
|
case "USER_ACTION_INVALID":
|
|
154
|
-
return "Invalid
|
|
154
|
+
return "Invalid code. Please try again.";
|
|
155
155
|
case "USER_ACTION_REJECTED":
|
|
156
|
-
return "Verification
|
|
156
|
+
return "Verification cancelled";
|
|
157
157
|
case "USER_ACTION_RESENT":
|
|
158
158
|
return "Verification code resent";
|
|
159
159
|
case "USER_ACTION_FAILED":
|
|
@@ -168,6 +168,31 @@ function getEventMessage(event) {
|
|
|
168
168
|
return eventType;
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
|
+
function isUserActionPrompt(text) {
|
|
172
|
+
return /\benter\b.+\b(code|otp)\b/i.test(text) || /\b(authorization|verification)\s+code\b/i.test(text) || /\bsent\s+to\s+your\b/i.test(text);
|
|
173
|
+
}
|
|
174
|
+
function getUserActionDisplayMessage(eventType, rawMessage) {
|
|
175
|
+
const raw = rawMessage?.trim();
|
|
176
|
+
const safeRaw = raw && !isUserActionPrompt(raw) ? raw : "";
|
|
177
|
+
switch (eventType) {
|
|
178
|
+
case "USER_ACTION_REQUIRED":
|
|
179
|
+
return raw || "Waiting for verification...";
|
|
180
|
+
case "USER_ACTION_SUCCESS":
|
|
181
|
+
return safeRaw || "Verification approved";
|
|
182
|
+
case "USER_ACTION_INVALID":
|
|
183
|
+
return safeRaw || "Invalid code. Please try again.";
|
|
184
|
+
case "USER_ACTION_REJECTED":
|
|
185
|
+
return safeRaw || "Verification cancelled";
|
|
186
|
+
case "USER_ACTION_EXPIRED":
|
|
187
|
+
return safeRaw || "Verification expired";
|
|
188
|
+
case "USER_ACTION_RESENT":
|
|
189
|
+
return safeRaw || "Verification code resent";
|
|
190
|
+
case "USER_ACTION_FAILED":
|
|
191
|
+
return safeRaw || "Verification failed";
|
|
192
|
+
default:
|
|
193
|
+
return safeRaw || raw || eventType;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
171
196
|
function workingPhaseDetailForDisplay(raw) {
|
|
172
197
|
const t = raw.trim();
|
|
173
198
|
if (!t) return "";
|
|
@@ -198,290 +223,8 @@ function extractResponseContent(response) {
|
|
|
198
223
|
}
|
|
199
224
|
return "";
|
|
200
225
|
}
|
|
201
|
-
function completeLastInProgressStep(steps) {
|
|
202
|
-
for (let i = steps.length - 1; i >= 0; i--) {
|
|
203
|
-
if (steps[i].status === "in_progress") {
|
|
204
|
-
steps[i].status = "completed";
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
function processStreamEvent(event, state) {
|
|
210
|
-
const eventType = event.eventType;
|
|
211
|
-
if (typeof eventType === "string" && eventType.toUpperCase() === "KEEP_ALIVE") {
|
|
212
|
-
return state;
|
|
213
|
-
}
|
|
214
|
-
const message = getEventMessage(event);
|
|
215
|
-
if (eventType !== "INTENT_THINKING" && eventType !== "INTENT_THINKING_CONT") {
|
|
216
|
-
if (state.currentThinkingStepId) {
|
|
217
|
-
const thinkingStep = state.steps.find((s) => s.id === state.currentThinkingStepId);
|
|
218
|
-
if (thinkingStep) {
|
|
219
|
-
thinkingStep.isThinking = false;
|
|
220
|
-
}
|
|
221
|
-
state.currentThinkingStepId = void 0;
|
|
222
|
-
}
|
|
223
|
-
state.activeThinkingText = void 0;
|
|
224
|
-
}
|
|
225
|
-
if (eventType === "COMPLETED" || eventType === "WORKFLOW_COMPLETED") {
|
|
226
|
-
let content = extractResponseContent(event.response);
|
|
227
|
-
const trace = event.trace && typeof event.trace === "object" ? event.trace : null;
|
|
228
|
-
if (!content && trace?.workflowMsg && typeof trace.workflowMsg === "string") {
|
|
229
|
-
content = trace.workflowMsg;
|
|
230
|
-
}
|
|
231
|
-
if (!content && trace?.aggregator && typeof trace.aggregator === "object") {
|
|
232
|
-
const agg = trace.aggregator;
|
|
233
|
-
if (typeof agg.response === "string") content = agg.response;
|
|
234
|
-
else content = extractResponseContent(agg.response);
|
|
235
|
-
}
|
|
236
|
-
if (content) {
|
|
237
|
-
state.accumulatedContent = content;
|
|
238
|
-
state.finalData = event.response ?? event.trace;
|
|
239
|
-
state.hasError = false;
|
|
240
|
-
state.errorMessage = "";
|
|
241
|
-
} else {
|
|
242
|
-
state.hasError = true;
|
|
243
|
-
state.errorMessage = "WORKFLOW_FAILED";
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
if (eventType === "STARTED" || eventType === "WORKFLOW_STARTED") ; else if (eventType === "COMPLETED" || eventType === "WORKFLOW_COMPLETED") {
|
|
247
|
-
state.steps.forEach((step) => {
|
|
248
|
-
if (step.status === "in_progress") {
|
|
249
|
-
step.status = "completed";
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
} else if (eventType === "INTENT_ERROR") {
|
|
253
|
-
state.errorMessage = message || event.errorMessage || "An error occurred";
|
|
254
|
-
const intentStep = state.steps.find(
|
|
255
|
-
(s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress"
|
|
256
|
-
);
|
|
257
|
-
if (intentStep) {
|
|
258
|
-
intentStep.status = "error";
|
|
259
|
-
}
|
|
260
|
-
} else if (eventType === "ERROR" || eventType === "WORKFLOW_ERROR") {
|
|
261
|
-
state.hasError = true;
|
|
262
|
-
state.errorMessage = message || event.errorMessage || "An error occurred";
|
|
263
|
-
} else if (eventType === "ORCHESTRATOR_COMPLETED") {
|
|
264
|
-
state.inOrchestratorPhase = false;
|
|
265
|
-
const orchestratorStep = state.steps.find(
|
|
266
|
-
(s) => s.eventType === "ORCHESTRATOR_THINKING" && s.status === "in_progress"
|
|
267
|
-
);
|
|
268
|
-
if (orchestratorStep) {
|
|
269
|
-
orchestratorStep.status = "completed";
|
|
270
|
-
if (event.elapsedMs) {
|
|
271
|
-
orchestratorStep.elapsedMs = event.elapsedMs;
|
|
272
|
-
}
|
|
273
|
-
if (orchestratorStep.id === state.currentExecutingStepId) {
|
|
274
|
-
state.currentExecutingStepId = void 0;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
} else if (eventType === "INTENT_COMPLETED") {
|
|
278
|
-
const intentStep = state.steps.find(
|
|
279
|
-
(s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress"
|
|
280
|
-
);
|
|
281
|
-
if (intentStep) {
|
|
282
|
-
intentStep.status = "completed";
|
|
283
|
-
if (event.elapsedMs) {
|
|
284
|
-
intentStep.elapsedMs = event.elapsedMs;
|
|
285
|
-
}
|
|
286
|
-
if (intentStep.id === state.currentExecutingStepId) {
|
|
287
|
-
state.currentExecutingStepId = void 0;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
} else if (eventType === "AGGREGATOR_COMPLETED") {
|
|
291
|
-
state.inAggregatorPhase = false;
|
|
292
|
-
const aggregatorStep = state.steps.find(
|
|
293
|
-
(s) => s.eventType === "AGGREGATOR_THINKING" && s.status === "in_progress"
|
|
294
|
-
);
|
|
295
|
-
if (aggregatorStep) {
|
|
296
|
-
aggregatorStep.status = "completed";
|
|
297
|
-
if (event.elapsedMs) {
|
|
298
|
-
aggregatorStep.elapsedMs = event.elapsedMs;
|
|
299
|
-
}
|
|
300
|
-
if (aggregatorStep.id === state.currentExecutingStepId) {
|
|
301
|
-
state.currentExecutingStepId = void 0;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
} else if (eventType === "ORCHESTRATOR_THINKING" || eventType === "INTENT_STARTED" || eventType === "INTENT_PROGRESS" || eventType === "AGGREGATOR_THINKING") {
|
|
305
|
-
if (eventType === "ORCHESTRATOR_THINKING") {
|
|
306
|
-
state.inOrchestratorPhase = true;
|
|
307
|
-
}
|
|
308
|
-
if (eventType === "AGGREGATOR_THINKING") {
|
|
309
|
-
state.inAggregatorPhase = true;
|
|
310
|
-
}
|
|
311
|
-
if (eventType === "INTENT_PROGRESS") {
|
|
312
|
-
const intentStep = state.steps.find(
|
|
313
|
-
(s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress"
|
|
314
|
-
);
|
|
315
|
-
if (intentStep) {
|
|
316
|
-
intentStep.message = message;
|
|
317
|
-
state.currentExecutingStepId = intentStep.id;
|
|
318
|
-
} else {
|
|
319
|
-
const stepId = `step-${state.stepCounter++}`;
|
|
320
|
-
state.steps.push({
|
|
321
|
-
id: stepId,
|
|
322
|
-
eventType: "INTENT_STARTED",
|
|
323
|
-
message,
|
|
324
|
-
status: "in_progress",
|
|
325
|
-
timestamp: Date.now(),
|
|
326
|
-
elapsedMs: event.elapsedMs
|
|
327
|
-
});
|
|
328
|
-
state.currentExecutingStepId = stepId;
|
|
329
|
-
}
|
|
330
|
-
} else {
|
|
331
|
-
const stepId = `step-${state.stepCounter++}`;
|
|
332
|
-
state.steps.push({
|
|
333
|
-
id: stepId,
|
|
334
|
-
eventType,
|
|
335
|
-
message,
|
|
336
|
-
status: "in_progress",
|
|
337
|
-
timestamp: Date.now(),
|
|
338
|
-
elapsedMs: event.elapsedMs
|
|
339
|
-
});
|
|
340
|
-
state.currentExecutingStepId = stepId;
|
|
341
|
-
}
|
|
342
|
-
} else if (eventType === "USER_ACTION_REQUIRED") {
|
|
343
|
-
completeLastInProgressStep(state.steps);
|
|
344
|
-
if (event.userActionRequest) {
|
|
345
|
-
state.userActionRequest = {
|
|
346
|
-
userActionId: event.userActionRequest.userActionId,
|
|
347
|
-
userActionType: event.userActionRequest.userActionType,
|
|
348
|
-
message: event.userActionRequest.message,
|
|
349
|
-
requestedSchema: event.userActionRequest.requestedSchema,
|
|
350
|
-
metadata: event.userActionRequest.metadata
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
state.userActionPending = true;
|
|
354
|
-
const stepId = `step-${state.stepCounter++}`;
|
|
355
|
-
state.steps.push({
|
|
356
|
-
id: stepId,
|
|
357
|
-
eventType,
|
|
358
|
-
message,
|
|
359
|
-
status: "in_progress",
|
|
360
|
-
timestamp: Date.now(),
|
|
361
|
-
elapsedMs: event.elapsedMs
|
|
362
|
-
});
|
|
363
|
-
state.currentExecutingStepId = stepId;
|
|
364
|
-
} else if (eventType === "USER_ACTION_SUCCESS") {
|
|
365
|
-
completeLastInProgressStep(state.steps);
|
|
366
|
-
state.userActionRequest = void 0;
|
|
367
|
-
state.userActionPending = false;
|
|
368
|
-
state.userActionResult = "approved";
|
|
369
|
-
const stepId = `step-${state.stepCounter++}`;
|
|
370
|
-
state.steps.push({
|
|
371
|
-
id: stepId,
|
|
372
|
-
eventType,
|
|
373
|
-
message,
|
|
374
|
-
status: "completed",
|
|
375
|
-
timestamp: Date.now(),
|
|
376
|
-
elapsedMs: event.elapsedMs
|
|
377
|
-
});
|
|
378
|
-
} else if (eventType === "USER_ACTION_INVALID") {
|
|
379
|
-
completeLastInProgressStep(state.steps);
|
|
380
|
-
const errorStepId = `step-${state.stepCounter++}`;
|
|
381
|
-
state.steps.push({
|
|
382
|
-
id: errorStepId,
|
|
383
|
-
eventType,
|
|
384
|
-
message,
|
|
385
|
-
status: "error",
|
|
386
|
-
timestamp: Date.now(),
|
|
387
|
-
elapsedMs: event.elapsedMs
|
|
388
|
-
});
|
|
389
|
-
const retryStepId = `step-${state.stepCounter++}`;
|
|
390
|
-
state.steps.push({
|
|
391
|
-
id: retryStepId,
|
|
392
|
-
eventType: "USER_ACTION_REQUIRED",
|
|
393
|
-
message: "Waiting for verification...",
|
|
394
|
-
status: "in_progress",
|
|
395
|
-
timestamp: Date.now()
|
|
396
|
-
});
|
|
397
|
-
state.currentExecutingStepId = retryStepId;
|
|
398
|
-
} else if (eventType === "USER_ACTION_EXPIRED") {
|
|
399
|
-
completeLastInProgressStep(state.steps);
|
|
400
|
-
state.userActionRequest = void 0;
|
|
401
|
-
state.userActionPending = false;
|
|
402
|
-
const stepId = `step-${state.stepCounter++}`;
|
|
403
|
-
state.steps.push({
|
|
404
|
-
id: stepId,
|
|
405
|
-
eventType,
|
|
406
|
-
message,
|
|
407
|
-
status: "error",
|
|
408
|
-
timestamp: Date.now(),
|
|
409
|
-
elapsedMs: event.elapsedMs
|
|
410
|
-
});
|
|
411
|
-
} else if (eventType === "USER_ACTION_REJECTED") {
|
|
412
|
-
completeLastInProgressStep(state.steps);
|
|
413
|
-
state.userActionRequest = void 0;
|
|
414
|
-
state.userActionPending = false;
|
|
415
|
-
state.userActionResult = "rejected";
|
|
416
|
-
const stepId = `step-${state.stepCounter++}`;
|
|
417
|
-
state.steps.push({
|
|
418
|
-
id: stepId,
|
|
419
|
-
eventType,
|
|
420
|
-
message,
|
|
421
|
-
status: "completed",
|
|
422
|
-
timestamp: Date.now(),
|
|
423
|
-
elapsedMs: event.elapsedMs
|
|
424
|
-
});
|
|
425
|
-
} else if (eventType === "USER_ACTION_RESENT") {
|
|
426
|
-
const stepId = `step-${state.stepCounter++}`;
|
|
427
|
-
state.steps.push({
|
|
428
|
-
id: stepId,
|
|
429
|
-
eventType,
|
|
430
|
-
message,
|
|
431
|
-
status: "completed",
|
|
432
|
-
timestamp: Date.now(),
|
|
433
|
-
elapsedMs: event.elapsedMs
|
|
434
|
-
});
|
|
435
|
-
} else if (eventType === "USER_ACTION_FAILED") {
|
|
436
|
-
completeLastInProgressStep(state.steps);
|
|
437
|
-
state.userActionRequest = void 0;
|
|
438
|
-
state.userActionPending = false;
|
|
439
|
-
const stepId = `step-${state.stepCounter++}`;
|
|
440
|
-
state.steps.push({
|
|
441
|
-
id: stepId,
|
|
442
|
-
eventType,
|
|
443
|
-
message,
|
|
444
|
-
status: "error",
|
|
445
|
-
timestamp: Date.now(),
|
|
446
|
-
elapsedMs: event.elapsedMs
|
|
447
|
-
});
|
|
448
|
-
} else if (eventType === "INTENT_THINKING") {
|
|
449
|
-
if (state.currentThinkingStepId) {
|
|
450
|
-
const prev = state.steps.find((s) => s.id === state.currentThinkingStepId);
|
|
451
|
-
if (prev) prev.isThinking = false;
|
|
452
|
-
}
|
|
453
|
-
const lastInProgress = [...state.steps].reverse().find((s) => s.status === "in_progress");
|
|
454
|
-
if (lastInProgress) {
|
|
455
|
-
lastInProgress.thinkingText = "";
|
|
456
|
-
lastInProgress.isThinking = true;
|
|
457
|
-
state.currentThinkingStepId = lastInProgress.id;
|
|
458
|
-
} else {
|
|
459
|
-
state.currentThinkingStepId = void 0;
|
|
460
|
-
}
|
|
461
|
-
if (state.allThinkingText) state.allThinkingText += "\n\n";
|
|
462
|
-
if (!state.inOrchestratorPhase && !state.inAggregatorPhase) {
|
|
463
|
-
state.activeThinkingText = "";
|
|
464
|
-
}
|
|
465
|
-
} else if (eventType === "INTENT_THINKING_CONT") {
|
|
466
|
-
const delta = event.message || "";
|
|
467
|
-
if (!delta) return state;
|
|
468
|
-
if (state.currentThinkingStepId) {
|
|
469
|
-
const step = state.steps.find((s) => s.id === state.currentThinkingStepId);
|
|
470
|
-
if (step) {
|
|
471
|
-
step.thinkingText = (step.thinkingText || "") + delta;
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
state.allThinkingText += delta;
|
|
475
|
-
if (!state.inOrchestratorPhase && !state.inAggregatorPhase) {
|
|
476
|
-
if (state.activeThinkingText == null) state.activeThinkingText = "";
|
|
477
|
-
state.activeThinkingText += delta;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
return state;
|
|
481
|
-
}
|
|
482
226
|
|
|
483
227
|
// src/utils/messageStateManager.ts
|
|
484
|
-
var FRIENDLY_ERROR_MESSAGE = "Oops, something went wrong. Please try again.";
|
|
485
228
|
function buildFormattedThinking(steps, allThinkingText) {
|
|
486
229
|
const parts = [];
|
|
487
230
|
const safeSteps = steps ?? [];
|
|
@@ -538,91 +281,32 @@ function buildFormattedThinking(steps, allThinkingText) {
|
|
|
538
281
|
break;
|
|
539
282
|
case "USER_ACTION_REQUIRED":
|
|
540
283
|
parts.push("**Verification Required**");
|
|
541
|
-
if (step.message)
|
|
284
|
+
if (step.message) {
|
|
285
|
+
parts.push(getUserActionDisplayMessage(step.eventType, step.message));
|
|
286
|
+
}
|
|
542
287
|
break;
|
|
543
288
|
case "USER_ACTION_SUCCESS":
|
|
544
|
-
parts.push(`\u2713 ${step.message
|
|
289
|
+
parts.push(`\u2713 ${getUserActionDisplayMessage(step.eventType, step.message)}`);
|
|
545
290
|
break;
|
|
546
291
|
case "USER_ACTION_REJECTED":
|
|
547
|
-
parts.push(
|
|
292
|
+
parts.push(getUserActionDisplayMessage(step.eventType, step.message));
|
|
548
293
|
break;
|
|
549
294
|
case "USER_ACTION_EXPIRED":
|
|
550
|
-
parts.push(`\u2717 ${step.message
|
|
295
|
+
parts.push(`\u2717 ${getUserActionDisplayMessage(step.eventType, step.message)}`);
|
|
296
|
+
break;
|
|
297
|
+
case "USER_ACTION_INVALID":
|
|
298
|
+
parts.push(`\u2717 ${getUserActionDisplayMessage(step.eventType, step.message)}`);
|
|
299
|
+
break;
|
|
300
|
+
case "USER_ACTION_RESENT":
|
|
301
|
+
parts.push(getUserActionDisplayMessage(step.eventType, step.message));
|
|
551
302
|
break;
|
|
552
303
|
case "USER_ACTION_FAILED":
|
|
553
|
-
parts.push(`\u2717 ${step.message
|
|
304
|
+
parts.push(`\u2717 ${getUserActionDisplayMessage(step.eventType, step.message)}`);
|
|
554
305
|
break;
|
|
555
306
|
}
|
|
556
307
|
}
|
|
557
308
|
return parts.length > 0 ? parts.join("\n") : allThinkingText;
|
|
558
309
|
}
|
|
559
|
-
function createStreamingMessageUpdate(state) {
|
|
560
|
-
const hasCompletedContent = state.accumulatedContent && state.finalData !== void 0;
|
|
561
|
-
const steps = state.hasError ? [] : [...state.steps];
|
|
562
|
-
const allThinking = state.hasError ? void 0 : state.allThinkingText;
|
|
563
|
-
return {
|
|
564
|
-
streamingContent: state.hasError ? FRIENDLY_ERROR_MESSAGE : hasCompletedContent ? state.accumulatedContent : "",
|
|
565
|
-
content: state.hasError ? FRIENDLY_ERROR_MESSAGE : "",
|
|
566
|
-
currentMessage: state.hasError ? void 0 : state.currentMessage,
|
|
567
|
-
streamProgress: state.hasError ? "error" : "processing",
|
|
568
|
-
isError: state.hasError,
|
|
569
|
-
errorDetails: state.hasError ? state.errorMessage : void 0,
|
|
570
|
-
executionId: state.executionId,
|
|
571
|
-
sessionId: state.sessionId,
|
|
572
|
-
steps,
|
|
573
|
-
currentExecutingStepId: state.hasError ? void 0 : state.currentExecutingStepId,
|
|
574
|
-
isCancelled: false,
|
|
575
|
-
userActionResult: state.userActionResult,
|
|
576
|
-
activeThinkingText: state.hasError ? void 0 : state.activeThinkingText,
|
|
577
|
-
allThinkingText: allThinking,
|
|
578
|
-
formattedThinkingText: state.hasError ? void 0 : buildFormattedThinking(steps, allThinking || "")
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
function createErrorMessageUpdate(error, state) {
|
|
582
|
-
const isAborted = error.name === "AbortError";
|
|
583
|
-
return {
|
|
584
|
-
isStreaming: false,
|
|
585
|
-
streamProgress: isAborted ? "processing" : "error",
|
|
586
|
-
isError: !isAborted,
|
|
587
|
-
isCancelled: isAborted,
|
|
588
|
-
errorDetails: isAborted ? void 0 : error.message,
|
|
589
|
-
content: isAborted ? state.accumulatedContent || "" : state.accumulatedContent || FRIENDLY_ERROR_MESSAGE,
|
|
590
|
-
// Preserve currentMessage when cancelled so UI can show it
|
|
591
|
-
currentMessage: isAborted ? state.currentMessage || "Thinking..." : void 0,
|
|
592
|
-
steps: [...state.steps].map((step) => {
|
|
593
|
-
if (step.status === "in_progress" && isAborted) {
|
|
594
|
-
return { ...step, status: "pending" };
|
|
595
|
-
}
|
|
596
|
-
return step;
|
|
597
|
-
}),
|
|
598
|
-
currentExecutingStepId: void 0
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
function createFinalMessage(streamingId, state) {
|
|
602
|
-
const steps = state.hasError ? [] : [...state.steps];
|
|
603
|
-
const allThinking = state.hasError ? void 0 : state.allThinkingText;
|
|
604
|
-
return {
|
|
605
|
-
id: streamingId,
|
|
606
|
-
sessionId: state.sessionId,
|
|
607
|
-
role: "assistant",
|
|
608
|
-
content: state.hasError ? FRIENDLY_ERROR_MESSAGE : state.accumulatedContent || "",
|
|
609
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
610
|
-
isStreaming: false,
|
|
611
|
-
streamProgress: state.hasError ? "error" : "completed",
|
|
612
|
-
isError: state.hasError,
|
|
613
|
-
errorDetails: state.hasError ? state.errorMessage : void 0,
|
|
614
|
-
executionId: state.executionId,
|
|
615
|
-
tracingData: state.finalData,
|
|
616
|
-
steps,
|
|
617
|
-
isCancelled: false,
|
|
618
|
-
currentExecutingStepId: void 0,
|
|
619
|
-
userActionResult: state.userActionResult,
|
|
620
|
-
activeThinkingText: void 0,
|
|
621
|
-
allThinkingText: allThinking,
|
|
622
|
-
formattedThinkingText: state.hasError ? void 0 : buildFormattedThinking(steps, allThinking || ""),
|
|
623
|
-
isResolvingImages: state.hasError ? void 0 : state.isResolvingImages
|
|
624
|
-
};
|
|
625
|
-
}
|
|
626
310
|
function createCancelledMessageUpdate(steps, currentMessage) {
|
|
627
311
|
const updatedSteps = steps.map((step) => {
|
|
628
312
|
if (step.status === "in_progress") {
|
|
@@ -642,35 +326,44 @@ function createCancelledMessageUpdate(steps, currentMessage) {
|
|
|
642
326
|
|
|
643
327
|
// src/utils/requestBuilder.ts
|
|
644
328
|
function buildRequestBody(config, userMessage, sessionId) {
|
|
645
|
-
const
|
|
646
|
-
const sessionAttributes =
|
|
329
|
+
const owner = config.session?.owner;
|
|
330
|
+
const sessionAttributes = owner?.attributes && Object.keys(owner.attributes).length > 0 ? owner.attributes : void 0;
|
|
647
331
|
return {
|
|
648
|
-
workflowName: config.
|
|
332
|
+
workflowName: config.workflow.name,
|
|
649
333
|
userInput: userMessage,
|
|
650
334
|
sessionId,
|
|
651
|
-
sessionOwnerId:
|
|
652
|
-
sessionOwnerLabel:
|
|
335
|
+
sessionOwnerId: owner?.id || "",
|
|
336
|
+
sessionOwnerLabel: owner?.name || "",
|
|
653
337
|
sessionAttributes,
|
|
654
338
|
options: {
|
|
655
339
|
clientTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
656
340
|
}
|
|
657
341
|
};
|
|
658
342
|
}
|
|
343
|
+
function getStageParamName(config) {
|
|
344
|
+
return config.api.stageQueryParam ?? "stage";
|
|
345
|
+
}
|
|
346
|
+
function getStage(config) {
|
|
347
|
+
return config.workflow.stage ?? "DEV";
|
|
348
|
+
}
|
|
659
349
|
function buildStreamingUrl(config) {
|
|
660
350
|
const endpoint = config.api.streamEndpoint || "/api/workflows/ask/stream";
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
if (config.
|
|
665
|
-
queryParams.append("workflowVersion", String(config.
|
|
351
|
+
const queryParams = new URLSearchParams({
|
|
352
|
+
[getStageParamName(config)]: getStage(config)
|
|
353
|
+
});
|
|
354
|
+
if (config.workflow.version !== void 0) {
|
|
355
|
+
queryParams.append("workflowVersion", String(config.workflow.version));
|
|
666
356
|
}
|
|
667
357
|
return `${config.api.baseUrl}${endpoint}?${queryParams.toString()}`;
|
|
668
358
|
}
|
|
669
|
-
function
|
|
359
|
+
function deriveBasePath(config) {
|
|
670
360
|
const endpoint = config.api.streamEndpoint || "/api/workflows/ask/stream";
|
|
671
361
|
const [endpointPath] = endpoint.split("?");
|
|
672
|
-
const
|
|
673
|
-
|
|
362
|
+
const normalized = endpointPath.replace(/\/+$/, "");
|
|
363
|
+
return normalized.endsWith("/stream") ? normalized.slice(0, -"/stream".length) : normalized;
|
|
364
|
+
}
|
|
365
|
+
function buildUserActionUrl(config, userActionId, action) {
|
|
366
|
+
const basePath = deriveBasePath(config);
|
|
674
367
|
const encodedUserActionId = encodeURIComponent(userActionId);
|
|
675
368
|
return `${config.api.baseUrl}${basePath}/user-action/${encodedUserActionId}/${action}`;
|
|
676
369
|
}
|
|
@@ -678,21 +371,23 @@ function buildResolveImagesUrl(config) {
|
|
|
678
371
|
if (config.api.resolveImagesEndpoint) {
|
|
679
372
|
return `${config.api.baseUrl}${config.api.resolveImagesEndpoint}`;
|
|
680
373
|
}
|
|
681
|
-
|
|
682
|
-
const [endpointPath] = streamEndpoint.split("?");
|
|
683
|
-
const normalizedEndpointPath = endpointPath.replace(/\/+$/, "");
|
|
684
|
-
const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
|
|
685
|
-
return `${config.api.baseUrl}${basePath}/resolve-image-urls`;
|
|
374
|
+
return `${config.api.baseUrl}${deriveBasePath(config)}/resolve-image-urls`;
|
|
686
375
|
}
|
|
687
376
|
function buildRequestHeaders(config) {
|
|
688
|
-
const headers = {
|
|
689
|
-
...config.api.headers
|
|
690
|
-
};
|
|
377
|
+
const headers = { ...config.api.headers };
|
|
691
378
|
if (config.api.authToken) {
|
|
692
379
|
headers.Authorization = `Bearer ${config.api.authToken}`;
|
|
693
380
|
}
|
|
694
381
|
return headers;
|
|
695
382
|
}
|
|
383
|
+
function buildScopeKey(config) {
|
|
384
|
+
return [
|
|
385
|
+
config.session?.userId ?? "",
|
|
386
|
+
config.workflow.id ?? "",
|
|
387
|
+
config.workflow.version ?? "",
|
|
388
|
+
config.workflow.stage ?? ""
|
|
389
|
+
].join("|");
|
|
390
|
+
}
|
|
696
391
|
|
|
697
392
|
// src/utils/userActionClient.ts
|
|
698
393
|
async function sendUserActionRequest(config, userActionId, action, data) {
|
|
@@ -708,683 +403,96 @@ async function sendUserActionRequest(config, userActionId, action, data) {
|
|
|
708
403
|
if (!response.ok) {
|
|
709
404
|
const errorText = await response.text();
|
|
710
405
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
711
|
-
}
|
|
712
|
-
return await response.json();
|
|
713
|
-
}
|
|
714
|
-
async function submitUserAction(config, userActionId, data) {
|
|
715
|
-
return sendUserActionRequest(config, userActionId, "submit", data);
|
|
716
|
-
}
|
|
717
|
-
async function cancelUserAction(config, userActionId) {
|
|
718
|
-
return sendUserActionRequest(config, userActionId, "cancel");
|
|
719
|
-
}
|
|
720
|
-
async function resendUserAction(config, userActionId) {
|
|
721
|
-
return sendUserActionRequest(config, userActionId, "resend");
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
// src/utils/chatStore.ts
|
|
725
|
-
var memoryStore = /* @__PURE__ */ new Map();
|
|
726
|
-
var chatStore = {
|
|
727
|
-
get(key) {
|
|
728
|
-
return memoryStore.get(key) ?? [];
|
|
729
|
-
},
|
|
730
|
-
set(key, messages) {
|
|
731
|
-
memoryStore.set(key, messages);
|
|
732
|
-
},
|
|
733
|
-
delete(key) {
|
|
734
|
-
memoryStore.delete(key);
|
|
735
|
-
}
|
|
736
|
-
};
|
|
737
|
-
|
|
738
|
-
// src/utils/activeStreamStore.ts
|
|
739
|
-
var streams = /* @__PURE__ */ new Map();
|
|
740
|
-
var activeStreamStore = {
|
|
741
|
-
has(key) {
|
|
742
|
-
return streams.has(key);
|
|
743
|
-
},
|
|
744
|
-
get(key) {
|
|
745
|
-
const entry = streams.get(key);
|
|
746
|
-
if (!entry) return null;
|
|
747
|
-
return { messages: entry.messages, isWaiting: entry.isWaiting };
|
|
748
|
-
},
|
|
749
|
-
// Called before startStream — registers the controller and initial messages
|
|
750
|
-
start(key, abortController, initialMessages) {
|
|
751
|
-
const existing = streams.get(key);
|
|
752
|
-
streams.set(key, {
|
|
753
|
-
messages: initialMessages,
|
|
754
|
-
isWaiting: true,
|
|
755
|
-
abortController,
|
|
756
|
-
listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
|
|
757
|
-
});
|
|
758
|
-
},
|
|
759
|
-
// Called by the stream on every event — applies the same updater pattern React uses
|
|
760
|
-
applyMessages(key, updater) {
|
|
761
|
-
const entry = streams.get(key);
|
|
762
|
-
if (!entry) return;
|
|
763
|
-
const next = typeof updater === "function" ? updater(entry.messages) : updater;
|
|
764
|
-
entry.messages = next;
|
|
765
|
-
entry.listeners.forEach((l) => l(next, entry.isWaiting));
|
|
766
|
-
},
|
|
767
|
-
setWaiting(key, waiting) {
|
|
768
|
-
const entry = streams.get(key);
|
|
769
|
-
if (!entry) return;
|
|
770
|
-
entry.isWaiting = waiting;
|
|
771
|
-
entry.listeners.forEach((l) => l(entry.messages, waiting));
|
|
772
|
-
},
|
|
773
|
-
// Called when stream completes — persists to chatStore and cleans up
|
|
774
|
-
complete(key) {
|
|
775
|
-
const entry = streams.get(key);
|
|
776
|
-
if (!entry) return;
|
|
777
|
-
entry.isWaiting = false;
|
|
778
|
-
entry.listeners.forEach((l) => l(entry.messages, false));
|
|
779
|
-
const toSave = entry.messages.filter((m) => !m.isStreaming);
|
|
780
|
-
if (toSave.length > 0) chatStore.set(key, toSave);
|
|
781
|
-
streams.delete(key);
|
|
782
|
-
},
|
|
783
|
-
// Subscribe — returns unsubscribe fn. Component calls this on mount, cleanup on unmount.
|
|
784
|
-
subscribe(key, listener) {
|
|
785
|
-
const entry = streams.get(key);
|
|
786
|
-
if (!entry) return () => {
|
|
787
|
-
};
|
|
788
|
-
entry.listeners.add(listener);
|
|
789
|
-
return () => {
|
|
790
|
-
streams.get(key)?.listeners.delete(listener);
|
|
791
|
-
};
|
|
792
|
-
},
|
|
793
|
-
// Explicit user cancel — aborts the controller and removes the entry
|
|
794
|
-
abort(key) {
|
|
795
|
-
const entry = streams.get(key);
|
|
796
|
-
if (!entry) return;
|
|
797
|
-
entry.abortController.abort();
|
|
798
|
-
streams.delete(key);
|
|
799
|
-
}
|
|
800
|
-
};
|
|
801
|
-
|
|
802
|
-
// src/utils/ragImageResolver.ts
|
|
803
|
-
var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
|
|
804
|
-
function hasRagImages(content) {
|
|
805
|
-
return RAG_IMAGE_REGEX.test(content);
|
|
806
|
-
}
|
|
807
|
-
async function waitForNextPaint(signal) {
|
|
808
|
-
if (signal?.aborted) return;
|
|
809
|
-
await new Promise((resolve) => {
|
|
810
|
-
let isSettled = false;
|
|
811
|
-
const finish = () => {
|
|
812
|
-
if (isSettled) return;
|
|
813
|
-
isSettled = true;
|
|
814
|
-
signal?.removeEventListener("abort", finish);
|
|
815
|
-
resolve();
|
|
816
|
-
};
|
|
817
|
-
signal?.addEventListener("abort", finish, { once: true });
|
|
818
|
-
if (typeof requestAnimationFrame === "function") {
|
|
819
|
-
requestAnimationFrame(() => {
|
|
820
|
-
setTimeout(finish, 0);
|
|
821
|
-
});
|
|
822
|
-
return;
|
|
823
|
-
}
|
|
824
|
-
setTimeout(finish, 0);
|
|
825
|
-
});
|
|
826
|
-
}
|
|
827
|
-
async function resolveRagImageUrls(config, content, signal) {
|
|
828
|
-
const url = buildResolveImagesUrl(config);
|
|
829
|
-
const baseHeaders = buildRequestHeaders(config);
|
|
830
|
-
const headers = { "Content-Type": "application/json", ...baseHeaders };
|
|
831
|
-
const response = await fetch(url, {
|
|
832
|
-
method: "POST",
|
|
833
|
-
headers,
|
|
834
|
-
body: JSON.stringify({ input: content }),
|
|
835
|
-
signal
|
|
836
|
-
});
|
|
837
|
-
if (!response.ok) {
|
|
838
|
-
const errorText = await response.text();
|
|
839
|
-
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
840
|
-
}
|
|
841
|
-
const text = await response.text();
|
|
842
|
-
try {
|
|
843
|
-
const parsed = JSON.parse(text);
|
|
844
|
-
if (typeof parsed === "string") return parsed;
|
|
845
|
-
if (typeof parsed.output === "string") return parsed.output;
|
|
846
|
-
if (typeof parsed.result === "string") return parsed.result;
|
|
847
|
-
} catch {
|
|
848
|
-
}
|
|
849
|
-
return text;
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
// src/hooks/useStreamManager.ts
|
|
853
|
-
function useStreamManager(config, callbacks, setMessages, setIsWaitingForResponse) {
|
|
854
|
-
const abortControllerRef = react.useRef(null);
|
|
855
|
-
const configRef = react.useRef(config);
|
|
856
|
-
configRef.current = config;
|
|
857
|
-
const callbacksRef = react.useRef(callbacks);
|
|
858
|
-
callbacksRef.current = callbacks;
|
|
859
|
-
const startStream = react.useCallback(
|
|
860
|
-
async (userMessage, streamingId, sessionId, externalAbortController) => {
|
|
861
|
-
abortControllerRef.current?.abort();
|
|
862
|
-
const abortController = externalAbortController ?? new AbortController();
|
|
863
|
-
abortControllerRef.current = abortController;
|
|
864
|
-
const state = {
|
|
865
|
-
accumulatedContent: "",
|
|
866
|
-
executionId: void 0,
|
|
867
|
-
currentSessionId: void 0,
|
|
868
|
-
finalData: void 0,
|
|
869
|
-
steps: [],
|
|
870
|
-
stepCounter: 0,
|
|
871
|
-
currentExecutingStepId: void 0,
|
|
872
|
-
currentThinkingStepId: void 0,
|
|
873
|
-
hasError: false,
|
|
874
|
-
errorMessage: "",
|
|
875
|
-
userActionRequest: void 0,
|
|
876
|
-
userActionPending: false,
|
|
877
|
-
userActionResult: void 0,
|
|
878
|
-
allThinkingText: "",
|
|
879
|
-
inOrchestratorPhase: false,
|
|
880
|
-
inAggregatorPhase: false
|
|
881
|
-
};
|
|
882
|
-
const THROTTLE_MS = 120;
|
|
883
|
-
const CHARS_PER_TICK = 10;
|
|
884
|
-
const displayedLengthRef = { current: 0 };
|
|
885
|
-
let throttleIntervalId = null;
|
|
886
|
-
const getActiveStepMessage = () => state.steps.find((s) => s.id === state.currentExecutingStepId)?.message || [...state.steps].reverse().find((s) => s.status === "in_progress")?.message;
|
|
887
|
-
const advanceDisplayLength = (full) => {
|
|
888
|
-
let newLen = Math.min(displayedLengthRef.current + CHARS_PER_TICK, full.length);
|
|
889
|
-
if (newLen > 0 && newLen < full.length) {
|
|
890
|
-
const code = full.charCodeAt(newLen - 1);
|
|
891
|
-
if (code >= 55296 && code <= 56319) {
|
|
892
|
-
newLen = Math.min(newLen + 1, full.length);
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
displayedLengthRef.current = newLen;
|
|
896
|
-
};
|
|
897
|
-
const clearThrottle = () => {
|
|
898
|
-
if (throttleIntervalId != null) {
|
|
899
|
-
clearInterval(throttleIntervalId);
|
|
900
|
-
throttleIntervalId = null;
|
|
901
|
-
}
|
|
902
|
-
};
|
|
903
|
-
abortController.signal.addEventListener("abort", clearThrottle, { once: true });
|
|
904
|
-
const ensureThrottle = () => {
|
|
905
|
-
if (throttleIntervalId != null) return;
|
|
906
|
-
throttleIntervalId = setInterval(() => {
|
|
907
|
-
if (abortController.signal.aborted) {
|
|
908
|
-
clearThrottle();
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
const full = state.activeThinkingText ?? "";
|
|
912
|
-
if (displayedLengthRef.current < full.length) {
|
|
913
|
-
advanceDisplayLength(full);
|
|
914
|
-
const displayText = full.slice(0, displayedLengthRef.current);
|
|
915
|
-
setMessages(
|
|
916
|
-
(prev) => prev.map(
|
|
917
|
-
(msg) => msg.id === streamingId ? {
|
|
918
|
-
...msg,
|
|
919
|
-
...createStreamingMessageUpdate({
|
|
920
|
-
...state,
|
|
921
|
-
activeThinkingText: displayText,
|
|
922
|
-
currentMessage: getActiveStepMessage() || "Thinking..."
|
|
923
|
-
})
|
|
924
|
-
} : msg
|
|
925
|
-
)
|
|
926
|
-
);
|
|
927
|
-
}
|
|
928
|
-
}, THROTTLE_MS);
|
|
929
|
-
};
|
|
930
|
-
const currentConfig = configRef.current;
|
|
931
|
-
const requestBody = buildRequestBody(currentConfig, userMessage, sessionId);
|
|
932
|
-
const url = buildStreamingUrl(currentConfig);
|
|
933
|
-
const headers = buildRequestHeaders(currentConfig);
|
|
934
|
-
try {
|
|
935
|
-
await streamWorkflowEvents(url, requestBody, headers, {
|
|
936
|
-
signal: abortController.signal,
|
|
937
|
-
onEvent: (event) => {
|
|
938
|
-
if (abortController.signal.aborted) {
|
|
939
|
-
return;
|
|
940
|
-
}
|
|
941
|
-
if (typeof event.eventType === "string" && event.eventType.toUpperCase() === "KEEP_ALIVE") {
|
|
942
|
-
if (event.executionId) state.executionId = event.executionId;
|
|
943
|
-
if (event.sessionId) state.currentSessionId = event.sessionId;
|
|
944
|
-
return;
|
|
945
|
-
}
|
|
946
|
-
if (event.executionId) state.executionId = event.executionId;
|
|
947
|
-
if (event.sessionId) state.currentSessionId = event.sessionId;
|
|
948
|
-
const activeThinkingLengthBeforeProcess = state.activeThinkingText?.length ?? 0;
|
|
949
|
-
processStreamEvent(event, state);
|
|
950
|
-
const eventType = event.eventType;
|
|
951
|
-
if (eventType === "INTENT_THINKING") {
|
|
952
|
-
displayedLengthRef.current = 0;
|
|
953
|
-
ensureThrottle();
|
|
954
|
-
} else if (eventType !== "INTENT_THINKING_CONT") {
|
|
955
|
-
displayedLengthRef.current = activeThinkingLengthBeforeProcess;
|
|
956
|
-
clearThrottle();
|
|
957
|
-
}
|
|
958
|
-
if (eventType === "USER_ACTION_REQUIRED" && state.userActionRequest) {
|
|
959
|
-
callbacksRef.current.onUserActionRequired?.(state.userActionRequest);
|
|
960
|
-
} else if (eventType.startsWith("USER_ACTION_") && eventType !== "USER_ACTION_REQUIRED") {
|
|
961
|
-
const msg = event.message?.trim() || event.errorMessage?.trim() || getEventMessage(event);
|
|
962
|
-
callbacksRef.current.onUserActionEvent?.(eventType, msg);
|
|
963
|
-
}
|
|
964
|
-
const isIntentThinkingEvent = eventType === "INTENT_THINKING" || eventType === "INTENT_THINKING_CONT";
|
|
965
|
-
const rawMessage = event.message?.trim() || event.errorMessage?.trim();
|
|
966
|
-
const currentMessage = isIntentThinkingEvent ? getActiveStepMessage() || "Thinking..." : rawMessage || (event.eventType?.startsWith("USER_ACTION_") ? getEventMessage(event) : getActiveStepMessage() || getEventMessage(event));
|
|
967
|
-
const displayThinking = state.activeThinkingText != null ? state.activeThinkingText.slice(0, displayedLengthRef.current) : state.activeThinkingText;
|
|
968
|
-
setMessages(
|
|
969
|
-
(prev) => prev.map(
|
|
970
|
-
(msg) => msg.id === streamingId ? {
|
|
971
|
-
...msg,
|
|
972
|
-
...createStreamingMessageUpdate({
|
|
973
|
-
...state,
|
|
974
|
-
activeThinkingText: displayThinking,
|
|
975
|
-
currentMessage
|
|
976
|
-
})
|
|
977
|
-
} : msg
|
|
978
|
-
)
|
|
979
|
-
);
|
|
980
|
-
},
|
|
981
|
-
onError: (error) => {
|
|
982
|
-
clearThrottle();
|
|
983
|
-
setIsWaitingForResponse(false);
|
|
984
|
-
if (error.name !== "AbortError") {
|
|
985
|
-
callbacksRef.current.onError?.(error);
|
|
986
|
-
}
|
|
987
|
-
if (state.userActionPending) {
|
|
988
|
-
state.userActionPending = false;
|
|
989
|
-
state.userActionRequest = void 0;
|
|
990
|
-
callbacksRef.current.onUserActionEvent?.(
|
|
991
|
-
"USER_ACTION_FAILED",
|
|
992
|
-
"Connection lost. Please try again."
|
|
993
|
-
);
|
|
994
|
-
}
|
|
995
|
-
setMessages(
|
|
996
|
-
(prev) => prev.map(
|
|
997
|
-
(msg) => msg.id === streamingId ? {
|
|
998
|
-
...msg,
|
|
999
|
-
...createErrorMessageUpdate(error, state)
|
|
1000
|
-
} : msg
|
|
1001
|
-
)
|
|
1002
|
-
);
|
|
1003
|
-
},
|
|
1004
|
-
onComplete: () => {
|
|
1005
|
-
clearThrottle();
|
|
1006
|
-
setIsWaitingForResponse(false);
|
|
1007
|
-
if (state.userActionPending) {
|
|
1008
|
-
state.userActionPending = false;
|
|
1009
|
-
state.userActionRequest = void 0;
|
|
1010
|
-
callbacksRef.current.onUserActionEvent?.(
|
|
1011
|
-
"USER_ACTION_FAILED",
|
|
1012
|
-
"Verification could not be completed."
|
|
1013
|
-
);
|
|
1014
|
-
}
|
|
1015
|
-
if (state.currentSessionId && state.currentSessionId !== sessionId) {
|
|
1016
|
-
callbacksRef.current.onSessionIdChange?.(state.currentSessionId);
|
|
1017
|
-
}
|
|
1018
|
-
const needsImageResolve = !state.hasError && !abortController.signal.aborted && hasRagImages(state.accumulatedContent);
|
|
1019
|
-
const finalMessage = createFinalMessage(streamingId, {
|
|
1020
|
-
...state,
|
|
1021
|
-
sessionId: state.currentSessionId || sessionId,
|
|
1022
|
-
isResolvingImages: needsImageResolve
|
|
1023
|
-
});
|
|
1024
|
-
setMessages(
|
|
1025
|
-
(prev) => prev.map(
|
|
1026
|
-
(msg) => msg.id === streamingId ? finalMessage : msg
|
|
1027
|
-
)
|
|
1028
|
-
);
|
|
1029
|
-
callbacksRef.current.onStreamComplete?.(finalMessage);
|
|
1030
|
-
}
|
|
1031
|
-
});
|
|
1032
|
-
clearThrottle();
|
|
1033
|
-
const shouldResolveImages = !abortController.signal.aborted && !state.hasError && hasRagImages(state.accumulatedContent);
|
|
1034
|
-
if (shouldResolveImages) {
|
|
1035
|
-
await waitForNextPaint(abortController.signal);
|
|
1036
|
-
}
|
|
1037
|
-
if (shouldResolveImages && !abortController.signal.aborted) {
|
|
1038
|
-
try {
|
|
1039
|
-
const resolvedContent = await resolveRagImageUrls(
|
|
1040
|
-
currentConfig,
|
|
1041
|
-
state.accumulatedContent,
|
|
1042
|
-
abortController.signal
|
|
1043
|
-
);
|
|
1044
|
-
setMessages(
|
|
1045
|
-
(prev) => prev.map(
|
|
1046
|
-
(msg) => msg.id === streamingId ? { ...msg, content: resolvedContent, isResolvingImages: false } : msg
|
|
1047
|
-
)
|
|
1048
|
-
);
|
|
1049
|
-
} catch {
|
|
1050
|
-
setMessages(
|
|
1051
|
-
(prev) => prev.map(
|
|
1052
|
-
(msg) => msg.id === streamingId ? { ...msg, isResolvingImages: false } : msg
|
|
1053
|
-
)
|
|
1054
|
-
);
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
return state.currentSessionId;
|
|
1058
|
-
} catch (error) {
|
|
1059
|
-
clearThrottle();
|
|
1060
|
-
setIsWaitingForResponse(false);
|
|
1061
|
-
if (error.name !== "AbortError") {
|
|
1062
|
-
callbacksRef.current.onError?.(error);
|
|
1063
|
-
}
|
|
1064
|
-
if (state.userActionPending) {
|
|
1065
|
-
state.userActionPending = false;
|
|
1066
|
-
state.userActionRequest = void 0;
|
|
1067
|
-
callbacksRef.current.onUserActionEvent?.(
|
|
1068
|
-
"USER_ACTION_FAILED",
|
|
1069
|
-
"Connection lost. Please try again."
|
|
1070
|
-
);
|
|
1071
|
-
}
|
|
1072
|
-
setMessages(
|
|
1073
|
-
(prev) => prev.map(
|
|
1074
|
-
(msg) => msg.id === streamingId ? {
|
|
1075
|
-
...msg,
|
|
1076
|
-
...createErrorMessageUpdate(error, state)
|
|
1077
|
-
} : msg
|
|
1078
|
-
)
|
|
1079
|
-
);
|
|
1080
|
-
return state.currentSessionId;
|
|
1081
|
-
}
|
|
1082
|
-
},
|
|
1083
|
-
[setMessages, setIsWaitingForResponse]
|
|
1084
|
-
);
|
|
1085
|
-
const cancelStream = react.useCallback(() => {
|
|
1086
|
-
abortControllerRef.current?.abort();
|
|
1087
|
-
}, []);
|
|
1088
|
-
return {
|
|
1089
|
-
startStream,
|
|
1090
|
-
cancelStream,
|
|
1091
|
-
abortControllerRef
|
|
1092
|
-
};
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
// src/hooks/useChat.ts
|
|
1096
|
-
function useChat(config, callbacks = {}) {
|
|
1097
|
-
const [messages, setMessages] = react.useState(() => {
|
|
1098
|
-
if (config.userId) return chatStore.get(config.userId);
|
|
1099
|
-
return config.initialMessages ?? [];
|
|
1100
|
-
});
|
|
1101
|
-
const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(false);
|
|
1102
|
-
const sessionIdRef = react.useRef(
|
|
1103
|
-
config.userId ? chatStore.get(config.userId).find((m) => m.sessionId)?.sessionId ?? config.initialSessionId ?? void 0 : config.initialSessionId ?? void 0
|
|
1104
|
-
);
|
|
1105
|
-
const prevUserIdRef = react.useRef(config.userId);
|
|
1106
|
-
const callbacksRef = react.useRef(callbacks);
|
|
1107
|
-
callbacksRef.current = callbacks;
|
|
1108
|
-
const configRef = react.useRef(config);
|
|
1109
|
-
configRef.current = config;
|
|
1110
|
-
const messagesRef = react.useRef(messages);
|
|
1111
|
-
messagesRef.current = messages;
|
|
1112
|
-
const storeAwareSetMessages = react.useCallback(
|
|
1113
|
-
(updater) => {
|
|
1114
|
-
const { userId } = configRef.current;
|
|
1115
|
-
if (userId && activeStreamStore.has(userId)) {
|
|
1116
|
-
activeStreamStore.applyMessages(userId, updater);
|
|
1117
|
-
}
|
|
1118
|
-
setMessages(updater);
|
|
1119
|
-
},
|
|
1120
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1121
|
-
[]
|
|
1122
|
-
);
|
|
1123
|
-
const storeAwareSetIsWaiting = react.useCallback(
|
|
1124
|
-
(waiting) => {
|
|
1125
|
-
const { userId } = configRef.current;
|
|
1126
|
-
if (userId && activeStreamStore.has(userId)) {
|
|
1127
|
-
activeStreamStore.setWaiting(userId, waiting);
|
|
1128
|
-
}
|
|
1129
|
-
setIsWaitingForResponse(waiting);
|
|
1130
|
-
},
|
|
1131
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1132
|
-
[]
|
|
1133
|
-
);
|
|
1134
|
-
const [userActionState, setUserActionState] = react.useState({
|
|
1135
|
-
request: null,
|
|
1136
|
-
result: null,
|
|
1137
|
-
clearOtpTrigger: 0
|
|
1138
|
-
});
|
|
1139
|
-
const userActionStateRef = react.useRef(userActionState);
|
|
1140
|
-
userActionStateRef.current = userActionState;
|
|
1141
|
-
const wrappedCallbacks = react.useMemo(() => ({
|
|
1142
|
-
...callbacksRef.current,
|
|
1143
|
-
onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
|
|
1144
|
-
onStreamStart: () => callbacksRef.current.onStreamStart?.(),
|
|
1145
|
-
onStreamComplete: (message) => callbacksRef.current.onStreamComplete?.(message),
|
|
1146
|
-
onError: (error) => callbacksRef.current.onError?.(error),
|
|
1147
|
-
onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
|
|
1148
|
-
onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
|
|
1149
|
-
onUserActionRequired: (request) => {
|
|
1150
|
-
setUserActionState((prev) => ({ ...prev, request, result: null }));
|
|
1151
|
-
callbacksRef.current.onUserActionRequired?.(request);
|
|
1152
|
-
},
|
|
1153
|
-
onUserActionEvent: (eventType, message) => {
|
|
1154
|
-
switch (eventType) {
|
|
1155
|
-
case "USER_ACTION_SUCCESS":
|
|
1156
|
-
setUserActionState((prev) => ({ ...prev, request: null, result: "approved" }));
|
|
1157
|
-
break;
|
|
1158
|
-
case "USER_ACTION_REJECTED":
|
|
1159
|
-
setUserActionState((prev) => ({ ...prev, request: null, result: "rejected" }));
|
|
1160
|
-
break;
|
|
1161
|
-
case "USER_ACTION_EXPIRED":
|
|
1162
|
-
case "USER_ACTION_FAILED":
|
|
1163
|
-
setUserActionState((prev) => ({ ...prev, request: null }));
|
|
1164
|
-
break;
|
|
1165
|
-
case "USER_ACTION_INVALID":
|
|
1166
|
-
setUserActionState((prev) => ({ ...prev, clearOtpTrigger: prev.clearOtpTrigger + 1 }));
|
|
1167
|
-
break;
|
|
1168
|
-
}
|
|
1169
|
-
callbacksRef.current.onUserActionEvent?.(eventType, message);
|
|
1170
|
-
}
|
|
1171
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1172
|
-
}), []);
|
|
1173
|
-
const { startStream, cancelStream: cancelStreamManager, abortControllerRef } = useStreamManager(
|
|
1174
|
-
config,
|
|
1175
|
-
wrappedCallbacks,
|
|
1176
|
-
storeAwareSetMessages,
|
|
1177
|
-
storeAwareSetIsWaiting
|
|
1178
|
-
);
|
|
1179
|
-
const sendMessage = react.useCallback(
|
|
1180
|
-
async (userMessage) => {
|
|
1181
|
-
if (!userMessage.trim()) return;
|
|
1182
|
-
if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
|
|
1183
|
-
sessionIdRef.current = generateId();
|
|
1184
|
-
callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
|
|
1185
|
-
}
|
|
1186
|
-
const userMessageId = `user-${Date.now()}`;
|
|
1187
|
-
const userMsg = {
|
|
1188
|
-
id: userMessageId,
|
|
1189
|
-
sessionId: sessionIdRef.current,
|
|
1190
|
-
role: "user",
|
|
1191
|
-
content: userMessage,
|
|
1192
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1193
|
-
};
|
|
1194
|
-
setMessages((prev) => [...prev, userMsg]);
|
|
1195
|
-
callbacksRef.current.onMessageSent?.(userMessage);
|
|
1196
|
-
setIsWaitingForResponse(true);
|
|
1197
|
-
callbacksRef.current.onStreamStart?.();
|
|
1198
|
-
const streamingId = `assistant-${Date.now()}`;
|
|
1199
|
-
const streamingMsg = {
|
|
1200
|
-
id: streamingId,
|
|
1201
|
-
sessionId: sessionIdRef.current,
|
|
1202
|
-
role: "assistant",
|
|
1203
|
-
content: "",
|
|
1204
|
-
streamingContent: "",
|
|
1205
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1206
|
-
isStreaming: true,
|
|
1207
|
-
streamProgress: "started",
|
|
1208
|
-
steps: [],
|
|
1209
|
-
currentExecutingStepId: void 0,
|
|
1210
|
-
isCancelled: false,
|
|
1211
|
-
currentMessage: void 0
|
|
1212
|
-
};
|
|
1213
|
-
setMessages((prev) => [...prev, streamingMsg]);
|
|
1214
|
-
const abortController = new AbortController();
|
|
1215
|
-
const { userId } = configRef.current;
|
|
1216
|
-
if (userId) {
|
|
1217
|
-
const initialMessages = [...messagesRef.current, userMsg, streamingMsg];
|
|
1218
|
-
activeStreamStore.start(userId, abortController, initialMessages);
|
|
1219
|
-
}
|
|
1220
|
-
const newSessionId = await startStream(
|
|
1221
|
-
userMessage,
|
|
1222
|
-
streamingId,
|
|
1223
|
-
sessionIdRef.current,
|
|
1224
|
-
abortController
|
|
1225
|
-
);
|
|
1226
|
-
if (userId) {
|
|
1227
|
-
activeStreamStore.complete(userId);
|
|
1228
|
-
}
|
|
1229
|
-
if (!abortController.signal.aborted && newSessionId && newSessionId !== sessionIdRef.current) {
|
|
1230
|
-
sessionIdRef.current = newSessionId;
|
|
1231
|
-
}
|
|
1232
|
-
},
|
|
1233
|
-
[startStream]
|
|
1234
|
-
);
|
|
1235
|
-
const clearMessages = react.useCallback(() => {
|
|
1236
|
-
if (configRef.current.userId) {
|
|
1237
|
-
chatStore.delete(configRef.current.userId);
|
|
1238
|
-
}
|
|
1239
|
-
setMessages([]);
|
|
1240
|
-
}, []);
|
|
1241
|
-
const prependMessages = react.useCallback((msgs) => {
|
|
1242
|
-
setMessages((prev) => [...msgs, ...prev]);
|
|
1243
|
-
}, []);
|
|
1244
|
-
const cancelStream = react.useCallback(() => {
|
|
1245
|
-
if (configRef.current.userId) {
|
|
1246
|
-
activeStreamStore.abort(configRef.current.userId);
|
|
1247
|
-
}
|
|
1248
|
-
cancelStreamManager();
|
|
1249
|
-
setIsWaitingForResponse(false);
|
|
1250
|
-
setUserActionState((prev) => ({ ...prev, request: null, result: null }));
|
|
1251
|
-
setMessages(
|
|
1252
|
-
(prev) => prev.map((msg) => {
|
|
1253
|
-
if (msg.isStreaming) {
|
|
1254
|
-
return {
|
|
1255
|
-
...msg,
|
|
1256
|
-
...createCancelledMessageUpdate(
|
|
1257
|
-
msg.steps || [],
|
|
1258
|
-
msg.currentMessage
|
|
1259
|
-
)
|
|
1260
|
-
};
|
|
1261
|
-
}
|
|
1262
|
-
return msg;
|
|
1263
|
-
})
|
|
1264
|
-
);
|
|
1265
|
-
}, [cancelStreamManager]);
|
|
1266
|
-
const resetSession = react.useCallback(() => {
|
|
1267
|
-
if (configRef.current.userId) {
|
|
1268
|
-
activeStreamStore.abort(configRef.current.userId);
|
|
1269
|
-
chatStore.delete(configRef.current.userId);
|
|
1270
|
-
}
|
|
1271
|
-
setMessages([]);
|
|
1272
|
-
sessionIdRef.current = void 0;
|
|
1273
|
-
abortControllerRef.current?.abort();
|
|
1274
|
-
setIsWaitingForResponse(false);
|
|
1275
|
-
setUserActionState({ request: null, result: null, clearOtpTrigger: 0 });
|
|
1276
|
-
}, []);
|
|
1277
|
-
const getSessionId = react.useCallback(() => {
|
|
1278
|
-
return sessionIdRef.current;
|
|
1279
|
-
}, []);
|
|
1280
|
-
const getMessages = react.useCallback(() => {
|
|
1281
|
-
return messages;
|
|
1282
|
-
}, [messages]);
|
|
1283
|
-
const approveUserAction = react.useCallback(
|
|
1284
|
-
async (otp) => {
|
|
1285
|
-
const request = userActionStateRef.current.request;
|
|
1286
|
-
if (!request) return;
|
|
1287
|
-
try {
|
|
1288
|
-
await submitUserAction(configRef.current, request.userActionId, { otp });
|
|
1289
|
-
} catch (error) {
|
|
1290
|
-
setUserActionState((prev) => ({
|
|
1291
|
-
...prev,
|
|
1292
|
-
clearOtpTrigger: prev.clearOtpTrigger + 1
|
|
1293
|
-
}));
|
|
1294
|
-
callbacksRef.current.onError?.(error);
|
|
1295
|
-
throw error;
|
|
1296
|
-
}
|
|
1297
|
-
},
|
|
1298
|
-
[]
|
|
1299
|
-
);
|
|
1300
|
-
const rejectUserAction = react.useCallback(async () => {
|
|
1301
|
-
const request = userActionStateRef.current.request;
|
|
1302
|
-
if (!request) return;
|
|
1303
|
-
try {
|
|
1304
|
-
setMessages((prev) => {
|
|
1305
|
-
let lastStreamingIdx = -1;
|
|
1306
|
-
for (let i = prev.length - 1; i >= 0; i--) {
|
|
1307
|
-
if (prev[i].role === "assistant" && prev[i].isStreaming) {
|
|
1308
|
-
lastStreamingIdx = i;
|
|
1309
|
-
break;
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
if (lastStreamingIdx === -1) return prev;
|
|
1313
|
-
return prev.map(
|
|
1314
|
-
(msg, i) => i === lastStreamingIdx ? { ...msg, currentMessage: "Rejecting..." } : msg
|
|
1315
|
-
);
|
|
1316
|
-
});
|
|
1317
|
-
await cancelUserAction(configRef.current, request.userActionId);
|
|
1318
|
-
} catch (error) {
|
|
1319
|
-
callbacksRef.current.onError?.(error);
|
|
1320
|
-
throw error;
|
|
1321
|
-
}
|
|
1322
|
-
}, []);
|
|
1323
|
-
const resendOtp = react.useCallback(async () => {
|
|
1324
|
-
const request = userActionStateRef.current.request;
|
|
1325
|
-
if (!request) return;
|
|
1326
|
-
try {
|
|
1327
|
-
await resendUserAction(configRef.current, request.userActionId);
|
|
1328
|
-
} catch (error) {
|
|
1329
|
-
callbacksRef.current.onError?.(error);
|
|
1330
|
-
throw error;
|
|
1331
|
-
}
|
|
1332
|
-
}, []);
|
|
1333
|
-
react.useEffect(() => {
|
|
1334
|
-
const { userId } = config;
|
|
1335
|
-
if (!userId) return;
|
|
1336
|
-
const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
|
|
1337
|
-
setMessages(msgs);
|
|
1338
|
-
setIsWaitingForResponse(isWaiting);
|
|
1339
|
-
});
|
|
1340
|
-
const active = activeStreamStore.get(userId);
|
|
1341
|
-
if (active) {
|
|
1342
|
-
setMessages(active.messages);
|
|
1343
|
-
setIsWaitingForResponse(active.isWaiting);
|
|
1344
|
-
}
|
|
1345
|
-
return unsubscribe;
|
|
1346
|
-
}, []);
|
|
1347
|
-
react.useEffect(() => {
|
|
1348
|
-
if (!config.userId) return;
|
|
1349
|
-
const toSave = messages.filter((m) => !m.isStreaming);
|
|
1350
|
-
if (toSave.length > 0) {
|
|
1351
|
-
chatStore.set(config.userId, toSave);
|
|
1352
|
-
}
|
|
1353
|
-
}, [messages, config.userId]);
|
|
1354
|
-
react.useEffect(() => {
|
|
1355
|
-
const prevUserId = prevUserIdRef.current;
|
|
1356
|
-
prevUserIdRef.current = config.userId;
|
|
1357
|
-
if (prevUserId === config.userId) return;
|
|
1358
|
-
if (prevUserId && !config.userId) {
|
|
1359
|
-
chatStore.delete(prevUserId);
|
|
1360
|
-
setMessages([]);
|
|
1361
|
-
sessionIdRef.current = void 0;
|
|
1362
|
-
setIsWaitingForResponse(false);
|
|
1363
|
-
setUserActionState({ request: null, result: null, clearOtpTrigger: 0 });
|
|
1364
|
-
} else if (config.userId) {
|
|
1365
|
-
const stored = chatStore.get(config.userId);
|
|
1366
|
-
setMessages(stored);
|
|
1367
|
-
sessionIdRef.current = stored.find((m) => m.sessionId)?.sessionId;
|
|
1368
|
-
}
|
|
1369
|
-
}, [config.userId]);
|
|
1370
|
-
return {
|
|
1371
|
-
messages,
|
|
1372
|
-
sendMessage,
|
|
1373
|
-
clearMessages,
|
|
1374
|
-
prependMessages,
|
|
1375
|
-
cancelStream,
|
|
1376
|
-
resetSession,
|
|
1377
|
-
getSessionId,
|
|
1378
|
-
getMessages,
|
|
1379
|
-
isWaitingForResponse,
|
|
1380
|
-
sessionId: sessionIdRef.current,
|
|
1381
|
-
// User action (OTP) state and methods
|
|
1382
|
-
userActionState,
|
|
1383
|
-
approveUserAction,
|
|
1384
|
-
rejectUserAction,
|
|
1385
|
-
resendOtp
|
|
1386
|
-
};
|
|
406
|
+
}
|
|
407
|
+
return await response.json();
|
|
408
|
+
}
|
|
409
|
+
async function submitUserAction(config, userActionId, data) {
|
|
410
|
+
return sendUserActionRequest(config, userActionId, "submit", data);
|
|
411
|
+
}
|
|
412
|
+
async function cancelUserAction(config, userActionId) {
|
|
413
|
+
return sendUserActionRequest(config, userActionId, "cancel");
|
|
1387
414
|
}
|
|
415
|
+
async function resendUserAction(config, userActionId) {
|
|
416
|
+
return sendUserActionRequest(config, userActionId, "resend");
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// src/utils/chatStore.ts
|
|
420
|
+
var memoryStore = /* @__PURE__ */ new Map();
|
|
421
|
+
var chatStore = {
|
|
422
|
+
get(key) {
|
|
423
|
+
return memoryStore.get(key) ?? [];
|
|
424
|
+
},
|
|
425
|
+
set(key, messages) {
|
|
426
|
+
memoryStore.set(key, messages);
|
|
427
|
+
},
|
|
428
|
+
delete(key) {
|
|
429
|
+
memoryStore.delete(key);
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
// src/utils/activeStreamStore.ts
|
|
434
|
+
var streams = /* @__PURE__ */ new Map();
|
|
435
|
+
var activeStreamStore = {
|
|
436
|
+
has(key) {
|
|
437
|
+
return streams.has(key);
|
|
438
|
+
},
|
|
439
|
+
get(key) {
|
|
440
|
+
const entry = streams.get(key);
|
|
441
|
+
if (!entry) return null;
|
|
442
|
+
return { messages: entry.messages, isWaiting: entry.isWaiting };
|
|
443
|
+
},
|
|
444
|
+
// Called before startStream — registers the controller and initial messages
|
|
445
|
+
start(key, abortController, initialMessages) {
|
|
446
|
+
const existing = streams.get(key);
|
|
447
|
+
streams.set(key, {
|
|
448
|
+
messages: initialMessages,
|
|
449
|
+
isWaiting: true,
|
|
450
|
+
abortController,
|
|
451
|
+
listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
|
|
452
|
+
});
|
|
453
|
+
},
|
|
454
|
+
// Called by the stream on every event — applies the same updater pattern React uses
|
|
455
|
+
applyMessages(key, updater) {
|
|
456
|
+
const entry = streams.get(key);
|
|
457
|
+
if (!entry) return;
|
|
458
|
+
const next = typeof updater === "function" ? updater(entry.messages) : updater;
|
|
459
|
+
entry.messages = next;
|
|
460
|
+
entry.listeners.forEach((l) => l(next, entry.isWaiting));
|
|
461
|
+
},
|
|
462
|
+
setWaiting(key, waiting) {
|
|
463
|
+
const entry = streams.get(key);
|
|
464
|
+
if (!entry) return;
|
|
465
|
+
entry.isWaiting = waiting;
|
|
466
|
+
entry.listeners.forEach((l) => l(entry.messages, waiting));
|
|
467
|
+
},
|
|
468
|
+
// Called when stream completes — persists to chatStore and cleans up
|
|
469
|
+
complete(key) {
|
|
470
|
+
const entry = streams.get(key);
|
|
471
|
+
if (!entry) return;
|
|
472
|
+
entry.isWaiting = false;
|
|
473
|
+
entry.listeners.forEach((l) => l(entry.messages, false));
|
|
474
|
+
const toSave = entry.messages.filter((m) => !m.isStreaming);
|
|
475
|
+
if (toSave.length > 0) chatStore.set(key, toSave);
|
|
476
|
+
streams.delete(key);
|
|
477
|
+
},
|
|
478
|
+
// Subscribe — returns unsubscribe fn. Component calls this on mount, cleanup on unmount.
|
|
479
|
+
subscribe(key, listener) {
|
|
480
|
+
const entry = streams.get(key);
|
|
481
|
+
if (!entry) return () => {
|
|
482
|
+
};
|
|
483
|
+
entry.listeners.add(listener);
|
|
484
|
+
return () => {
|
|
485
|
+
streams.get(key)?.listeners.delete(listener);
|
|
486
|
+
};
|
|
487
|
+
},
|
|
488
|
+
// Explicit user cancel — aborts the controller and removes the entry
|
|
489
|
+
abort(key) {
|
|
490
|
+
const entry = streams.get(key);
|
|
491
|
+
if (!entry) return;
|
|
492
|
+
entry.abortController.abort();
|
|
493
|
+
streams.delete(key);
|
|
494
|
+
}
|
|
495
|
+
};
|
|
1388
496
|
|
|
1389
497
|
// src/utils/v2EventProcessor.ts
|
|
1390
498
|
function getEventText(event, field) {
|
|
@@ -1410,7 +518,7 @@ function addThinkingLine(state, header, detail) {
|
|
|
1410
518
|
function appendThinkingText(state, text) {
|
|
1411
519
|
state.formattedThinkingText += text;
|
|
1412
520
|
}
|
|
1413
|
-
function
|
|
521
|
+
function completeLastInProgressStep(steps) {
|
|
1414
522
|
for (let i = steps.length - 1; i >= 0; i--) {
|
|
1415
523
|
if (steps[i].status === "in_progress") {
|
|
1416
524
|
steps[i].status = "completed";
|
|
@@ -1624,7 +732,7 @@ function processStreamEventV2(event, state) {
|
|
|
1624
732
|
break;
|
|
1625
733
|
}
|
|
1626
734
|
case "USER_ACTION_REQUIRED": {
|
|
1627
|
-
|
|
735
|
+
completeLastInProgressStep(state.steps);
|
|
1628
736
|
if (event.userActionRequest) {
|
|
1629
737
|
state.userActionRequest = {
|
|
1630
738
|
userActionId: event.userActionRequest.userActionId,
|
|
@@ -1636,14 +744,18 @@ function processStreamEventV2(event, state) {
|
|
|
1636
744
|
}
|
|
1637
745
|
state.userActionPending = true;
|
|
1638
746
|
const req = event.userActionRequest;
|
|
747
|
+
const displayMessage = getUserActionDisplayMessage(
|
|
748
|
+
eventType,
|
|
749
|
+
req?.message || message
|
|
750
|
+
);
|
|
1639
751
|
if (req) {
|
|
1640
|
-
addThinkingLine(state, "**Verification Required**",
|
|
752
|
+
addThinkingLine(state, "**Verification Required**", displayMessage);
|
|
1641
753
|
}
|
|
1642
754
|
const stepId = `step-${state.stepCounter++}`;
|
|
1643
755
|
state.steps.push({
|
|
1644
756
|
id: stepId,
|
|
1645
757
|
eventType,
|
|
1646
|
-
message,
|
|
758
|
+
message: displayMessage,
|
|
1647
759
|
status: "in_progress",
|
|
1648
760
|
timestamp: Date.now(),
|
|
1649
761
|
elapsedMs: event.elapsedMs
|
|
@@ -1653,8 +765,9 @@ function processStreamEventV2(event, state) {
|
|
|
1653
765
|
break;
|
|
1654
766
|
}
|
|
1655
767
|
case "USER_ACTION_SUCCESS": {
|
|
1656
|
-
|
|
1657
|
-
|
|
768
|
+
const displayMessage = getUserActionDisplayMessage(eventType, event.message);
|
|
769
|
+
appendThinkingText(state, "\n\u2713 " + displayMessage);
|
|
770
|
+
completeLastInProgressStep(state.steps);
|
|
1658
771
|
state.userActionRequest = void 0;
|
|
1659
772
|
state.userActionPending = false;
|
|
1660
773
|
state.userActionResult = "approved";
|
|
@@ -1662,7 +775,7 @@ function processStreamEventV2(event, state) {
|
|
|
1662
775
|
state.steps.push({
|
|
1663
776
|
id: stepId,
|
|
1664
777
|
eventType,
|
|
1665
|
-
message,
|
|
778
|
+
message: displayMessage,
|
|
1666
779
|
status: "completed",
|
|
1667
780
|
timestamp: Date.now(),
|
|
1668
781
|
elapsedMs: event.elapsedMs
|
|
@@ -1671,12 +784,14 @@ function processStreamEventV2(event, state) {
|
|
|
1671
784
|
break;
|
|
1672
785
|
}
|
|
1673
786
|
case "USER_ACTION_INVALID": {
|
|
1674
|
-
|
|
787
|
+
const displayMessage = getUserActionDisplayMessage(eventType, event.message);
|
|
788
|
+
appendThinkingText(state, "\n\u2717 " + displayMessage);
|
|
789
|
+
completeLastInProgressStep(state.steps);
|
|
1675
790
|
const errorStepId = `step-${state.stepCounter++}`;
|
|
1676
791
|
state.steps.push({
|
|
1677
792
|
id: errorStepId,
|
|
1678
793
|
eventType,
|
|
1679
|
-
message,
|
|
794
|
+
message: displayMessage,
|
|
1680
795
|
status: "error",
|
|
1681
796
|
timestamp: Date.now(),
|
|
1682
797
|
elapsedMs: event.elapsedMs
|
|
@@ -1694,8 +809,9 @@ function processStreamEventV2(event, state) {
|
|
|
1694
809
|
break;
|
|
1695
810
|
}
|
|
1696
811
|
case "USER_ACTION_REJECTED": {
|
|
1697
|
-
|
|
1698
|
-
|
|
812
|
+
const displayMessage = getUserActionDisplayMessage(eventType, event.message);
|
|
813
|
+
appendThinkingText(state, "\n" + displayMessage);
|
|
814
|
+
completeLastInProgressStep(state.steps);
|
|
1699
815
|
state.userActionRequest = void 0;
|
|
1700
816
|
state.userActionPending = false;
|
|
1701
817
|
state.userActionResult = "rejected";
|
|
@@ -1703,7 +819,7 @@ function processStreamEventV2(event, state) {
|
|
|
1703
819
|
state.steps.push({
|
|
1704
820
|
id: stepId,
|
|
1705
821
|
eventType,
|
|
1706
|
-
message,
|
|
822
|
+
message: displayMessage,
|
|
1707
823
|
status: "completed",
|
|
1708
824
|
timestamp: Date.now(),
|
|
1709
825
|
elapsedMs: event.elapsedMs
|
|
@@ -1712,15 +828,16 @@ function processStreamEventV2(event, state) {
|
|
|
1712
828
|
break;
|
|
1713
829
|
}
|
|
1714
830
|
case "USER_ACTION_EXPIRED": {
|
|
1715
|
-
|
|
1716
|
-
|
|
831
|
+
const displayMessage = getUserActionDisplayMessage(eventType, event.message);
|
|
832
|
+
appendThinkingText(state, "\n\u2717 " + displayMessage);
|
|
833
|
+
completeLastInProgressStep(state.steps);
|
|
1717
834
|
state.userActionRequest = void 0;
|
|
1718
835
|
state.userActionPending = false;
|
|
1719
836
|
const stepId = `step-${state.stepCounter++}`;
|
|
1720
837
|
state.steps.push({
|
|
1721
838
|
id: stepId,
|
|
1722
839
|
eventType,
|
|
1723
|
-
message,
|
|
840
|
+
message: displayMessage,
|
|
1724
841
|
status: "error",
|
|
1725
842
|
timestamp: Date.now(),
|
|
1726
843
|
elapsedMs: event.elapsedMs
|
|
@@ -1729,11 +846,13 @@ function processStreamEventV2(event, state) {
|
|
|
1729
846
|
break;
|
|
1730
847
|
}
|
|
1731
848
|
case "USER_ACTION_RESENT": {
|
|
849
|
+
const displayMessage = getUserActionDisplayMessage(eventType, event.message);
|
|
850
|
+
appendThinkingText(state, "\n" + displayMessage);
|
|
1732
851
|
const stepId = `step-${state.stepCounter++}`;
|
|
1733
852
|
state.steps.push({
|
|
1734
853
|
id: stepId,
|
|
1735
854
|
eventType,
|
|
1736
|
-
message,
|
|
855
|
+
message: displayMessage,
|
|
1737
856
|
status: "completed",
|
|
1738
857
|
timestamp: Date.now(),
|
|
1739
858
|
elapsedMs: event.elapsedMs
|
|
@@ -1742,15 +861,19 @@ function processStreamEventV2(event, state) {
|
|
|
1742
861
|
break;
|
|
1743
862
|
}
|
|
1744
863
|
case "USER_ACTION_FAILED": {
|
|
1745
|
-
|
|
1746
|
-
|
|
864
|
+
const displayMessage = getUserActionDisplayMessage(
|
|
865
|
+
eventType,
|
|
866
|
+
event.message || event.errorMessage
|
|
867
|
+
);
|
|
868
|
+
appendThinkingText(state, "\n\u2717 " + displayMessage);
|
|
869
|
+
completeLastInProgressStep(state.steps);
|
|
1747
870
|
state.userActionRequest = void 0;
|
|
1748
871
|
state.userActionPending = false;
|
|
1749
872
|
const stepId = `step-${state.stepCounter++}`;
|
|
1750
873
|
state.steps.push({
|
|
1751
874
|
id: stepId,
|
|
1752
875
|
eventType,
|
|
1753
|
-
message,
|
|
876
|
+
message: displayMessage,
|
|
1754
877
|
status: "error",
|
|
1755
878
|
timestamp: Date.now(),
|
|
1756
879
|
elapsedMs: event.elapsedMs
|
|
@@ -1781,8 +904,58 @@ function processStreamEventV2(event, state) {
|
|
|
1781
904
|
return state;
|
|
1782
905
|
}
|
|
1783
906
|
|
|
907
|
+
// src/utils/ragImageResolver.ts
|
|
908
|
+
var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
|
|
909
|
+
function hasRagImages(content) {
|
|
910
|
+
return RAG_IMAGE_REGEX.test(content);
|
|
911
|
+
}
|
|
912
|
+
async function waitForNextPaint(signal) {
|
|
913
|
+
if (signal?.aborted) return;
|
|
914
|
+
await new Promise((resolve) => {
|
|
915
|
+
let isSettled = false;
|
|
916
|
+
const finish = () => {
|
|
917
|
+
if (isSettled) return;
|
|
918
|
+
isSettled = true;
|
|
919
|
+
signal?.removeEventListener("abort", finish);
|
|
920
|
+
resolve();
|
|
921
|
+
};
|
|
922
|
+
signal?.addEventListener("abort", finish, { once: true });
|
|
923
|
+
if (typeof requestAnimationFrame === "function") {
|
|
924
|
+
requestAnimationFrame(() => {
|
|
925
|
+
setTimeout(finish, 0);
|
|
926
|
+
});
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
setTimeout(finish, 0);
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
async function resolveRagImageUrls(config, content, signal) {
|
|
933
|
+
const url = buildResolveImagesUrl(config);
|
|
934
|
+
const baseHeaders = buildRequestHeaders(config);
|
|
935
|
+
const headers = { "Content-Type": "application/json", ...baseHeaders };
|
|
936
|
+
const response = await fetch(url, {
|
|
937
|
+
method: "POST",
|
|
938
|
+
headers,
|
|
939
|
+
body: JSON.stringify({ input: content }),
|
|
940
|
+
signal
|
|
941
|
+
});
|
|
942
|
+
if (!response.ok) {
|
|
943
|
+
const errorText = await response.text();
|
|
944
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
945
|
+
}
|
|
946
|
+
const text = await response.text();
|
|
947
|
+
try {
|
|
948
|
+
const parsed = JSON.parse(text);
|
|
949
|
+
if (typeof parsed === "string") return parsed;
|
|
950
|
+
if (typeof parsed.output === "string") return parsed.output;
|
|
951
|
+
if (typeof parsed.result === "string") return parsed.result;
|
|
952
|
+
} catch {
|
|
953
|
+
}
|
|
954
|
+
return text;
|
|
955
|
+
}
|
|
956
|
+
|
|
1784
957
|
// src/hooks/useStreamManagerV2.ts
|
|
1785
|
-
var
|
|
958
|
+
var FRIENDLY_ERROR_MESSAGE = "Oops, something went wrong. Please try again.";
|
|
1786
959
|
function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForResponse) {
|
|
1787
960
|
const abortControllerRef = react.useRef(null);
|
|
1788
961
|
const configRef = react.useRef(config);
|
|
@@ -1822,7 +995,10 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1822
995
|
if (eventType === "USER_ACTION_REQUIRED" && state.userActionRequest) {
|
|
1823
996
|
callbacksRef.current.onUserActionRequired?.(state.userActionRequest);
|
|
1824
997
|
} else if (eventType.startsWith("USER_ACTION_") && eventType !== "USER_ACTION_REQUIRED") {
|
|
1825
|
-
const msg =
|
|
998
|
+
const msg = getUserActionDisplayMessage(
|
|
999
|
+
eventType,
|
|
1000
|
+
event.message?.trim() || event.errorMessage?.trim() || getEventMessage(event)
|
|
1001
|
+
);
|
|
1826
1002
|
callbacksRef.current.onUserActionEvent?.(eventType, msg);
|
|
1827
1003
|
}
|
|
1828
1004
|
const activeStep = state.steps.find((s) => s.id === state.currentExecutingStepId);
|
|
@@ -1830,8 +1006,8 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1830
1006
|
const currentMessage = activeStep?.message || lastInProgressStep?.message || getEventMessage(event);
|
|
1831
1007
|
if (state.hasError) {
|
|
1832
1008
|
updateMessage({
|
|
1833
|
-
streamingContent:
|
|
1834
|
-
content:
|
|
1009
|
+
streamingContent: FRIENDLY_ERROR_MESSAGE,
|
|
1010
|
+
content: FRIENDLY_ERROR_MESSAGE,
|
|
1835
1011
|
streamProgress: "error",
|
|
1836
1012
|
isError: true,
|
|
1837
1013
|
errorDetails: state.errorMessage,
|
|
@@ -1881,7 +1057,7 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1881
1057
|
isError: !isAborted,
|
|
1882
1058
|
isCancelled: isAborted,
|
|
1883
1059
|
errorDetails: isAborted ? void 0 : error.message,
|
|
1884
|
-
content: isAborted ? state.finalResponse || "" : state.finalResponse ||
|
|
1060
|
+
content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
|
|
1885
1061
|
currentMessage: isAborted ? "Thinking..." : void 0,
|
|
1886
1062
|
formattedThinkingText: state.formattedThinkingText || void 0,
|
|
1887
1063
|
steps: [...state.steps].map((step) => {
|
|
@@ -1913,7 +1089,7 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1913
1089
|
id: streamingId,
|
|
1914
1090
|
sessionId: state.sessionId || sessionId,
|
|
1915
1091
|
role: "assistant",
|
|
1916
|
-
content: state.hasError ?
|
|
1092
|
+
content: state.hasError ? FRIENDLY_ERROR_MESSAGE : state.finalResponse || "",
|
|
1917
1093
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1918
1094
|
isStreaming: false,
|
|
1919
1095
|
streamProgress: state.hasError ? "error" : "completed",
|
|
@@ -1984,7 +1160,7 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1984
1160
|
isError: !isAborted,
|
|
1985
1161
|
isCancelled: isAborted,
|
|
1986
1162
|
errorDetails: isAborted ? void 0 : error.message,
|
|
1987
|
-
content: isAborted ? state.finalResponse || "" : state.finalResponse ||
|
|
1163
|
+
content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
|
|
1988
1164
|
formattedThinkingText: state.formattedThinkingText || void 0,
|
|
1989
1165
|
steps: [...state.steps].map((step) => {
|
|
1990
1166
|
if (step.status === "in_progress" && isAborted) {
|
|
@@ -2011,45 +1187,199 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
2011
1187
|
};
|
|
2012
1188
|
}
|
|
2013
1189
|
|
|
1190
|
+
// src/utils/workflowUsersClient.ts
|
|
1191
|
+
var DEFAULT_SESSION_PAGE_SIZE = 20;
|
|
1192
|
+
var DEFAULT_CONVERSATION_PAGE_SIZE = 50;
|
|
1193
|
+
function getStageParamName2(config) {
|
|
1194
|
+
return config.api.stageQueryParam ?? "stage";
|
|
1195
|
+
}
|
|
1196
|
+
function requireOwnerId(config) {
|
|
1197
|
+
const ownerId = config.session?.owner?.id;
|
|
1198
|
+
if (!ownerId) {
|
|
1199
|
+
throw new Error(
|
|
1200
|
+
"workflowUsersClient: session.owner.id is required to call this endpoint."
|
|
1201
|
+
);
|
|
1202
|
+
}
|
|
1203
|
+
return ownerId;
|
|
1204
|
+
}
|
|
1205
|
+
function requireWorkflowName(config) {
|
|
1206
|
+
const workflowName = config.workflow.name;
|
|
1207
|
+
if (!workflowName) {
|
|
1208
|
+
throw new Error(
|
|
1209
|
+
"workflowUsersClient: workflow.name is required to call this endpoint."
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
return workflowName;
|
|
1213
|
+
}
|
|
1214
|
+
function requireWorkflowId(config) {
|
|
1215
|
+
const workflowId = config.workflow.id;
|
|
1216
|
+
if (!workflowId) {
|
|
1217
|
+
throw new Error(
|
|
1218
|
+
"workflowUsersClient: workflow.id is required to call this endpoint."
|
|
1219
|
+
);
|
|
1220
|
+
}
|
|
1221
|
+
return workflowId;
|
|
1222
|
+
}
|
|
1223
|
+
function isPlaygroundYaakAuth(config) {
|
|
1224
|
+
if (config.api.authToken) return false;
|
|
1225
|
+
const headers = config.api.headers;
|
|
1226
|
+
if (!headers) return false;
|
|
1227
|
+
for (const key of Object.keys(headers)) {
|
|
1228
|
+
const lower = key.toLowerCase();
|
|
1229
|
+
if (lower === "yaak-api-key" || lower === "x-yaak-api-key") return true;
|
|
1230
|
+
}
|
|
1231
|
+
return false;
|
|
1232
|
+
}
|
|
1233
|
+
async function fetchJson(url, headers, signal) {
|
|
1234
|
+
const response = await fetch(url, {
|
|
1235
|
+
method: "GET",
|
|
1236
|
+
headers,
|
|
1237
|
+
signal
|
|
1238
|
+
});
|
|
1239
|
+
if (!response.ok) {
|
|
1240
|
+
const errorText = await response.text();
|
|
1241
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
1242
|
+
}
|
|
1243
|
+
return await response.json();
|
|
1244
|
+
}
|
|
1245
|
+
async function listSessions(config, opts = {}) {
|
|
1246
|
+
const ownerId = requireOwnerId(config);
|
|
1247
|
+
const useYaakEndpoint = isPlaygroundYaakAuth(config);
|
|
1248
|
+
const params = new URLSearchParams({
|
|
1249
|
+
page: String(opts.page ?? 0),
|
|
1250
|
+
size: String(opts.size ?? DEFAULT_SESSION_PAGE_SIZE)
|
|
1251
|
+
});
|
|
1252
|
+
if (useYaakEndpoint) {
|
|
1253
|
+
params.set("workflowUserId", ownerId);
|
|
1254
|
+
params.set("workflowName", requireWorkflowName(config));
|
|
1255
|
+
} else {
|
|
1256
|
+
params.set("workflowId", requireWorkflowId(config));
|
|
1257
|
+
}
|
|
1258
|
+
if (config.workflow.stage) {
|
|
1259
|
+
params.set(getStageParamName2(config), config.workflow.stage);
|
|
1260
|
+
}
|
|
1261
|
+
const url = useYaakEndpoint ? `${config.api.baseUrl}/api/workflows/ask/sessions?${params.toString()}` : `${config.api.baseUrl}/api/workflow-users/${encodeURIComponent(
|
|
1262
|
+
ownerId
|
|
1263
|
+
)}/sessions?${params.toString()}`;
|
|
1264
|
+
return fetchJson(
|
|
1265
|
+
url,
|
|
1266
|
+
buildRequestHeaders(config),
|
|
1267
|
+
opts.signal
|
|
1268
|
+
);
|
|
1269
|
+
}
|
|
1270
|
+
async function listConversations(config, opts) {
|
|
1271
|
+
const ownerId = requireOwnerId(config);
|
|
1272
|
+
const useYaakEndpoint = isPlaygroundYaakAuth(config);
|
|
1273
|
+
const params = new URLSearchParams({
|
|
1274
|
+
sessionId: opts.sessionId,
|
|
1275
|
+
page: String(opts.page ?? 0),
|
|
1276
|
+
size: String(opts.size ?? DEFAULT_CONVERSATION_PAGE_SIZE)
|
|
1277
|
+
});
|
|
1278
|
+
if (useYaakEndpoint) {
|
|
1279
|
+
params.set("workflowUserId", ownerId);
|
|
1280
|
+
params.set("workflowName", requireWorkflowName(config));
|
|
1281
|
+
}
|
|
1282
|
+
if (config.workflow.stage) {
|
|
1283
|
+
params.set(getStageParamName2(config), config.workflow.stage);
|
|
1284
|
+
}
|
|
1285
|
+
const url = useYaakEndpoint ? `${config.api.baseUrl}/api/workflows/ask/conversations?${params.toString()}` : `${config.api.baseUrl}/api/workflow-users/${encodeURIComponent(
|
|
1286
|
+
ownerId
|
|
1287
|
+
)}/conversations?${params.toString()}`;
|
|
1288
|
+
return fetchJson(
|
|
1289
|
+
url,
|
|
1290
|
+
buildRequestHeaders(config),
|
|
1291
|
+
opts.signal
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
|
|
2014
1295
|
// src/hooks/useChatV2.ts
|
|
1296
|
+
function conversationEntryToMessages(entry, sessionId) {
|
|
1297
|
+
return [
|
|
1298
|
+
{
|
|
1299
|
+
id: `history-user-${entry.executionId}`,
|
|
1300
|
+
sessionId,
|
|
1301
|
+
role: "user",
|
|
1302
|
+
content: entry.query,
|
|
1303
|
+
timestamp: entry.createdAt,
|
|
1304
|
+
isHistorical: true
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
id: `history-assistant-${entry.executionId}`,
|
|
1308
|
+
sessionId,
|
|
1309
|
+
role: "assistant",
|
|
1310
|
+
content: entry.response,
|
|
1311
|
+
timestamp: entry.createdAt,
|
|
1312
|
+
executionId: entry.executionId,
|
|
1313
|
+
isHistorical: true
|
|
1314
|
+
}
|
|
1315
|
+
];
|
|
1316
|
+
}
|
|
1317
|
+
function streamKeyFor(scopeKey, sessionId) {
|
|
1318
|
+
return `${scopeKey}|sid:${sessionId ?? ""}`;
|
|
1319
|
+
}
|
|
2015
1320
|
function useChatV2(config, callbacks = {}) {
|
|
1321
|
+
const scopeKey = react.useMemo(() => buildScopeKey(config), [
|
|
1322
|
+
config.session?.userId,
|
|
1323
|
+
config.workflow.id,
|
|
1324
|
+
config.workflow.version,
|
|
1325
|
+
config.workflow.stage
|
|
1326
|
+
]);
|
|
1327
|
+
const initialSessionId = chatStore.get(scopeKey).find((m) => m.sessionId)?.sessionId ?? config.session?.initialId ?? void 0;
|
|
2016
1328
|
const [messages, setMessages] = react.useState(() => {
|
|
2017
|
-
|
|
2018
|
-
|
|
1329
|
+
const stored = chatStore.get(scopeKey);
|
|
1330
|
+
if (stored.length > 0) return stored;
|
|
1331
|
+
return config.session?.initialMessages ?? [];
|
|
2019
1332
|
});
|
|
2020
1333
|
const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(false);
|
|
2021
|
-
const
|
|
2022
|
-
|
|
2023
|
-
);
|
|
2024
|
-
const
|
|
1334
|
+
const [loadingSessionId, setLoadingSessionId] = react.useState(void 0);
|
|
1335
|
+
const [currentSessionId, setCurrentSessionId] = react.useState(initialSessionId);
|
|
1336
|
+
const sessionIdRef = react.useRef(initialSessionId);
|
|
1337
|
+
const prevScopeKeyRef = react.useRef(scopeKey);
|
|
1338
|
+
const activeStreamSessionRef = react.useRef(void 0);
|
|
2025
1339
|
const callbacksRef = react.useRef(callbacks);
|
|
2026
1340
|
callbacksRef.current = callbacks;
|
|
2027
1341
|
const configRef = react.useRef(config);
|
|
2028
1342
|
configRef.current = config;
|
|
1343
|
+
const scopeKeyRef = react.useRef(scopeKey);
|
|
1344
|
+
scopeKeyRef.current = scopeKey;
|
|
2029
1345
|
const messagesRef = react.useRef(messages);
|
|
2030
1346
|
messagesRef.current = messages;
|
|
2031
1347
|
const storeAwareSetMessages = react.useCallback(
|
|
2032
1348
|
(updater) => {
|
|
2033
|
-
const
|
|
2034
|
-
|
|
2035
|
-
|
|
1349
|
+
const scope = scopeKeyRef.current;
|
|
1350
|
+
const streamSid = activeStreamSessionRef.current;
|
|
1351
|
+
if (streamSid !== void 0) {
|
|
1352
|
+
const streamKey = streamKeyFor(scope, streamSid);
|
|
1353
|
+
if (activeStreamStore.has(streamKey)) {
|
|
1354
|
+
activeStreamStore.applyMessages(
|
|
1355
|
+
streamKey,
|
|
1356
|
+
updater
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
if (sessionIdRef.current === streamSid) {
|
|
1360
|
+
setMessages(updater);
|
|
1361
|
+
}
|
|
1362
|
+
return;
|
|
2036
1363
|
}
|
|
2037
1364
|
setMessages(updater);
|
|
2038
1365
|
},
|
|
2039
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2040
1366
|
[]
|
|
2041
1367
|
);
|
|
2042
|
-
const storeAwareSetIsWaiting = react.useCallback(
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
1368
|
+
const storeAwareSetIsWaiting = react.useCallback((waiting) => {
|
|
1369
|
+
const scope = scopeKeyRef.current;
|
|
1370
|
+
const streamSid = activeStreamSessionRef.current;
|
|
1371
|
+
if (streamSid !== void 0) {
|
|
1372
|
+
const streamKey = streamKeyFor(scope, streamSid);
|
|
1373
|
+
if (activeStreamStore.has(streamKey)) {
|
|
1374
|
+
activeStreamStore.setWaiting(streamKey, waiting);
|
|
2047
1375
|
}
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
1376
|
+
if (sessionIdRef.current === streamSid) {
|
|
1377
|
+
setIsWaitingForResponse(waiting);
|
|
1378
|
+
}
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
setIsWaitingForResponse(waiting);
|
|
1382
|
+
}, []);
|
|
2053
1383
|
const [userActionState, setUserActionState] = react.useState({
|
|
2054
1384
|
request: null,
|
|
2055
1385
|
result: null,
|
|
@@ -2057,38 +1387,43 @@ function useChatV2(config, callbacks = {}) {
|
|
|
2057
1387
|
});
|
|
2058
1388
|
const userActionStateRef = react.useRef(userActionState);
|
|
2059
1389
|
userActionStateRef.current = userActionState;
|
|
2060
|
-
const wrappedCallbacks = react.useMemo(
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
1390
|
+
const wrappedCallbacks = react.useMemo(
|
|
1391
|
+
() => ({
|
|
1392
|
+
...callbacksRef.current,
|
|
1393
|
+
onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
|
|
1394
|
+
onStreamStart: () => callbacksRef.current.onStreamStart?.(),
|
|
1395
|
+
onStreamComplete: (message) => callbacksRef.current.onStreamComplete?.(message),
|
|
1396
|
+
onError: (error) => callbacksRef.current.onError?.(error),
|
|
1397
|
+
onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
|
|
1398
|
+
onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
|
|
1399
|
+
onUserActionRequired: (request) => {
|
|
1400
|
+
setUserActionState((prev) => ({ ...prev, request, result: null }));
|
|
1401
|
+
callbacksRef.current.onUserActionRequired?.(request);
|
|
1402
|
+
},
|
|
1403
|
+
onUserActionEvent: (eventType, message) => {
|
|
1404
|
+
switch (eventType) {
|
|
1405
|
+
case "USER_ACTION_SUCCESS":
|
|
1406
|
+
setUserActionState((prev) => ({ ...prev, request: null, result: "approved" }));
|
|
1407
|
+
break;
|
|
1408
|
+
case "USER_ACTION_REJECTED":
|
|
1409
|
+
setUserActionState((prev) => ({ ...prev, request: null, result: "rejected" }));
|
|
1410
|
+
break;
|
|
1411
|
+
case "USER_ACTION_EXPIRED":
|
|
1412
|
+
case "USER_ACTION_FAILED":
|
|
1413
|
+
setUserActionState((prev) => ({ ...prev, request: null }));
|
|
1414
|
+
break;
|
|
1415
|
+
case "USER_ACTION_INVALID":
|
|
1416
|
+
setUserActionState((prev) => ({
|
|
1417
|
+
...prev,
|
|
1418
|
+
clearOtpTrigger: prev.clearOtpTrigger + 1
|
|
1419
|
+
}));
|
|
1420
|
+
break;
|
|
1421
|
+
}
|
|
1422
|
+
callbacksRef.current.onUserActionEvent?.(eventType, message);
|
|
2087
1423
|
}
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
}), []);
|
|
1424
|
+
}),
|
|
1425
|
+
[]
|
|
1426
|
+
);
|
|
2092
1427
|
const { startStream, cancelStream: cancelStreamManager, abortControllerRef } = useStreamManagerV2(
|
|
2093
1428
|
config,
|
|
2094
1429
|
wrappedCallbacks,
|
|
@@ -2098,8 +1433,10 @@ function useChatV2(config, callbacks = {}) {
|
|
|
2098
1433
|
const sendMessage = react.useCallback(
|
|
2099
1434
|
async (userMessage) => {
|
|
2100
1435
|
if (!userMessage.trim()) return;
|
|
2101
|
-
|
|
1436
|
+
const autoGen = configRef.current.session?.autoGenerateId;
|
|
1437
|
+
if (!sessionIdRef.current && autoGen !== false) {
|
|
2102
1438
|
sessionIdRef.current = generateId();
|
|
1439
|
+
setCurrentSessionId(sessionIdRef.current);
|
|
2103
1440
|
callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
|
|
2104
1441
|
}
|
|
2105
1442
|
const userMessageId = `user-${Date.now()}`;
|
|
@@ -2131,38 +1468,40 @@ function useChatV2(config, callbacks = {}) {
|
|
|
2131
1468
|
};
|
|
2132
1469
|
setMessages((prev) => [...prev, streamingMsg]);
|
|
2133
1470
|
const abortController = new AbortController();
|
|
2134
|
-
const
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
1471
|
+
const scope = scopeKeyRef.current;
|
|
1472
|
+
const streamSessionId = sessionIdRef.current;
|
|
1473
|
+
const streamKey = streamKeyFor(scope, streamSessionId);
|
|
1474
|
+
activeStreamSessionRef.current = streamSessionId;
|
|
1475
|
+
const initialMessages = [...messagesRef.current, userMsg, streamingMsg];
|
|
1476
|
+
activeStreamStore.start(streamKey, abortController, initialMessages);
|
|
2139
1477
|
const newSessionId = await startStream(
|
|
2140
1478
|
userMessage,
|
|
2141
1479
|
streamingId,
|
|
2142
|
-
|
|
1480
|
+
streamSessionId,
|
|
2143
1481
|
abortController
|
|
2144
1482
|
);
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
if (!abortController.signal.aborted && newSessionId && newSessionId !== sessionIdRef.current) {
|
|
1483
|
+
activeStreamStore.complete(streamKey);
|
|
1484
|
+
activeStreamSessionRef.current = void 0;
|
|
1485
|
+
if (!abortController.signal.aborted && newSessionId && newSessionId !== streamSessionId && sessionIdRef.current === streamSessionId) {
|
|
2149
1486
|
sessionIdRef.current = newSessionId;
|
|
1487
|
+
setCurrentSessionId(newSessionId);
|
|
2150
1488
|
}
|
|
2151
1489
|
},
|
|
2152
1490
|
[startStream]
|
|
2153
1491
|
);
|
|
2154
1492
|
const clearMessages = react.useCallback(() => {
|
|
2155
|
-
|
|
2156
|
-
chatStore.delete(configRef.current.userId);
|
|
2157
|
-
}
|
|
1493
|
+
chatStore.delete(scopeKeyRef.current);
|
|
2158
1494
|
setMessages([]);
|
|
2159
1495
|
}, []);
|
|
2160
1496
|
const prependMessages = react.useCallback((msgs) => {
|
|
2161
1497
|
setMessages((prev) => [...msgs, ...prev]);
|
|
2162
1498
|
}, []);
|
|
2163
1499
|
const cancelStream = react.useCallback(() => {
|
|
2164
|
-
|
|
2165
|
-
|
|
1500
|
+
const scope = scopeKeyRef.current;
|
|
1501
|
+
const viewSid = sessionIdRef.current;
|
|
1502
|
+
const viewStreamKey = streamKeyFor(scope, viewSid);
|
|
1503
|
+
if (activeStreamStore.has(viewStreamKey)) {
|
|
1504
|
+
activeStreamStore.abort(viewStreamKey);
|
|
2166
1505
|
}
|
|
2167
1506
|
cancelStreamManager();
|
|
2168
1507
|
setIsWaitingForResponse(false);
|
|
@@ -2172,10 +1511,7 @@ function useChatV2(config, callbacks = {}) {
|
|
|
2172
1511
|
if (msg.isStreaming) {
|
|
2173
1512
|
return {
|
|
2174
1513
|
...msg,
|
|
2175
|
-
...createCancelledMessageUpdate(
|
|
2176
|
-
msg.steps || [],
|
|
2177
|
-
msg.currentMessage
|
|
2178
|
-
)
|
|
1514
|
+
...createCancelledMessageUpdate(msg.steps || [], msg.currentMessage)
|
|
2179
1515
|
};
|
|
2180
1516
|
}
|
|
2181
1517
|
return msg;
|
|
@@ -2183,39 +1519,37 @@ function useChatV2(config, callbacks = {}) {
|
|
|
2183
1519
|
);
|
|
2184
1520
|
}, [cancelStreamManager]);
|
|
2185
1521
|
const resetSession = react.useCallback(() => {
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
1522
|
+
const scope = scopeKeyRef.current;
|
|
1523
|
+
const viewSid = sessionIdRef.current;
|
|
1524
|
+
const viewStreamKey = streamKeyFor(scope, viewSid);
|
|
1525
|
+
if (activeStreamStore.has(viewStreamKey)) {
|
|
1526
|
+
activeStreamStore.abort(viewStreamKey);
|
|
2189
1527
|
}
|
|
1528
|
+
chatStore.delete(scope);
|
|
2190
1529
|
setMessages([]);
|
|
2191
1530
|
sessionIdRef.current = void 0;
|
|
1531
|
+
setCurrentSessionId(void 0);
|
|
1532
|
+
activeStreamSessionRef.current = void 0;
|
|
2192
1533
|
abortControllerRef.current?.abort();
|
|
2193
1534
|
setIsWaitingForResponse(false);
|
|
2194
1535
|
setUserActionState({ request: null, result: null, clearOtpTrigger: 0 });
|
|
2195
1536
|
}, []);
|
|
2196
|
-
const getSessionId = react.useCallback(() =>
|
|
2197
|
-
|
|
1537
|
+
const getSessionId = react.useCallback(() => sessionIdRef.current, []);
|
|
1538
|
+
const getMessages = react.useCallback(() => messages, [messages]);
|
|
1539
|
+
const approveUserAction = react.useCallback(async (otp) => {
|
|
1540
|
+
const request = userActionStateRef.current.request;
|
|
1541
|
+
if (!request) return;
|
|
1542
|
+
try {
|
|
1543
|
+
await submitUserAction(configRef.current, request.userActionId, { otp });
|
|
1544
|
+
} catch (error) {
|
|
1545
|
+
setUserActionState((prev) => ({
|
|
1546
|
+
...prev,
|
|
1547
|
+
clearOtpTrigger: prev.clearOtpTrigger + 1
|
|
1548
|
+
}));
|
|
1549
|
+
callbacksRef.current.onError?.(error);
|
|
1550
|
+
throw error;
|
|
1551
|
+
}
|
|
2198
1552
|
}, []);
|
|
2199
|
-
const getMessages = react.useCallback(() => {
|
|
2200
|
-
return messages;
|
|
2201
|
-
}, [messages]);
|
|
2202
|
-
const approveUserAction = react.useCallback(
|
|
2203
|
-
async (otp) => {
|
|
2204
|
-
const request = userActionStateRef.current.request;
|
|
2205
|
-
if (!request) return;
|
|
2206
|
-
try {
|
|
2207
|
-
await submitUserAction(configRef.current, request.userActionId, { otp });
|
|
2208
|
-
} catch (error) {
|
|
2209
|
-
setUserActionState((prev) => ({
|
|
2210
|
-
...prev,
|
|
2211
|
-
clearOtpTrigger: prev.clearOtpTrigger + 1
|
|
2212
|
-
}));
|
|
2213
|
-
callbacksRef.current.onError?.(error);
|
|
2214
|
-
throw error;
|
|
2215
|
-
}
|
|
2216
|
-
},
|
|
2217
|
-
[]
|
|
2218
|
-
);
|
|
2219
1553
|
const rejectUserAction = react.useCallback(async () => {
|
|
2220
1554
|
const request = userActionStateRef.current.request;
|
|
2221
1555
|
if (!request) return;
|
|
@@ -2249,43 +1583,91 @@ function useChatV2(config, callbacks = {}) {
|
|
|
2249
1583
|
throw error;
|
|
2250
1584
|
}
|
|
2251
1585
|
}, []);
|
|
1586
|
+
const inFlightLoadRef = react.useRef(null);
|
|
1587
|
+
const loadingSessionIdRef = react.useRef(void 0);
|
|
1588
|
+
loadingSessionIdRef.current = loadingSessionId;
|
|
1589
|
+
const loadSession = react.useCallback(async (sessionId) => {
|
|
1590
|
+
const inFlight = inFlightLoadRef.current;
|
|
1591
|
+
if (inFlight && inFlight.sessionId === sessionId) {
|
|
1592
|
+
return inFlight.promise;
|
|
1593
|
+
}
|
|
1594
|
+
if (sessionIdRef.current === sessionId && loadingSessionIdRef.current !== sessionId) {
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
const run = async () => {
|
|
1598
|
+
const scope = scopeKeyRef.current;
|
|
1599
|
+
setUserActionState({ request: null, result: null, clearOtpTrigger: 0 });
|
|
1600
|
+
sessionIdRef.current = sessionId;
|
|
1601
|
+
setCurrentSessionId(sessionId);
|
|
1602
|
+
callbacksRef.current.onSessionIdChange?.(sessionId);
|
|
1603
|
+
const streamKey = streamKeyFor(scope, sessionId);
|
|
1604
|
+
const active = activeStreamStore.get(streamKey);
|
|
1605
|
+
if (active) {
|
|
1606
|
+
setMessages(active.messages);
|
|
1607
|
+
setIsWaitingForResponse(active.isWaiting);
|
|
1608
|
+
setLoadingSessionId(void 0);
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
setIsWaitingForResponse(false);
|
|
1612
|
+
setMessages([]);
|
|
1613
|
+
chatStore.delete(scope);
|
|
1614
|
+
setLoadingSessionId(sessionId);
|
|
1615
|
+
try {
|
|
1616
|
+
const response = await listConversations(configRef.current, { sessionId });
|
|
1617
|
+
const entries = response.data ?? [];
|
|
1618
|
+
const historical = entries.flatMap((entry) => conversationEntryToMessages(entry, sessionId));
|
|
1619
|
+
if (sessionIdRef.current === sessionId) {
|
|
1620
|
+
setMessages(historical);
|
|
1621
|
+
}
|
|
1622
|
+
if (historical.length > 0) {
|
|
1623
|
+
chatStore.set(scope, historical);
|
|
1624
|
+
}
|
|
1625
|
+
} catch (error) {
|
|
1626
|
+
callbacksRef.current.onError?.(error);
|
|
1627
|
+
throw error;
|
|
1628
|
+
} finally {
|
|
1629
|
+
setLoadingSessionId((current) => current === sessionId ? void 0 : current);
|
|
1630
|
+
}
|
|
1631
|
+
};
|
|
1632
|
+
const promise = run().finally(() => {
|
|
1633
|
+
if (inFlightLoadRef.current?.sessionId === sessionId) {
|
|
1634
|
+
inFlightLoadRef.current = null;
|
|
1635
|
+
}
|
|
1636
|
+
});
|
|
1637
|
+
inFlightLoadRef.current = { sessionId, promise };
|
|
1638
|
+
return promise;
|
|
1639
|
+
}, []);
|
|
2252
1640
|
react.useEffect(() => {
|
|
2253
|
-
const
|
|
2254
|
-
|
|
2255
|
-
const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
|
|
1641
|
+
const key = streamKeyFor(scopeKey, currentSessionId);
|
|
1642
|
+
const unsubscribe = activeStreamStore.subscribe(key, (msgs, isWaiting) => {
|
|
2256
1643
|
setMessages(msgs);
|
|
2257
1644
|
setIsWaitingForResponse(isWaiting);
|
|
2258
1645
|
});
|
|
2259
|
-
const active = activeStreamStore.get(
|
|
1646
|
+
const active = activeStreamStore.get(key);
|
|
2260
1647
|
if (active) {
|
|
2261
1648
|
setMessages(active.messages);
|
|
2262
1649
|
setIsWaitingForResponse(active.isWaiting);
|
|
2263
1650
|
}
|
|
2264
1651
|
return unsubscribe;
|
|
2265
|
-
}, []);
|
|
1652
|
+
}, [scopeKey, currentSessionId]);
|
|
2266
1653
|
react.useEffect(() => {
|
|
2267
|
-
if (!config.userId) return;
|
|
2268
1654
|
const toSave = messages.filter((m) => !m.isStreaming);
|
|
2269
1655
|
if (toSave.length > 0) {
|
|
2270
|
-
chatStore.set(
|
|
1656
|
+
chatStore.set(scopeKey, toSave);
|
|
2271
1657
|
}
|
|
2272
|
-
}, [messages,
|
|
1658
|
+
}, [messages, scopeKey]);
|
|
2273
1659
|
react.useEffect(() => {
|
|
2274
|
-
const
|
|
2275
|
-
|
|
2276
|
-
if (
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
setMessages(stored);
|
|
2286
|
-
sessionIdRef.current = stored.find((m) => m.sessionId)?.sessionId;
|
|
2287
|
-
}
|
|
2288
|
-
}, [config.userId]);
|
|
1660
|
+
const prevKey = prevScopeKeyRef.current;
|
|
1661
|
+
prevScopeKeyRef.current = scopeKey;
|
|
1662
|
+
if (prevKey === scopeKey) return;
|
|
1663
|
+
const stored = chatStore.get(scopeKey);
|
|
1664
|
+
setMessages(stored);
|
|
1665
|
+
const restoredSessionId = stored.find((m) => m.sessionId)?.sessionId ?? configRef.current.session?.initialId ?? void 0;
|
|
1666
|
+
sessionIdRef.current = restoredSessionId;
|
|
1667
|
+
setCurrentSessionId(restoredSessionId);
|
|
1668
|
+
setIsWaitingForResponse(false);
|
|
1669
|
+
setUserActionState({ request: null, result: null, clearOtpTrigger: 0 });
|
|
1670
|
+
}, [scopeKey]);
|
|
2289
1671
|
return {
|
|
2290
1672
|
messages,
|
|
2291
1673
|
sendMessage,
|
|
@@ -2296,11 +1678,13 @@ function useChatV2(config, callbacks = {}) {
|
|
|
2296
1678
|
getSessionId,
|
|
2297
1679
|
getMessages,
|
|
2298
1680
|
isWaitingForResponse,
|
|
2299
|
-
sessionId:
|
|
1681
|
+
sessionId: currentSessionId,
|
|
2300
1682
|
userActionState,
|
|
2301
1683
|
approveUserAction,
|
|
2302
1684
|
rejectUserAction,
|
|
2303
|
-
resendOtp
|
|
1685
|
+
resendOtp,
|
|
1686
|
+
loadSession,
|
|
1687
|
+
loadingSessionId
|
|
2304
1688
|
};
|
|
2305
1689
|
}
|
|
2306
1690
|
function getSpeechRecognition() {
|
|
@@ -2520,14 +1904,16 @@ function useVoice(config = {}, callbacks = {}) {
|
|
|
2520
1904
|
}
|
|
2521
1905
|
|
|
2522
1906
|
exports.buildFormattedThinking = buildFormattedThinking;
|
|
1907
|
+
exports.buildScopeKey = buildScopeKey;
|
|
2523
1908
|
exports.cancelUserAction = cancelUserAction;
|
|
2524
1909
|
exports.createInitialV2State = createInitialV2State;
|
|
2525
1910
|
exports.generateId = generateId;
|
|
1911
|
+
exports.listConversations = listConversations;
|
|
1912
|
+
exports.listSessions = listSessions;
|
|
2526
1913
|
exports.processStreamEventV2 = processStreamEventV2;
|
|
2527
1914
|
exports.resendUserAction = resendUserAction;
|
|
2528
1915
|
exports.streamWorkflowEvents = streamWorkflowEvents;
|
|
2529
1916
|
exports.submitUserAction = submitUserAction;
|
|
2530
|
-
exports.useChat = useChat;
|
|
2531
1917
|
exports.useChatV2 = useChatV2;
|
|
2532
1918
|
exports.useVoice = useVoice;
|
|
2533
1919
|
exports.workingPhaseDetailForDisplay = workingPhaseDetailForDisplay;
|