From 631552c554095b0c520376db9131dac0f5427f94 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 26 Apr 2026 14:14:06 +0100 Subject: [PATCH] perf: speed up dispatch-from-config tests --- .../reply/dispatch-from-config.test.ts | 116 +++++++++++++++++- src/auto-reply/reply/dispatch-from-config.ts | 44 ++++--- 2 files changed, 143 insertions(+), 17 deletions(-) diff --git a/src/auto-reply/reply/dispatch-from-config.test.ts b/src/auto-reply/reply/dispatch-from-config.test.ts index ec58c5c9888..22cc2cc944a 100644 --- a/src/auto-reply/reply/dispatch-from-config.test.ts +++ b/src/auto-reply/reply/dispatch-from-config.test.ts @@ -150,6 +150,107 @@ const replyMediaPathMocks = vi.hoisted(() => ({ const runtimePluginMocks = vi.hoisted(() => ({ ensureRuntimePluginsLoaded: vi.fn(), })); +const conversationBindingMocks = vi.hoisted(() => { + type BindingMsgContext = { + OriginatingChannel?: string | null; + Surface?: string | null; + Provider?: string | null; + AccountId?: string | null; + MessageThreadId?: string | number | null; + ThreadParentId?: string | null; + SenderId?: string | null; + SessionKey?: string | null; + ParentSessionKey?: string | null; + OriginatingTo?: string | null; + To?: string | null; + From?: string | null; + NativeChannelId?: string | null; + }; + type BindingConfig = { + channels?: Record; + }; + + const normalizeText = (value: string | number | null | undefined) => + typeof value === "number" ? `${value}` : (value ?? "").trim(); + const normalizeChannel = (value: string | null | undefined) => normalizeText(value).toLowerCase(); + const resolveChannel = (ctx: BindingMsgContext, commandChannel?: string | null) => + normalizeChannel(ctx.OriginatingChannel ?? commandChannel ?? ctx.Surface ?? ctx.Provider); + const resolveAccountId = (ctx: BindingMsgContext, cfg: BindingConfig, channel: string) => + normalizeText(ctx.AccountId) || + normalizeText(cfg.channels?.[channel]?.defaultAccount) || + "default"; + const resolveTarget = (channel: string, value: string | null | undefined) => { + const target = normalizeText(value); + if (!target) { + return undefined; + } + const channelPrefix = `${channel}:`; + return target.toLowerCase().startsWith(channelPrefix) + ? target.slice(channelPrefix.length) + : target; + }; + const resolveThreadId = (ctx: BindingMsgContext) => + normalizeText(ctx.MessageThreadId) || undefined; + + const resolveConversationBindingContextFromMessage = vi.fn( + (params: { cfg: BindingConfig; ctx: BindingMsgContext }) => { + const channel = resolveChannel(params.ctx); + if (!channel) { + return null; + } + const threadId = resolveThreadId(params.ctx); + const baseConversationId = + resolveTarget(channel, params.ctx.OriginatingTo) ?? resolveTarget(channel, params.ctx.To); + const conversationId = threadId ?? baseConversationId; + if (!conversationId) { + return null; + } + const parentConversationId = + threadId && baseConversationId && baseConversationId !== threadId + ? baseConversationId + : resolveTarget(channel, params.ctx.ThreadParentId); + return { + channel, + accountId: resolveAccountId(params.ctx, params.cfg, channel), + conversationId, + ...(parentConversationId ? { parentConversationId } : {}), + ...(threadId ? { threadId } : {}), + }; + }, + ); + + return { + resolveConversationBindingAccountIdFromMessage: (params: { + ctx: BindingMsgContext; + cfg: BindingConfig; + commandChannel?: string | null; + }) => + resolveAccountId(params.ctx, params.cfg, resolveChannel(params.ctx, params.commandChannel)), + resolveConversationBindingChannelFromMessage: ( + ctx: BindingMsgContext, + commandChannel?: string | null, + ) => resolveChannel(ctx, commandChannel), + resolveConversationBindingContextFromAcpCommand: (params: { + cfg: BindingConfig; + ctx: BindingMsgContext; + command?: { to?: string | null; senderId?: string | null }; + sessionKey?: string | null; + parentSessionKey?: string | null; + }) => + resolveConversationBindingContextFromMessage({ + cfg: params.cfg, + ctx: { + ...params.ctx, + SenderId: params.command?.senderId ?? params.ctx.SenderId, + SessionKey: params.sessionKey ?? params.ctx.SessionKey, + ParentSessionKey: params.parentSessionKey ?? params.ctx.ParentSessionKey, + To: params.command?.to ?? params.ctx.To, + }, + }), + resolveConversationBindingContextFromMessage, + resolveConversationBindingThreadIdFromMessage: (ctx: BindingMsgContext) => resolveThreadId(ctx), + }; +}); const threadInfoMocks = vi.hoisted(() => ({ parseSessionThreadInfo: vi.fn< (sessionKey: string | undefined) => { @@ -345,6 +446,18 @@ vi.mock("./reply-media-paths.runtime.js", () => ({ vi.mock("../../agents/runtime-plugins.js", () => ({ ensureRuntimePluginsLoaded: runtimePluginMocks.ensureRuntimePluginsLoaded, })); +vi.mock("./conversation-binding-input.js", () => ({ + resolveConversationBindingAccountIdFromMessage: + conversationBindingMocks.resolveConversationBindingAccountIdFromMessage, + resolveConversationBindingChannelFromMessage: + conversationBindingMocks.resolveConversationBindingChannelFromMessage, + resolveConversationBindingContextFromAcpCommand: + conversationBindingMocks.resolveConversationBindingContextFromAcpCommand, + resolveConversationBindingContextFromMessage: + conversationBindingMocks.resolveConversationBindingContextFromMessage, + resolveConversationBindingThreadIdFromMessage: + conversationBindingMocks.resolveConversationBindingThreadIdFromMessage, +})); vi.mock("../../tts/status-config.js", () => ({ resolveStatusTtsSnapshot: () => ({ autoMode: "always", @@ -771,7 +884,8 @@ describe("dispatchReplyFromConfig", () => { OriginatingTo: undefined, }); - const replyResolver = async () => ({ text: "hi" }) satisfies ReplyPayload; + const replyResolver = async () => + ({ text: "hi", mediaUrl: "https://example.test/reply.png" }) satisfies ReplyPayload; await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver }); expect(dispatcher.sendFinalReply).not.toHaveBeenCalled(); diff --git a/src/auto-reply/reply/dispatch-from-config.ts b/src/auto-reply/reply/dispatch-from-config.ts index e89b7d6841f..9e48a73367d 100644 --- a/src/auto-reply/reply/dispatch-from-config.ts +++ b/src/auto-reply/reply/dispatch-from-config.ts @@ -432,26 +432,38 @@ export async function dispatchReplyFromConfig( }); const routeReplyTo = replyRoute.to; const deliveryChannel = shouldRouteToOriginating ? routeReplyChannel : currentSurface; - const { createReplyMediaPathNormalizer } = await loadReplyMediaPathsRuntime(); - const normalizeReplyMediaPaths = createReplyMediaPathNormalizer({ - cfg, - sessionKey: acpDispatchSessionKey, - workspaceDir, - messageProvider: deliveryChannel, - accountId: replyRoute.accountId, - groupId, - groupChannel: ctx.GroupChannel, - groupSpace: ctx.GroupSpace, - requesterSenderId: ctx.SenderId, - requesterSenderName: ctx.SenderName, - requesterSenderUsername: ctx.SenderUsername, - requesterSenderE164: ctx.SenderE164, - }); + let normalizeReplyMediaPaths: + | ReturnType< + (typeof import("./reply-media-paths.runtime.js"))["createReplyMediaPathNormalizer"] + > + | undefined; + const getNormalizeReplyMediaPaths = async () => { + if (normalizeReplyMediaPaths) { + return normalizeReplyMediaPaths; + } + const { createReplyMediaPathNormalizer } = await loadReplyMediaPathsRuntime(); + normalizeReplyMediaPaths = createReplyMediaPathNormalizer({ + cfg, + sessionKey: acpDispatchSessionKey, + workspaceDir, + messageProvider: deliveryChannel, + accountId: replyRoute.accountId, + groupId, + groupChannel: ctx.GroupChannel, + groupSpace: ctx.GroupSpace, + requesterSenderId: ctx.SenderId, + requesterSenderName: ctx.SenderName, + requesterSenderUsername: ctx.SenderUsername, + requesterSenderE164: ctx.SenderE164, + }); + return normalizeReplyMediaPaths; + }; const normalizeReplyMediaPayload = async (payload: ReplyPayload): Promise => { if (!resolveSendableOutboundReplyParts(payload).hasMedia) { return payload; } - return await normalizeReplyMediaPaths(payload); + const normalizeReplyMediaPayloadPaths = await getNormalizeReplyMediaPaths(); + return await normalizeReplyMediaPayloadPaths(payload); }; const routeReplyToOriginating = async (