- Added functionality to resolve short message IDs to full UUIDs and vice versa, optimizing token usage. - Introduced a reply cache to store message context for replies when metadata is omitted in webhook payloads. - Updated message handling to utilize short IDs for outbound messages and replies, improving efficiency. - Enhanced error messages to clarify required parameters for actions like react, edit, and unsend. - Added tests to ensure correct behavior of new features and maintain existing functionality.
121 lines
3.3 KiB
TypeScript
121 lines
3.3 KiB
TypeScript
import type { TypingMode } from "../../config/types.js";
|
|
import { isSilentReplyText, SILENT_REPLY_TOKEN } from "../tokens.js";
|
|
import type { TypingController } from "./typing.js";
|
|
|
|
export type TypingModeContext = {
|
|
configured?: TypingMode;
|
|
isGroupChat: boolean;
|
|
wasMentioned: boolean;
|
|
isHeartbeat: boolean;
|
|
};
|
|
|
|
export const DEFAULT_GROUP_TYPING_MODE: TypingMode = "message";
|
|
|
|
export function resolveTypingMode({
|
|
configured,
|
|
isGroupChat,
|
|
wasMentioned,
|
|
isHeartbeat,
|
|
}: TypingModeContext): TypingMode {
|
|
if (isHeartbeat) return "never";
|
|
if (configured) return configured;
|
|
if (!isGroupChat || wasMentioned) return "instant";
|
|
return DEFAULT_GROUP_TYPING_MODE;
|
|
}
|
|
|
|
export type TypingSignaler = {
|
|
mode: TypingMode;
|
|
shouldStartImmediately: boolean;
|
|
shouldStartOnMessageStart: boolean;
|
|
shouldStartOnText: boolean;
|
|
shouldStartOnReasoning: boolean;
|
|
signalRunStart: () => Promise<void>;
|
|
signalMessageStart: () => Promise<void>;
|
|
signalTextDelta: (text?: string) => Promise<void>;
|
|
signalReasoningDelta: () => Promise<void>;
|
|
signalToolStart: () => Promise<void>;
|
|
};
|
|
|
|
export function createTypingSignaler(params: {
|
|
typing: TypingController;
|
|
mode: TypingMode;
|
|
isHeartbeat: boolean;
|
|
}): TypingSignaler {
|
|
const { typing, mode, isHeartbeat } = params;
|
|
const shouldStartImmediately = mode === "instant";
|
|
const shouldStartOnMessageStart = mode === "message";
|
|
const shouldStartOnText = mode === "message" || mode === "instant";
|
|
const shouldStartOnReasoning = mode === "thinking";
|
|
const disabled = isHeartbeat || mode === "never";
|
|
let hasRenderableText = false;
|
|
|
|
const isRenderableText = (text?: string): boolean => {
|
|
const trimmed = text?.trim();
|
|
if (!trimmed) return false;
|
|
return !isSilentReplyText(trimmed, SILENT_REPLY_TOKEN);
|
|
};
|
|
|
|
const signalRunStart = async () => {
|
|
if (disabled || !shouldStartImmediately) return;
|
|
await typing.startTypingLoop();
|
|
};
|
|
|
|
const signalMessageStart = async () => {
|
|
if (disabled || !shouldStartOnMessageStart) return;
|
|
if (!hasRenderableText) return;
|
|
await typing.startTypingLoop();
|
|
};
|
|
|
|
const signalTextDelta = async (text?: string) => {
|
|
if (disabled) return;
|
|
const renderable = isRenderableText(text);
|
|
if (renderable) {
|
|
hasRenderableText = true;
|
|
} else if (text?.trim()) {
|
|
return;
|
|
}
|
|
if (shouldStartOnText) {
|
|
await typing.startTypingOnText(text);
|
|
return;
|
|
}
|
|
if (shouldStartOnReasoning) {
|
|
if (!typing.isActive()) {
|
|
await typing.startTypingLoop();
|
|
}
|
|
typing.refreshTypingTtl();
|
|
}
|
|
};
|
|
|
|
const signalReasoningDelta = async () => {
|
|
if (disabled || !shouldStartOnReasoning) return;
|
|
if (!hasRenderableText) return;
|
|
await typing.startTypingLoop();
|
|
typing.refreshTypingTtl();
|
|
};
|
|
|
|
const signalToolStart = async () => {
|
|
if (disabled) return;
|
|
// Start typing as soon as tools begin executing, even before the first text delta.
|
|
if (!typing.isActive()) {
|
|
await typing.startTypingLoop();
|
|
typing.refreshTypingTtl();
|
|
return;
|
|
}
|
|
// Keep typing indicator alive during tool execution.
|
|
typing.refreshTypingTtl();
|
|
};
|
|
|
|
return {
|
|
mode,
|
|
shouldStartImmediately,
|
|
shouldStartOnMessageStart,
|
|
shouldStartOnText,
|
|
shouldStartOnReasoning,
|
|
signalRunStart,
|
|
signalMessageStart,
|
|
signalTextDelta,
|
|
signalReasoningDelta,
|
|
signalToolStart,
|
|
};
|
|
}
|