feat: 包含 Kotlin 原始代码和 Rust Windows 桌面版
- flomo-ai/: Android Kotlin 原始项目 - flomo-ai-desktop/: Rust + egui Windows 桌面移植版 - LLM API 调用、提示词管理、主题切换、配置持久化 - MinGW 工具链编译,无控制台窗口
This commit is contained in:
93
flomo-ai-desktop/src/api/llm_client.rs
Normal file
93
flomo-ai-desktop/src/api/llm_client.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
use crate::config::AppSettings;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ChatMessage {
|
||||
pub role: String,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ChatCompletionRequest {
|
||||
pub model: String,
|
||||
pub messages: Vec<ChatMessage>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ChatChoice {
|
||||
pub message: ChatMessage,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ChatCompletionResponse {
|
||||
pub choices: Vec<ChatChoice>,
|
||||
}
|
||||
|
||||
pub fn call_llm(settings: &AppSettings, user_input: String, selected_prompt: Option<String>) -> Result<String, String> {
|
||||
let mut messages = Vec::new();
|
||||
|
||||
if let Some(prompt) = selected_prompt {
|
||||
if !prompt.is_empty() {
|
||||
messages.push(ChatMessage {
|
||||
role: "system".to_string(),
|
||||
content: prompt,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
messages.push(ChatMessage {
|
||||
role: "user".to_string(),
|
||||
content: user_input,
|
||||
});
|
||||
|
||||
let request = ChatCompletionRequest {
|
||||
model: settings.llm_config.model.clone(),
|
||||
messages,
|
||||
};
|
||||
|
||||
let client = reqwest::blocking::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(120))
|
||||
.build()
|
||||
.map_err(|e| format!("Failed to create HTTP client: {}", e))?;
|
||||
|
||||
let mut req_builder = client
|
||||
.post(format!("{}/chat/completions", settings.llm_config.base_url))
|
||||
.header("Content-Type", "application/json");
|
||||
|
||||
if !settings.llm_config.api_key.is_empty() {
|
||||
req_builder = req_builder.header("Authorization", format!("Bearer {}", settings.llm_config.api_key));
|
||||
}
|
||||
|
||||
for header in &settings.header_configs {
|
||||
if !header.key.is_empty() {
|
||||
req_builder = req_builder.header(&header.key, &header.value);
|
||||
}
|
||||
}
|
||||
|
||||
let body = serde_json::to_string(&request)
|
||||
.map_err(|e| format!("Failed to serialize request: {}", e))?;
|
||||
|
||||
let response = req_builder
|
||||
.body(body)
|
||||
.send()
|
||||
.map_err(|e| format!("Request failed: {}", e))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let status = response.status();
|
||||
let error_body = response.text().unwrap_or_default();
|
||||
return Err(format!("API error {}: {}", status, error_body));
|
||||
}
|
||||
|
||||
let response_text = response
|
||||
.text()
|
||||
.map_err(|e| format!("Failed to read response: {}", e))?;
|
||||
|
||||
let completion: ChatCompletionResponse = serde_json::from_str(&response_text)
|
||||
.map_err(|e| format!("Failed to parse response: {}", e))?;
|
||||
|
||||
if completion.choices.is_empty() {
|
||||
return Err("No response from API".to_string());
|
||||
}
|
||||
|
||||
Ok(completion.choices[0].message.content.clone())
|
||||
}
|
||||
Reference in New Issue
Block a user