You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

55 lines
1.9 KiB
Rust

//! LLM provider abstraction layer.
//!
//! Defines the `LlmProvider` trait that all LLM providers implement,
//! along with shared types and the provider factory function.
pub mod anthropic;
pub mod factory;
pub mod gemini;
pub mod openai;
pub mod schema;
use async_trait::async_trait;
use serde_json::Value;
use crate::errors::AppError;
/// Trait defining the contract for LLM provider implementations.
///
/// Each provider (Gemini, OpenAI, Anthropic) implements this trait
/// to provide a unified interface for structured LLM calls.
#[async_trait]
pub trait LlmProvider: Send + Sync {
/// Returns the provider identifier (e.g., "gemini", "openai", "anthropic").
fn provider_id(&self) -> &str;
/// Call the LLM with a prompt and expected JSON schema.
///
/// # Arguments
/// * `model` — The model identifier (e.g., "gpt-4o-mini")
/// * `system_prompt` — System-level instructions
/// * `user_prompt` — The user's prompt
/// * `response_schema` — JSON Schema defining the expected response structure
async fn call_llm(
&self,
model: &str,
system_prompt: &str,
user_prompt: &str,
response_schema: &Value,
) -> Result<Value, AppError>;
}
/// Shared HTTP error mapping for LLM provider responses.
pub fn map_provider_http_error(status: u16, provider_name: &str) -> AppError {
match status {
400 => AppError::BadRequest("Invalid request to LLM provider".into()),
401 => AppError::BadRequest("Invalid or unauthorized API key".into()),
403 => AppError::BadRequest("Access denied by LLM provider".into()),
404 => AppError::BadRequest("Model not found or not available".into()),
429 | 529 => AppError::RateLimited(
"LLM provider rate limit exceeded. Please try again later.".into(),
),
_ => AppError::Internal(anyhow::anyhow!("{} returned HTTP {}", provider_name, status)),
}
}