mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-03 04:30:22 +00:00
fix(slack): trim DM reply overhead and restore Codex auto transport (#53957)
* perf(slack): instrument runtime and trim DM overhead * perf(slack): lazy-init draft previews * perf(slack): add turn summary diagnostics * perf(core): trim repeated runtime setup noise * perf(core): preselect default web search providers * perf(agent): restore OpenAI auto transport defaults * refactor(slack): drop temporary perf wiring * fix(slack): address follow-up review notes * fix(security): tighten slack and runtime defaults * style(web-search): fix import ordering * style(agent): remove useless spread fallback * docs(changelog): note slack runtime hardening
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { isSlackStreamingEnabled, resolveSlackStreamingThreadHint } from "./dispatch.js";
|
||||
import {
|
||||
isSlackStreamingEnabled,
|
||||
resolveSlackStreamingThreadHint,
|
||||
shouldEnableSlackPreviewStreaming,
|
||||
shouldInitializeSlackDraftStream,
|
||||
} from "./dispatch.js";
|
||||
|
||||
describe("slack native streaming defaults", () => {
|
||||
it("is enabled for partial mode when native streaming is on", () => {
|
||||
@@ -45,3 +50,80 @@ describe("slack native streaming thread hint", () => {
|
||||
).toBe("2000.1");
|
||||
});
|
||||
});
|
||||
|
||||
describe("slack preview streaming eligibility", () => {
|
||||
it("stays on for room messages when streaming mode is enabled", () => {
|
||||
expect(
|
||||
shouldEnableSlackPreviewStreaming({
|
||||
mode: "partial",
|
||||
isDirectMessage: false,
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("stays off for top-level DMs without a reply thread", () => {
|
||||
expect(
|
||||
shouldEnableSlackPreviewStreaming({
|
||||
mode: "partial",
|
||||
isDirectMessage: true,
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("allows DM preview when the reply is threaded", () => {
|
||||
expect(
|
||||
shouldEnableSlackPreviewStreaming({
|
||||
mode: "partial",
|
||||
isDirectMessage: true,
|
||||
threadTs: "1000.1",
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps top-level DMs off even when replyToMode would create a reply thread", () => {
|
||||
const streamThreadHint = resolveSlackStreamingThreadHint({
|
||||
replyToMode: "all",
|
||||
incomingThreadTs: undefined,
|
||||
messageTs: "1000.4",
|
||||
isThreadReply: false,
|
||||
});
|
||||
|
||||
expect(
|
||||
shouldEnableSlackPreviewStreaming({
|
||||
mode: "partial",
|
||||
isDirectMessage: true,
|
||||
threadTs: undefined,
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(streamThreadHint).toBe("1000.4");
|
||||
});
|
||||
});
|
||||
|
||||
describe("slack draft stream initialization", () => {
|
||||
it("stays off when preview streaming is disabled", () => {
|
||||
expect(
|
||||
shouldInitializeSlackDraftStream({
|
||||
previewStreamingEnabled: false,
|
||||
useStreaming: false,
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("stays off when native streaming is active", () => {
|
||||
expect(
|
||||
shouldInitializeSlackDraftStream({
|
||||
previewStreamingEnabled: true,
|
||||
useStreaming: true,
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("turns on only for preview-only paths", () => {
|
||||
expect(
|
||||
shouldInitializeSlackDraftStream({
|
||||
previewStreamingEnabled: true,
|
||||
useStreaming: false,
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,6 +50,27 @@ export function isSlackStreamingEnabled(params: {
|
||||
return params.nativeStreaming;
|
||||
}
|
||||
|
||||
export function shouldEnableSlackPreviewStreaming(params: {
|
||||
mode: "off" | "partial" | "block" | "progress";
|
||||
isDirectMessage: boolean;
|
||||
threadTs?: string;
|
||||
}): boolean {
|
||||
if (params.mode === "off") {
|
||||
return false;
|
||||
}
|
||||
if (!params.isDirectMessage) {
|
||||
return true;
|
||||
}
|
||||
return Boolean(params.threadTs);
|
||||
}
|
||||
|
||||
export function shouldInitializeSlackDraftStream(params: {
|
||||
previewStreamingEnabled: boolean;
|
||||
useStreaming: boolean;
|
||||
}): boolean {
|
||||
return params.previewStreamingEnabled && !params.useStreaming;
|
||||
}
|
||||
|
||||
export function resolveSlackStreamingThreadHint(params: {
|
||||
replyToMode: "off" | "first" | "all";
|
||||
incomingThreadTs: string | undefined;
|
||||
@@ -213,21 +234,29 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
||||
streamMode: account.config.streamMode,
|
||||
nativeStreaming: account.config.nativeStreaming,
|
||||
});
|
||||
const previewStreamingEnabled = slackStreaming.mode !== "off";
|
||||
const streamingEnabled = isSlackStreamingEnabled({
|
||||
mode: slackStreaming.mode,
|
||||
nativeStreaming: slackStreaming.nativeStreaming,
|
||||
});
|
||||
const streamThreadHint = resolveSlackStreamingThreadHint({
|
||||
replyToMode: prepared.replyToMode,
|
||||
incomingThreadTs,
|
||||
messageTs,
|
||||
isThreadReply,
|
||||
});
|
||||
const previewStreamingEnabled = shouldEnableSlackPreviewStreaming({
|
||||
mode: slackStreaming.mode,
|
||||
isDirectMessage: prepared.isDirectMessage,
|
||||
threadTs: streamThreadHint,
|
||||
});
|
||||
const streamingEnabled = isSlackStreamingEnabled({
|
||||
mode: slackStreaming.mode,
|
||||
nativeStreaming: slackStreaming.nativeStreaming,
|
||||
});
|
||||
const useStreaming = shouldUseStreaming({
|
||||
streamingEnabled,
|
||||
threadTs: streamThreadHint,
|
||||
});
|
||||
const shouldUseDraftStream = shouldInitializeSlackDraftStream({
|
||||
previewStreamingEnabled,
|
||||
useStreaming,
|
||||
});
|
||||
let streamSession: SlackStreamSession | null = null;
|
||||
let streamFailed = false;
|
||||
let usedReplyThreadTs: string | undefined;
|
||||
@@ -372,22 +401,24 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
||||
},
|
||||
});
|
||||
|
||||
const draftStream = createSlackDraftStream({
|
||||
target: prepared.replyTarget,
|
||||
token: ctx.botToken,
|
||||
accountId: account.accountId,
|
||||
maxChars: Math.min(ctx.textLimit, SLACK_TEXT_LIMIT),
|
||||
resolveThreadTs: () => {
|
||||
const ts = replyPlan.nextThreadTs();
|
||||
if (ts) {
|
||||
usedReplyThreadTs ??= ts;
|
||||
}
|
||||
return ts;
|
||||
},
|
||||
onMessageSent: () => replyPlan.markSent(),
|
||||
log: logVerbose,
|
||||
warn: logVerbose,
|
||||
});
|
||||
const draftStream = shouldUseDraftStream
|
||||
? createSlackDraftStream({
|
||||
target: prepared.replyTarget,
|
||||
token: ctx.botToken,
|
||||
accountId: account.accountId,
|
||||
maxChars: Math.min(ctx.textLimit, SLACK_TEXT_LIMIT),
|
||||
resolveThreadTs: () => {
|
||||
const ts = replyPlan.nextThreadTs();
|
||||
if (ts) {
|
||||
usedReplyThreadTs ??= ts;
|
||||
}
|
||||
return ts;
|
||||
},
|
||||
onMessageSent: () => replyPlan.markSent(),
|
||||
log: logVerbose,
|
||||
warn: logVerbose,
|
||||
})
|
||||
: undefined;
|
||||
let hasStreamedMessage = false;
|
||||
const streamMode = slackStreaming.draftMode;
|
||||
let appendRenderedText = "";
|
||||
@@ -410,7 +441,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
||||
if (!next.changed) {
|
||||
return;
|
||||
}
|
||||
draftStream.update(next.rendered);
|
||||
draftStream?.update(next.rendered);
|
||||
hasStreamedMessage = true;
|
||||
return;
|
||||
}
|
||||
@@ -420,26 +451,25 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
||||
if (statusUpdateCount > 1 && statusUpdateCount % 4 !== 0) {
|
||||
return;
|
||||
}
|
||||
draftStream.update(buildStatusFinalPreviewText(statusUpdateCount));
|
||||
draftStream?.update(buildStatusFinalPreviewText(statusUpdateCount));
|
||||
hasStreamedMessage = true;
|
||||
return;
|
||||
}
|
||||
|
||||
draftStream.update(trimmed);
|
||||
draftStream?.update(trimmed);
|
||||
hasStreamedMessage = true;
|
||||
};
|
||||
const onDraftBoundary =
|
||||
useStreaming || !previewStreamingEnabled
|
||||
? undefined
|
||||
: async () => {
|
||||
if (hasStreamedMessage) {
|
||||
draftStream.forceNewMessage();
|
||||
hasStreamedMessage = false;
|
||||
appendRenderedText = "";
|
||||
appendSourceText = "";
|
||||
statusUpdateCount = 0;
|
||||
}
|
||||
};
|
||||
const onDraftBoundary = !shouldUseDraftStream
|
||||
? undefined
|
||||
: async () => {
|
||||
if (hasStreamedMessage) {
|
||||
draftStream?.forceNewMessage();
|
||||
hasStreamedMessage = false;
|
||||
appendRenderedText = "";
|
||||
appendSourceText = "";
|
||||
statusUpdateCount = 0;
|
||||
}
|
||||
};
|
||||
|
||||
const { queuedFinal, counts } = await dispatchInboundMessage({
|
||||
ctx: prepared.ctxPayload,
|
||||
@@ -466,8 +496,8 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
||||
onReasoningEnd: onDraftBoundary,
|
||||
},
|
||||
});
|
||||
await draftStream.flush();
|
||||
draftStream.stop();
|
||||
await draftStream?.flush();
|
||||
draftStream?.stop();
|
||||
markDispatchIdle();
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@@ -493,7 +523,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
||||
}
|
||||
|
||||
if (!anyReplyDelivered) {
|
||||
await draftStream.clear();
|
||||
await draftStream?.clear();
|
||||
if (prepared.isRoomish) {
|
||||
clearHistoryEntriesIfEnabled({
|
||||
historyMap: ctx.channelHistories,
|
||||
|
||||
Reference in New Issue
Block a user