Skip to content

Commit f21e0a2

Browse files
committed
update
1 parent 79984fe commit f21e0a2

5 files changed

Lines changed: 164 additions & 22 deletions

File tree

src-tauri/src/plugins/chat.rs

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1+
use base64::Engine;
12
use futures_util::StreamExt;
23
use once_cell::sync::Lazy;
34
use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION, CONTENT_TYPE};
45
use std::collections::HashMap;
56
use std::sync::Mutex;
67
use tauri::{Emitter, Runtime};
78
use tokio::sync::{mpsc, oneshot};
8-
use base64::Engine;
99

1010
static CANCEL_CHANNELS: Lazy<Mutex<HashMap<String, oneshot::Sender<()>>>> =
1111
Lazy::new(|| Mutex::new(HashMap::new()));
1212

13-
14-
1513
#[tauri::command]
1614
pub async fn chat_stream<R: Runtime>(
1715
window: tauri::Window<R>,
@@ -42,7 +40,10 @@ pub async fn chat_stream<R: Runtime>(
4240
.await
4341
.map_err(|e| {
4442
println!("请求发送失败: {}", e);
45-
window.clone().emit(&format!("chat-stream-error-{}", request_id), e.to_string()).unwrap();
43+
window
44+
.clone()
45+
.emit(&format!("chat-stream-error-{}", request_id), e.to_string())
46+
.unwrap();
4647
e.to_string()
4748
})?;
4849

@@ -134,10 +135,7 @@ pub async fn image_generate(
134135
AUTHORIZATION,
135136
HeaderValue::from_str(&format!("Bearer {}", api_key)).map_err(|e| e.to_string())?,
136137
);
137-
headers.insert(
138-
"X-DashScope-Async",
139-
HeaderValue::from_static("enable"),
140-
);
138+
headers.insert("X-DashScope-Async", HeaderValue::from_static("enable"));
141139

142140
println!("正在发送图像生成请求到: {}", api_url);
143141
let response = client
@@ -154,7 +152,10 @@ pub async fn image_generate(
154152
if !response.status().is_success() {
155153
let status = response.status();
156154
let error_text = response.text().await.map_err(|e| e.to_string())?;
157-
println!("图像生成请求失败,状态码: {}, 错误信息: {}", status, error_text);
155+
println!(
156+
"图像生成请求失败,状态码: {}, 错误信息: {}",
157+
status, error_text
158+
);
158159
return Err(format!("请求失败: {} - {}", status, error_text));
159160
}
160161

@@ -163,10 +164,7 @@ pub async fn image_generate(
163164
}
164165

165166
#[tauri::command]
166-
pub async fn image_result(
167-
api_url: String,
168-
api_key: String,
169-
) -> Result<serde_json::Value, String> {
167+
pub async fn image_result(api_url: String, api_key: String) -> Result<serde_json::Value, String> {
170168
let client = reqwest::Client::builder()
171169
.build()
172170
.map_err(|e| e.to_string())?;
@@ -192,12 +190,15 @@ pub async fn image_result(
192190
if !response.status().is_success() {
193191
let status = response.status();
194192
let error_text = response.text().await.map_err(|e| e.to_string())?;
195-
println!("获取图像生成结果失败,状态码: {}, 错误信息: {}", status, error_text);
193+
println!(
194+
"获取图像生成结果失败,状态码: {}, 错误信息: {}",
195+
status, error_text
196+
);
196197
return Err(format!("请求失败: {} - {}", status, error_text));
197198
}
198199

199200
let mut response_json: serde_json::Value = response.json().await.map_err(|e| e.to_string())?;
200-
201+
201202
// 如果任务成功完成,获取图片并转换为base64
202203
if let Some(output) = response_json.get("output") {
203204
if let Some(task_status) = output.get("task_status") {
@@ -211,19 +212,24 @@ pub async fn image_result(
211212
.send()
212213
.await
213214
.map_err(|e| e.to_string())?;
214-
215-
let image_bytes = image_response.bytes().await.map_err(|e| e.to_string())?;
216-
215+
216+
let image_bytes =
217+
image_response.bytes().await.map_err(|e| e.to_string())?;
218+
217219
// 转换为base64
218-
let base64_image = base64::engine::general_purpose::STANDARD.encode(&image_bytes);
220+
let base64_image =
221+
base64::engine::general_purpose::STANDARD.encode(&image_bytes);
219222
let data_url = format!("data:image/png;base64,{}", base64_image);
220-
223+
221224
// 创建新的结果对象
222225
if let Some(output) = response_json.get_mut("output") {
223226
if let Some(results) = output.get_mut("results") {
224227
if let Some(first_result) = results.get_mut(0) {
225228
if let Some(obj) = first_result.as_object_mut() {
226-
obj.insert("base64".to_string(), serde_json::Value::String(data_url));
229+
obj.insert(
230+
"base64".to_string(),
231+
serde_json::Value::String(data_url),
232+
);
227233
}
228234
}
229235
}
@@ -234,6 +240,6 @@ pub async fn image_result(
234240
}
235241
}
236242
}
237-
243+
238244
Ok(response_json)
239245
}

src/assets/variables.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
font-family: "harmony";
1111
}
1212

13+
[data-font=mono] * {
14+
font-family: "mono";
15+
}
16+
1317
.mono {
1418
font-family: "mono" !important;
1519
}

src/page/workflow/nodes/PluginNode.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ export class PluginNodeExecutor extends NodeExecutor {
167167
const pluginResult = await (
168168
await ToolPlugin.get(plugin.props.id)
169169
).execute(pluginConfig.tool, processedArgs);
170+
console.log("pluginResult", pluginResult);
170171

171172
if (!pluginResult) {
172173
throw new Error("Plugin execution result is empty");

src/skills/instance/image.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { ImageManager } from "@/resources/Image";
2+
import { SkillManager } from "../SkillManager";
3+
import { gen } from "@/utils/generator";
4+
import { readFile, writeFile } from "@tauri-apps/plugin-fs";
5+
6+
/** 将本地图片转换为base64 */
7+
SkillManager.register("imageLoading", {
8+
name: "图片加载",
9+
description: "将本地图片连接转换为base64放到数据库中, 并返回图片id",
10+
params: {
11+
type: "object",
12+
properties: {
13+
path: {
14+
type: "string",
15+
description: "图片绝对路径",
16+
},
17+
},
18+
required: ["path"],
19+
},
20+
execute: async (params: Record<string, any>) => {
21+
const { path } = params;
22+
const id = gen.id();
23+
24+
try {
25+
// 读取文件内容
26+
const binaryData = await readFile(path);
27+
28+
// 将二进制数据转换为base64
29+
const base64String = btoa(
30+
new Uint8Array(binaryData).reduce(
31+
(data, byte) => data + String.fromCharCode(byte),
32+
"",
33+
),
34+
);
35+
36+
// 根据文件扩展名确定内容类型
37+
const fileExt = path.split(".").pop()?.toLowerCase() || "";
38+
let contentType = "image/png"; // 默认
39+
40+
// 常见图片格式的MIME类型映射
41+
const mimeTypes: Record<string, string> = {
42+
jpg: "image/jpeg",
43+
jpeg: "image/jpeg",
44+
png: "image/png",
45+
gif: "image/gif",
46+
bmp: "image/bmp",
47+
webp: "image/webp",
48+
svg: "image/svg+xml",
49+
};
50+
51+
if (fileExt in mimeTypes) {
52+
contentType = mimeTypes[fileExt];
53+
}
54+
55+
// 构建完整的Data URL
56+
const dataUrl = `data:${contentType};base64,${base64String}`;
57+
58+
// 保存图片到数据库
59+
await ImageManager.setImage(id, dataUrl, contentType);
60+
61+
return id;
62+
} catch (error) {
63+
console.error("图片加载失败:", error);
64+
throw new Error(`图片加载失败: ${error}`);
65+
}
66+
},
67+
});
68+
69+
SkillManager.register("imageSave", {
70+
name: "图片保存",
71+
description: "将图片ID对应的图片保存到本地, 并返回图片路径",
72+
params: {
73+
type: "object",
74+
properties: {
75+
id: {
76+
type: "string",
77+
description: "图片ID",
78+
},
79+
path: {
80+
type: "string",
81+
description: "图片绝对路径,不包含扩展名",
82+
},
83+
},
84+
required: ["id", "path"],
85+
},
86+
execute: async (params: Record<string, any>) => {
87+
const { id, path } = params;
88+
89+
try {
90+
// 从ImageManager获取图片数据
91+
const imageData = await ImageManager.getImageBody(id);
92+
if (!imageData) {
93+
throw new Error(`未找到ID为${id}的图片`);
94+
}
95+
let mimeType = "image/png";
96+
let fileExtension = "png";
97+
// 从DataURL中提取MIME类型
98+
const mimeMatch = imageData.match(/^data:([^;]+);/);
99+
if (mimeMatch && mimeMatch[1]) {
100+
mimeType = mimeMatch[1];
101+
// 从MIME类型提取扩展名
102+
const extMap: Record<string, string> = {
103+
"image/jpeg": "jpg",
104+
"image/png": "png",
105+
"image/gif": "gif",
106+
"image/bmp": "bmp",
107+
"image/webp": "webp",
108+
"image/svg+xml": "svg",
109+
};
110+
fileExtension = extMap[mimeType] || "png";
111+
}
112+
113+
// 提取Base64数据并转换为二进制数组
114+
const base64Data = imageData.replace(/^data:[^;]+;base64,/, "");
115+
const binaryData = new Uint8Array(
116+
atob(base64Data)
117+
.split("")
118+
.map((char) => char.charCodeAt(0)),
119+
);
120+
121+
// 使用writeFile保存二进制数据
122+
await writeFile(path + "." + fileExtension, binaryData);
123+
124+
return `图片已成功保存到: ${path}`;
125+
} catch (error) {
126+
console.error("图片保存失败:", error);
127+
throw new Error(`图片保存失败: ${error}`);
128+
}
129+
},
130+
});

src/skills/instance/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
import "./Search";
2+
import "./image";

0 commit comments

Comments
 (0)