From 3c0d3ba23aa339037630ce735d06b04abdfd1a99 Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Tue, 5 May 2026 16:43:48 -0400 Subject: [PATCH 1/2] feat: add BlockChunk type to chat.{start,append,stop}Stream methods --- slack_sdk/models/messages/chunk.py | 26 +++++++++++++++ tests/slack_sdk/models/test_chunks.py | 46 ++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/slack_sdk/models/messages/chunk.py b/slack_sdk/models/messages/chunk.py index 657d95ae4..f016ec905 100644 --- a/slack_sdk/models/messages/chunk.py +++ b/slack_sdk/models/messages/chunk.py @@ -3,6 +3,7 @@ from slack_sdk.models import show_unknown_key_warning from slack_sdk.models.basic_objects import JsonObject +from slack_sdk.models.blocks import Block from slack_sdk.models.blocks.block_elements import UrlSourceElement @@ -38,6 +39,8 @@ def parse(cls, chunk: Union[Dict, "Chunk"]) -> Optional["Chunk"]: return PlanUpdateChunk(**chunk) elif type == TaskUpdateChunk.type: return TaskUpdateChunk(**chunk) + elif type == BlocksChunk.type: + return BlocksChunk(**chunk) else: cls.logger.warning(f"Unknown chunk detected and skipped ({chunk})") return None @@ -132,3 +135,26 @@ def __init__( self.details = details self.output = output self.sources = sources + + +class BlocksChunk(Chunk): + type = "blocks" + + @property + def attributes(self) -> Set[str]: # type: ignore[override] + return super().attributes.union({"blocks"}) + + def __init__( + self, + *, + blocks: Sequence[Union[Dict, Block]], + **others: Dict, + ): + """Used for passing an array of blocks within a streaming message. + + https://docs.slack.dev/messaging/sending-and-scheduling-messages#text-streaming + """ + super().__init__(type=self.type) + show_unknown_key_warning(self, others) + + self.blocks = blocks diff --git a/tests/slack_sdk/models/test_chunks.py b/tests/slack_sdk/models/test_chunks.py index 78845b307..dd31d467f 100644 --- a/tests/slack_sdk/models/test_chunks.py +++ b/tests/slack_sdk/models/test_chunks.py @@ -1,7 +1,8 @@ import unittest +from slack_sdk.models.blocks import PlainTextObject, SectionBlock from slack_sdk.models.blocks.block_elements import UrlSourceElement -from slack_sdk.models.messages.chunk import MarkdownTextChunk, PlanUpdateChunk, TaskUpdateChunk +from slack_sdk.models.messages.chunk import BlocksChunk, Chunk, MarkdownTextChunk, PlanUpdateChunk, TaskUpdateChunk class MarkdownTextChunkTests(unittest.TestCase): @@ -89,3 +90,46 @@ def test_json(self): ], }, ) + + +class BlocksChunkTests(unittest.TestCase): + def test_json_with_dicts(self): + self.assertDictEqual( + BlocksChunk( + blocks=[ + {"type": "section", "text": {"type": "plain_text", "text": "Hello"}}, + ] + ).to_dict(), + { + "type": "blocks", + "blocks": [ + {"type": "section", "text": {"type": "plain_text", "text": "Hello"}}, + ], + }, + ) + + def test_json_with_block_objects(self): + self.assertDictEqual( + BlocksChunk( + blocks=[ + SectionBlock(text=PlainTextObject(text="Hello")), + ] + ).to_dict(), + { + "type": "blocks", + "blocks": [ + {"type": "section", "text": {"type": "plain_text", "text": "Hello"}}, + ], + }, + ) + + def test_parse(self): + chunk = Chunk.parse( + { + "type": "blocks", + "blocks": [{"type": "section", "text": {"type": "plain_text", "text": "Hello"}}], + } + ) + self.assertIsInstance(chunk, BlocksChunk) + self.assertEqual(chunk.type, "blocks") + self.assertEqual(len(chunk.blocks), 1) From 8b8ab8b3b21619065d965c2e1a01fd682814047c Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Wed, 6 May 2026 10:36:27 -0400 Subject: [PATCH 2/2] alphabetize --- slack_sdk/models/messages/chunk.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/slack_sdk/models/messages/chunk.py b/slack_sdk/models/messages/chunk.py index f016ec905..a7dfc4de4 100644 --- a/slack_sdk/models/messages/chunk.py +++ b/slack_sdk/models/messages/chunk.py @@ -33,14 +33,14 @@ def parse(cls, chunk: Union[Dict, "Chunk"]) -> Optional["Chunk"]: else: if "type" in chunk: type = chunk["type"] - if type == MarkdownTextChunk.type: + if type == BlocksChunk.type: + return BlocksChunk(**chunk) + elif type == MarkdownTextChunk.type: return MarkdownTextChunk(**chunk) elif type == PlanUpdateChunk.type: return PlanUpdateChunk(**chunk) elif type == TaskUpdateChunk.type: return TaskUpdateChunk(**chunk) - elif type == BlocksChunk.type: - return BlocksChunk(**chunk) else: cls.logger.warning(f"Unknown chunk detected and skipped ({chunk})") return None