From 2052967824cad09034c109c0fc70d8b7f03a90ed Mon Sep 17 00:00:00 2001 From: worldmozara Date: Wed, 15 Jul 2026 13:57:29 +0800 Subject: [PATCH] core/config: default DeepSeek models v4-flash and v4-pro When preset deepseek omits models, seed deepseek-v4-flash and deepseek-v4-pro as text-capable entries for selection and docs. --- CLAUDE.md | 2 +- README.md | 2 +- doc/plyngent.example.toml | 6 ++++-- src/plyngent/cli/editor.py | 5 +++++ src/plyngent/config/models.py | 9 +++++++++ tests/test_config/test_config.py | 18 ++++++++++++++++++ tests/test_runtime/test_client_factory.py | 2 ++ 7 files changed, 40 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 970a19e..6814d11 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -36,7 +36,7 @@ lmproto/deepseek/openai_compat/ ← Extends base via inheritance + extra fields - **`openai_compatible/model.py`** — tagged chat messages (`SystemChatMessage`, `UserChatMessage`, …), tools, request/response, streaming chunks. - **`openai_compatible/client.py`** — `BaseOpenAIClient` / `OpenAIClient` via `niquests` async + SSE. - **`openai_compatible/config.py`** — `OpenAIConfig` (token + base URL). -- **DeepSeek** — `DeepseekOpenAIClient`; models add `reasoning_content`, `prefix`, `ThinkingOptions`. +- **DeepSeek** — `DeepseekOpenAIClient`; models add `reasoning_content`, `prefix`, `ThinkingOptions`. Config default model ids: `deepseek-v4-flash`, `deepseek-v4-pro`. ### Config (`config/`) diff --git a/README.md b/README.md index 6599be6..ce050a0 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ confirm_destructive = true max_context_tokens = 200000 ``` -Supported provider presets today: `openai`, `openai-compatible`, `deepseek` (OpenAI convention). Anthropic presets are modeled in config but not wired in the runtime client yet. +Supported provider presets today: `openai`, `openai-compatible`, `deepseek` (OpenAI convention; default models `deepseek-v4-flash` and `deepseek-v4-pro` if `models` is omitted). Anthropic presets are modeled in config but not wired in the runtime client yet. If `[database]` is omitted (or SQLite `url` is empty/`":memory:"`), chat uses a durable file under the user data dir (e.g. `~/.local/share/plyngent/chat.db` on Linux). diff --git a/doc/plyngent.example.toml b/doc/plyngent.example.toml index 454a027..bf19a56 100644 --- a/doc/plyngent.example.toml +++ b/doc/plyngent.example.toml @@ -36,11 +36,13 @@ access_key_or_token = "sk-replace-me" # "gpt-4o-mini" = { text = true } # --- DeepSeek (OpenAI-compatible convention) --- +# Default models if [providers.*.models] is omitted: deepseek-v4-flash, deepseek-v4-pro. # [providers.deepseek] # preset = "deepseek" # access_key_or_token = "sk-replace-me" -# url = "https://api.deepseek.com" +# # url defaults to https://api.deepseek.com/v1 when empty # extras = { convention = "openai" } # # [providers.deepseek.models] -# "deepseek-chat" = { text = true } +# "deepseek-v4-flash" = { text = true } +# "deepseek-v4-pro" = { text = true } diff --git a/src/plyngent/cli/editor.py b/src/plyngent/cli/editor.py index 41d91ba..09293a7 100644 --- a/src/plyngent/cli/editor.py +++ b/src/plyngent/cli/editor.py @@ -38,6 +38,11 @@ _MINIMAL_CONFIG = """\ # # [providers.example.models] # "gpt-4o-mini" = { text = true } +# +# [providers.deepseek] +# preset = "deepseek" +# access_key_or_token = "sk-..." +# # models default to deepseek-v4-flash and deepseek-v4-pro if omitted """ diff --git a/src/plyngent/config/models.py b/src/plyngent/config/models.py index c8a03b6..4c1f407 100644 --- a/src/plyngent/config/models.py +++ b/src/plyngent/config/models.py @@ -58,9 +58,18 @@ class AnthropicProvider(ProviderConfig, tag="anthropic"): """Anthropic API provider.""" +def _default_deepseek_models() -> dict[str, ModelConfig]: + """Current DeepSeek text catalog when TOML omits ``models``.""" + return { + "deepseek-v4-flash": ModelConfig(text=True), + "deepseek-v4-pro": ModelConfig(text=True), + } + + class DeepseekProvider(ProviderConfig, tag="deepseek"): """Deepseek API provider with optional extras (e.g. convention).""" + models: dict[str, ModelConfig] = field(default_factory=_default_deepseek_models) extras: dict[str, str] = field(default_factory=dict) diff --git a/tests/test_config/test_config.py b/tests/test_config/test_config.py index 75d844b..ca10e3a 100644 --- a/tests/test_config/test_config.py +++ b/tests/test_config/test_config.py @@ -43,6 +43,9 @@ def test_read_valid_config() -> None: assert isinstance(providers["test2"], OpenAICompatibleProvider) assert isinstance(providers["test3"], AnthropicProvider) assert isinstance(providers["foo1"], DeepseekProvider) + # TOML omitted models → DeepSeek defaults. + assert set(providers["foo1"].models) == {"deepseek-v4-flash", "deepseek-v4-pro"} + assert providers["foo1"].models["deepseek-v4-flash"].text is True db = config.database assert db["implementation"] == "sqlite" assert db["url"] == ":memory:" @@ -50,6 +53,21 @@ def test_read_valid_config() -> None: assert db["password"] is None +def test_deepseek_default_models_on_construct() -> None: + provider = DeepseekProvider(access_key_or_token="sk-test") + assert set(provider.models) == {"deepseek-v4-flash", "deepseek-v4-pro"} + + +def test_deepseek_explicit_models_override_defaults() -> None: + from plyngent.config import ModelConfig + + provider = DeepseekProvider( + access_key_or_token="sk-test", + models={"custom-only": ModelConfig(text=True)}, + ) + assert set(provider.models) == {"custom-only"} + + def test_read_empty_config() -> None: config = plyngent.config.load(Path(__file__).parent / "plyngent-empty.toml") assert isinstance(config.providers, Mapping) diff --git a/tests/test_runtime/test_client_factory.py b/tests/test_runtime/test_client_factory.py index 2e02cc2..d0b56df 100644 --- a/tests/test_runtime/test_client_factory.py +++ b/tests/test_runtime/test_client_factory.py @@ -43,6 +43,8 @@ def test_deepseek_openai_convention() -> None: client = create_client(provider) assert isinstance(client, DeepseekOpenAIClient) assert provider_to_openai_config(provider).base_url == "https://api.deepseek.com/v1" + assert "deepseek-v4-flash" in provider.models + assert "deepseek-v4-pro" in provider.models def test_deepseek_anthropic_convention_not_implemented() -> None: