Skip to content

Commit f3d5544

Browse files
authoredFeb 10, 2025··
Add support for intent-progress assist events (#24143)
1 parent 1b3d4b7 commit f3d5544

File tree

2 files changed

+119
-8
lines changed

2 files changed

+119
-8
lines changed
 

‎src/components/ha-assist-chat.ts

+90-8
Original file line numberDiff line numberDiff line change
@@ -295,10 +295,12 @@ export class HaAssistChat extends LitElement {
295295
this._addMessage(userMessage);
296296
this.requestUpdate("_audioRecorder");
297297

298-
const hassMessage: AssistMessage = {
298+
let hassMessage = {
299299
who: "hass",
300300
text: "…",
301+
error: false,
301302
};
303+
let currentDeltaRole = "";
302304
// To make sure the answer is placed at the right user text, we add it before we process it
303305
try {
304306
const unsub = await runAssistPipeline(
@@ -328,6 +330,43 @@ export class HaAssistChat extends LitElement {
328330
this._addMessage(hassMessage);
329331
}
330332

333+
if (event.type === "intent-progress") {
334+
const delta = event.data.chat_log_delta;
335+
336+
// new message
337+
if (delta.role) {
338+
// If currentDeltaRole exists, it means we're receiving our
339+
// second or later message. Let's add it to the chat.
340+
if (currentDeltaRole && delta.role && hassMessage.text !== "…") {
341+
// Remove progress indicator of previous message
342+
hassMessage.text = hassMessage.text.substring(
343+
0,
344+
hassMessage.text.length - 1
345+
);
346+
347+
hassMessage = {
348+
who: "hass",
349+
text: "…",
350+
error: false,
351+
};
352+
this._addMessage(hassMessage);
353+
}
354+
currentDeltaRole = delta.role;
355+
}
356+
357+
if (
358+
currentDeltaRole === "assistant" &&
359+
"content" in delta &&
360+
delta.content
361+
) {
362+
hassMessage.text =
363+
hassMessage.text.substring(0, hassMessage.text.length - 1) +
364+
delta.content +
365+
"…";
366+
this.requestUpdate("_conversation");
367+
}
368+
}
369+
331370
if (event.type === "intent-end") {
332371
this._conversationId = event.data.intent_output.conversation_id;
333372
const plain = event.data.intent_output.response.speech?.plain;
@@ -435,28 +474,71 @@ export class HaAssistChat extends LitElement {
435474
this._processing = true;
436475
this._audio?.pause();
437476
this._addMessage({ who: "user", text });
438-
const message: AssistMessage = {
477+
let hassMessage = {
439478
who: "hass",
440479
text: "…",
480+
error: false,
441481
};
482+
let currentDeltaRole = "";
442483
// To make sure the answer is placed at the right user text, we add it before we process it
443-
this._addMessage(message);
484+
this._addMessage(hassMessage);
444485
try {
445486
const unsub = await runAssistPipeline(
446487
this.hass,
447488
(event) => {
489+
if (event.type === "intent-progress") {
490+
const delta = event.data.chat_log_delta;
491+
492+
// new message and previous message has content
493+
if (delta.role) {
494+
// If currentDeltaRole exists, it means we're receiving our
495+
// second or later message. Let's add it to the chat.
496+
if (
497+
currentDeltaRole &&
498+
delta.role === "assistant" &&
499+
hassMessage.text !== "…"
500+
) {
501+
// Remove progress indicator of previous message
502+
hassMessage.text = hassMessage.text.substring(
503+
0,
504+
hassMessage.text.length - 1
505+
);
506+
507+
hassMessage = {
508+
who: "hass",
509+
text: "…",
510+
error: false,
511+
};
512+
this._addMessage(hassMessage);
513+
}
514+
currentDeltaRole = delta.role;
515+
}
516+
517+
if (
518+
currentDeltaRole === "assistant" &&
519+
"content" in delta &&
520+
delta.content
521+
) {
522+
hassMessage.text =
523+
hassMessage.text.substring(0, hassMessage.text.length - 1) +
524+
delta.content +
525+
"…";
526+
this.requestUpdate("_conversation");
527+
}
528+
}
529+
448530
if (event.type === "intent-end") {
449531
this._conversationId = event.data.intent_output.conversation_id;
450532
const plain = event.data.intent_output.response.speech?.plain;
451533
if (plain) {
452-
message.text = plain.speech;
534+
hassMessage.text = plain.speech;
453535
}
454536
this.requestUpdate("_conversation");
455537
unsub();
456538
}
457539
if (event.type === "error") {
458-
message.text = event.data.message;
459-
message.error = true;
540+
hassMessage.text = event.data.message;
541+
hassMessage.error = true;
460542
this.requestUpdate("_conversation");
461543
unsub();
462544
}
@@ -470,8 +552,8 @@ export class HaAssistChat extends LitElement {
470552
}
471553
);
472554
} catch {
473-
message.text = this.hass.localize("ui.dialogs.voice_command.error");
474-
message.error = true;
555+
hassMessage.text = this.hass.localize("ui.dialogs.voice_command.error");
556+
hassMessage.error = true;
475557
this.requestUpdate("_conversation");
476558
} finally {
477559
this._processing = false;

‎src/data/assist_pipeline.ts

+29
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,34 @@ interface PipelineIntentStartEvent extends PipelineEventBase {
108108
intent_input: string;
109109
};
110110
}
111+
112+
interface ConversationChatLogAssistantDelta {
113+
role: "assistant";
114+
content: string;
115+
tool_calls: {
116+
id: string;
117+
tool_name: string;
118+
tool_args: Record<string, unknown>;
119+
}[];
120+
}
121+
122+
interface ConversationChatLogToolResultDelta {
123+
role: "tool_result";
124+
agent_id: string;
125+
tool_call_id: string;
126+
tool_name: string;
127+
tool_result: unknown;
128+
}
129+
interface PipelineIntentProgressEvent extends PipelineEventBase {
130+
type: "intent-progress";
131+
data: {
132+
chat_log_delta:
133+
| Partial<ConversationChatLogAssistantDelta>
134+
// These always come in 1 chunk
135+
| ConversationChatLogToolResultDelta;
136+
};
137+
}
138+
111139
interface PipelineIntentEndEvent extends PipelineEventBase {
112140
type: "intent-end";
113141
data: {
@@ -141,6 +169,7 @@ export type PipelineRunEvent =
141169
| PipelineSTTStartEvent
142170
| PipelineSTTEndEvent
143171
| PipelineIntentStartEvent
172+
| PipelineIntentProgressEvent
144173
| PipelineIntentEndEvent
145174
| PipelineTTSStartEvent
146175
| PipelineTTSEndEvent;

0 commit comments

Comments
 (0)
Please sign in to comment.