Agents: emit final assistant event for reply tags
This commit is contained in:
parent
3a85cb1833
commit
dded462469
@ -72,6 +72,7 @@ Status: stable.
|
||||
- **BREAKING:** Gateway auth mode "none" is removed; gateway now requires token/password (Tailscale Serve identity still allowed).
|
||||
|
||||
### Fixes
|
||||
- TUI: emit final assistant event when reply tags hide stream. (#4495) Thanks @ukeate.
|
||||
- Telegram: use undici fetch for per-account proxy dispatcher. (#4456) Thanks @spiceoogway.
|
||||
- Telegram: avoid silent empty replies by tracking normalization skips before fallback. (#3796)
|
||||
- Telegram: scope native skill commands to bound agent per bot. (#4360) Thanks @robhparker.
|
||||
|
||||
@ -176,6 +176,30 @@ export function handleMessageEnd(
|
||||
});
|
||||
|
||||
const text = ctx.stripBlockTags(rawText, { thinking: false, final: false });
|
||||
const { text: cleanedText, mediaUrls } = parseReplyDirectives(text);
|
||||
const { text: previousCleanedText } = parseReplyDirectives(ctx.state.lastStreamedAssistant ?? "");
|
||||
if (cleanedText && cleanedText !== previousCleanedText) {
|
||||
const deltaText = cleanedText.startsWith(previousCleanedText)
|
||||
? cleanedText.slice(previousCleanedText.length)
|
||||
: cleanedText;
|
||||
emitAgentEvent({
|
||||
runId: ctx.params.runId,
|
||||
stream: "assistant",
|
||||
data: {
|
||||
text: cleanedText,
|
||||
delta: deltaText,
|
||||
mediaUrls: mediaUrls?.length ? mediaUrls : undefined,
|
||||
},
|
||||
});
|
||||
void ctx.params.onAgentEvent?.({
|
||||
stream: "assistant",
|
||||
data: {
|
||||
text: cleanedText,
|
||||
delta: deltaText,
|
||||
mediaUrls: mediaUrls?.length ? mediaUrls : undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
const rawThinking =
|
||||
ctx.state.includeReasoning || ctx.state.streamReasoning
|
||||
? extractAssistantThinking(assistantMessage) || extractThinkingFromTaggedText(rawText)
|
||||
|
||||
@ -221,6 +221,50 @@ describe("subscribeEmbeddedPiSession", () => {
|
||||
expect(payloads[0]?.text).toBe("MEDIA:");
|
||||
});
|
||||
|
||||
it("emits agent events on message_end when reply tags hide final text", () => {
|
||||
let handler: ((evt: unknown) => void) | undefined;
|
||||
const session: StubSession = {
|
||||
subscribe: (fn) => {
|
||||
handler = fn;
|
||||
return () => {};
|
||||
},
|
||||
};
|
||||
|
||||
const onAgentEvent = vi.fn();
|
||||
|
||||
subscribeEmbeddedPiSession({
|
||||
session: session as unknown as Parameters<typeof subscribeEmbeddedPiSession>[0]["session"],
|
||||
runId: "run",
|
||||
onAgentEvent,
|
||||
});
|
||||
|
||||
handler?.({ type: "message_start", message: { role: "assistant" } });
|
||||
handler?.({
|
||||
type: "message_update",
|
||||
message: { role: "assistant" },
|
||||
assistantMessageEvent: { type: "text_delta", delta: "[[reply_to: 123" },
|
||||
});
|
||||
handler?.({
|
||||
type: "message_update",
|
||||
message: { role: "assistant" },
|
||||
assistantMessageEvent: { type: "text_delta", delta: "]] Hello" },
|
||||
});
|
||||
|
||||
const assistantMessage = {
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "[[reply_to: 123]] Hello" }],
|
||||
} as AssistantMessage;
|
||||
handler?.({ type: "message_end", message: assistantMessage });
|
||||
|
||||
const payloads = onAgentEvent.mock.calls
|
||||
.map((call) => call[0]?.data as Record<string, unknown> | undefined)
|
||||
.filter((value): value is Record<string, unknown> => Boolean(value));
|
||||
expect(payloads.length).toBeGreaterThan(0);
|
||||
const last = payloads[payloads.length - 1];
|
||||
expect(last?.text).toBe("Hello");
|
||||
expect(last?.delta).toBe("Hello");
|
||||
});
|
||||
|
||||
it("emits agent events when media arrives without text", () => {
|
||||
let handler: ((evt: unknown) => void) | undefined;
|
||||
const session: StubSession = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user