systemprompt_cli/commands/admin/setup/
ai_config.rs1use std::path::Path;
15
16use anyhow::{Context, Result};
17use serde_yaml::Value;
18use systemprompt_identifiers::ProviderId;
19use systemprompt_logging::CliService;
20
21use super::secrets::SecretsData;
22use crate::CliConfig;
23use crate::commands::admin::config::config_section::{read_yaml_file, write_yaml_file};
24
25const STANDARD_PROVIDERS: [&str; 3] = ["gemini", "anthropic", "openai"];
26
27pub(super) fn reconcile(
28 project_root: &Path,
29 primary: &ProviderId,
30 secrets: &SecretsData,
31 config: &CliConfig,
32) -> Result<()> {
33 let path = project_root.join("services").join("ai").join("config.yaml");
34 if !path.exists() {
35 if !config.is_json_output() {
36 CliService::info(&format!(
37 "No {} — skipping AI default-provider reconcile",
38 path.display()
39 ));
40 }
41 return Ok(());
42 }
43
44 let mut doc = read_yaml_file(&path)?;
45 apply_ai_defaults(&mut doc, primary.as_str(), &secrets.present_providers())?;
46 write_yaml_file(&path, &doc)?;
47
48 if !config.is_json_output() {
49 CliService::success(&format!(
50 "Set AI default provider to '{}' in {}",
51 primary.as_str(),
52 path.display()
53 ));
54 }
55 Ok(())
56}
57
58pub fn apply_ai_defaults(doc: &mut Value, default_provider: &str, present: &[&str]) -> Result<()> {
59 let ai = doc
60 .get_mut("ai")
61 .and_then(Value::as_mapping_mut)
62 .context("services/ai/config.yaml has no 'ai' mapping")?;
63
64 ai.insert(
65 Value::String("default_provider".to_owned()),
66 Value::String(default_provider.to_owned()),
67 );
68
69 if let Some(providers) = ai
70 .get_mut(Value::String("providers".to_owned()))
71 .and_then(Value::as_mapping_mut)
72 {
73 for name in STANDARD_PROVIDERS {
74 if let Some(block) = providers
75 .get_mut(Value::String(name.to_owned()))
76 .and_then(Value::as_mapping_mut)
77 {
78 block.insert(
79 Value::String("enabled".to_owned()),
80 Value::Bool(present.contains(&name)),
81 );
82 }
83 }
84 }
85
86 Ok(())
87}