fix(agent): preserve MoA facade when rebuilding primary client (stream retry, rotation, fallback+restore)

When agent.provider == "moa", the MoAClient facade *is* the client - there is
no real OpenAI wire endpoint behind the moa://local placeholder. Client rebuilds
(_replace_primary_openai_client: stream-retry pool cleanup, credential rotation,
dead-connection cleanup, fallback+restore) go through create_openai_client and
produce a native OpenAI client while provider stays "moa". The next primary
call then either raises a `_moa_prepared_request` TypeError (#78382) or, when
_client_kwargs carry an unrelated relay base_url, leaks the request to a foreign
gateway (observed as HTTP 503 "group ... no available channel" from an
unrelated new-api relay right after an aggregator empty-stream retry).

Fix: in create_openai_client, when provider is "moa", return
build_moa_facade(agent, model) instead of a native client. This covers every
rebuild entry point. The three already-fixed call sites
(restore_primary_runtime, try_recover_primary_transport, switch_model) assign
the facade directly and do not go through create_openai_client, so they are
unaffected.

Closes #78382
This commit is contained in:
牧濑红莉栖(BOT)
2026-08-10 17:24:01 +08:00
committed by Teknium
parent f45813ea77
commit 24eae1fa15
+11
View File
@@ -2370,6 +2370,17 @@ def create_openai_client(agent, client_kwargs: dict, *, reason: str, shared: boo
# copy locks the contract so future transport/keepalive work can't reintroduce
# the same class of bug.
client_kwargs = dict(client_kwargs)
# The MoA virtual provider has no real OpenAI wire endpoint - the facade
# *is* the client. Rebuilding a native OpenAI client while
# agent.provider == "moa" (client replacement, stream-retry pool cleanup,
# credential rotation, fallback+restore) drops the facade: the next primary
# call either raises a `_moa_prepared_request` TypeError (#78382) or, when
# _client_kwargs carry an unrelated relay base_url, leaks the request to a
# foreign gateway. Rebuild the facade instead (build_moa_facade also
# re-wires the reference relay, see #53802).
if (getattr(agent, "provider", "") or "").strip().lower() == "moa":
from agent.moa_loop import build_moa_facade
return build_moa_facade(agent, getattr(agent, "model", None) or "default")
ssl_ca_cert = client_kwargs.pop("ssl_ca_cert", None)
ssl_verify_cfg = client_kwargs.pop("ssl_verify", None)
httpx_verify = resolve_httpx_verify(ca_bundle=ssl_ca_cert, ssl_verify=ssl_verify_cfg)