mirror of
https://github.com/NCBM/plyngent.git
synced 2026-11-08 16:37:55 +08:00
runtime: route DeepSeek convention=responses to Responses client
This commit is contained in:
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING
|
|||||||
from plyngent.config.routing import EffectiveProvider, resolve_effective_provider
|
from plyngent.config.routing import EffectiveProvider, resolve_effective_provider
|
||||||
from plyngent.lmproto.anthropic import AnthropicClient
|
from plyngent.lmproto.anthropic import AnthropicClient
|
||||||
from plyngent.lmproto.anthropic.config import AnthropicConfig as AnthropicConfigCls
|
from plyngent.lmproto.anthropic.config import AnthropicConfig as AnthropicConfigCls
|
||||||
from plyngent.lmproto.deepseek import DeepseekOpenAIClient
|
from plyngent.lmproto.deepseek import DeepseekOpenAIClient, DeepseekResponsesClient
|
||||||
from plyngent.lmproto.openai import OpenAIClient
|
from plyngent.lmproto.openai import OpenAIClient
|
||||||
from plyngent.lmproto.openai_compatible import OpenAICompatibleClient, OpenAIConfig
|
from plyngent.lmproto.openai_compatible import OpenAICompatibleClient, OpenAIConfig
|
||||||
from plyngent.lmproto.openai_compatible.config import (
|
from plyngent.lmproto.openai_compatible.config import (
|
||||||
@@ -22,7 +22,9 @@ DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1"
|
|||||||
DEFAULT_ANTHROPIC_BASE_URL = "https://api.anthropic.com/v1"
|
DEFAULT_ANTHROPIC_BASE_URL = "https://api.anthropic.com/v1"
|
||||||
DEFAULT_DEEPSEEK_BASE_URL = "https://api.deepseek.com"
|
DEFAULT_DEEPSEEK_BASE_URL = "https://api.deepseek.com"
|
||||||
|
|
||||||
type ProtocolClient = OpenAIClient | OpenAICompatibleClient | DeepseekOpenAIClient | AnthropicClient
|
type ProtocolClient = (
|
||||||
|
OpenAIClient | OpenAICompatibleClient | DeepseekOpenAIClient | DeepseekResponsesClient | AnthropicClient
|
||||||
|
)
|
||||||
# Backward-compatible name used by older imports/tests.
|
# Backward-compatible name used by older imports/tests.
|
||||||
type OpenAICompatibleClientUnion = ProtocolClient
|
type OpenAICompatibleClientUnion = ProtocolClient
|
||||||
|
|
||||||
@@ -97,6 +99,8 @@ def create_client(provider: Provider, *, model: str | None = None) -> ProtocolCl
|
|||||||
|
|
||||||
``preset`` always decides API conventions: ``openai`` → /responses,
|
``preset`` always decides API conventions: ``openai`` → /responses,
|
||||||
``openai-compatible`` → /chat/completions, ``anthropic`` → /messages.
|
``openai-compatible`` → /chat/completions, ``anthropic`` → /messages.
|
||||||
|
Exception: a ``deepseek`` preset picks the Responses surface when its
|
||||||
|
effective ``convention`` is ``"responses"``, else chat completions.
|
||||||
"""
|
"""
|
||||||
effective = resolve_effective_provider(provider, model=model)
|
effective = resolve_effective_provider(provider, model=model)
|
||||||
if effective.preset == "openai":
|
if effective.preset == "openai":
|
||||||
@@ -104,6 +108,8 @@ def create_client(provider: Provider, *, model: str | None = None) -> ProtocolCl
|
|||||||
if effective.preset == "openai-compatible":
|
if effective.preset == "openai-compatible":
|
||||||
return OpenAICompatibleClient(provider_to_openai_config(effective))
|
return OpenAICompatibleClient(provider_to_openai_config(effective))
|
||||||
if effective.preset == "deepseek":
|
if effective.preset == "deepseek":
|
||||||
|
if effective.convention == "responses":
|
||||||
|
return DeepseekResponsesClient(provider_to_openai_config(effective))
|
||||||
return DeepseekOpenAIClient(provider_to_openai_config(effective))
|
return DeepseekOpenAIClient(provider_to_openai_config(effective))
|
||||||
if effective.preset == "anthropic":
|
if effective.preset == "anthropic":
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -73,6 +73,43 @@ def test_deepseek_ignores_legacy_convention_extra() -> None:
|
|||||||
assert isinstance(client, DeepseekOpenAIClient)
|
assert isinstance(client, DeepseekOpenAIClient)
|
||||||
|
|
||||||
|
|
||||||
|
def test_deepseek_responses_convention() -> None:
|
||||||
|
from plyngent.lmproto.deepseek import DeepseekResponsesClient
|
||||||
|
|
||||||
|
provider = DeepseekProvider(access_key_or_token="sk-test", convention="responses")
|
||||||
|
client = create_client(provider)
|
||||||
|
assert isinstance(client, DeepseekResponsesClient)
|
||||||
|
assert client.kind == "responses"
|
||||||
|
assert provider_to_openai_config(provider).base_url == "https://api.deepseek.com"
|
||||||
|
|
||||||
|
|
||||||
|
def test_deepseek_default_and_legacy_conventions_stay_chat() -> None:
|
||||||
|
from plyngent.lmproto.deepseek import DeepseekResponsesClient
|
||||||
|
|
||||||
|
default_client = create_client(DeepseekProvider(access_key_or_token="sk-test"))
|
||||||
|
assert isinstance(default_client, DeepseekOpenAIClient)
|
||||||
|
assert not isinstance(default_client, DeepseekResponsesClient)
|
||||||
|
legacy = DeepseekProvider(access_key_or_token="sk-test", convention="anthropic")
|
||||||
|
assert isinstance(create_client(legacy), DeepseekOpenAIClient)
|
||||||
|
|
||||||
|
|
||||||
|
def test_deepseek_model_level_convention_override() -> None:
|
||||||
|
from plyngent.lmproto.deepseek import DeepseekResponsesClient
|
||||||
|
|
||||||
|
provider = DeepseekProvider(
|
||||||
|
access_key_or_token="sk-test",
|
||||||
|
models={
|
||||||
|
"deepseek-v4-flash": ModelConfig(convention="responses"),
|
||||||
|
"deepseek-v4-pro": ModelConfig(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
flash = create_client(provider, model="deepseek-v4-flash")
|
||||||
|
assert isinstance(flash, DeepseekResponsesClient)
|
||||||
|
assert flash.kind == "responses"
|
||||||
|
pro = create_client(provider, model="deepseek-v4-pro")
|
||||||
|
assert isinstance(pro, DeepseekOpenAIClient)
|
||||||
|
|
||||||
|
|
||||||
def test_anthropic_client_created() -> None:
|
def test_anthropic_client_created() -> None:
|
||||||
from plyngent.lmproto.anthropic import AnthropicClient
|
from plyngent.lmproto.anthropic import AnthropicClient
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user