use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum LlmSource {
#[default]
Openrouter,
Bedrock,
#[serde(rename = "anthropic-api")]
AnthropicApi,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum LlmEffort {
Low,
Medium,
High,
Xhigh,
Max,
}
impl LlmEffort {
pub fn as_str(self) -> &'static str {
match self {
Self::Low => "low",
Self::Medium => "medium",
Self::High => "high",
Self::Xhigh => "xhigh",
Self::Max => "max",
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LlmFallbackScope {
#[default]
LowConfidence,
Unanswered,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LlmConfig {
#[serde(default)]
pub source: LlmSource,
#[serde(default = "default_api_key_env")]
pub api_key_env: String,
#[serde(default)]
pub region: Option<String>,
#[serde(default)]
pub model: Option<String>,
#[serde(default)]
pub effort: Option<LlmEffort>,
}
fn default_api_key_env() -> String {
"OPENROUTER_API_KEY".to_string()
}
impl Default for LlmConfig {
fn default() -> Self {
Self {
source: LlmSource::default(),
api_key_env: default_api_key_env(),
region: None,
model: None,
effort: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fallback_scope_and_effort_parse() {
let cfg: LlmConfig =
serde_yaml::from_str("source: anthropic-api\neffort: low\n").expect("parse");
assert_eq!(cfg.effort, Some(LlmEffort::Low));
assert_eq!(cfg.effort.map(LlmEffort::as_str), Some("low"));
assert!(serde_yaml::from_str::<LlmConfig>("effort: lowest\n").is_err());
let scope: LlmFallbackScope = serde_yaml::from_str("unanswered").expect("scope");
assert_eq!(scope, LlmFallbackScope::Unanswered);
assert_eq!(LlmFallbackScope::default(), LlmFallbackScope::LowConfidence);
assert!(serde_yaml::from_str::<LlmFallbackScope>("abstentions").is_err());
}
}