Skip to content

Conversation

@zimeg
Copy link
Member

@zimeg zimeg commented Sep 24, 2025

Summary

This PR adds a WebClient#chatStream method that creates chat streams 🤖

Preview

const streamer = client.chatStream({
  channel: "C0123456789",
  thread_ts: "1700000001.123456",
  recipient_team_id: "T0123456789",
  recipient_user_id: "U0123456789",
  buffer_size: 500,
});
await streamer.append({
  markdown_text: "**hello wo",
});
await streamer.append({
  markdown_text: "rld!**",
});
await streamer.stop();

Testing

WIP-

Notes

  • Will save documentation generation for a later commit since the diff is significant at the moment I find.
  • A default text buffer is used to avoid reaching rate limits in frequent updates.
  • Super open to thoughts on the approach and implementation - does this seem like an alright change?
  • Planning to follow up with tests after discussion to avoid extra changes too 👾

Requirements

@zimeg zimeg requested a review from mwbrooks September 24, 2025 03:25
@zimeg zimeg self-assigned this Sep 24, 2025
@zimeg zimeg added semver:minor enhancement M-T: A feature request for new functionality pkg:web-api applies to `@slack/web-api` labels Sep 24, 2025
@codecov
Copy link

codecov bot commented Sep 24, 2025

Codecov Report

❌ Patch coverage is 99.18256% with 3 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (ai-apps@520a2d6). Learn more about missing BASE report.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             ai-apps    #2379   +/-   ##
==========================================
  Coverage           ?   92.99%           
==========================================
  Files              ?       40           
  Lines              ?    11076           
  Branches           ?      714           
==========================================
  Hits               ?    10300           
  Misses             ?      764           
  Partials           ?       12           
Flag Coverage Δ
cli-hooks 95.23% <ø> (?)
cli-test 94.80% <ø> (?)
oauth 77.39% <ø> (?)
socket-mode 61.87% <ø> (?)
web-api 98.06% <99.18%> (?)
webhook 96.66% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

*/
public async stream(params: ChatStartStreamArguments): Promise<ChatStreamer> {
const streamer = new ChatStreamer(this);
await streamer.start(params);
Copy link
Contributor

Choose a reason for hiding this comment

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

startStream turns off the loading animation, we should defer it until we have the first bytes to stream.

Copy link
Member Author

Choose a reason for hiding this comment

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

@mcodik Great callout! The changes of 826e46c address this but also make the ChatStreamer class more opinionated to no longer expose a "start" method.

Instead we expose "append" and "stop" with the actual "chat.startStream" method being called when sending the first of the markdown text!

I find this alright to use in development but am open to adjustments however needed 🤖

if (!this.streamTs) {
throw new Error('failed to append stream: ts not found');
}
const response = await this.client.chat.appendStream({
Copy link
Contributor

Choose a reason for hiding this comment

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

plz buffer some characters here, plus some ways for developers to tune the buffer size

Copy link
Member Author

Choose a reason for hiding this comment

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

@mcodik The WebClient#chatStream method now configures this with changes of 826e46c:

const streamer = client.chatStream({
  channel: "C0123456789",
  thread_ts: "1700000001.123456",
  recipient_team_id: "T0123456789",
  recipient_user_id: "U0123456789",
  buffer_size: 500,                  // This defaults to "250" characters at the moment
});

Copy link
Contributor

@mcodik mcodik left a comment

Choose a reason for hiding this comment

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

looks great thanks!

@zimeg zimeg marked this pull request as ready for review September 29, 2025 23:59
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.

Exciting work @zimeg! I've left a few comments and I'll take it for a spin now 🧪

Comment on lines 35 to 48
* @example
* const client = new WebClient(process.env.SLACK_BOT_TOKEN);
* const logger = new ConsoleLogger();
* const args = {
* channel: "C0123456789",
* thread_ts: "1700000001.123456",
* recipient_team_id: "T0123456789",
* recipient_user_id: "U0123456789",
* };
* const streamer = new ChatStreamer(client, logger, args, { buffer_size: 500 });
* await streamer.append({
* markdown_text: "**hello world!**",
* });
* await streamer.stop();
Copy link
Member

Choose a reason for hiding this comment

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

praise: Love seeing the example for the generated docs!

* @see {@link https://docs.slack.dev/reference/methods/chat.appendStream}
*/
async append(
params: Omit<ChatAppendStreamArguments, 'channel' | 'ts'>,
Copy link
Member

Choose a reason for hiding this comment

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

praise: Nice touch that we're just passing in the appendSteam args.

❓ question: I noticed that the constructor calls it args while the append calls it params. Is there a reason?

Copy link
Member Author

Choose a reason for hiding this comment

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

@mwbrooks Thanks for calling this out! I agree that a more general typing is nice to have here! 👾

I noticed that the constructor calls it args while the append calls it params. Is there a reason?

A partial reason might be quick naming, but I've added fcbce94 and 0f852cd to be more consistent in how method arguments mapping to args and params including options 🙏 ✨

Comment on lines 158 to 169
if (!this.streamTs) {
const response = await this.client.chat.startStream({
...this.args,
token: this.token,
...params,
markdown_text: this.buffer,
});
this.buffer = '';
this.streamTs = response.ts;
this.state = 'in_progress';
return response;
}
Copy link
Member

Choose a reason for hiding this comment

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

question: Why did you choose to client.chat.startStream(...) in both the .stop(...) and .flushBuffer(...) instead of the constructor? Just curious.

Copy link
Member Author

Choose a reason for hiding this comment

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

@mwbrooks This skips an extra .appendStream method call if streamer.stop() is called on an existing stream I am hoping. We also avoid adding this to the constructor to delay this as an asynchronous process to favor loading states:

startStream turns off the loading animation, we should defer it until we have the first bytes to stream.

🔗 #2379 (comment)

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.

✅ Nice work @zimeg! Throwing an approval on here - you can work through my feedback and decide what to keep or not.

🧪 I've manually tested locally with a sample app and it works very well. I played with buffer sizes between 250-512 and didn't notice any major difference, so I think 250 (256?) is a good default!

@zimeg zimeg merged commit ffeb783 into ai-apps Sep 30, 2025
161 of 165 checks passed
@zimeg zimeg deleted the zimeg-feat-web-api-chat-streamer branch September 30, 2025 15:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement M-T: A feature request for new functionality pkg:web-api applies to `@slack/web-api` semver:minor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants