use reqwest::Client; use serde::{Deserialize, Serialize}; use crate::error::Result; use crate::lang; pub struct OpenRouterClient { api_key: String, model: String, client: Client, } #[derive(Serialize)] struct ChatRequest { model: String, messages: Vec, max_tokens: u32, } #[derive(Serialize)] struct Message { role: String, content: String, } #[derive(Deserialize)] struct ChatResponse { choices: Vec, } #[derive(Deserialize)] struct Choice { message: MessageContent, } #[derive(Deserialize)] struct MessageContent { content: String, } impl OpenRouterClient { pub fn new(api_key: String, model: String) -> Self { Self { api_key, model, client: Client::new(), } } pub async fn chat(&self, system_prompt: &str, user_message: &str) -> Result { let body = ChatRequest { model: self.model.clone(), messages: vec![ Message { role: "system".into(), content: system_prompt.to_string(), }, Message { role: "user".into(), content: user_message.to_string(), }, ], max_tokens: 500, }; let resp = self .client .post("https://openrouter.ai/api/v1/chat/completions") .header("Authorization", format!("Bearer {}", self.api_key)) .json(&body) .send() .await?; if !resp.status().is_success() { let status = resp.status(); let text = resp.text().await.unwrap_or_default(); return Err(crate::error::Error::Custom(lang::fmt_openrouter_err( &status, &text, ))); } let chat_resp: ChatResponse = resp.json().await?; Ok(chat_resp .choices .into_iter() .next() .map(|c| c.message.content) .unwrap_or_default()) } pub async fn chat_with_history( &self, system_prompt: &str, messages: Vec<(String, String)>, // (role, content) pairs ) -> Result { let mut msg_vec = vec![Message { role: "system".into(), content: system_prompt.to_string(), }]; for (role, content) in messages { msg_vec.push(Message { role, content }); } let body = ChatRequest { model: self.model.clone(), messages: msg_vec, max_tokens: 500, }; let resp = self .client .post("https://openrouter.ai/api/v1/chat/completions") .header("Authorization", format!("Bearer {}", self.api_key)) .json(&body) .send() .await?; if !resp.status().is_success() { let status = resp.status(); let text = resp.text().await.unwrap_or_default(); return Err(crate::error::Error::Custom(lang::fmt_openrouter_err( &status, &text, ))); } let chat_resp: ChatResponse = resp.json().await?; Ok(chat_resp .choices .into_iter() .next() .map(|c| c.message.content) .unwrap_or_default()) } }