Skip to content

Migrate Teams adapter to microsoft-teams-apps (official MS Python SDK) #93

Description

@patrick-chinchill

Background

PR #88 lands native Teams DM streaming as a hand-rolled implementation of the Bot Framework REST streaming wire format (channelData.streamType, streamSequence, streamId, streaminfo entity, 1 req/sec throttle). Upstream vercel/chat#416 (in chat@4.27.0) achieves the same UX by delegating to IStreamer.emit() from @microsoft/teams.apps — an SDK that handled the wire format and throttling under the hood.

When PR #88 was started, the equivalent Python SDK didn't exist. It does now: Microsoft shipped microsoft-teams-apps as GA on May 1, 2026 (announcement), with the same ctx.stream.emit(text) primitive upstream uses in TS:

@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    async for chunk in agent.run(...):
        if chunk.text:
            ctx.stream.emit(chunk.text)

Goal

Migrate chat_sdk/adapters/teams off our hand-rolled Bot Framework REST loop and onto microsoft-teams-apps. After migration, we no longer own the streaming wire format, the throttle gate, or the activity-routing layer — all delegated to the SDK.

Scope

  • src/chat_sdk/adapters/teams/adapter.py (~2,300 LOC)
  • src/chat_sdk/adapters/teams/cards.py (~440 LOC — keep ours for the divider-rendering parity fix per UPSTREAM_SYNC.md row, or migrate if SDK behavior matches)
  • tests/test_teams_*.py (~4,800 LOC across 6 files — large reduction expected as wire-format assertions become SDK-internal)
  • docs/UPSTREAM_SYNC.md — rows 482, 488, 489, 490, 491, 492 reference the hand-rolled implementation; rewrite or delete each after migration

What changes structurally

  1. Webhook entry: today we own HTTP + JWT verification (handle_webhook). With the SDK, we plug into App via HttpServerAdapter (FastAPI/Starlette/aiohttp wrapper).
  2. Activity handlers (_handle_message_activity, _handle_message_action, _handle_adaptive_card_action, _handle_reaction_activity): receive ctx: ActivityContext[...] from the SDK instead of building Message + thread_id from raw activity dicts. Delegate into our Chat.process_* from the SDK callbacks.
  3. Streaming (_stream_via_emit, _close_stream_session, _active_streams, _TeamsStreamSession): delete entirely (~150 lines + supporting class). Replace with ctx.stream.emit(chunk) inside the message handler. Cumulative-text snapshot + throttle gate become SDK-internal.
  4. Send/edit/delete/reactions/typing (post_message, edit_message, delete_message, add_reaction, remove_reaction, start_typing): replace _teams_send HTTP loop with ctx.send() + app.send(conversation_id, activity) for proactive messages.
  5. Auth: drop our JWT verification, app password handling, federated-credential plumbing (SDK handles these).
  6. Optional dep: chat-sdk-python[teams] switches from aiohttp>=3.9 (hand-rolled HTTP) to microsoft-teams-apps.

Required prerequisites

  • Python version floor bump: microsoft-teams-apps requires Python >=3.12. Our current floor is 3.10. Need to bump pyproject and update python_requires / CI matrix. Coordinate timing with downstream consumers.
  • Audit microsoft-teams-apps API stability: GA on 2026-05-01 — confirm semver guarantees and that there's no breaking churn expected in 2.x.
  • Validate against an integration test: stand up a real Teams bot using the SDK + chat-sdk-python to confirm the streaming UX and webhook routing work end-to-end before deleting our HTTP layer.

Out of scope for 4.27

Per discussion on PR #88, this migration is too large to land inside the 4.27 sync cycle (4-6 focused days; would block waves 2 & 3; SDK is only 2 weeks GA so carries early-adopter risk).

For 4.27 we landed the bounded P1 fixes (PR #88, commit b29efe0):

  • streamId placement on the streaminfo entity (Bot Framework REST contract)
  • 1 req/sec emit throttle with native_stream_min_emit_interval_ms config + StreamOptions.update_interval_ms override
  • Re-raise on send failure so Thread.stream history matches user-visible text

This issue tracks the broader migration as the headline feature for 0.4.28 (or a 0.4.27.x point release if SDK adoption goes faster than expected).

Acceptance criteria

  • src/chat_sdk/adapters/teams/adapter.py no longer imports aiohttp for Bot Framework REST calls; all webhook + send/edit/stream paths go through microsoft-teams-apps
  • _stream_via_emit, _close_stream_session, _active_streams, _TeamsStreamSession deleted
  • All wire-format-specific tests removed (TestNativeStreamingWireFormat, TestNativeStreamingThrottle, TestStreamInfoEntityContract) — the SDK owns these contracts
  • UPSTREAM_SYNC.md rows 482, 488, 489, 490, 491, 492 rewritten or deleted
  • chat-sdk-python[teams] extras switched from aiohttp to microsoft-teams-apps
  • Python floor bumped to 3.12 in pyproject.toml, CI matrix, README
  • Integration-tested with a real Teams bot (manual + smoke test)

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions