use crate::config::ProfileConfig;
use crate::config::ProviderType;
use stakpak_shared::models::llm::ProviderConfig;
pub const DEFAULT_MODEL: &str = "claude-opus-4-6";
pub fn generate_openai_profile() -> ProfileConfig {
let mut profile = ProfileConfig {
provider: Some(ProviderType::Local),
model: Some("gpt-4.1".to_string()),
..ProfileConfig::default()
};
profile.providers.insert(
"openai".to_string(),
ProviderConfig::OpenAI {
api_key: None,
api_endpoint: None,
auth: None,
},
);
profile
}
pub fn generate_gemini_profile() -> ProfileConfig {
let mut profile = ProfileConfig {
provider: Some(ProviderType::Local),
model: Some("gemini-2.5-pro".to_string()),
..ProfileConfig::default()
};
profile.providers.insert(
"gemini".to_string(),
ProviderConfig::Gemini {
api_key: None,
api_endpoint: None,
auth: None,
},
);
profile
}
pub fn generate_github_copilot_profile() -> ProfileConfig {
let mut profile = ProfileConfig {
provider: Some(ProviderType::Local),
model: Some("github-copilot/gpt-4o".to_string()),
..ProfileConfig::default()
};
profile.providers.insert(
"github-copilot".to_string(),
ProviderConfig::GitHubCopilot {
api_endpoint: None,
auth: None,
},
);
profile
}
pub fn generate_anthropic_profile() -> ProfileConfig {
let mut profile = ProfileConfig {
provider: Some(ProviderType::Local),
model: Some(DEFAULT_MODEL.to_string()),
..ProfileConfig::default()
};
profile.providers.insert(
"anthropic".to_string(),
ProviderConfig::Anthropic {
api_key: None,
api_endpoint: None,
access_token: None,
auth: None,
},
);
profile
}
pub fn generate_custom_provider_profile(
provider_name: String,
api_endpoint: String,
api_key: Option<String>,
model_name: String,
) -> ProfileConfig {
let mut profile = ProfileConfig {
provider: Some(ProviderType::Local),
model: Some(format!("{}/{}", provider_name, model_name)),
..ProfileConfig::default()
};
profile.providers.insert(
provider_name,
ProviderConfig::Custom {
api_key,
api_endpoint,
auth: None,
},
);
profile
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BuiltinProvider {
OpenAI,
Gemini,
Anthropic,
}
impl BuiltinProvider {
pub fn display_name(&self) -> &'static str {
match self {
BuiltinProvider::OpenAI => "OpenAI",
BuiltinProvider::Gemini => "Gemini",
BuiltinProvider::Anthropic => "Anthropic",
}
}
pub fn default_model(&self) -> &'static str {
match self {
BuiltinProvider::OpenAI => "gpt-4.1",
BuiltinProvider::Gemini => "gemini-2.5-pro",
BuiltinProvider::Anthropic => DEFAULT_MODEL,
}
}
}
#[derive(Debug, Clone)]
pub struct ProviderSetup {
pub provider: BuiltinProvider,
pub api_key: String,
}
pub fn generate_multi_provider_profile(
providers: Vec<ProviderSetup>,
default_model: String,
) -> ProfileConfig {
let mut profile = ProfileConfig {
provider: Some(ProviderType::Local),
model: Some(default_model),
..ProfileConfig::default()
};
for setup in providers {
match setup.provider {
BuiltinProvider::OpenAI => {
profile.providers.insert(
"openai".to_string(),
ProviderConfig::OpenAI {
api_key: Some(setup.api_key),
api_endpoint: None,
auth: None,
},
);
}
BuiltinProvider::Gemini => {
profile.providers.insert(
"gemini".to_string(),
ProviderConfig::Gemini {
api_key: Some(setup.api_key),
api_endpoint: None,
auth: None,
},
);
}
BuiltinProvider::Anthropic => {
profile.providers.insert(
"anthropic".to_string(),
ProviderConfig::Anthropic {
api_key: Some(setup.api_key),
api_endpoint: None,
access_token: None,
auth: None,
},
);
}
}
}
profile
}
pub fn config_to_toml_preview(profile: &ProfileConfig, profile_name: &str) -> String {
let mut toml = format!("[profiles.{}]\n", profile_name);
if let Some(provider) = &profile.provider {
toml.push_str(&format!(
"provider = \"{}\"\n",
match provider {
ProviderType::Remote => "remote",
ProviderType::Local => "local",
}
));
}
if let Some(ref model) = profile.model {
toml.push_str(&format!("model = \"{}\"\n", model));
}
for (name, config) in &profile.providers {
toml.push_str(&format!(
"\n[profiles.{}.providers.{}]\n",
profile_name, name
));
match config {
ProviderConfig::OpenAI {
api_key,
api_endpoint,
..
} => {
toml.push_str("type = \"openai\"\n");
if let Some(endpoint) = api_endpoint {
toml.push_str(&format!("api_endpoint = \"{}\"\n", endpoint));
}
if let Some(key) = api_key {
toml.push_str(&format!(
"api_key = \"{}\"\n",
if key.is_empty() { "" } else { "***" }
));
}
}
ProviderConfig::Anthropic {
api_key,
api_endpoint,
access_token,
..
} => {
toml.push_str("type = \"anthropic\"\n");
if let Some(endpoint) = api_endpoint {
toml.push_str(&format!("api_endpoint = \"{}\"\n", endpoint));
}
if let Some(key) = api_key {
toml.push_str(&format!(
"api_key = \"{}\"\n",
if key.is_empty() { "" } else { "***" }
));
}
if let Some(token) = access_token {
toml.push_str(&format!(
"access_token = \"{}\"\n",
if token.is_empty() { "" } else { "***" }
));
}
}
ProviderConfig::Gemini {
api_key,
api_endpoint,
..
} => {
toml.push_str("type = \"gemini\"\n");
if let Some(endpoint) = api_endpoint {
toml.push_str(&format!("api_endpoint = \"{}\"\n", endpoint));
}
if let Some(key) = api_key {
toml.push_str(&format!(
"api_key = \"{}\"\n",
if key.is_empty() { "" } else { "***" }
));
}
}
ProviderConfig::Custom {
api_key,
api_endpoint,
..
} => {
toml.push_str("type = \"custom\"\n");
toml.push_str(&format!("api_endpoint = \"{}\"\n", api_endpoint));
if let Some(key) = api_key {
toml.push_str(&format!(
"api_key = \"{}\"\n",
if key.is_empty() { "" } else { "***" }
));
}
}
ProviderConfig::Stakpak {
api_key,
api_endpoint,
..
} => {
toml.push_str("type = \"stakpak\"\n");
if let Some(key) = api_key {
toml.push_str(&format!(
"api_key = \"{}\"\n",
if key.is_empty() { "" } else { "***" }
));
}
if let Some(endpoint) = api_endpoint {
toml.push_str(&format!("api_endpoint = \"{}\"\n", endpoint));
}
}
ProviderConfig::Bedrock {
region,
profile_name,
} => {
toml.push_str("type = \"amazon-bedrock\"\n");
toml.push_str(&format!("region = \"{}\"\n", region));
if let Some(profile) = profile_name {
toml.push_str(&format!("profile_name = \"{}\"\n", profile));
}
}
ProviderConfig::GitHubCopilot { api_endpoint, auth } => {
toml.push_str("type = \"github-copilot\"\n");
if let Some(endpoint) = api_endpoint {
toml.push_str(&format!("api_endpoint = \"{}\"\n", endpoint));
}
if let Some(a) = auth {
toml.push_str(&format!("# auth: {} (set)\n", a.auth_type_display()));
}
}
}
}
toml
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_custom_provider_profile() {
let profile = generate_custom_provider_profile(
"litellm".to_string(),
"http://localhost:4000".to_string(),
Some("sk-1234".to_string()),
"claude-opus".to_string(),
);
assert!(matches!(profile.provider, Some(ProviderType::Local)));
assert_eq!(profile.model, Some("litellm/claude-opus".to_string()));
let provider = profile
.providers
.get("litellm")
.expect("litellm provider should exist");
match provider {
ProviderConfig::Custom {
api_key,
api_endpoint,
..
} => {
assert_eq!(api_endpoint, "http://localhost:4000");
assert_eq!(api_key, &Some("sk-1234".to_string()));
}
_ => panic!("Expected Custom provider"),
}
}
#[test]
fn test_generate_custom_provider_profile_without_api_key() {
let profile = generate_custom_provider_profile(
"ollama".to_string(),
"http://localhost:11434/v1".to_string(),
None,
"llama3".to_string(),
);
assert!(matches!(profile.provider, Some(ProviderType::Local)));
assert_eq!(profile.model, Some("ollama/llama3".to_string()));
let provider = profile
.providers
.get("ollama")
.expect("ollama provider should exist");
match provider {
ProviderConfig::Custom {
api_key,
api_endpoint,
..
} => {
assert_eq!(api_endpoint, "http://localhost:11434/v1");
assert!(api_key.is_none());
}
_ => panic!("Expected Custom provider"),
}
}
#[test]
fn test_config_to_toml_preview_with_custom_provider() {
let mut profile = ProfileConfig {
provider: Some(ProviderType::Local),
model: Some("litellm/claude-opus".to_string()),
..ProfileConfig::default()
};
profile.providers.insert(
"litellm".to_string(),
ProviderConfig::Custom {
api_endpoint: "http://localhost:4000".to_string(),
api_key: Some("sk-1234".to_string()),
auth: None,
},
);
let toml = config_to_toml_preview(&profile, "default");
assert!(toml.contains("provider = \"local\""));
assert!(toml.contains("model = \"litellm/claude-opus\""));
assert!(toml.contains("[profiles.default.providers.litellm]"));
assert!(toml.contains("type = \"custom\""));
assert!(toml.contains("api_endpoint = \"http://localhost:4000\""));
assert!(toml.contains("api_key = \"***\"")); }
#[test]
fn test_config_to_toml_preview_custom_provider_no_api_key() {
let mut profile = ProfileConfig {
provider: Some(ProviderType::Local),
model: Some("ollama/llama3".to_string()),
..ProfileConfig::default()
};
profile.providers.insert(
"ollama".to_string(),
ProviderConfig::Custom {
api_endpoint: "http://localhost:11434/v1".to_string(),
api_key: None,
auth: None,
},
);
let toml = config_to_toml_preview(&profile, "default");
assert!(toml.contains("[profiles.default.providers.ollama]"));
assert!(toml.contains("type = \"custom\""));
assert!(toml.contains("api_endpoint = \"http://localhost:11434/v1\""));
let lines: Vec<&str> = toml.lines().collect();
let has_api_key_in_ollama_section = lines
.iter()
.skip_while(|l| !l.contains("providers.ollama"))
.take_while(|l| !l.starts_with('[') || l.contains("providers.ollama"))
.any(|l| l.contains("api_key"));
assert!(!has_api_key_in_ollama_section);
}
#[test]
fn test_generate_multi_provider_profile() {
let providers = vec![
ProviderSetup {
provider: BuiltinProvider::Anthropic,
api_key: "sk-ant-xxx".to_string(),
},
ProviderSetup {
provider: BuiltinProvider::OpenAI,
api_key: "sk-xxx".to_string(),
},
];
let profile = generate_multi_provider_profile(providers, DEFAULT_MODEL.to_string());
assert!(matches!(profile.provider, Some(ProviderType::Local)));
assert_eq!(profile.model, Some(DEFAULT_MODEL.to_string()));
assert!(profile.providers.contains_key("anthropic"));
assert!(profile.providers.contains_key("openai"));
}
}