Skip to content

Preserve text_format through HttpStream's intermediate and final activities - #581

Merged
Kavin (singhk97) merged 5 commits into
mainfrom
copilot/preserve-text-format-http-stream
Sep 1, 2026
Merged

Preserve text_format through HttpStream's intermediate and final activities#581
Kavin (singhk97) merged 5 commits into
mainfrom
copilot/preserve-text-format-http-stream

Conversation

Copilot AI commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Ports microsoft/teams.ts#762 and its follow-up #784 to the Python SDK.

On every flush(), HttpStream rebuilds the outbound typing chunk and forwards the last emitted message's attachments, entities, suggested actions, and channel data ("last message wins" via self._final_activity) — but not text_format. So stream.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 preserved text_format, since they reuse the real _final_activity object rather than rebuilding a copy (unlike TS). Only the streamed chunks needed fixing.

Changes

  • Add text_format + a with_text_format() builder to _TypingBase (shared by TypingActivity / TypingActivityInput), mirroring MessageActivityInput.
  • HttpStream._flush() applies the last emitted message's text_format to the combined streamed-text chunk (last-message-wins).
  • Informative updates keep their own 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_activity isn't even set when informative updates are sent).
  • update() gains an optional text_format. StreamerProtocol.update / HttpStream.update now accept text_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 and emit() it.
  • examples/stream: refresh the extended-markdown scenario and demo the new update(text, "markdown") overload.
  • Unit tests for text_format on typing activities, its propagation across streamed chunks / final message / timeout fallback, informative-update format independence (red/green against the old overwrite), and the update() overload.

Testing

  • ruff format --check, ruff check, pyright, pytest packages (1107 passed) — all pass.
  • Verified live in Teams: streaming a message with text_format="extendedmarkdown" renders task lists and strikethrough on each intermediate chunk.

Created from a Microsoft Teams 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
@singhk97

Copy link
Copy Markdown
Contributor

After fix demo:

after-fix-cs.mp4

Comment thread examples/stream/src/main.py Outdated
…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>
Copilot AI lite review requested due to automatic review settings August 31, 2026 20:03

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

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_format support (and a with_text_format() builder) to typing activities via _TypingBase.
  • Propagate the last-emitted message’s text_format to combined streamed typing chunks, while ensuring informative updates keep their own text_format.
  • Extend stream.update() (protocol + implementation) to accept an optional text_format, and add/refresh unit tests and the examples/stream extended-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 explicit is not None check when deciding whether to apply text_format to 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.

Comment thread packages/api/src/microsoft_teams/api/activities/typing.py
Comment thread packages/apps/src/microsoft_teams/apps/http_stream.py
Comment thread packages/apps/src/microsoft_teams/apps/http_stream.py Outdated
Kavin (singhk97) and others added 3 commits August 31, 2026 14:21
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>
@singhk97

Copy link
Copy Markdown
Contributor

I attest that I have verified

Comment thread examples/stream/README.md
@singhk97
Kavin (singhk97) merged commit aa148a7 into main Sep 1, 2026
8 checks passed
@singhk97
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
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.

5 participants