Skip to content

Support resetting response streams#495

Open
heyitsaamir wants to merge 10 commits into
microsoft:mainfrom
heyitsaamir:heyitsaamir-prototype-response-streams
Open

Support resetting response streams#495
heyitsaamir wants to merge 10 commits into
microsoft:mainfrom
heyitsaamir:heyitsaamir-prototype-response-streams

Conversation

@heyitsaamir

@heyitsaamir heyitsaamir commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • make HttpStream reusable after close: a later emit() or update() starts a new streamed message cycle on the same stream instance
  • keep repeated close() calls idempotent until another emit/update occurs
  • update the stream example with a multi-stream flow that emits an Adaptive Card in the first stream final message, closes, then reuses ctx.stream for a second streamed segment
  • add coverage for close idempotency followed by emit-driven stream reopening
  • refresh uv.lock to include the missing formatted-messaging workspace member and dependency metadata

Context

This addresses the scenario in #489 where a handler needs to:

  1. stream part of a response,
  2. include a normal message or card in the flow,
  3. continue with another streamed response.

Before this change, the only way to do that was to close ctx.stream and then reach into the private ctx._activity_sender.create_stream(...) API to create another stream.

Motivation

This is useful for conversational AI and tool-using bot flows where a response is not always one uninterrupted text stream.

For example, a bot may need to stream an explanation while it reasons or searches, include a structured checkpoint message, approval prompt, or card, and then continue streaming the final answer after that intermediate interaction.

Without a public lifecycle API, developers either have to avoid streaming for these richer flows or access private SDK internals to create a fresh stream. Reopening the stream on the next emit gives them a supported way to split one handler's response into multiple sequential streamed messages while keeping the SDK's one-stream-object model.

Alternatives considered

ctx.new_stream()

We first considered adding a public ctx.new_stream() method that would create and return another stream object.

That solves the private API issue, but it makes the model feel like a handler can manage multiple stream objects at once. In practice, Teams appears to behave more reliably when there is only one active stream at a time. Multiple stream objects also complicate ownership, finalization, event handler registration, and retry behavior.

Context-managed multiple streams

We also considered having ActivityContext track all streams created during a handler and close them all at the end.

That keeps cleanup centralized, but it still encourages multiple open streams during one handler. It also makes ordering less explicit: a stream could remain active while a normal message or a second stream is sent, which is exactly the behavior that seemed risky.

close(reset=True)

We also tried an explicit close(reset=True) option. It worked, but it made the API more awkward and introduced a flag for what is conceptually the next stream cycle.

The approach in this PR keeps close() simple: it finalizes the current streamed message. Repeated close() calls remain idempotent. If the caller later emits or updates, the stream starts a new cycle automatically.

Example

The stream example now includes a multi-stream path:

Screen.Recording.2026-07-01.at.6.08.51.PM.mov
ctx.stream.emit("[stream 1] Starting the first streamed response.")
ctx.stream.emit(MessageActivityInput(text="...").add_card(card))
await ctx.stream.close()

ctx.stream.emit("[stream 2] Reusing ctx.stream after close().")

This demonstrates the intended lifecycle: one streamed message is finalized, and the same ctx.stream instance is reused for the next streamed message when the handler emits again.

Addresses #489

heyitsaamir and others added 2 commits July 1, 2026 18:04
Add a reset option to stream close so handlers can finalize one streamed message and reuse the same stream instance for a later streamed message. Update the stream example with a multi-stream flow and cover the lifecycle in HttpStream tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Include the formatted-messaging workspace member and refreshed dependency metadata in uv.lock.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@heyitsaamir heyitsaamir marked this pull request as ready for review July 2, 2026 01:05
Copilot AI review requested due to automatic review settings July 2, 2026 01:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a reset option to the streaming close lifecycle so a handler can finalize one streamed response, send a non-streamed message (or card), and then reuse the same stream instance for a later streamed segment—addressing the “multiple streams in one handler” scenario without reaching into private context internals.

Changes:

  • Extend StreamerProtocol.close / HttpStream.close with reset: bool = False to allow reusing a stream instance after finalizing a streamed message.
  • Switch HttpStream.on_close to persistent subscriptions so close handlers can fire for each finalized streamed message.
  • Update the streaming example to demonstrate a “multi-stream” flow and add a regression test for reset + reuse.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
uv.lock Adds missing workspace member metadata and refreshes dependency metadata.
packages/apps/tests/test_http_stream.py Adds coverage for closing with reset=True and reusing the same stream instance.
packages/apps/src/microsoft_teams/apps/plugins/streamer.py Updates the public StreamerProtocol API/docs to include close(reset=...).
packages/apps/src/microsoft_teams/apps/http_stream.py Implements close(reset=...) semantics and allows multiple close events via .on("close", ...).
examples/stream/src/main.py Demonstrates interleaving two stream segments with a normal message in between.
examples/stream/README.md Documents how to run the multi-stream demo.

Comment thread packages/apps/src/microsoft_teams/apps/plugins/streamer.py Outdated
Comment thread packages/apps/src/microsoft_teams/apps/plugins/streamer.py
Comment thread packages/apps/tests/test_http_stream.py Outdated
heyitsaamir and others added 4 commits July 1, 2026 18:10
Add a standalone simple-card path to the stream example for validating Adaptive Card delivery independently of streaming reset behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update the multi-stream stream example to send the simple Adaptive Card between streamed response segments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update the multi-stream example to emit the Adaptive Card through ctx.stream before close(reset=True), so the card is part of the first stream's final message.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Make close finalize the current streamed message while a later emit or update starts a new stream cycle on the same instance. Keep repeated close calls idempotent until another emit occurs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread examples/stream/src/main.py Outdated
def create_simple_card() -> AdaptiveCard:
return AdaptiveCard.model_validate(
{
"type": "AdaptiveCard",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: can we use the builder methods instead?

Comment thread packages/apps/src/microsoft_teams/apps/http_stream.py Outdated
heyitsaamir and others added 4 commits July 2, 2026 16:06
Rename the per-message reset helper and add a separate helper for preparing the stream instance for the next stream cycle.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Prefer the HttpStream.closed property where the code is checking whether the current stream cycle has been finalized.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Align protocol documentation and example text with the emit-after-close stream cycle behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace AdaptiveCard.model_validate in the stream example with the builder-style AdaptiveCard and TextBlock classes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants