feat: v0.7.0 — AgentDef provider/model + install --provider/--model/--no-config : résolution flag > agent > défaut, erreur si provider inconnu (liste du registre), warning si modèle absent, résolution affichée en résumé et au --dry-run (closes #90)
- Providers réels livrés dans le catalogue par défaut (anthropic★/openai/deepseek/google) + hints provider sur claude-code/deepseek-harness - Suppression par null dans l'overlay : am providers remove écrit <nom>: null (merge traite null comme suppression, set_path remplace le null avant de descendre) - Cascade @secret (#89) : le provider déclaré de l'agent prime sur default_provider
This commit is contained in:
+32
-4
@@ -148,7 +148,7 @@ pub struct Settings {
|
||||
/// they live in the OS keyring under `providers/<name>/api_key`
|
||||
/// (issue #89).
|
||||
#[serde(default)]
|
||||
pub providers: Option<BTreeMap<String, ProviderDef>>,
|
||||
pub providers: Option<BTreeMap<String, Option<ProviderDef>>>,
|
||||
}
|
||||
|
||||
/// One entry of the LLM provider registry (issue #88).
|
||||
@@ -275,7 +275,10 @@ impl Settings {
|
||||
|
||||
/// 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))
|
||||
self.providers
|
||||
.as_ref()
|
||||
.and_then(|m| m.get(name))
|
||||
.and_then(|p| p.as_ref())
|
||||
}
|
||||
|
||||
/// Name of the default provider (issue #88).
|
||||
@@ -331,6 +334,14 @@ pub struct AgentDef {
|
||||
/// CLI flag carrying the model name at launch (issue #71, e.g. "--model").
|
||||
#[serde(default)]
|
||||
pub model_arg: Option<String>,
|
||||
/// Preferred provider of this agent (issue #90, e.g. "anthropic" for
|
||||
/// claude-code). Falls back to settings.default_provider when unset.
|
||||
#[serde(default)]
|
||||
pub provider: Option<String>,
|
||||
/// Preferred model for this agent (issue #90). Falls back to the
|
||||
/// provider's default_model when unset.
|
||||
#[serde(default)]
|
||||
pub model: Option<String>,
|
||||
#[serde(default)]
|
||||
pub tags: Vec<String>,
|
||||
/// When false, the agent is listed but cannot be installed locally.
|
||||
@@ -588,6 +599,10 @@ pub struct InstallSpec {
|
||||
/// Commands executed after a successful install (PATH includes the bin dir).
|
||||
#[serde(default)]
|
||||
pub post_install: Vec<String>,
|
||||
/// The agent accepts a post-install provider configuration (issue #91).
|
||||
/// False for agents without any API (issue #90).
|
||||
#[serde(default = "default_true")]
|
||||
pub configurable: bool,
|
||||
}
|
||||
|
||||
impl InstallSpec {
|
||||
@@ -815,12 +830,21 @@ pub fn merge(base: &mut Config, overlay: Config) {
|
||||
s.default_provider = o.default_provider;
|
||||
}
|
||||
// Providers merge key by key (issue #88): an overlay adds or replaces
|
||||
// one provider without wiping the others.
|
||||
// one provider without wiping the others. An explicit `null` in the
|
||||
// overlay DELETES the provider (the standard YAML overlay pattern) so a
|
||||
// provider shipped in the default catalog can be removed by the user.
|
||||
if let Some(overlay_providers) = o.providers {
|
||||
match s.providers.as_mut() {
|
||||
Some(map) => {
|
||||
for (k, v) in overlay_providers {
|
||||
map.insert(k, v);
|
||||
match v {
|
||||
Some(def) => {
|
||||
map.insert(k, Some(def));
|
||||
}
|
||||
None => {
|
||||
map.remove(&k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => s.providers = Some(overlay_providers),
|
||||
@@ -900,6 +924,10 @@ pub fn validate(config: &Config) -> Vec<String> {
|
||||
}
|
||||
if let Some(providers) = &config.settings.providers {
|
||||
for (name, p) in providers {
|
||||
// A null entry is a deletion marker (issue #90): skip it.
|
||||
let Some(p) = p else {
|
||||
continue;
|
||||
};
|
||||
if p.base_url.trim().is_empty() {
|
||||
problems.push(format!("provider '{name}': 'base_url' is required"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user