@personaai/react 0.7.3 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -25
- package/dist/index.cjs +77 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +77 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -258,6 +258,16 @@ interface UseChatOptions {
|
|
|
258
258
|
onError?: (error: Error) => void;
|
|
259
259
|
/** Hook for receiving every low-level AG-UI streaming event (tool calls, steps, subagents) */
|
|
260
260
|
onEvent?: (event: PersonaStreamingEvent) => void;
|
|
261
|
+
/**
|
|
262
|
+
* Pass the object returned by `useVoice()` (sharing the same `threadId`) to have `useChat`
|
|
263
|
+
* merge live voice turns into `messages` automatically — one bubble per utterance, deduped
|
|
264
|
+
* against thread history and against a voice turn that already persisted back into this
|
|
265
|
+
* thread, with the in-progress agent line updated in place while it's still being spoken.
|
|
266
|
+
* No manual sync effects needed in the host app; injected messages get a `voice-` prefixed id.
|
|
267
|
+
* Only applied while the passed hook's `state` is active (not `idle`/`ended`/`error`); the
|
|
268
|
+
* host app still owns starting/stopping the call itself (e.g. `voice.start()`/`voice.stop()`).
|
|
269
|
+
*/
|
|
270
|
+
voice?: UseVoiceResult;
|
|
261
271
|
}
|
|
262
272
|
interface SendMessageOverride {
|
|
263
273
|
agentId?: string;
|
|
@@ -274,6 +284,13 @@ interface SendMessageOverride {
|
|
|
274
284
|
threadId?: string | Promise<string | undefined>;
|
|
275
285
|
/** Answers/approves a paused interrupt from a previous turn instead of starting a fresh one. */
|
|
276
286
|
resume?: PersonaResumeValue;
|
|
287
|
+
/**
|
|
288
|
+
* Caller-supplied context (e.g. a live end-user profile snapshot) appended to this turn's
|
|
289
|
+
* system prompt only — never persisted, never visible to later turns. Capped at 4000
|
|
290
|
+
* characters server-side (rejected with a 400, not truncated). Mirrors
|
|
291
|
+
* `@personaai/sdk`'s `SendMessageOptions.contextOverride`.
|
|
292
|
+
*/
|
|
293
|
+
contextOverride?: string;
|
|
277
294
|
}
|
|
278
295
|
/**
|
|
279
296
|
* `useVoice`'s call state, driven by `voice_activity` events from the
|
package/dist/index.d.ts
CHANGED
|
@@ -258,6 +258,16 @@ interface UseChatOptions {
|
|
|
258
258
|
onError?: (error: Error) => void;
|
|
259
259
|
/** Hook for receiving every low-level AG-UI streaming event (tool calls, steps, subagents) */
|
|
260
260
|
onEvent?: (event: PersonaStreamingEvent) => void;
|
|
261
|
+
/**
|
|
262
|
+
* Pass the object returned by `useVoice()` (sharing the same `threadId`) to have `useChat`
|
|
263
|
+
* merge live voice turns into `messages` automatically — one bubble per utterance, deduped
|
|
264
|
+
* against thread history and against a voice turn that already persisted back into this
|
|
265
|
+
* thread, with the in-progress agent line updated in place while it's still being spoken.
|
|
266
|
+
* No manual sync effects needed in the host app; injected messages get a `voice-` prefixed id.
|
|
267
|
+
* Only applied while the passed hook's `state` is active (not `idle`/`ended`/`error`); the
|
|
268
|
+
* host app still owns starting/stopping the call itself (e.g. `voice.start()`/`voice.stop()`).
|
|
269
|
+
*/
|
|
270
|
+
voice?: UseVoiceResult;
|
|
261
271
|
}
|
|
262
272
|
interface SendMessageOverride {
|
|
263
273
|
agentId?: string;
|
|
@@ -274,6 +284,13 @@ interface SendMessageOverride {
|
|
|
274
284
|
threadId?: string | Promise<string | undefined>;
|
|
275
285
|
/** Answers/approves a paused interrupt from a previous turn instead of starting a fresh one. */
|
|
276
286
|
resume?: PersonaResumeValue;
|
|
287
|
+
/**
|
|
288
|
+
* Caller-supplied context (e.g. a live end-user profile snapshot) appended to this turn's
|
|
289
|
+
* system prompt only — never persisted, never visible to later turns. Capped at 4000
|
|
290
|
+
* characters server-side (rejected with a 400, not truncated). Mirrors
|
|
291
|
+
* `@personaai/sdk`'s `SendMessageOptions.contextOverride`.
|
|
292
|
+
*/
|
|
293
|
+
contextOverride?: string;
|
|
277
294
|
}
|
|
278
295
|
/**
|
|
279
296
|
* `useVoice`'s call state, driven by `voice_activity` events from the
|
package/dist/index.js
CHANGED
|
@@ -392,6 +392,9 @@ function useChat(options = {}) {
|
|
|
392
392
|
const [presentedFile, setPresentedFile] = useState(null);
|
|
393
393
|
const abortControllerRef = useRef(null);
|
|
394
394
|
const loadedThreadIdRef = useRef(void 0);
|
|
395
|
+
const voiceThreadRef = useRef(threadId);
|
|
396
|
+
const voicePrevLenRef = useRef(0);
|
|
397
|
+
const voiceStreamingIdRef = useRef(null);
|
|
395
398
|
const stop = useCallback(() => {
|
|
396
399
|
if (abortControllerRef.current) {
|
|
397
400
|
chatLogger.info("stop streaming", {});
|
|
@@ -497,6 +500,75 @@ function useChat(options = {}) {
|
|
|
497
500
|
chatLogger.debug("loadThreadMessages trigger", { threadId });
|
|
498
501
|
void loadThreadMessages(threadId);
|
|
499
502
|
}, [threadId, isStreaming, messages.length, loadThreadMessages, chatLogger]);
|
|
503
|
+
const voice = options.voice;
|
|
504
|
+
useEffect(() => {
|
|
505
|
+
if (!voice) return;
|
|
506
|
+
if (voiceThreadRef.current !== threadId) {
|
|
507
|
+
voiceThreadRef.current = threadId;
|
|
508
|
+
voicePrevLenRef.current = voice.transcript.length;
|
|
509
|
+
voiceStreamingIdRef.current = null;
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
const isVoiceActive = voice.state !== "idle" && voice.state !== "ended" && voice.state !== "error";
|
|
513
|
+
const curLen = voice.transcript.length;
|
|
514
|
+
if (curLen < voicePrevLenRef.current) {
|
|
515
|
+
voicePrevLenRef.current = 0;
|
|
516
|
+
voiceStreamingIdRef.current = null;
|
|
517
|
+
}
|
|
518
|
+
if (!isVoiceActive) {
|
|
519
|
+
voicePrevLenRef.current = voice.transcript.length;
|
|
520
|
+
if (voiceStreamingIdRef.current) {
|
|
521
|
+
const doneId = voiceStreamingIdRef.current;
|
|
522
|
+
voiceStreamingIdRef.current = null;
|
|
523
|
+
setMessages(
|
|
524
|
+
(prev) => prev.map((m) => m.id === doneId ? { ...m, isStreaming: false } : m)
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (curLen > voicePrevLenRef.current) {
|
|
530
|
+
const newLines = voice.transcript.slice(voicePrevLenRef.current);
|
|
531
|
+
voicePrevLenRef.current = curLen;
|
|
532
|
+
for (const line of newLines) {
|
|
533
|
+
const text = (line.text || "").trim();
|
|
534
|
+
if (!text) continue;
|
|
535
|
+
const role = line.speaker === "user" ? "user" : "assistant";
|
|
536
|
+
const voiceId = `voice-${line.id}`;
|
|
537
|
+
setMessages((prev) => {
|
|
538
|
+
if (prev.some((m) => m.id === voiceId)) return prev;
|
|
539
|
+
if (prev.some(
|
|
540
|
+
(m) => !m.id.startsWith("voice-") && m.role === role && m.content.trim() === text
|
|
541
|
+
))
|
|
542
|
+
return prev;
|
|
543
|
+
const last = prev[prev.length - 1];
|
|
544
|
+
if (last?.id.startsWith("voice-") && last.role === "assistant" && role === "assistant") {
|
|
545
|
+
const merged = text.startsWith(last.content) ? text : `${last.content} ${text}`.trim();
|
|
546
|
+
return [...prev.slice(0, -1), { ...last, content: merged, isStreaming: false }];
|
|
547
|
+
}
|
|
548
|
+
return [...prev, { id: voiceId, role, content: text, createdAt: /* @__PURE__ */ new Date() }];
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
const partialText = voice.partial?.speaker === "agent" ? voice.partial.text.trim() : "";
|
|
553
|
+
if (partialText) {
|
|
554
|
+
setMessages((prev) => {
|
|
555
|
+
const last = prev[prev.length - 1];
|
|
556
|
+
if (last?.id.startsWith("voice-") && last.role === "assistant") {
|
|
557
|
+
voiceStreamingIdRef.current = last.id;
|
|
558
|
+
return [...prev.slice(0, -1), { ...last, content: partialText, isStreaming: true }];
|
|
559
|
+
}
|
|
560
|
+
const id = voiceStreamingIdRef.current || `voice-partial-${Date.now()}`;
|
|
561
|
+
voiceStreamingIdRef.current = id;
|
|
562
|
+
return [...prev, { id, role: "assistant", content: partialText, isStreaming: true, createdAt: /* @__PURE__ */ new Date() }];
|
|
563
|
+
});
|
|
564
|
+
} else if (voice.state !== "speaking" && voiceStreamingIdRef.current) {
|
|
565
|
+
const doneId = voiceStreamingIdRef.current;
|
|
566
|
+
voiceStreamingIdRef.current = null;
|
|
567
|
+
setMessages(
|
|
568
|
+
(prev) => prev.map((m) => m.id === doneId ? { ...m, isStreaming: false } : m)
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
}, [voice?.transcript, voice?.partial, voice?.state, threadId]);
|
|
500
572
|
const sendMessage = useCallback(
|
|
501
573
|
async (contentToSend, overrideOptions) => {
|
|
502
574
|
const prompt = (contentToSend ?? input).trim();
|
|
@@ -523,12 +595,14 @@ function useChat(options = {}) {
|
|
|
523
595
|
agentId: targetAgentId,
|
|
524
596
|
threadId: threadId ?? overrideOptions?.threadId,
|
|
525
597
|
hasResume: !!overrideOptions?.resume,
|
|
598
|
+
hasContextOverride: !!overrideOptions?.contextOverride,
|
|
526
599
|
messageCount: messages.length
|
|
527
600
|
});
|
|
528
601
|
chatLogger.trace("sendMessage details", {
|
|
529
602
|
agentId: targetAgentId,
|
|
530
603
|
promptPreview: prompt.slice(0, 200),
|
|
531
|
-
hasResume: !!overrideOptions?.resume
|
|
604
|
+
hasResume: !!overrideOptions?.resume,
|
|
605
|
+
hasContextOverride: !!overrideOptions?.contextOverride
|
|
532
606
|
});
|
|
533
607
|
const userMessage = {
|
|
534
608
|
id: `user-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
|
@@ -583,7 +657,8 @@ function useChat(options = {}) {
|
|
|
583
657
|
agentId: targetAgentId,
|
|
584
658
|
messages: payloadMessages,
|
|
585
659
|
threadId: resolvedThreadId,
|
|
586
|
-
resume: overrideOptions?.resume
|
|
660
|
+
resume: overrideOptions?.resume,
|
|
661
|
+
contextOverride: overrideOptions?.contextOverride
|
|
587
662
|
}),
|
|
588
663
|
signal: controller.signal
|
|
589
664
|
});
|