48 lines
1.4 KiB
Rust
48 lines
1.4 KiB
Rust
|
|
use std::env;
|
||
|
|
|
||
|
|
#[derive(Debug, Clone)]
|
||
|
|
pub struct Config {
|
||
|
|
pub api_key: String,
|
||
|
|
pub base_url: String,
|
||
|
|
pub model: String,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Config {
|
||
|
|
/// Resolve from environment. Prefers GROKBOY_*, then XAI_*, then OPENAI_*.
|
||
|
|
pub fn from_env() -> Result<Self, String> {
|
||
|
|
let api_key = env::var("GROKBOY_API_KEY")
|
||
|
|
.or_else(|_| env::var("XAI_API_KEY"))
|
||
|
|
.or_else(|_| env::var("OPENAI_API_KEY"))
|
||
|
|
.map_err(|_| {
|
||
|
|
"missing API key: set GROKBOY_API_KEY (or XAI_API_KEY / OPENAI_API_KEY)".to_string()
|
||
|
|
})?;
|
||
|
|
if api_key.trim().is_empty() {
|
||
|
|
return Err("API key is empty".into());
|
||
|
|
}
|
||
|
|
let base_url = env::var("GROKBOY_BASE_URL")
|
||
|
|
.or_else(|_| env::var("OPENAI_BASE_URL"))
|
||
|
|
.unwrap_or_else(|_| "https://api.x.ai/v1".into());
|
||
|
|
let model = env::var("GROKBOY_MODEL").unwrap_or_else(|_| "grok-4".into());
|
||
|
|
Ok(Self {
|
||
|
|
api_key,
|
||
|
|
base_url: base_url.trim_end_matches('/').to_string(),
|
||
|
|
model,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(test)]
|
||
|
|
mod tests {
|
||
|
|
use super::*;
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn rejects_missing_key() {
|
||
|
|
// Ensure we don't panic; actual env may have keys in CI — just type-check path.
|
||
|
|
let _ = Config {
|
||
|
|
api_key: "x".into(),
|
||
|
|
base_url: "https://api.x.ai/v1".into(),
|
||
|
|
model: "grok-4".into(),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|