From f84ee6ced728ba0aaabc50a604b6b8aa4b2c88f3 Mon Sep 17 00:00:00 2001 From: Julian Freeman Date: Mon, 23 Feb 2026 18:33:13 -0400 Subject: [PATCH] support generic openai api --- package.json | 2 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/src/lib.rs | 63 +++++++++++++++++++++++++++++---------- src-tauri/tauri.conf.json | 2 +- src/App.vue | 32 +++++++++++++------- src/stores/settings.ts | 6 ++-- 7 files changed, 77 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index f6969cd..7cb739e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "gemmatrans-client", "private": true, - "version": "0.2.0", + "version": "0.2.1", "type": "module", "scripts": { "dev": "vite", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index b3ba08d..6f3a642 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1251,7 +1251,7 @@ dependencies = [ [[package]] name = "gemmatrans-client" -version = "0.2.0" +version = "0.2.1" dependencies = [ "futures-util", "reqwest 0.12.28", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index feff389..13bd2a4 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gemmatrans-client" -version = "0.2.0" +version = "0.2.1" description = "A translategemma client" authors = ["Julian"] edition = "2021" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 37f8bcc..bbd5200 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -3,38 +3,61 @@ 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, - prompt: String, + messages: Vec, stream: bool, } #[derive(Deserialize)] -struct OllamaResponse { - response: Option, - // done: bool, +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(); - let url = format!("{}/api/generate", api_address); + // 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 res = client - .post(&url) - .json(&payload) + 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.response.unwrap_or_default()); + 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(); @@ -44,13 +67,21 @@ async fn translate( let chunk = item.map_err(|e| e.to_string())?; let text = String::from_utf8_lossy(&chunk); - // Handle potential multiple JSON objects in one chunk for line in text.lines() { - if line.trim().is_empty() { continue; } - if let Ok(json) = serde_json::from_str::(line) { - if let Some(token) = json.response { - full_response.push_str(&token); - app.emit("translation-chunk", token).map_err(|e| e.to_string())?; + 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())?; + } + } + } } } } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 0913695..151e0c7 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "gemmatrans-client", - "version": "0.2.0", + "version": "0.2.1", "identifier": "top.volan.gemmatrans-client", "build": { "beforeDevCommand": "pnpm dev", diff --git a/src/App.vue b/src/App.vue index 3ae2245..511e3b6 100644 --- a/src/App.vue +++ b/src/App.vue @@ -181,7 +181,9 @@ const translate = async () => { const requestBody = { model: settings.modelName, - prompt: prompt, + messages: [ + { role: "user", content: prompt } + ], stream: settings.enableStreaming }; @@ -189,7 +191,8 @@ const translate = async () => { try { const response = await invoke('translate', { - apiAddress: settings.ollamaApiAddress, + apiAddress: settings.apiBaseUrl, + apiKey: settings.apiKey, payload: requestBody }); @@ -463,29 +466,38 @@ const translate = async () => {
-

API 配置

+

模型接口配置

- +
- + + +
+
+
- +

在生成时即时渲染文本