fix: suppress thinking stream + typing

This commit is contained in:
Peter Steinberger
2025-12-23 14:17:18 +00:00
parent 6d551b0d6e
commit df5284beaf
6 changed files with 40 additions and 3 deletions

View File

@@ -18,6 +18,8 @@
- Group `/new` resets now work with @mentions so activation guidance appears on fresh sessions. - Group `/new` resets now work with @mentions so activation guidance appears on fresh sessions.
- Group chat activation context is now injected into the system prompt at session start (and after activation changes), including /new greetings. - Group chat activation context is now injected into the system prompt at session start (and after activation changes), including /new greetings.
- Typing indicators now start only once a reply payload is produced (no "thinking" typing for silent runs). - Typing indicators now start only once a reply payload is produced (no "thinking" typing for silent runs).
- WhatsApp group typing now starts immediately only when the bot is mentioned; otherwise it waits until real output exists.
- Streamed `<think>` segments are stripped before partial replies are emitted.
- Canvas defaults/A2UI auto-nav aligned; debug status overlay centered; redundant await removed in `CanvasManager`. - Canvas defaults/A2UI auto-nav aligned; debug status overlay centered; redundant await removed in `CanvasManager`.
- Gateway launchd loop fixed by removing redundant `kickstart -k`. - Gateway launchd loop fixed by removing redundant `kickstart -k`.
- CLI now hints when Peekaboo is unauthorized. - CLI now hints when Peekaboo is unauthorized.

View File

@@ -13,6 +13,29 @@ import {
inferToolMetaFromArgs, inferToolMetaFromArgs,
} from "./pi-embedded-utils.js"; } from "./pi-embedded-utils.js";
const THINKING_TAG_RE = /<\s*\/?\s*think(?:ing)?\s*>/gi;
function stripThinkingSegments(text: string): string {
if (!text || !THINKING_TAG_RE.test(text)) return text;
THINKING_TAG_RE.lastIndex = 0;
let result = "";
let lastIndex = 0;
let inThinking = false;
for (const match of text.matchAll(THINKING_TAG_RE)) {
const idx = match.index ?? 0;
if (!inThinking) {
result += text.slice(lastIndex, idx);
}
const tag = match[0].toLowerCase();
inThinking = !tag.includes("/");
lastIndex = idx + match[0].length;
}
if (!inThinking) {
result += text.slice(lastIndex);
}
return result;
}
export function subscribeEmbeddedPiSession(params: { export function subscribeEmbeddedPiSession(params: {
session: AgentSession; session: AgentSession;
runId: string; runId: string;
@@ -159,7 +182,7 @@ export function subscribeEmbeddedPiSession(params: {
: ""; : "";
if (chunk) { if (chunk) {
deltaBuffer += chunk; deltaBuffer += chunk;
const next = deltaBuffer.trim(); const next = stripThinkingSegments(deltaBuffer).trim();
if (next && next !== lastStreamedAssistant) { if (next && next !== lastStreamedAssistant) {
lastStreamedAssistant = next; lastStreamedAssistant = next;
const { text: cleanedText, mediaUrls } = const { text: cleanedText, mediaUrls } =
@@ -194,7 +217,9 @@ export function subscribeEmbeddedPiSession(params: {
if (evt.type === "message_end") { if (evt.type === "message_end") {
const msg = (evt as AgentEvent & { message: AppMessage }).message; const msg = (evt as AgentEvent & { message: AppMessage }).message;
if (msg?.role === "assistant") { if (msg?.role === "assistant") {
const text = extractAssistantText(msg as AssistantMessage); const text = stripThinkingSegments(
extractAssistantText(msg as AssistantMessage),
);
if (text) assistantTexts.push(text); if (text) assistantTexts.push(text);
deltaBuffer = ""; deltaBuffer = "";
} }

View File

@@ -653,8 +653,14 @@ export async function getReplyFromConfig(
} }
const isFirstTurnInSession = isNewSession || !systemSent; const isFirstTurnInSession = isNewSession || !systemSent;
const isGroupChat = sessionCtx.ChatType === "group";
const wasMentioned = ctx.WasMentioned === true;
const shouldEagerType = !isGroupChat || wasMentioned;
if (shouldEagerType) {
await startTypingLoop();
}
const shouldInjectGroupIntro = const shouldInjectGroupIntro =
sessionCtx.ChatType === "group" && isGroupChat &&
(isFirstTurnInSession || sessionEntry?.groupActivationNeedsSystemIntro); (isFirstTurnInSession || sessionEntry?.groupActivationNeedsSystemIntro);
const groupIntro = const groupIntro =
shouldInjectGroupIntro shouldInjectGroupIntro

View File

@@ -16,6 +16,7 @@ export type MsgContext = {
SenderName?: string; SenderName?: string;
SenderE164?: string; SenderE164?: string;
Surface?: string; Surface?: string;
WasMentioned?: boolean;
}; };
export type TemplateContext = MsgContext & { export type TemplateContext = MsgContext & {

View File

@@ -1143,6 +1143,7 @@ export async function monitorWebProvider(
), ),
SenderName: msg.senderName, SenderName: msg.senderName,
SenderE164: msg.senderE164, SenderE164: msg.senderE164,
WasMentioned: msg.wasMentioned,
Surface: "whatsapp", Surface: "whatsapp",
}, },
{ {
@@ -1306,6 +1307,7 @@ export async function monitorWebProvider(
"group mention debug", "group mention debug",
); );
const wasMentioned = mentionDebug.wasMentioned; const wasMentioned = mentionDebug.wasMentioned;
msg.wasMentioned = wasMentioned;
const activation = resolveGroupActivationFor(conversationId); const activation = resolveGroupActivationFor(conversationId);
const requireMention = activation !== "always"; const requireMention = activation !== "always";
if (!shouldBypassMention && requireMention && !wasMentioned) { if (!shouldBypassMention && requireMention && !wasMentioned) {

View File

@@ -58,6 +58,7 @@ export type WebInboundMessage = {
mediaPath?: string; mediaPath?: string;
mediaType?: string; mediaType?: string;
mediaUrl?: string; mediaUrl?: string;
wasMentioned?: boolean;
}; };
export async function monitorWebInbox(options: { export async function monitorWebInbox(options: {