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<()> {
61 let ai = doc
62 .get_mut("ai")
63 .and_then(Value::as_mapping_mut)
64 .context("services/ai/config.yaml has no 'ai' mapping")?;
65
66 ai.insert(
67 Value::String("default_provider".to_owned()),
68 Value::String(default_provider.to_owned()),
69 );
70
71 if let Some(providers) = ai
72 .get_mut(Value::String("providers".to_owned()))
73 .and_then(Value::as_mapping_mut)
74 {
75 for name in STANDARD_PROVIDERS {
76 if let Some(block) = providers
77 .get_mut(Value::String(name.to_owned()))
78 .and_then(Value::as_mapping_mut)
79 {
80 block.insert(
81 Value::String("enabled".to_owned()),
82 Value::Bool(present.contains(&name)),
83 );
84 }
85 }
86 }
87
88 Ok(())
89}