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