use serde::{Deserialize, Deserializer, Serialize}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Default)] #[serde(rename_all = "lowercase")] pub enum AiProvider { #[default] Ollama, Fireworks, OpenRouter, } /// Deserialize a provider string, coercing legacy `openai`/`anthropic` and any /// unknown value to `Ollama`. Keeps existing config files loadable after the /// stub providers were removed. impl<'de> Deserialize<'de> for AiProvider { fn deserialize>(d: D) -> Result { let s = String::deserialize(d)?; Ok(match s.as_str() { "fireworks" => AiProvider::Fireworks, "openrouter" => AiProvider::OpenRouter, _ => AiProvider::Ollama, }) } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AiSettings { pub provider: AiProvider, pub ollama_url: String, #[serde(default)] pub fireworks_api_key: Option, #[serde(default)] pub openrouter_api_key: Option, pub model: String, } impl Default for AiSettings { fn default() -> Self { Self { provider: AiProvider::Ollama, ollama_url: "http://localhost:11434".to_string(), fireworks_api_key: None, openrouter_api_key: None, model: String::new(), } } } /// Generic chat message used by all chat providers (Ollama, Fireworks, OpenAI-compatible). /// `{role, content}` shape is identical across these APIs. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OllamaChatMessage { pub role: String, pub content: String, } #[derive(Debug, Clone, Serialize)] pub struct OllamaChatRequest { pub model: String, pub messages: Vec, pub stream: bool, #[serde(skip_serializing_if = "Option::is_none")] pub format: Option, } #[derive(Debug, Deserialize)] pub struct OllamaChatResponse { pub message: OllamaChatMessage, } #[derive(Debug, Deserialize)] pub struct OllamaTagsResponse { pub models: Vec, } /// Generic chat-model descriptor exposed to the UI dropdown. /// Reused as the return shape for both Ollama and Fireworks model listings. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OllamaModel { pub name: String, } // --------------------------------------------------------------------------- // OpenAI-compatible chat-completions (Fireworks, OpenRouter) // These request/response shapes are shared by every OpenAI-compatible provider; // the `Fireworks*` names are retained for historical reasons. // --------------------------------------------------------------------------- #[derive(Debug, Clone, Serialize)] pub struct FireworksChatRequest { pub model: String, pub messages: Vec, pub temperature: f32, #[serde(skip_serializing_if = "Option::is_none")] pub response_format: Option, } #[derive(Debug, Clone, Serialize)] pub struct FireworksResponseFormat { #[serde(rename = "type")] pub kind: String, } #[derive(Debug, Deserialize)] pub struct FireworksChatResponse { pub choices: Vec, } #[derive(Debug, Deserialize)] pub struct FireworksChoice { pub message: OllamaChatMessage, } #[derive(Debug, Deserialize)] pub struct FireworksModelsResponse { pub data: Vec, } #[derive(Debug, Deserialize)] pub struct FireworksModelEntry { pub id: String, }