//! 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; } /// 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)), } }