Looking for the chatbot template? It's now here.

Streaming

Stream real-time text responses from AI models and other async sources to chat platforms.

Chat SDK accepts any AsyncIterable<string> as a message, enabling real-time streaming of AI responses and other incremental content to chat platforms.

AI SDK integration

Pass an AI SDK textStream directly to thread.post():

lib/bot.ts
import { ToolLoopAgent } from "ai";

const agent = new ToolLoopAgent({
  model: "anthropic/claude-4.5-sonnet",
  instructions: "You are a helpful assistant.",
});

bot.onNewMention(async (thread, message) => {
  const result = await agent.stream({ prompt: message.text });
  await thread.post(result.textStream);
});

Custom streams

Any async iterable works:

lib/bot.ts
const stream = (async function* () {
  yield "Processing";
  yield "...";
  yield " done!";
})();

await thread.post(stream);

Platform behavior

PlatformMethodDescription
SlackNative streaming APIUses Slack's chatStream for smooth, real-time updates
TeamsPost + EditPosts a message then edits it as chunks arrive
Google ChatPost + EditPosts a message then edits it as chunks arrive
DiscordPost + EditPosts a message then edits it as chunks arrive

The post+edit fallback throttles edits to avoid rate limits. Configure the update interval when creating your Chat instance:

lib/bot.ts
const bot = new Chat({
  // ...
  streamingUpdateIntervalMs: 500, // Default: 500ms
});

Stop blocks (Slack only)

When streaming in Slack, you can attach Block Kit elements to the final message using stopBlocks. This is useful for adding action buttons after a streamed response completes:

lib/bot.ts
await thread.stream(textStream, {
  stopBlocks: [
    {
      type: "actions",
      elements: [{
        type: "button",
        text: { type: "plain_text", text: "Retry" },
        action_id: "retry",
      }],
    },
  ],
});

Streaming with conversation history

Combine message history with streaming for multi-turn AI conversations:

lib/bot.ts
bot.onSubscribedMessage(async (thread, message) => {
  // Fetch recent messages for context
  const result = await thread.adapter.fetchMessages(thread.id, { limit: 20 });

  const history = result.messages
    .filter((msg) => msg.text.trim())
    .map((msg) => ({
      role: msg.author.isMe ? "assistant" as const : "user" as const,
      content: msg.text,
    }));

  const response = await agent.stream({ prompt: history });
  await thread.post(response.textStream);
});