perf(teams): defer httpx import to first webhook call (#22831)

Same pattern as the google_chat lazy-load (PR #22681), applied to the
Teams plugin. The bundled `plugins/platforms/teams/adapter.py` did
`import httpx` at module top, which dragged the entire httpx +
httpcore stack into every process that triggered plugin discovery —
including `hermes` invocations that never instantiate the Teams
adapter.

`httpx` is only needed inside one method
(`TeamsMeetingPipeline._write_summary_via_incoming_webhook`), and the
`httpx.AsyncBaseTransport` parameter annotation is already string-only
thanks to the existing `from __future__ import annotations`. Move the
runtime import inside the method.

Measured impact (7-run medians, 9950X3D):
  teams plugin alone:    118 → 89 ms  (-25%)
                         46 → 38 MB   (-17%)
  import cli (full):     unchanged
  import model_tools:    unchanged

The full-CLI numbers are flat because httpx is loaded transitively
from many other modules on that path. The microbench win is the real
signal: 29 ms / 8 MB shaved off any process that touches the teams
plugin without otherwise pulling httpx — primarily future workflows
where the gateway is enabled but Teams is not configured.

Tests: 44/44 `tests/gateway/test_teams.py` pass; 345 across all
plugin-platform suites (teams + qqbot + google_chat). The test file
imports `httpx` itself for the `MockTransport` fixture, which is
correct — tests legitimately use httpx, only the plugin's module-level
import was the issue.
This commit is contained in:
Teknium 2026-05-09 14:42:12 -07:00 committed by GitHub
parent 840ebe063e
commit 550f6e2efc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -30,7 +30,14 @@ import os
from typing import Any, Dict, Optional
from urllib.parse import quote
import httpx
# httpx is imported lazily — only the ``_write_summary_via_incoming_webhook``
# code path actually constructs an ``AsyncClient``. Top-level import here
# pulled in the entire httpx + httpcore stack (~37 ms, ~15 MB) on every
# process that triggered plugin discovery, even ones that never instantiate
# the Teams adapter. ``from __future__ import annotations`` above keeps the
# ``httpx.AsyncBaseTransport`` parameter annotation valid as a string at
# runtime; nothing in the codebase calls ``typing.get_type_hints()`` on
# this class so the annotation never has to resolve to a real symbol.
try:
from aiohttp import web
@ -199,6 +206,10 @@ class TeamsSummaryWriter:
payload: Any,
config: dict[str, Any],
) -> dict[str, Any]:
# Lazy import — see module-level note. The teams plugin loads on
# every CLI invocation as a side effect of plugin discovery, but
# 99% of those processes never reach this method.
import httpx
webhook_url = str(config.get("incoming_webhook_url") or "").strip()
if not webhook_url:
raise ValueError("TEAMS_INCOMING_WEBHOOK_URL is required for incoming_webhook mode.")