use tauri::{AppHandle, Emitter}; use serde::{Deserialize, Serialize}; use futures_util::StreamExt; use reqwest::Client; #[derive(Serialize, Deserialize, Clone)] struct Message { role: String, content: String, } #[derive(Serialize, Deserialize, Clone)] struct TranslationPayload { model: String, messages: Vec, stream: bool, } #[derive(Deserialize)] struct OpenAIResponse { choices: Vec, } #[derive(Deserialize)] struct Choice { message: Option, delta: Option, } #[derive(Deserialize)] struct Delta { content: Option, } #[tauri::command] async fn translate( app: AppHandle, api_address: String, api_key: String, payload: TranslationPayload, ) -> Result { let client = Client::new(); // Ensure URL doesn't have double slashes if api_address ends with / let base_url = api_address.trim_end_matches('/'); let url = format!("{}/chat/completions", base_url); let mut request = client.post(&url).json(&payload); if !api_key.is_empty() { request = request.header("Authorization", format!("Bearer {}", api_key)); } let res = request .send() .await .map_err(|e| e.to_string())?; if !payload.stream { let data = res.json::().await.map_err(|e| e.to_string())?; return Ok(data.choices.get(0).and_then(|c| c.message.as_ref()).map(|m| m.content.clone()).unwrap_or_default()); } let mut stream = res.bytes_stream(); let mut full_response = String::new(); while let Some(item) = stream.next().await { let chunk = item.map_err(|e| e.to_string())?; let text = String::from_utf8_lossy(&chunk); for line in text.lines() { let line = line.trim(); if line.is_empty() { continue; } if line == "data: [DONE]" { break; } if let Some(data_str) = line.strip_prefix("data: ") { if let Ok(json) = serde_json::from_str::(data_str) { if let Some(choice) = json.choices.get(0) { if let Some(delta) = &choice.delta { if let Some(content) = &delta.content { full_response.push_str(content); app.emit("translation-chunk", content).map_err(|e| e.to_string())?; } } } } } } } Ok(full_response) } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_http::init()) .invoke_handler(tauri::generate_handler![translate]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }