Preserve textFormat through TeamsStreamingWriter's intermediate and final activities - #661
Merged
Mehak Bindra (MehakBindra) merged 5 commits intoSep 1, 2026
Conversation
…inal activities Mirrors microsoft/teams.ts PR #762 for the .NET streaming writer. - StreamingActivityInput (the typing-type chunks used for informative and intermediate streaming updates) now models textFormat and gets a WithTextFormat builder method, matching MessageActivityInput. - TeamsStreamingWriter.WithTextFormat(TextFormat) sets the format applied to every subsequent intermediate chunk, and is used as the final message's default format unless the caller sets TextFormat explicitly on the activity passed to FinalizeResponseAsync. - The tracked format resets when the writer is reused for a new streamed message after finalize, matching how other per-stream state is reset. Without this, streamed extended markdown content (task lists, strikethrough, etc.) rendered as plain markdown during intermediate typing chunks and only switched to the intended format on the final message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot
AI
requested
a lite review from Copilot
and removed request for
Copilot
August 27, 2026 20:04
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
Collaborator
|
After fix demo: after-fix-cs.mp4 |
Corina (corinagum)
approved these changes
Aug 27, 2026
Copilot started reviewing on behalf of
Mehak Bindra (MehakBindra)
August 28, 2026 22:50
View session
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a formatting inconsistency in TeamsStreamingWriter so that TextFormat (notably Extended Markdown) is preserved across intermediate “typing” chunks and the final message, rather than only being applied at finalize time.
Changes:
- Adds
TextFormatsupport toStreamingActivityInputplus a fluentWithTextFormat()builder API. - Adds
TeamsStreamingWriter.WithTextFormat(TextFormat)to track/apply a per-stream format to informative + streaming chunks, and as the default for the final message unless explicitly overridden. - Adds unit tests covering propagation, unset behavior, final override precedence, and state reset on writer reuse.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| test/Microsoft.Teams.Apps.UnitTests/TeamsStreamingWriterTests.cs | Adds coverage for TextFormat propagation/override/reset behaviors in streaming. |
| src/Microsoft.Teams.Apps/TeamsStreamingWriter.cs | Tracks a per-stream TextFormat and applies it to intermediate chunks and (as a default) to the final message. |
| src/Microsoft.Teams.Apps/TeamsStreamingWriter.Activity.cs | Models textFormat on StreamingActivityInput and exposes it via the builder. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Mehak Bindra (MehakBindra)
requested changes
Aug 28, 2026
Replace writer-level WithTextFormat with per-chunk overloads: - Add AppendResponseAsync(MessageActivityInput) honoring Text + TextFormat - Add SendInformativeUpdateAsync(string, TextFormat) for per-informative format - Track last-streamed format for the final message's default (explicit final wins) Also update the StreamingBot sample: make Azure OpenAI optional (canned streamed fallback when unconfigured), demonstrate the per-chunk API in the extended-markdown path, and simplify to a single 'extended markdown' command. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: bf9dec16-4ade-483a-b60f-88d651e345f4
Collaborator
|
I attest that I have verified |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: bf9dec16-4ade-483a-b60f-88d651e345f4
Collaborator
|
I attest that I have verified |
Mehak Bindra (MehakBindra)
approved these changes
Sep 1, 2026
Mehak Bindra (MehakBindra)
deleted the
copilot/preserve-textformat-streaming-writer
branch
September 1, 2026 20:07
pull Bot
pushed a commit
to Mattlk13/teams.ts
that referenced
this pull request
Sep 1, 2026
…rmat to update() (microsoft#784) ## Summary Follow-up to microsoft#762. Two fixes for `textFormat` on streamed **informative updates**: 1. **Source informative `textFormat` from the update itself.** In `HttpStream.flush()`, informative typing chunks previously took their format from `this.finalActivity?.textFormat`. But `finalActivity` is only set once a *message* is drained from the queue — which happens *after* informative updates are collected — so an informative update's own `textFormat` was ignored. It now reads `informativeUpdate.textFormat` directly. The streamed-text chunk keeps its correct last-emitted-message-wins behavior. 2. **`update()` can now carry a `textFormat`.** `IStreamer.update(text)` / `HttpStream.update(text)` gain an optional, nullable `textFormat`: ```ts update(text: string, textFormat?: TextFormat | null): void; ``` `undefined`/`null` → Teams default (`markdown`); an explicit value (e.g. `extendedmarkdown`) → that format. Previously the only way to format an informative update was to hand-build a `{ type: 'typing', ..., textFormat }` object and call `emit()`. ## Sample `examples/stream` demonstrates the new `update()` overload and cleans up the `extended-markdown` scenario's deltas to match the existing sample style while showcasing checkboxes and ~~strikethrough~~. ## Tests Added to `http-stream.spec.ts`: - informative update carries its own `textFormat`, independent of `finalActivity` (red/green — fails against the old `finalActivity`-sourced logic) - `update(text, 'extendedmarkdown')` sends an informative chunk with that format - `update(text)` and `update(text, null)` omit `textFormat` All apps tests pass (648/648), build + eslint clean. ## Cross-language Counterpart PRs (not yet merged) will fold in the equivalent changes: - Python microsoft/teams.py#581 - .NET microsoft/teams.net#661 (design differs — coordinated separately) --------- Co-authored-by: GitHub Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0073e3f0-b7b4-464a-9bdf-30f97d8081ec
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Mirrors microsoft/teams.ts#762, applied to the .NET streaming abstraction (
TeamsStreamingWriter).The .NET streamer has a different shape than the TS
HttpStream(raw string deltas are accumulated viaAppendResponseAsync, with a structuredMessageActivityInputonly supplied atFinalizeResponseAsync), so the bug shows up slightly differently here:StreamingActivityInput(thetyping-type chunk used for informative/intermediate updates) never modeledtextFormatat all, so there was no way to make intermediate streamed chunks render as anything other than the default format — only the final message (built directly from the caller-suppliedMessageActivityInput) could carry it. In practice this meant content intended to stream as Extended Markdown (task lists, strikethrough, etc.) rendered as plain markdown while streaming and only switched format once the final message landed.Fix
TextFormatonStreamingActivityInput(the typing-type chunk used for informative/intermediate updates), with aWithTextFormat()builder mirroringMessageActivityInput.TeamsStreamingWriter.WithTextFormat(TextFormat): sets the format applied to every subsequent informative/intermediate typing chunk, and used as the final message's default format unless the caller setsTextFormatexplicitly on the activity passed toFinalizeResponseAsync(explicit final value wins).FinalizeResponseAsync, consistent with how the rest of the per-stream state (accumulated text, sequence, etc.) resets.Note
Extended Markdown is available in public developer preview, which is why the corresponding
TextFormats.ExtendedMarkdownconstant remains behind theExperimentalTeamsExtendedMarkdowndiagnostic.Testing
TeamsStreamingWriterTests.cscovering: format applied to informative + streaming chunks + final message, no format sent when unset, explicit final-activity format overriding the writer's tracked format, and reset on stream reuse.Microsoft.Teams.Apps.UnitTestssuite passes (532/532).Created from a Microsoft Teams conversation.
I attest that I have verified