























@@ -905,7 +905,7 @@ describe("buildReplyPayloads media filter integration", () => {
905905expect(replyPayloads).toHaveLength(0);
906906});
907907908-it("drops all final payloads when block pipeline streamed successfully", async () => {
908+it("preserves unsent text-only final payloads after block pipeline streamed partial content", async () => {
909909const pipeline: Parameters<typeof buildReplyPayloads>[0]["blockReplyPipeline"] = {
910910didStream: () => true,
911911isAborted: () => false,
@@ -916,8 +916,36 @@ describe("buildReplyPayloads media filter integration", () => {
916916hasBuffered: () => false,
917917getSentMediaUrls: () => [],
918918};
919-// shouldDropFinalPayloads short-circuits to [] when the pipeline streamed
920-// without aborting, so hasSentPayload is never reached.
919+// The pipeline streamed some partial content, but the final text payload was
920+// never sent (hasSentPayload returns false). The old bug dropped all text-only
921+// finals unconditionally; the fix preserves unsent finals.
922+const { replyPayloads } = await buildReplyPayloads({
923+ ...baseParams,
924+blockStreamingEnabled: true,
925+blockReplyPipeline: pipeline,
926+replyToMode: "all",
927+payloads: [{ text: "response", replyToId: "post-123" }],
928+});
929+930+expect(replyPayloads).toHaveLength(1);
931+expect(replyPayloads[0]?.text).toBe("response");
932+});
933+934+it("drops already-sent text-only final payloads after block pipeline streamed the exact same text", async () => {
935+const pipeline: Parameters<typeof buildReplyPayloads>[0]["blockReplyPipeline"] = {
936+didStream: () => true,
937+isAborted: () => false,
938+hasSentPayload: () => true,
939+hasSentExactPayload: (payload) =>
940+payload.text === "response" && !payload.mediaUrl && !payload.mediaUrls,
941+enqueue: () => {},
942+flush: async () => {},
943+stop: () => {},
944+hasBuffered: () => false,
945+getSentMediaUrls: () => [],
946+};
947+// The final text-only payload matches what the pipeline already sent,
948+// so it should be dropped.
921949const { replyPayloads } = await buildReplyPayloads({
922950 ...baseParams,
923951blockStreamingEnabled: true,
@@ -929,6 +957,60 @@ describe("buildReplyPayloads media filter integration", () => {
929957expect(replyPayloads).toHaveLength(0);
930958});
931959960+it("drops a text-only final with an empty envelope assembled from multiple streamed blocks", async () => {
961+const pipeline: Parameters<typeof buildReplyPayloads>[0]["blockReplyPipeline"] = {
962+didStream: () => true,
963+isAborted: () => false,
964+hasSentPayload: () => true,
965+hasSentExactPayload: () => false,
966+enqueue: () => {},
967+flush: async () => {},
968+stop: () => {},
969+hasBuffered: () => false,
970+getSentMediaUrls: () => [],
971+};
972+973+const { replyPayloads } = await buildReplyPayloads({
974+ ...baseParams,
975+blockStreamingEnabled: true,
976+blockReplyPipeline: pipeline,
977+payloads: [{ text: "first block second block", channelData: {} }],
978+});
979+980+expect(replyPayloads).toHaveLength(0);
981+});
982+983+it("preserves final rich content when only its text was streamed", async () => {
984+const pipeline: Parameters<typeof buildReplyPayloads>[0]["blockReplyPipeline"] = {
985+didStream: () => true,
986+isAborted: () => false,
987+hasSentPayload: () => true,
988+hasSentExactPayload: () => false,
989+enqueue: () => {},
990+flush: async () => {},
991+stop: () => {},
992+hasBuffered: () => false,
993+getSentMediaUrls: () => [],
994+};
995+const presentation = {
996+blocks: [{ type: "buttons" as const, buttons: [{ label: "Open", value: "open" }] }],
997+};
998+999+const { replyPayloads } = await buildReplyPayloads({
1000+ ...baseParams,
1001+blockStreamingEnabled: true,
1002+blockReplyPipeline: pipeline,
1003+payloads: [{ text: "response", presentation }],
1004+});
1005+1006+expect(replyPayloads).toEqual([
1007+expect.objectContaining({
1008+text: "response",
1009+ presentation,
1010+}),
1011+]);
1012+});
1013+9321014it("keeps unsent final media after block pipeline streamed the text", async () => {
9331015const pipeline: Parameters<typeof buildReplyPayloads>[0]["blockReplyPipeline"] = {
9341016didStream: () => true,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。