use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Capability {
DeepThinking,
FastThinking,
CodeGeneration,
CodeReview,
Architecture,
Testing,
Refactoring,
Documentation,
Explanation,
SecurityAudit,
Performance,
}
impl Capability {
pub fn all() -> Vec<Capability> {
vec![
Capability::DeepThinking,
Capability::FastThinking,
Capability::CodeGeneration,
Capability::CodeReview,
Capability::Architecture,
Capability::Testing,
Capability::Refactoring,
Capability::Documentation,
Capability::Explanation,
Capability::SecurityAudit,
Capability::Performance,
]
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ProviderType {
Llm {
model_id: String,
api_endpoint: String,
},
Agent {
agent_id: String,
cli_command: String,
working_dir: PathBuf,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CostLevel {
Cheap,
Moderate,
Expensive,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Latency {
Fast,
Medium,
Slow,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Provider {
pub id: String,
pub name: String,
#[serde(rename = "type")]
pub provider_type: ProviderType,
pub capabilities: Vec<Capability>,
pub cost_level: CostLevel,
pub latency: Latency,
#[serde(default)]
pub keywords: Vec<String>,
}
impl Provider {
pub fn new(
id: impl Into<String>,
name: impl Into<String>,
provider_type: ProviderType,
capabilities: Vec<Capability>,
) -> Self {
Self {
id: id.into(),
name: name.into(),
provider_type,
capabilities,
cost_level: CostLevel::Moderate,
latency: Latency::Medium,
keywords: Vec::new(),
}
}
pub fn with_cost(mut self, cost: CostLevel) -> Self {
self.cost_level = cost;
self
}
pub fn with_latency(mut self, latency: Latency) -> Self {
self.latency = latency;
self
}
pub fn with_keywords(mut self, keywords: Vec<String>) -> Self {
self.keywords = keywords;
self
}
pub fn has_capability(&self, capability: &Capability) -> bool {
self.capabilities.contains(capability)
}
pub fn has_all_capabilities(&self, capabilities: &[Capability]) -> bool {
capabilities.iter().all(|c| self.has_capability(c))
}
pub fn matches_keywords(&self, text: &str) -> bool {
let text_lower = text.to_lowercase();
self.keywords
.iter()
.any(|k| text_lower.contains(&k.to_lowercase()))
}
pub fn capability_names(&self) -> Vec<String> {
self.capabilities
.iter()
.map(|c| format!("{:?}", c).to_lowercase())
.collect()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ProcessId(pub u64);
impl ProcessId {
pub fn new() -> Self {
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(1);
ProcessId(COUNTER.fetch_add(1, Ordering::SeqCst))
}
}
impl Default for ProcessId {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for ProcessId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_provider_creation() {
let provider = Provider::new(
"test-llm",
"Test LLM",
ProviderType::Llm {
model_id: "gpt-4".to_string(),
api_endpoint: "https://api.openai.com".to_string(),
},
vec![Capability::CodeGeneration, Capability::CodeReview],
)
.with_cost(CostLevel::Expensive)
.with_latency(Latency::Medium);
assert_eq!(provider.id, "test-llm");
assert!(provider.has_capability(&Capability::CodeGeneration));
assert!(!provider.has_capability(&Capability::DeepThinking));
assert_eq!(provider.cost_level, CostLevel::Expensive);
}
#[test]
fn test_agent_provider() {
let provider = Provider::new(
"@codex",
"Codex Agent",
ProviderType::Agent {
agent_id: "@codex".to_string(),
cli_command: "opencode".to_string(),
working_dir: PathBuf::from("/workspace"),
},
vec![Capability::CodeGeneration],
);
assert!(matches!(provider.provider_type, ProviderType::Agent { .. }));
}
#[test]
fn test_process_id_generation() {
let id1 = ProcessId::new();
let id2 = ProcessId::new();
assert_ne!(id1, id2);
assert!(id2.0 > id1.0);
}
}