feat(providers): OpenAI-compatible provider types as runtime data (providers.yaml)

Replace the five copy-paste OpenAI-compatible provider structs (moonshot,
moonshot_code, deepseek, zai, lm_studio) with one DeclaredProvider engine
driven by a providers.yaml catalog loaded at boot from the cwd — edit and
restart, no rebuild. The YAML carries identity, endpoints, per-model JSON
field mapping, id-glob enrichment rules and the reasoning knob (effort /
thinking request kinds); capability_flags keep vision and future input
modalities declarative. Anthropic, Ollama, OpenAI and OpenRouter stay
native (different wire protocols or bespoke parsing) and register
alongside; colliding declared ids are skipped. The shipped catalog is
validated by a unit test.
This commit is contained in:
2026-07-18 16:19:05 +01:00
parent 760dae06e5
commit 4fea04c57f
11 changed files with 1005 additions and 543 deletions
+11 -5
View File
@@ -47,6 +47,7 @@ use crate::tts::TtsManager;
use tokio::sync::RwLock;
use core_api::plugin::Plugin;
use core_api::provider::ApiProvider;
use super::runtime::Runtime;
@@ -66,11 +67,16 @@ impl Models {
provider_registry.register_builtin(crate::llm::providers::anthropic::AnthropicProvider::new());
provider_registry.register_builtin(crate::llm::providers::openrouter::OpenRouterProvider::new());
provider_registry.register_builtin(crate::llm::providers::ollama::OllamaProvider::new());
provider_registry.register_builtin(crate::llm::providers::lm_studio::LmStudioProvider::new());
provider_registry.register_builtin(crate::llm::providers::deepseek::DeepSeekProvider::new());
provider_registry.register_builtin(crate::llm::providers::zai::ZaiProvider::new());
provider_registry.register_builtin(crate::llm::providers::moonshot::MoonshotProvider::new());
provider_registry.register_builtin(crate::llm::providers::moonshot::MoonshotCodeProvider::new());
// OpenAI-compatible providers are runtime data (providers.yaml), not code.
for p in crate::llm::providers::declared::load(std::path::Path::new(
crate::llm::providers::declared::PROVIDERS_FILE,
)) {
if provider_registry.contains(p.type_id()) {
warn!(type_id = p.type_id(), "declared provider id collides with a native provider — skipped");
continue;
}
provider_registry.register_builtin(p);
}
let provider_registry = Arc::new(provider_registry);
info!("provider registry ready ({} built-in providers)", provider_registry.all().len());