feat: v0.7.0 — registre de providers LLM : am providers list/show/add/remove/default, settings.providers + default_provider (closes #88)
This commit is contained in:
@@ -139,6 +139,30 @@ pub struct Settings {
|
||||
/// Plugin scripts (issue #75): default timeout and enable list.
|
||||
#[serde(default)]
|
||||
pub plugins: Option<PluginSettings>,
|
||||
/// Name of the provider used by default (issue #88) — the provider
|
||||
/// whose default model is applied at install/run when none is given.
|
||||
#[serde(default)]
|
||||
pub default_provider: Option<String>,
|
||||
/// LLM provider registry (issue #88): provider name -> base URL,
|
||||
/// available models and default model. API tokens are NOT stored here —
|
||||
/// they live in the OS keyring under `providers/<name>/api_key`
|
||||
/// (issue #89).
|
||||
#[serde(default)]
|
||||
pub providers: Option<BTreeMap<String, ProviderDef>>,
|
||||
}
|
||||
|
||||
/// One entry of the LLM provider registry (issue #88).
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ProviderDef {
|
||||
/// Base URL of the provider API (e.g. https://api.openai.com/v1).
|
||||
pub base_url: String,
|
||||
/// Model used by default for this provider.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub default_model: Option<String>,
|
||||
/// Models offered by this provider.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub models: Vec<String>,
|
||||
}
|
||||
|
||||
/// Plugin scripts settings (issue #75): how long one plugin may run and
|
||||
@@ -248,6 +272,24 @@ impl Settings {
|
||||
self.stop_timeout_secs
|
||||
}
|
||||
}
|
||||
|
||||
/// Look up one provider of the registry (issue #88).
|
||||
pub fn provider(&self, name: &str) -> Option<&ProviderDef> {
|
||||
self.providers.as_ref().and_then(|m| m.get(name))
|
||||
}
|
||||
|
||||
/// Name of the default provider (issue #88).
|
||||
pub fn default_provider_name(&self) -> Option<&str> {
|
||||
self.default_provider.as_deref()
|
||||
}
|
||||
|
||||
/// The default provider entry, when both the name and the definition
|
||||
/// exist (issue #88).
|
||||
pub fn default_provider(&self) -> Option<(&String, &ProviderDef)> {
|
||||
let name = self.default_provider.as_ref()?;
|
||||
let def = self.provider(name)?;
|
||||
Some((name, def))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -769,6 +811,21 @@ pub fn merge(base: &mut Config, overlay: Config) {
|
||||
if o.plugins.is_some() {
|
||||
s.plugins = o.plugins;
|
||||
}
|
||||
if o.default_provider.is_some() {
|
||||
s.default_provider = o.default_provider;
|
||||
}
|
||||
// Providers merge key by key (issue #88): an overlay adds or replaces
|
||||
// one provider without wiping the others.
|
||||
if let Some(overlay_providers) = o.providers {
|
||||
match s.providers.as_mut() {
|
||||
Some(map) => {
|
||||
for (k, v) in overlay_providers {
|
||||
map.insert(k, v);
|
||||
}
|
||||
}
|
||||
None => s.providers = Some(overlay_providers),
|
||||
}
|
||||
}
|
||||
for (k, v) in o.hooks {
|
||||
s.hooks.insert(k, v);
|
||||
}
|
||||
@@ -834,6 +891,28 @@ pub fn validate(config: &Config) -> Vec<String> {
|
||||
problems.push(format!("agent '{}': dependency without a name", agent.name));
|
||||
}
|
||||
}
|
||||
if let Some(d) = &config.settings.default_provider {
|
||||
if config.settings.provider(d).is_none() {
|
||||
problems.push(format!(
|
||||
"settings.default_provider '{d}' is not defined in settings.providers"
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(providers) = &config.settings.providers {
|
||||
for (name, p) in providers {
|
||||
if p.base_url.trim().is_empty() {
|
||||
problems.push(format!("provider '{name}': 'base_url' is required"));
|
||||
}
|
||||
if !name
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-' || c == '_')
|
||||
{
|
||||
problems.push(format!(
|
||||
"provider name '{name}' must be a lowercase slug ([a-z0-9_-])"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
problems
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user