Preserve text_format through HttpStream's intermediate and final activities - #581
Merged
Merged
Conversation
…vities
HttpStream sends intermediate typing chunks built from scratch on every
flush. It already applies last-message-wins semantics for attachments,
entities, suggested_actions, and channel_data via _final_activity, but
never forwarded text_format to the rebuilt TypingActivityInput chunks
sent during flush(), so stream.emit(MessageActivityInput(...).with_text_format('extendedmarkdown'))
was silently dropped from intermediate updates (though it was already
preserved on the final/timeout-fallback message since those reuse the
_final_activity object directly).
- Add text_format to _TypingBase (shared by TypingActivity and
TypingActivityInput) with a with_text_format() builder, mirroring
MessageActivityInput.
- HttpStream._flush() now reads the last emitted message's text_format
off self._final_activity and applies it to informative updates and
the combined typing chunk.
- Add unit tests covering text_format on typing activities and
propagation across intermediate chunks, the final message, and the
timeout fallback.
- Add an extended-markdown streaming scenario to the stream example app,
mirroring microsoft/teams.ts#762.
Mirrors microsoft/teams.ts#762.
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:53
Contributor
|
After fix demo: after-fix-cs.mp4 |
…format to update() Port of microsoft/teams.ts#784 (follow-up to #762). - HttpStream._flush() no longer overwrites an informative update's own text_format with the last emitted message's format; informative chunks now keep the value set on the update itself. - StreamerProtocol.update / HttpStream.update gain an optional text_format arg so informative updates can carry a format without hand-building a typing activity. - examples/stream: refresh the extended-markdown scenario and use the new update(text, 'markdown') overload. - Tests for informative-update format independence and the update() overload. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the Python streaming implementation (HttpStream) so text_format is preserved consistently across intermediate typing chunks, informative updates, and the final message—aligning behavior with the TypeScript SDK and preventing extended markdown streams from rendering incorrectly mid-stream.
Changes:
- Add
text_formatsupport (and awith_text_format()builder) to typing activities via_TypingBase. - Propagate the last-emitted message’s
text_formatto combined streamed typing chunks, while ensuring informative updates keep their owntext_format. - Extend
stream.update()(protocol + implementation) to accept an optionaltext_format, and add/refresh unit tests and theexamples/streamextended-markdown scenario.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/apps/tests/test_http_stream.py | Adds tests covering text_format propagation for intermediate chunks, final send, timeout fallback, and informative updates. |
| packages/apps/src/microsoft_teams/apps/plugins/streamer.py | Extends StreamerProtocol.update() to accept optional text_format and documents the behavior. |
| packages/apps/src/microsoft_teams/apps/http_stream.py | Implements text_format preservation in _flush() and adds update(text, text_format) support for informative updates. |
| packages/api/tests/unit/test_typing.py | Adds unit tests for typing activity text_format builder and default behavior. |
| packages/api/src/microsoft_teams/api/activities/typing.py | Adds text_format field + with_text_format() builder to typing activity models. |
| examples/stream/src/main.py | Adds an extended-markdown streaming demo showcasing per-chunk text_format. |
| examples/stream/README.md | Documents the new extended-markdown scenario and the update(text, \"markdown\") overload. |
Suppressed comments (1)
packages/apps/src/microsoft_teams/apps/http_stream.py:346
- In
_flush(), use an explicitis not Nonecheck when deciding whether to applytext_formatto the combined typing chunk. This keeps the logic aligned with the Optional type and the public contract (omit vs explicit).
to_send = TypingActivityInput(text=self._text)
if text_format:
to_send.with_text_format(text_format)
await self._send_activity(to_send)
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
|
I attest that I have verified |
Lily Du (lilyydu)
approved these changes
Sep 1, 2026
Kavin (singhk97)
approved these changes
Sep 1, 2026
Kavin (singhk97)
deleted the
copilot/preserve-text-format-http-stream
branch
September 1, 2026 16:24
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
Ports microsoft/teams.ts#762 and its follow-up #784 to the Python SDK.
On every
flush(),HttpStreamrebuilds the outbound typing chunk and forwards the last emitted message's attachments, entities, suggested actions, and channel data ("last message wins" viaself._final_activity) — but nottext_format. Sostream.emit(MessageActivityInput(text=...).with_text_format("extendedmarkdown"))was dropped from every intermediate chunk, and streamed Extended Markdown (task lists, strikethrough) rendered as plain markdown until the message closed.The final message and timeout fallback (
_send_final) already preservedtext_format, since they reuse the real_final_activityobject rather than rebuilding a copy (unlike TS). Only the streamed chunks needed fixing.Changes
text_format+ awith_text_format()builder to_TypingBase(shared byTypingActivity/TypingActivityInput), mirroringMessageActivityInput.HttpStream._flush()applies the last emitted message'stext_formatto the combined streamed-text chunk (last-message-wins).text_format._flush()previously overwrote each informative update's format with_final_activity.text_format; it now leaves the update's own value intact (_final_activityisn't even set when informative updates are sent).update()gains an optionaltext_format.StreamerProtocol.update/HttpStream.updatenow accepttext_format;None→ Teams default (markdown), an explicit value → that format. Previously the only way to format an informative update was to hand-build a typing activity andemit()it.examples/stream: refresh theextended-markdownscenario and demo the newupdate(text, "markdown")overload.text_formaton typing activities, its propagation across streamed chunks / final message / timeout fallback, informative-update format independence (red/green against the old overwrite), and theupdate()overload.Testing
ruff format --check,ruff check,pyright,pytest packages(1107 passed) — all pass.text_format="extendedmarkdown"renders task lists and strikethrough on each intermediate chunk.Created from a Microsoft Teams conversation.