mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 12:41:37 +00:00
fix(telegram): split streaming preview per assistant block (#22613)
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: 26f35f4411
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
This commit is contained in:
@@ -28,7 +28,7 @@ import {
|
||||
import { stripHeartbeatToken } from "../heartbeat.js";
|
||||
import type { TemplateContext } from "../templating.js";
|
||||
import type { VerboseLevel } from "../thinking.js";
|
||||
import { isSilentReplyText, SILENT_REPLY_TOKEN } from "../tokens.js";
|
||||
import { isSilentReplyPrefixText, isSilentReplyText, SILENT_REPLY_TOKEN } from "../tokens.js";
|
||||
import type { GetReplyOptions, ReplyPayload } from "../types.js";
|
||||
import {
|
||||
buildEmbeddedRunBaseParams,
|
||||
@@ -157,6 +157,9 @@ export async function runAgentTurnWithFallback(params: {
|
||||
return { text: sanitized, skip: false };
|
||||
};
|
||||
const handlePartialForTyping = async (payload: ReplyPayload): Promise<string | undefined> => {
|
||||
if (isSilentReplyPrefixText(payload.text, SILENT_REPLY_TOKEN)) {
|
||||
return undefined;
|
||||
}
|
||||
const { text, skip } = normalizeStreamingText(payload);
|
||||
if (skip || !text) {
|
||||
return undefined;
|
||||
|
||||
@@ -383,6 +383,50 @@ describe("runReplyAgent typing (heartbeat)", () => {
|
||||
expect(typing.startTypingLoop).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("suppresses partial streaming for NO_REPLY prefixes", async () => {
|
||||
const onPartialReply = vi.fn();
|
||||
state.runEmbeddedPiAgentMock.mockImplementationOnce(async (params: AgentRunParams) => {
|
||||
await params.onPartialReply?.({ text: "NO_" });
|
||||
await params.onPartialReply?.({ text: "NO_RE" });
|
||||
await params.onPartialReply?.({ text: "NO_REPLY" });
|
||||
return { payloads: [{ text: "NO_REPLY" }], meta: {} };
|
||||
});
|
||||
|
||||
const { run, typing } = createMinimalRun({
|
||||
opts: { isHeartbeat: false, onPartialReply },
|
||||
typingMode: "message",
|
||||
});
|
||||
await run();
|
||||
|
||||
expect(onPartialReply).not.toHaveBeenCalled();
|
||||
expect(typing.startTypingOnText).not.toHaveBeenCalled();
|
||||
expect(typing.startTypingLoop).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not suppress partial streaming for normal 'No' prefixes", async () => {
|
||||
const onPartialReply = vi.fn();
|
||||
state.runEmbeddedPiAgentMock.mockImplementationOnce(async (params: AgentRunParams) => {
|
||||
await params.onPartialReply?.({ text: "No" });
|
||||
await params.onPartialReply?.({ text: "No, that is valid" });
|
||||
return { payloads: [{ text: "No, that is valid" }], meta: {} };
|
||||
});
|
||||
|
||||
const { run, typing } = createMinimalRun({
|
||||
opts: { isHeartbeat: false, onPartialReply },
|
||||
typingMode: "message",
|
||||
});
|
||||
await run();
|
||||
|
||||
expect(onPartialReply).toHaveBeenCalledTimes(2);
|
||||
expect(onPartialReply).toHaveBeenNthCalledWith(1, { text: "No", mediaUrls: undefined });
|
||||
expect(onPartialReply).toHaveBeenNthCalledWith(2, {
|
||||
text: "No, that is valid",
|
||||
mediaUrls: undefined,
|
||||
});
|
||||
expect(typing.startTypingOnText).toHaveBeenCalled();
|
||||
expect(typing.startTypingLoop).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not start typing on assistant message start without prior text in message mode", async () => {
|
||||
state.runEmbeddedPiAgentMock.mockImplementationOnce(async (params: AgentRunParams) => {
|
||||
await params.onAssistantMessageStart?.();
|
||||
|
||||
Reference in New Issue
Block a user