Skip to content

feat: add support for say_stream utility#1462

Open
WilliamBergamin wants to merge 5 commits intomainfrom
say_stream
Open

feat: add support for say_stream utility#1462
WilliamBergamin wants to merge 5 commits intomainfrom
say_stream

Conversation

@WilliamBergamin
Copy link
Contributor

Summary

This PR aims to introduce a new kwarg say_stream, it allows developers the ability to easily use a WebClient.chat_stream object initialized with logical default values

Testing

  1. clone this branch
  2. build the project with scripts/build_pypi_package.sh
  3. Import the package in a Bolt project
  4. Play around with say_stream

Category

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter
  • Document pages under /docs
  • Others

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

  • I've read and understood the Contributing Guidelines and have done my best effort to follow them.
  • I've read and agree to the Code of Conduct.
  • I've run ./scripts/install_all_and_run_tests.sh after making the changes.

@WilliamBergamin WilliamBergamin added this to the 1.27.1 milestone Mar 17, 2026
@WilliamBergamin WilliamBergamin self-assigned this Mar 17, 2026
@WilliamBergamin WilliamBergamin added enhancement New feature or request area:async area:sync semver:minor experiment Experimental feature documented with ExperimentalWarning and pydoc Experiment section labels Mar 17, 2026
@codecov
Copy link

codecov bot commented Mar 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.43%. Comparing base (785b813) to head (55ea797).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1462      +/-   ##
==========================================
+ Coverage   91.35%   91.43%   +0.08%     
==========================================
  Files         229      232       +3     
  Lines        7262     7330      +68     
==========================================
+ Hits         6634     6702      +68     
  Misses        628      628              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@WilliamBergamin WilliamBergamin marked this pull request as ready for review March 17, 2026 22:45
@WilliamBergamin WilliamBergamin requested a review from a team as a code owner March 17, 2026 22:45
Copy link
Member

@mwbrooks mwbrooks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WilliamBergamin Thanks a bunch for this PR! It's super exciting.

❓ Can you please provide some code for us to manually test? I'm having trouble importing SayStream and I'm unsure if I'm importing the incorrect path or have the sample app configured incorrectly. What is the import path?

Copy link
Member

@zimeg zimeg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎙️ Leaving a few comments too but an example listener in description might be helpful for later CHANGELOG I agree!

Comment on lines +35 to +44
# TODO: in the future we might want to introduce a "proper" extract_ts utility
thread_ts = req.context.thread_ts or event.get("ts")
if req.context.channel_id and thread_ts:
req.context["say_stream"] = SayStream(
client=req.context.client,
channel_id=req.context.channel_id,
thread_ts=thread_ts,
team_id=req.context.team_id,
user_id=req.context.user_id,
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👾 thought: Defaulting to different threading behavior for say and say_stream concerns me somewhat.

🔮 ramble: I understand thread_ts is required to stream chat at this time, but if "parent" messages can be streamed in the future we might want to revisit also say behavior?

Comment on lines +59 to +60
assert say_stream.team_id == context.team_id
assert say_stream.user_id == context.user_id
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 suggestion: We might want to also assert that team_id and user_id are defined too if this isn't clear elsewhere!

called = {"value": False}

@app.message("")
def handle_user_message(say_stream: SayStream):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def handle_user_message(say_stream: SayStream):
def handle_bot_message(say_stream: SayStream):

🔍 suggestion: To match the event!

Comment on lines +141 to +147
@assistant.thread_started
def start_thread(say_stream: SayStream):
assert say_stream is not None
assert isinstance(say_stream, SayStream)
assert say_stream.channel_id == "D111"
assert say_stream.thread_ts == "1726133698.626339"
called["value"] = True
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 question: Can we also assert that user_id and team_id are set here? Or perhaps not? It's unclear to me what we might expect for events from a direct message...

Comment on lines +183 to +184
assert say_stream is None
assert context.say_stream is None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🌟 praise: This is a pleasant assertion to have for application code guards too I think!

Comment on lines +23 to +24
team_id: Optional[str] = None,
user_id: Optional[str] = None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
team_id: Optional[str] = None,
user_id: Optional[str] = None,
recipient_team_id: Optional[str] = None,
recipient_user_id: Optional[str] = None,

📚 question: Are these options meant to match bolt conventions more than the API arguments?

⚡ thought: Indirection in these concepts might be confusing and I'd favor matching the API in case new arguments are added but am asking these questions in search of preference most!

📰 ramble: Also I didn't notice this difference until test files FWIW-

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:async area:sync enhancement New feature or request experiment Experimental feature documented with ExperimentalWarning and pydoc Experiment section semver:minor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants