runtime: route DeepSeek convention=anthropic to Anthropic client

This commit is contained in:
2026-08-01 02:31:21 +08:00
parent 439c24a1ce
commit a198fa9d41
4 changed files with 94 additions and 19 deletions
+3 -1
View File
@@ -1,7 +1,9 @@
"""Convert agent chat history/tools to Anthropic Messages API shapes and back.
Agent memory and events stay chat-completions-shaped; only the transport uses
``POST /messages``. OpenAI / DeepSeek paths never enter this module.
``POST /messages``. Real Anthropic and the DeepSeek ``convention="anthropic"``
surface both route through this module; OpenAI / openai-compatible paths never
enter it.
"""
from __future__ import annotations
+3 -1
View File
@@ -1,7 +1,9 @@
"""Anthropic Messages dispatch: present ``/messages`` as chat completions to the loop.
Agent memory and events stay chat-completions-shaped; only the transport uses
``POST /messages``. OpenAI / DeepSeek paths never enter this module.
``POST /messages``. Real Anthropic and the DeepSeek ``convention="anthropic"``
surface both route through this module; OpenAI / openai-compatible paths never
enter it.
"""
from __future__ import annotations
+40 -15
View File
@@ -6,7 +6,11 @@ from typing import TYPE_CHECKING
from plyngent.config.routing import EffectiveProvider, resolve_effective_provider
from plyngent.lmproto.anthropic import AnthropicClient
from plyngent.lmproto.anthropic.config import AnthropicConfig as AnthropicConfigCls
from plyngent.lmproto.deepseek import DeepseekOpenAIClient, DeepseekResponsesClient
from plyngent.lmproto.deepseek import (
DeepseekAnthropicClient,
DeepseekOpenAIClient,
DeepseekResponsesClient,
)
from plyngent.lmproto.openai import OpenAIClient
from plyngent.lmproto.openai_compatible import OpenAICompatibleClient, OpenAIConfig
from plyngent.lmproto.openai_compatible.config import (
@@ -23,7 +27,12 @@ DEFAULT_ANTHROPIC_BASE_URL = "https://api.anthropic.com/v1"
DEFAULT_DEEPSEEK_BASE_URL = "https://api.deepseek.com"
type ProtocolClient = (
OpenAIClient | OpenAICompatibleClient | DeepseekOpenAIClient | DeepseekResponsesClient | AnthropicClient
OpenAIClient
| OpenAICompatibleClient
| DeepseekOpenAIClient
| DeepseekResponsesClient
| DeepseekAnthropicClient
| AnthropicClient
)
# Backward-compatible name used by older imports/tests.
type OpenAICompatibleClientUnion = ProtocolClient
@@ -94,13 +103,37 @@ def provider_to_openai_config(
)
def _anthropic_client(effective: EffectiveProvider) -> AnthropicClient:
"""Build an Anthropic Messages client (real or DeepSeek convention) for *effective*."""
try:
http_timeout = normalize_http_timeout(effective.timeout)
except InvalidHttpTimeoutError as exc:
raise ProviderNotSupportedError(str(exc)) from exc
if effective.preset == "deepseek":
return DeepseekAnthropicClient(
AnthropicConfigCls(
api_key=effective.access_key_or_token,
base_url=effective.url or DEFAULT_DEEPSEEK_BASE_URL,
timeout=http_timeout,
)
)
return AnthropicClient(
AnthropicConfigCls(
api_key=effective.access_key_or_token,
base_url=effective.url or DEFAULT_ANTHROPIC_BASE_URL,
timeout=http_timeout,
)
)
def create_client(provider: Provider, *, model: str | None = None) -> ProtocolClient:
"""Build a protocol client for *provider*, applying model-level routing.
``preset`` always decides API conventions: ``openai`` → /responses,
``openai-compatible`` → /chat/completions, ``anthropic`` → /messages.
Exception: a ``deepseek`` preset picks the Responses surface when its
effective ``convention`` is ``"responses"``, else chat completions.
Exception: a ``deepseek`` preset picks the Responses or Anthropic surface
when its effective ``convention`` is ``"responses"`` / ``"anthropic"``,
else chat completions.
"""
effective = resolve_effective_provider(provider, model=model)
if effective.preset == "openai":
@@ -110,18 +143,10 @@ def create_client(provider: Provider, *, model: str | None = None) -> ProtocolCl
if effective.preset == "deepseek":
if effective.convention == "responses":
return DeepseekResponsesClient(provider_to_openai_config(effective))
if effective.convention == "anthropic":
return _anthropic_client(effective)
return DeepseekOpenAIClient(provider_to_openai_config(effective))
if effective.preset == "anthropic":
try:
http_timeout = normalize_http_timeout(effective.timeout)
except InvalidHttpTimeoutError as exc:
raise ProviderNotSupportedError(str(exc)) from exc
return AnthropicClient(
AnthropicConfigCls(
api_key=effective.access_key_or_token,
base_url=effective.url or DEFAULT_ANTHROPIC_BASE_URL,
timeout=http_timeout,
)
)
return _anthropic_client(effective)
msg = f"provider preset {effective.preset!r} is not supported"
raise ProviderNotSupportedError(msg)