feat: add transport ABC + AnthropicTransport wired to all paths
Add ProviderTransport ABC (4 abstract methods: convert_messages,
convert_tools, build_kwargs, normalize_response) plus optional hooks
(validate_response, extract_cache_stats, map_finish_reason).
Add transport registry with lazy discovery — get_transport() auto-imports
transport modules on first call.
Add AnthropicTransport — delegates to existing anthropic_adapter.py
functions, wired to ALL Anthropic code paths in run_agent.py:
- Main normalize loop (L10775)
- Main build_kwargs (L6673)
- Response validation (L9366)
- Finish reason mapping (L9534)
- Cache stats extraction (L9827)
- Truncation normalize (L9565)
- Memory flush build_kwargs + normalize (L7363, L7395)
- Iteration-limit summary + retry (L8465, L8498)
Zero direct adapter imports remain for transport methods. Client lifecycle,
streaming, auth, and credential management stay on AIAgent.
20 new tests (ABC contract, registry, AnthropicTransport methods).
359 anthropic-related tests pass (0 failures).
PR 3 of the provider transport refactor.
2026-04-21 12:05:18 +05:30
|
|
|
"""Transport layer types and registry for provider response normalization.
|
|
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
|
from agent.transports import get_transport
|
|
|
|
|
transport = get_transport("anthropic_messages")
|
|
|
|
|
result = transport.normalize_response(raw_response)
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from agent.transports.types import NormalizedResponse, ToolCall, Usage, build_tool_call, map_finish_reason # noqa: F401
|
|
|
|
|
|
|
|
|
|
_REGISTRY: dict = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def register_transport(api_mode: str, transport_cls: type) -> None:
|
|
|
|
|
"""Register a transport class for an api_mode string."""
|
|
|
|
|
_REGISTRY[api_mode] = transport_cls
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_transport(api_mode: str):
|
|
|
|
|
"""Get a transport instance for the given api_mode.
|
|
|
|
|
|
|
|
|
|
Returns None if no transport is registered for this api_mode.
|
|
|
|
|
This allows gradual migration — call sites can check for None
|
|
|
|
|
and fall back to the legacy code path.
|
|
|
|
|
"""
|
|
|
|
|
if not _REGISTRY:
|
|
|
|
|
_discover_transports()
|
|
|
|
|
cls = _REGISTRY.get(api_mode)
|
|
|
|
|
if cls is None:
|
|
|
|
|
return None
|
|
|
|
|
return cls()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _discover_transports() -> None:
|
|
|
|
|
"""Import all transport modules to trigger auto-registration."""
|
|
|
|
|
try:
|
|
|
|
|
import agent.transports.anthropic # noqa: F401
|
|
|
|
|
except ImportError:
|
|
|
|
|
pass
|
2026-04-21 14:24:41 +05:30
|
|
|
try:
|
|
|
|
|
import agent.transports.codex # noqa: F401
|
|
|
|
|
except ImportError:
|
|
|
|
|
pass
|
feat: add ChatCompletionsTransport + wire all default paths
Third concrete transport — handles the default 'chat_completions' api_mode used
by ~16 OpenAI-compatible providers (OpenRouter, Nous, NVIDIA, Qwen, Ollama,
DeepSeek, xAI, Kimi, custom, etc.). Wires build_kwargs + validate_response to
production paths.
Based on PR #13447 by @kshitijk4poor, with fixes:
- Preserve tool_call.extra_content (Gemini thought_signature) via
ToolCall.provider_data — the original shim stripped it, causing 400 errors
on multi-turn Gemini 3 thinking requests.
- Preserve reasoning_content distinctly from reasoning (DeepSeek/Moonshot) so
the thinking-prefill retry check (_has_structured) still triggers.
- Port Kimi/Moonshot quirks (32000 max_tokens, top-level reasoning_effort,
extra_body.thinking) that landed on main after the original PR was opened.
- Keep _qwen_prepare_chat_messages_inplace alive and call it through the
transport when sanitization already deepcopied (avoids a second deepcopy).
- Skip the back-compat SimpleNamespace shim in the main normalize loop — for
chat_completions, response.choices[0].message is already the right shape
with .content/.tool_calls/.reasoning/.reasoning_content/.reasoning_details
and per-tool-call .extra_content from the OpenAI SDK.
run_agent.py: -239 lines in _build_api_kwargs default branch extracted to the
transport. build_kwargs now owns: codex-field sanitization, Qwen portal prep,
developer role swap, provider preferences, max_tokens resolution (ephemeral >
user > NVIDIA 16384 > Qwen 65536 > Kimi 32000 > anthropic_max_output), Kimi
reasoning_effort + extra_body.thinking, OpenRouter/Nous/GitHub reasoning,
Nous product attribution tags, Ollama num_ctx, custom-provider think=false,
Qwen vl_high_resolution_images, request_overrides.
39 new transport tests (8 build_kwargs, 5 Kimi, 4 validate, 4 normalize
including extra_content regression, 3 cache stats, 3 basic). Tests/run_agent/
targeted suite passes (885/885 + 15 skipped; the 1 remaining failure is the
test_concurrent_interrupt flake present on origin/main).
2026-04-21 20:48:20 -07:00
|
|
|
try:
|
|
|
|
|
import agent.transports.chat_completions # noqa: F401
|
|
|
|
|
except ImportError:
|
|
|
|
|
pass
|
2026-04-21 20:57:32 -07:00
|
|
|
try:
|
|
|
|
|
import agent.transports.bedrock # noqa: F401
|
|
|
|
|
except ImportError:
|
|
|
|
|
pass
|