Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions client-js/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
TranscriptData,
TransportState,
UICancelJobGroupData,
UserLLMTextData,
UICommandData,
UIEventData,
UIJobGroupData,
Expand Down Expand Up @@ -149,6 +150,7 @@ export type RTVIEventCallbacks = Partial<{
onUserMuteStarted: () => void;
onUserMuteStopped: () => void;
onUserTranscript: (data: TranscriptData) => void;
onUserLlmText: (data: UserLLMTextData) => void;
onBotOutput: (data: BotOutputData) => void;
/** @deprecated Use onBotOutput instead */
onBotTranscript: (data: BotLLMTextData) => void;
Expand Down Expand Up @@ -1144,6 +1146,12 @@ export class PipecatClient extends RTVIEventEmitter {
this._options.callbacks?.onUserTranscript?.(TranscriptData);
break;
}
case RTVIMessageType.USER_LLM_TEXT: {
const llmTextData = ev.data as UserLLMTextData;
this._options.callbacks?.onUserLlmText?.(llmTextData);
this.emit(RTVIEvent.UserLlmText, llmTextData);
break;
}
case RTVIMessageType.BOT_OUTPUT: {
this._options.callbacks?.onBotOutput?.(ev.data as BotOutputData);
break;
Expand Down
3 changes: 3 additions & 0 deletions client-js/rtvi/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
TranscriptData,
UICommandData,
UIJobGroupData,
UserLLMTextData,
} from "./messages";

export enum RTVIEvent {
Expand Down Expand Up @@ -65,6 +66,7 @@ export enum RTVIEvent {
BotTranscript = "botTranscript",

// llm events
UserLlmText = "userLlmText",
BotLlmText = "botLlmText",
BotLlmStarted = "botLlmStarted",
BotLlmStopped = "botLlmStopped",
Expand Down Expand Up @@ -150,6 +152,7 @@ export type RTVIEvents = Partial<{
botTranscript: (data: BotLLMTextData) => void;

// llm events
userLlmText: (data: UserLLMTextData) => void;
botLlmText: (data: BotLLMTextData) => void;
botLlmStarted: () => void;
botLlmStopped: () => void;
Expand Down
4 changes: 4 additions & 0 deletions client-js/rtvi/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ export type BotLLMTextData = {
text: string;
};

export type UserLLMTextData = {
text: string;
};

export type BotTTSTextData = {
text: string;
};
Expand Down
1 change: 1 addition & 0 deletions client-react/src/conversation/conversationActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export const mergeMessages = (
lastMerged.role === currentMessage.role &&
currentMessage.role !== "system" &&
currentMessage.role !== "function_call" &&
!lastMerged.final &&
timeDiff < MERGE_WINDOW_MS;

if (shouldMerge) {
Expand Down
23 changes: 23 additions & 0 deletions client-react/src/conversation/useConversationEventWiring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,29 @@ export function useConversationEventWiring() {
)
);

// user-llm-text fires when the user's input reaches the LLM — both in voice
// mode (after STT) and in chat mode (typed text, no STT). In voice mode
// user-transcription has already created an in-progress user message, so we
// only need to finalize it. In chat mode no user message exists yet, so we
// create one from the LLM text first, then finalize.
useRTVIClientEvent(
RTVIEvent.UserLlmText,
useAtomCallback(
useCallback((get, set, data) => {
const messages = get(messagesAtom);
const lastUserIdx = findLastIndex(
messages,
(m: ConversationMessage) => m.role === "user"
);

if (lastUserIdx === -1 || messages[lastUserIdx].final) {
upsertUserTranscript(get, set, data.text ?? "", true);
}
finalizeLastMessage(get, set, "user");
}, [])
)
);

useRTVIClientEvent(
RTVIEvent.UserStoppedSpeaking,
useAtomCallback(
Expand Down