Jesús Pérez dd68d190ef ci: Update pre-commit hooks configuration
- Exclude problematic markdown files from linting (existing legacy issues)
- Make clippy check less aggressive (warnings only, not -D warnings)
- Move cargo test to manual stage (too slow for pre-commit)
- Exclude SVG files from end-of-file-fixer and trailing-whitespace
- Add markdown linting exclusions for existing documentation

This allows pre-commit hooks to run successfully on new code without
blocking commits due to existing issues in legacy documentation files.
2026-01-11 21:32:56 +00:00

152 lines
5.5 KiB
Rust

use crate::config::ProviderConfig;
use serde::{Deserialize, Serialize};
/// Provider cost and efficiency score for decision making.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProviderCostScore {
/// Provider name (claude, gpt4, gemini, ollama)
pub provider: String,
/// Estimated cost in cents for the token count
pub estimated_cost_cents: u32,
/// Quality score (0.0-1.0) from KG success rate for task type
pub quality_score: f64,
/// Cost efficiency: (quality_score * 100) / (cost_cents + 1)
/// Prevents division by zero for Ollama (free = $0)
pub cost_efficiency: f64,
}
/// Service for ranking providers by cost efficiency.
pub struct CostRanker;
impl CostRanker {
/// Estimate cost in cents for token usage on provider.
/// Formula: (input_tokens * rate_in + output_tokens * rate_out) / 1M * 100
/// Costs are stored in dollars, converted to cents for calculation.
pub fn estimate_cost(config: &ProviderConfig, input_tokens: u64, output_tokens: u64) -> u32 {
// Convert dollar rates to cents
let input_cost_cents = config.cost_per_1m_input * 100.0;
let output_cost_cents = config.cost_per_1m_output * 100.0;
let input_total = (input_tokens as f64 * input_cost_cents) / 1_000_000.0;
let output_total = (output_tokens as f64 * output_cost_cents) / 1_000_000.0;
(input_total + output_total).round() as u32
}
/// Get quality score for provider + task type.
/// In practice, queries KG for success rate. For now, uses provided value.
pub fn get_quality_score(provider: &str, task_type: &str, _quality_data: Option<f64>) -> f64 {
// Default quality scores until KG integration provides actual metrics
match (provider, task_type) {
("claude", _) => 0.95, // Highest quality
("gpt4", _) => 0.92, // Very good
("gemini", _) => 0.88, // Good
("ollama", _) => 0.75, // Decent for local
(_, _) => 0.5, // Unknown
}
}
/// Rank providers by cost efficiency.
/// Formula: efficiency = (quality_score * 100) / (cost_cents + 1)
/// Higher efficiency = better value for money.
/// Ordered by efficiency descending (best value first).
pub fn rank_by_efficiency(
providers: Vec<(String, ProviderConfig)>,
task_type: &str,
input_tokens: u64,
output_tokens: u64,
) -> Vec<ProviderCostScore> {
let mut scores: Vec<ProviderCostScore> = providers
.into_iter()
.map(|(provider_name, config)| {
let cost = Self::estimate_cost(&config, input_tokens, output_tokens);
let quality = Self::get_quality_score(&provider_name, task_type, None);
let efficiency = (quality * 100.0) / (cost as f64 + 1.0);
ProviderCostScore {
provider: provider_name,
estimated_cost_cents: cost,
quality_score: quality,
cost_efficiency: efficiency,
}
})
.collect();
// Sort by efficiency descending (best value first)
scores.sort_by(|a, b| {
b.cost_efficiency
.partial_cmp(&a.cost_efficiency)
.unwrap_or(std::cmp::Ordering::Equal)
});
scores
}
/// Select cheapest provider when budget is tight.
/// Orders by cost ascending (cheapest first).
pub fn rank_by_cost(
providers: Vec<(String, ProviderConfig)>,
input_tokens: u64,
output_tokens: u64,
) -> Vec<ProviderCostScore> {
let mut scores: Vec<ProviderCostScore> = providers
.into_iter()
.map(|(provider_name, config)| {
let cost = Self::estimate_cost(&config, input_tokens, output_tokens);
let quality = Self::get_quality_score(&provider_name, "generic", None);
let efficiency = (quality * 100.0) / (cost as f64 + 1.0);
ProviderCostScore {
provider: provider_name,
estimated_cost_cents: cost,
quality_score: quality,
cost_efficiency: efficiency,
}
})
.collect();
// Sort by cost ascending (cheapest first)
scores.sort_by_key(|s| s.estimated_cost_cents);
scores
}
/// Calculate cost-benefit ratio for task.
/// Returns tuple: (provider, cost_cents, efficiency_score)
pub fn cost_benefit_ratio(
providers: Vec<(String, ProviderConfig)>,
task_type: &str,
input_tokens: u64,
output_tokens: u64,
) -> Vec<(String, u32, f64)> {
let ranked = Self::rank_by_efficiency(providers, task_type, input_tokens, output_tokens);
ranked
.into_iter()
.map(|score| {
(
score.provider,
score.estimated_cost_cents,
score.cost_efficiency,
)
})
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_quality_score() {
let claude_quality = CostRanker::get_quality_score("claude", "coding", None);
assert_eq!(claude_quality, 0.95);
let ollama_quality = CostRanker::get_quality_score("ollama", "coding", None);
assert_eq!(ollama_quality, 0.75);
let unknown_quality = CostRanker::get_quality_score("unknown", "coding", None);
assert_eq!(unknown_quality, 0.5);
}
}