mirror of
https://github.com/NCBM/plyngent.git
synced 2026-07-23 05:55:16 +08:00
core/config: default OpenAI preset and model catalog
Missing preset becomes openai; omitted models seed gpt-5.4 / mini / nano.
This commit is contained in:
@@ -58,7 +58,7 @@ confirm_destructive = true
|
|||||||
max_context_tokens = 200000
|
max_context_tokens = 200000
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
Supported provider presets today: `openai` (default if `preset` is omitted; default models `gpt-5.4` / `gpt-5.4-mini` / `gpt-5.4-nano` when `models` is omitted), `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).
|
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).
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ access_key_or_token = "sk-replace-me"
|
|||||||
"gpt-4o-mini" = { text = true, cost_factor = 1.0 }
|
"gpt-4o-mini" = { text = true, cost_factor = 1.0 }
|
||||||
|
|
||||||
# --- OpenAI platform (Responses API; tag is "openai") ---
|
# --- OpenAI platform (Responses API; tag is "openai") ---
|
||||||
|
# If ``preset`` is omitted, it defaults to "openai".
|
||||||
|
# If ``models`` is omitted, defaults: gpt-5.4, gpt-5.4-mini, gpt-5.4-nano.
|
||||||
# [providers.openai]
|
# [providers.openai]
|
||||||
# preset = "openai"
|
# preset = "openai"
|
||||||
# access_key_or_token = "sk-replace-me"
|
# access_key_or_token = "sk-replace-me"
|
||||||
|
|||||||
@@ -53,14 +53,27 @@ class ProviderConfig(Struct, tag_field="preset", omit_defaults=True):
|
|||||||
models: dict[str, ModelConfig] = field(default_factory=dict)
|
models: dict[str, ModelConfig] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
def _default_openai_models() -> dict[str, ModelConfig]:
|
||||||
|
"""Current OpenAI text catalog when TOML omits ``models``."""
|
||||||
|
return {
|
||||||
|
"gpt-5.4": ModelConfig(text=True),
|
||||||
|
"gpt-5.4-mini": ModelConfig(text=True),
|
||||||
|
"gpt-5.4-nano": ModelConfig(text=True),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class OpenAIProvider(ProviderConfig, tag="openai"):
|
class OpenAIProvider(ProviderConfig, tag="openai"):
|
||||||
"""OpenAI platform provider (agent uses Responses API).
|
"""OpenAI platform provider (agent uses Responses API).
|
||||||
|
|
||||||
``provider_tools`` are hosted/provider-side tools (e.g. web_search) passed
|
``provider_tools`` are hosted/provider-side tools (e.g. web_search) passed
|
||||||
through to ``POST /responses`` as opaque dicts. They are **not** local
|
through to ``POST /responses`` as opaque dicts. They are **not** local
|
||||||
``ToolRegistry`` handlers and are ignored by non-OpenAI clients.
|
``ToolRegistry`` handlers and are ignored by non-OpenAI clients.
|
||||||
|
|
||||||
|
When ``models`` is omitted in TOML, seeds a small default catalog (same idea
|
||||||
|
as DeepSeek). Explicit ``models = {}`` stays empty (recoverable / free-form).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
models: dict[str, ModelConfig] = field(default_factory=_default_openai_models)
|
||||||
provider_tools: list[dict[str, Any]] = field(default_factory=list)
|
provider_tools: list[dict[str, Any]] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -60,9 +60,14 @@ def _parse_providers(
|
|||||||
bad_providers[name] = raw_entry
|
bad_providers[name] = raw_entry
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
entry = cast("dict[str, object]", raw_entry)
|
||||||
|
# Missing preset → OpenAI platform (Responses-capable defaults).
|
||||||
|
if "preset" not in entry:
|
||||||
|
entry = {**entry, "preset": "openai"}
|
||||||
|
|
||||||
# Try tagged-union dispatch via msgspec
|
# Try tagged-union dispatch via msgspec
|
||||||
try:
|
try:
|
||||||
provider: Provider = msgspec.convert(raw_entry, Provider)
|
provider: Provider = msgspec.convert(entry, Provider)
|
||||||
except msgspec.ValidationError:
|
except msgspec.ValidationError:
|
||||||
bad_providers[name] = raw_entry
|
bad_providers[name] = raw_entry
|
||||||
continue
|
continue
|
||||||
@@ -72,12 +77,13 @@ def _parse_providers(
|
|||||||
cls = type(provider)
|
cls = type(provider)
|
||||||
known = set(cls.__struct_fields__)
|
known = set(cls.__struct_fields__)
|
||||||
known.add("preset") # tag_field, excluded from __struct_fields__
|
known.add("preset") # tag_field, excluded from __struct_fields__
|
||||||
if set(cast("dict[str, object]", raw_entry).keys()) - known:
|
# Validate against the entry we actually converted (may have injected preset).
|
||||||
|
if set(entry.keys()) - known:
|
||||||
bad_providers[name] = raw_entry
|
bad_providers[name] = raw_entry
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Empty models: keep as recoverable (DeepSeek seeds defaults when
|
# Empty models: recoverable. OpenAI/DeepSeek seed defaults when
|
||||||
# models is omitted, so only explicit models={} lands here).
|
# ``models`` is omitted; only explicit models={} lands here.
|
||||||
if not provider.models:
|
if not provider.models:
|
||||||
recoverable[name] = provider
|
recoverable[name] = provider
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -58,6 +58,27 @@ def test_deepseek_default_models_on_construct() -> None:
|
|||||||
assert set(provider.models) == {"deepseek-v4-flash", "deepseek-v4-pro"}
|
assert set(provider.models) == {"deepseek-v4-flash", "deepseek-v4-pro"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_openai_default_models_on_construct() -> None:
|
||||||
|
provider = OpenAIProvider(access_key_or_token="sk-test")
|
||||||
|
assert set(provider.models) == {"gpt-5.4", "gpt-5.4-mini", "gpt-5.4-nano"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_openai_omitted_preset_and_models_from_toml(tmp_path: Path) -> None:
|
||||||
|
path = tmp_path / "openai-defaults.toml"
|
||||||
|
_ = path.write_text(
|
||||||
|
"""
|
||||||
|
[providers.oai]
|
||||||
|
access_key_or_token = "sk-test"
|
||||||
|
""",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
config = plyngent.config.load(path)
|
||||||
|
assert "oai" in config.providers
|
||||||
|
provider = config.providers["oai"]
|
||||||
|
assert isinstance(provider, OpenAIProvider)
|
||||||
|
assert set(provider.models) == {"gpt-5.4", "gpt-5.4-mini", "gpt-5.4-nano"}
|
||||||
|
|
||||||
|
|
||||||
def test_deepseek_explicit_models_override_defaults() -> None:
|
def test_deepseek_explicit_models_override_defaults() -> None:
|
||||||
from plyngent.config import ModelConfig
|
from plyngent.config import ModelConfig
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user