From 66b5dd7bddd960b282d2acd7e22a688467d7c4c8 Mon Sep 17 00:00:00 2001 From: Robert Helewka Date: Tue, 5 May 2026 21:04:45 -0400 Subject: [PATCH] forward: override auth_flow (generic) instead of async_auth_flow The async_auth_flow override was being driven via 'await' in httpx's async dispatcher, which yielded 'NoneType is not awaitable' because a plain generator yielding a Request doesn't produce an awaitable. httpx.Auth has three hooks: sync_auth_flow, async_auth_flow, and the generic auth_flow. The default sync/async implementations delegate to auth_flow when subclasses override only that one, which is exactly the behaviour we want: one plain-generator implementation shared across sync and async clients. Override auth_flow, drop sync/async overrides. --- pallas/_fastagent_patch.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/pallas/_fastagent_patch.py b/pallas/_fastagent_patch.py index c6001ec..2e67732 100644 --- a/pallas/_fastagent_patch.py +++ b/pallas/_fastagent_patch.py @@ -82,7 +82,7 @@ class _DynamicBearerAuth(httpx.Auth): def _current_token(self) -> str | None: return _lookup_bearer(self._server_config) - def sync_auth_flow(self, request: httpx.Request): + def _inject(self, request: httpx.Request) -> None: token = self._current_token() if token: request.headers["Authorization"] = f"Bearer {token}" @@ -97,13 +97,18 @@ class _DynamicBearerAuth(httpx.Auth): "forward.skipped server=%s reason=no_inbound_bearer via=auth_flow", self._server_name, ) - yield request - async def async_auth_flow(self, request: httpx.Request): - # httpx calls this generator per-request; all our work is - # pre-response so we yield once and we're done. - for item in self.sync_auth_flow(request): - yield item + def auth_flow(self, request: httpx.Request): + # Both ``sync_auth_flow`` and ``async_auth_flow`` on httpx.Auth + # delegate to ``auth_flow`` when subclasses override only the + # generic path, which is exactly what we want: one implementation + # that works for both sync and async clients. httpx drives this + # as a *plain* generator (the async side resolves the yielded + # request via its own await machinery), so do NOT mark this + # ``async def`` — that triggers + # ``object NoneType can't be used in 'await' expression``. + self._inject(request) + yield request # ── Opt-in server names discovered from raw YAML ────────────────────────────── # Fast-agent's ``Settings(**merged)`` pipeline silently discards unknown keys