dan92 commited on
Commit
6a3fff8
·
verified ·
1 Parent(s): 1bbd3a1

Upload index.js

Browse files
Files changed (1) hide show
  1. src/index.js +1384 -0
src/index.js ADDED
@@ -0,0 +1,1384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const models = {
2
+ "o1-preview": "o1-preview",
3
+ "o1-mini": "o1-preview",
4
+ "claude-3-5-sonnet-20240620": "claude-3-5-sonnet",
5
+ "claude-3-5-sonnet-20241022": "claude-3-5-sonnet",
6
+ "claude-3-5-haiku-20241022": "claude-3-5-haiku",
7
+ "claude-3-haiku-20240307": "claude-3-5-haiku",
8
+ "gpt-4o": "gpt-4o",
9
+ "gpt-4o-mini": "gpt-4o-mini",
10
+ "gpt-4o-file": "gpt-4o",
11
+ "gpt-4o-mini-file": "gpt-4o-mini",
12
+ "claude-3-5-sonnet-file": "claude-3-5-sonnet",
13
+ "claude-3-5-haiku-file": "claude-3-5-haiku",
14
+ "gemini-1.5-pro-latest": "gemini-1.5-pro",
15
+ "gemini-1.5-flash-latest": "gemini-1.5-flash",
16
+ "genspark": "genspark",
17
+ "deepl": "deepl", // 添加翻译模型
18
+ };
19
+
20
+ // 新增画图模型
21
+ const drawingModels = new Set([
22
+ "dall-e-3",
23
+ "flux",
24
+ "flux-speed",
25
+ "flux-pro/ultra",
26
+ "ideogram",
27
+ "recraft-v3",
28
+ ]);
29
+
30
+ // 添加缓存相关的常量配置
31
+ const CACHE_CONFIG = {
32
+ TTL: 30 * 60, // 缓存30分钟aa
33
+ EXCLUDED_MODELS: ["o1-preview"], // 不缓存的模型
34
+ MAX_CACHE_SIZE: 100, // 最大缓存条目数
35
+ };
36
+
37
+ // 简单的内存缓存实现
38
+ class MessageCache {
39
+ constructor() {
40
+ this.cache = new Map();
41
+ this.keyTimestamps = new Map();
42
+ }
43
+
44
+ generateKey(messages, model) {
45
+ // 生成缓存key
46
+ const contentStr = JSON.stringify(
47
+ messages.map((m) => ({
48
+ role: m.role,
49
+ content: m.content,
50
+ }))
51
+ );
52
+ return `${model}:${contentStr}`;
53
+ }
54
+
55
+ set(key, value) {
56
+ // 检查缓存大小
57
+ if (this.cache.size >= CACHE_CONFIG.MAX_CACHE_SIZE) {
58
+ // 删除最旧的缓存
59
+ const oldestKey = [...this.keyTimestamps.entries()].sort(
60
+ ([, a], [, b]) => a - b
61
+ )[0][0];
62
+ this.cache.delete(oldestKey);
63
+ this.keyTimestamps.delete(oldestKey);
64
+ }
65
+
66
+ this.cache.set(key, value);
67
+ this.keyTimestamps.set(key, Date.now());
68
+ }
69
+
70
+ get(key) {
71
+ const timestamp = this.keyTimestamps.get(key);
72
+ if (!timestamp) return null;
73
+
74
+ // 检查是否过期
75
+ if (Date.now() - timestamp > CACHE_CONFIG.TTL * 1000) {
76
+ this.cache.delete(key);
77
+ this.keyTimestamps.delete(key);
78
+ return null;
79
+ }
80
+
81
+ return this.cache.get(key);
82
+ }
83
+ }
84
+
85
+ const messageCache = new MessageCache();
86
+
87
+ class SessionPool {
88
+ constructor() {
89
+ this.sessions = [];
90
+ this.currentIndex = 0;
91
+ }
92
+ initialize(sessionString) {
93
+ if (!sessionString) return;
94
+ this.sessions = sessionString
95
+ .split(",")
96
+ .map((s) => s.trim())
97
+ .filter((s) => s);
98
+ this.currentIndex = Math.floor(Math.random() * this.sessions.length);
99
+ }
100
+ getNext() {
101
+ if (this.sessions.length === 0) return null;
102
+ const session = this.sessions[this.currentIndex];
103
+ this.currentIndex = (this.currentIndex + 1) % this.sessions.length;
104
+ return session;
105
+ }
106
+ size() {
107
+ return this.sessions.length;
108
+ }
109
+ }
110
+
111
+ const sessionPool = new SessionPool();
112
+
113
+ const corsHeaders = {
114
+ "Access-Control-Allow-Origin": "*",
115
+ "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
116
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
117
+ };
118
+
119
+ function doubleEncode(str) {
120
+ return encodeURIComponent(encodeURIComponent(str));
121
+ }
122
+
123
+ function createErrorResponse(message, status = 500) {
124
+ return new Response(JSON.stringify({ error: message }), {
125
+ status,
126
+ headers: { ...corsHeaders, "Content-Type": "application/json" },
127
+ });
128
+ }
129
+
130
+ async function cleanupProject(session_id, project_id) {
131
+ try {
132
+ const headers = {
133
+ Cookie: `session_id=${session_id}`,
134
+ "User-Agent": "Apifox/1.0.0 (https://apifox.com)",
135
+ Accept: "*/*",
136
+ Host: "www.genspark.ai",
137
+ Connection: "keep-alive",
138
+ "Accept-Encoding": "gzip, deflate, br", // 添加压缩支持
139
+ };
140
+ const response = await fetch(
141
+ `https://www.genspark.ai/api/project/delete?project_id=${project_id}`,
142
+ {
143
+ method: "GET",
144
+ headers,
145
+ }
146
+ );
147
+ return response.ok;
148
+ } catch (error) {
149
+ console.error("Cleanup project error:", error);
150
+ return false;
151
+ }
152
+ }
153
+
154
+ function extractProjectId(text) {
155
+ try {
156
+ if (text.startsWith("data: ")) {
157
+ const content = JSON.parse(text.slice(6));
158
+ if (content.type === "project_start" && content.id) {
159
+ return content.id;
160
+ }
161
+ }
162
+ } catch (e) {}
163
+ return null;
164
+ }
165
+
166
+ function processChunk(chunk, messageId, model) {
167
+ try {
168
+ const content = chunk.replace(/^data:\s*/, "").trim();
169
+ if (!content || content === "[DONE]") return null;
170
+
171
+ const parsed = JSON.parse(content);
172
+ if (!parsed) return null;
173
+
174
+ // 处理翻译模型的特殊返回格式
175
+ if (model === "deepl") {
176
+ if (parsed.field_name === "content" && parsed.field_value) {
177
+ return JSON.stringify({
178
+ translations: [
179
+ {
180
+ detected_source_language:
181
+ parsed.session_state?.detected_source_lang || "",
182
+ text: parsed.field_value,
183
+ },
184
+ ],
185
+ });
186
+ }
187
+ } else if (drawingModels.has(model)) {
188
+ // 处理画图模型的响应
189
+ if (parsed.content) {
190
+ return JSON.stringify({
191
+ created: Date.now(),
192
+ data: [
193
+ {
194
+ url: parsed.content,
195
+ revised_prompt: parsed.prompt || "",
196
+ },
197
+ ],
198
+ });
199
+ }
200
+ } else {
201
+ if (parsed.delta) {
202
+ return `data: ${JSON.stringify({
203
+ id: `chatcmpl-${messageId}`,
204
+ choices: [
205
+ {
206
+ index: 0,
207
+ delta: { content: parsed.delta },
208
+ },
209
+ ],
210
+ created: Math.floor(Date.now() / 1000),
211
+ model: models[model],
212
+ object: "chat.completion.chunk",
213
+ })}\n\n`;
214
+ }
215
+ if (
216
+ parsed.field_value &&
217
+ parsed.field_name !== "session_state.answer_is_finished" &&
218
+ parsed.field_name !== "content" &&
219
+ parsed.field_name !== "session_state" &&
220
+ !parsed.delta &&
221
+ parsed.type !== "project_field"
222
+ ) {
223
+ return JSON.stringify({
224
+ id: `chatcmpl-${messageId}`,
225
+ object: "chat.completion",
226
+ created: Math.floor(Date.now() / 1000),
227
+ model,
228
+ choices: [
229
+ {
230
+ index: 0,
231
+ message: {
232
+ role: "assistant",
233
+ content: parsed.field_value,
234
+ },
235
+ finish_reason: "stop",
236
+ },
237
+ ],
238
+ usage: {
239
+ prompt_tokens: 100,
240
+ completion_tokens: 50,
241
+ total_tokens: parsed.field_value.length,
242
+ },
243
+ });
244
+ }
245
+ }
246
+ } catch (e) {}
247
+ return null;
248
+ }
249
+
250
+ async function* streamGenerator(reader) {
251
+ let buffer = "";
252
+ const decoder = new TextDecoder();
253
+
254
+ try {
255
+ while (true) {
256
+ const { done, value } = await reader.read();
257
+ if (done) break;
258
+
259
+ buffer += decoder.decode(value, { stream: true });
260
+ const lines = buffer.split("\n");
261
+ buffer = lines.pop() || "";
262
+
263
+ for (const line of lines) {
264
+ if (line.trim()) yield line;
265
+ }
266
+ }
267
+ if (buffer.trim()) yield buffer;
268
+ } catch (e) {
269
+ console.error("Stream error:", e);
270
+ }
271
+ }
272
+
273
+ async function makeRequest(
274
+ session_id,
275
+ requestModel,
276
+ messages,
277
+ target_lang_code,
278
+ source_lang_code,
279
+ num_images = 1,
280
+ size = "1024x1024",
281
+ quality = "standard",
282
+ style = "vivid"
283
+ ) {
284
+ const headers = {
285
+ Cookie: `session_id=${session_id}`,
286
+ "User-Agent": "Apifox/1.0.0 (https://apifox.com)",
287
+ "Content-Type": "application/json",
288
+ Accept: "*/*",
289
+ Host: "www.genspark.ai",
290
+ Connection: "keep-alive",
291
+ };
292
+ let retryCount = 0;
293
+ while (retryCount < 3) {
294
+ try {
295
+ let requestBody = {};
296
+ if (requestModel === "deepl") {
297
+ // 构建翻译模型的请求体
298
+ requestBody = {
299
+ type: "COPILOT_MOA_TRANSLATOR",
300
+ current_query_string: "type=COPILOT_MOA_TRANSLATOR",
301
+ messages,
302
+ user_s_input: messages[messages.length - 1].content,
303
+ action_params: {},
304
+ extra_data: {
305
+ models: [models[requestModel]],
306
+ run_with_another_model: false,
307
+ target_lang_code: target_lang_code || "zh-hans",
308
+ source_lang_code: source_lang_code || "",
309
+ writingContent: null,
310
+ },
311
+ };
312
+ } else if (drawingModels.has(requestModel)) {
313
+ // 构建画图模型的请求体
314
+ requestBody = {
315
+ type: "COPILOT_MOA_IMAGE",
316
+ current_query_string: "type=COPILOT_MOA_IMAGE",
317
+ messages,
318
+ user_s_input: messages[messages.length - 1].content,
319
+ action_params: {},
320
+ extra_data: {
321
+ model_configs: [],
322
+ llm_model: "gpt-4",
323
+ imageModelMap: {},
324
+ writingContent: null,
325
+ },
326
+ };
327
+ for (let i = 0; i < num_images; i++) {
328
+ requestBody.extra_data.model_configs.push({
329
+ model: requestModel === "dall-e-3" ? "dalle-3" : requestModel,
330
+ size: size,
331
+ quality: quality,
332
+ style: style,
333
+ });
334
+ }
335
+ } else {
336
+ // 构建聊天模型的请求体
337
+ requestBody = {
338
+ type: "COPILOT_MOA_CHAT",
339
+ current_query_string: "type=COPILOT_MOA_CHAT",
340
+ messages,
341
+ action_params: {},
342
+ extra_data: {
343
+ models: [models[requestModel] || models["claude-3-5-sonnet-20241022"]],
344
+ run_with_another_model: false,
345
+ writingContent: null,
346
+ },
347
+ };
348
+ }
349
+
350
+ const response = await fetch(
351
+ "https://www.genspark.ai/api/copilot/ask",
352
+ {
353
+ method: "POST",
354
+ headers,
355
+ body: JSON.stringify(requestBody),
356
+ }
357
+ );
358
+ if (!response.ok) {
359
+ retryCount++;
360
+ await new Promise((resolve) =>
361
+ setTimeout(resolve, 1000 * retryCount)
362
+ );
363
+ continue;
364
+ }
365
+ return response;
366
+ } catch (error) {
367
+ retryCount++;
368
+ await new Promise((resolve) =>
369
+ setTimeout(resolve, 1000 * retryCount)
370
+ );
371
+ if (retryCount >= 3) throw error;
372
+ }
373
+ }
374
+ throw new Error("请求失败,已重试3次");
375
+ }
376
+
377
+ async function searchModel(session_id, messages) {
378
+ const headers = {
379
+ Cookie: `session_id=${session_id}`,
380
+ "User-Agent": "Apifox/1.0.0 (https://apifox.com)",
381
+ "Content-Type": "application/json",
382
+ Accept: "*/*",
383
+ Host: "www.genspark.ai",
384
+ Connection: "keep-alive",
385
+ };
386
+ let retryCount = 0;
387
+ while (retryCount < 3) {
388
+ try {
389
+ const query = doubleEncode(messages[messages.length - 1].content);
390
+ const response = await fetch(
391
+ `https://www.genspark.ai/api/search/stream?query=${query}`,
392
+ {
393
+ method: "POST",
394
+ headers,
395
+ }
396
+ );
397
+ if (!response.ok) {
398
+ retryCount++;
399
+ await new Promise((resolve) =>
400
+ setTimeout(resolve, 1000 * retryCount)
401
+ );
402
+ continue;
403
+ }
404
+ return response;
405
+ } catch (error) {
406
+ retryCount++;
407
+ await new Promise((resolve) =>
408
+ setTimeout(resolve, 1000 * retryCount)
409
+ );
410
+ if (retryCount >= 3) throw error;
411
+ }
412
+ }
413
+ throw new Error("请求失败,已重试3次");
414
+ }
415
+
416
+ async function pollTaskId(taskId, session_id, maxRetries, retryInterval) {
417
+ let retries = 0;
418
+ while (retries < maxRetries) {
419
+ try {
420
+ const statusResponse = await fetch(
421
+ `https://www.genspark.ai/api/spark/image_generation_task_status?task_id=${taskId}`,
422
+ {
423
+ headers: {
424
+ Cookie: `session_id=${session_id}`,
425
+ "User-Agent": "Apifox/1.0.0 (https://apifox.com)",
426
+ Accept: "*/*",
427
+ Host: "www.genspark.ai",
428
+ Connection: "keep-alive",
429
+ },
430
+ }
431
+ );
432
+ if (!statusResponse.ok) {
433
+ throw new Error(`Status check failed: ${statusResponse.status}`);
434
+ }
435
+ const data = await statusResponse.json();
436
+ if (data.data.status === "SUCCESS") {
437
+ const urls = data.data.image_urls_nowatermark;
438
+ if (urls?.length) {
439
+ return urls;
440
+ }
441
+ } else if (data.data.status === "FAILED") {
442
+ throw new Error("Image generation failed");
443
+ }
444
+ } catch (error) {
445
+ console.error(`Poll error for task ${taskId} (retry ${retries}):`, error);
446
+ }
447
+ await new Promise((r) => setTimeout(r, retryInterval));
448
+ retries++;
449
+ }
450
+ throw new Error(`Timeout waiting for task: ${taskId}`);
451
+ }
452
+
453
+ function arrayBufferToBase64(buffer) {
454
+ let binary = '';
455
+ const bytes = new Uint8Array(buffer);
456
+ const len = bytes.byteLength;
457
+ const chunkSize = 8192; // 分块大小,可以根据需要调整
458
+
459
+ for (let i = 0; i < len; i += chunkSize) {
460
+ const chunk = bytes.subarray(i, i + chunkSize);
461
+ binary += String.fromCharCode.apply(null, chunk);
462
+ }
463
+ return btoa(binary);
464
+ }
465
+
466
+ // 添加处理消息的函数
467
+ async function processMessages(messages, env) {
468
+ // 对每个消息进行处理
469
+ for (let message of messages) {
470
+ // 检查 message.content 是否为数组
471
+ if (Array.isArray(message.content)) {
472
+ // 处理 content 数组中的每个条目
473
+ for (let i = 0; i < message.content.length; i++) {
474
+ let entry = message.content[i];
475
+ if (entry.type === 'image_url') {
476
+ // 处理 image_url
477
+ let url = entry.image_url?.url;
478
+ if (!url) {
479
+ throw new Error('image_url.url is required');
480
+ }
481
+
482
+ let base64ImageData;
483
+ if (url.startsWith('data:image')) {
484
+ // 已经是 base64 编码
485
+ base64ImageData = url;
486
+ } else {
487
+ // 从 URL 获取图片并编码为 base64
488
+ base64ImageData = await fetchImageAsDataURL(url, env);
489
+ }
490
+ // 用 base64 数据 URL 替换
491
+ entry.image_url.url = base64ImageData;
492
+ }
493
+ }
494
+ }
495
+ // 否则,message.content 是字符串,无需处理
496
+ }
497
+ }
498
+
499
+ // 添加获取图片并编码为 base64 的函数
500
+ async function fetchImageAsDataURL(url, env) {
501
+ // 检查是否在 KV 缓存中
502
+ let cacheKey = `image:${url}`;
503
+ let cachedData = await env.kv.get(cacheKey);
504
+ if (cachedData) {
505
+ return cachedData;
506
+ }
507
+ // 获取图片
508
+ let response = await fetch(url);
509
+ if (!response.ok) {
510
+ throw new Error(`Failed to fetch image from ${url}`);
511
+ }
512
+ let arrayBuffer = await response.arrayBuffer();
513
+ let base64Data = arrayBufferToBase64(arrayBuffer);
514
+ // 获取内容类型
515
+ let contentType = response.headers.get('Content-Type') || 'image/jpeg';
516
+ // ��建数据 URL
517
+ let dataURL = `data:${contentType};base64,${base64Data}`;
518
+ // 存储到 KV 缓存
519
+ await env.kv.put(cacheKey, dataURL, { expirationTtl: CACHE_CONFIG.TTL });
520
+ return dataURL;
521
+ }
522
+
523
+ // 提取最后的用户输入文本
524
+ function extractLastUserInput(messages) {
525
+ // 从后向前遍历 messages
526
+ for (let i = messages.length - 1; i >= 0; i--) {
527
+ let message = messages[i];
528
+ if (message.role === 'user') {
529
+ if (Array.isArray(message.content)) {
530
+ // 处理 content 数组
531
+ for (let j = message.content.length - 1; j >= 0; j--) {
532
+ let entry = message.content[j];
533
+ if (entry.type === 'text' && entry.text) {
534
+ return entry.text;
535
+ }
536
+ }
537
+ } else if (typeof message.content === 'string') {
538
+ // content 是字符串
539
+ return message.content;
540
+ }
541
+ }
542
+ }
543
+ return '';
544
+ }
545
+
546
+ export default {
547
+ async fetch(request, env, ctx) {
548
+ if (request.method === "OPTIONS") {
549
+ return new Response(null, { headers: corsHeaders });
550
+ }
551
+
552
+ const url = new URL(request.url);
553
+ if (url.pathname === "/health") {
554
+ return new Response(JSON.stringify({ status: "ok" }), {
555
+ headers: { ...corsHeaders, "Content-Type": "application/json" },
556
+ });
557
+ }
558
+
559
+ if (url.pathname === "/v1/models" && request.method === "GET") {
560
+ return new Response(
561
+ JSON.stringify({
562
+ object: "list",
563
+ data: Object.keys(models).map((model) => ({
564
+ id: model,
565
+ object: "model",
566
+ created: 1706745938,
567
+ owned_by: "genspark",
568
+ })),
569
+ }),
570
+ { headers: { ...corsHeaders, "Content-Type": "application/json" } }
571
+ );
572
+ }
573
+
574
+ // 添加对 /translate 接口的支持
575
+ if (
576
+ (url.pathname === "/v1/chat/completions" ||
577
+ url.pathname === "/translate") &&
578
+ request.method === "POST"
579
+ ) {
580
+ try {
581
+ let requestData = await request.json();
582
+ let {
583
+ messages,
584
+ prompt,
585
+ stream = false,
586
+ model = "deepl",
587
+ target_lang_code,
588
+ source_lang_code,
589
+ } = requestData;
590
+
591
+ // 如果是 /translate 接口,调整请求数据
592
+ if (url.pathname === "/translate") {
593
+ const { text, source_lang, target_lang } = requestData;
594
+ messages = [{ role: "user", content: text }];
595
+ model = "deepl";
596
+ target_lang_code = target_lang || "zh-hans";
597
+ source_lang_code = source_lang || "";
598
+ }
599
+
600
+ // 如果没有 messages,且有 prompt,创建 messages
601
+ if (!messages && prompt) {
602
+ messages = [{ role: "user", content: prompt }];
603
+ }
604
+
605
+ // 提取 num_images, size, response_format
606
+ const {
607
+ num_images = 1,
608
+ size = "1024x1024",
609
+ response_format = "url",
610
+ quality = "standard",
611
+ style = "vivid",
612
+ } = requestData;
613
+
614
+ const authHeader = request.headers.get("authorization");
615
+
616
+ if (sessionPool.size() === 0) {
617
+ const sessionString = authHeader?.replace("Bearer ", "");
618
+ sessionPool.initialize(sessionString);
619
+ if (sessionPool.size() === 0) {
620
+ return createErrorResponse("未提供有效的 session_id", 401);
621
+ }
622
+ }
623
+ const session_id = sessionPool.getNext();
624
+ if (!session_id) {
625
+ return createErrorResponse("无可用的会话ID", 500);
626
+ }
627
+
628
+ // 添加缓存处理
629
+ const cacheKey = messageCache.generateKey(messages, model);
630
+ let cachedResponse = null;
631
+ if (!CACHE_CONFIG.EXCLUDED_MODELS.includes(model)) {
632
+ cachedResponse = messageCache.get(cacheKey);
633
+ }
634
+ if (cachedResponse) {
635
+ return new Response(cachedResponse, {
636
+ headers: { ...corsHeaders, "Content-Type": "application/json" },
637
+ });
638
+ }
639
+
640
+ // 处理 messages,处理图片
641
+ await processMessages(messages, env);
642
+ const user_s_input = extractLastUserInput(messages);
643
+
644
+ if (drawingModels.has(model)) {
645
+ // 处理画图请求
646
+ // 构建请求体
647
+ const requestBody = {
648
+ type: "COPILOT_MOA_IMAGE",
649
+ current_query_string: "type=COPILOT_MOA_IMAGE",
650
+ messages,
651
+ user_s_input: user_s_input,
652
+ action_params: {},
653
+ extra_data: {
654
+ model_configs: [],
655
+ llm_model: "gpt-4",
656
+ imageModelMap: {},
657
+ writingContent: null,
658
+ },
659
+ };
660
+ for (let i = 0; i < num_images; i++) {
661
+ requestBody.extra_data.model_configs.push({
662
+ model: model === "dall-e-3" ? "dalle-3" : model,
663
+ size: size,
664
+ quality: quality,
665
+ style: style,
666
+ });
667
+ }
668
+ const headers = {
669
+ Cookie: `session_id=${session_id}`,
670
+ "User-Agent": "Apifox/1.0.0 (https://apifox.com)",
671
+ "Content-Type": "application/json",
672
+ Accept: "*/*",
673
+ Host: "www.genspark.ai",
674
+ Connection: "keep-alive",
675
+ };
676
+ if (stream === true || stream === "true") {
677
+ // Handle streaming response
678
+ const { readable, writable } = new TransformStream();
679
+ const writer = writable.getWriter();
680
+
681
+ ctx.waitUntil(
682
+ (async () => {
683
+ try {
684
+ const response = await fetch(
685
+ "https://www.genspark.ai/api/copilot/ask",
686
+ {
687
+ method: "POST",
688
+ headers,
689
+ body: JSON.stringify(requestBody),
690
+ }
691
+ );
692
+
693
+ if (!response.ok) {
694
+ await writer.write(
695
+ new TextEncoder().encode(
696
+ `data: ${JSON.stringify({
697
+ error: "上游服务请求失败",
698
+ })}\n\n`
699
+ )
700
+ );
701
+ await writer.close();
702
+ return;
703
+ }
704
+
705
+ const reader = response.body.getReader();
706
+ let taskIds = [];
707
+
708
+ // Read the response from upstream and extract task IDs
709
+ let responseText = "";
710
+ for await (const line of streamGenerator(reader)) {
711
+ responseText += line + "\n";
712
+ if (line.includes("task_id")) {
713
+ try {
714
+ const contentLine = line.replace("data: ", "");
715
+ const { content: innerContent } = JSON.parse(contentLine);
716
+ if (innerContent) {
717
+ const { generated_images } = JSON.parse(innerContent);
718
+ if (generated_images) {
719
+ for (const img of generated_images) {
720
+ if (img.task_id) {
721
+ taskIds.push(img.task_id);
722
+ }
723
+ }
724
+ }
725
+ }
726
+ } catch (e) {}
727
+ }
728
+ }
729
+
730
+ if (!taskIds.length) {
731
+ await writer.write(
732
+ new TextEncoder().encode(
733
+ `data: ${JSON.stringify({
734
+ error: "获取任务ID失败",
735
+ })}\n\n`
736
+ )
737
+ );
738
+ await writer.close();
739
+ return;
740
+ }
741
+
742
+ // Now we can poll the task IDs and send images to the client
743
+ const maxRetries = 30;
744
+ const retryInterval = 2000;
745
+
746
+ for (const taskId of taskIds) {
747
+ let retries = 0;
748
+ let imageSent = false;
749
+ while (retries < maxRetries && !imageSent) {
750
+ try {
751
+ const statusResponse = await fetch(
752
+ `https://www.genspark.ai/api/spark/image_generation_task_status?task_id=${taskId}`,
753
+ {
754
+ headers,
755
+ }
756
+ );
757
+
758
+ if (!statusResponse.ok) {
759
+ throw new Error(
760
+ `Status check failed: ${statusResponse.status}`
761
+ );
762
+ }
763
+
764
+ const data = await statusResponse.json();
765
+ if (data.data.status === "SUCCESS") {
766
+ const urls = data.data.image_urls_nowatermark;
767
+ if (urls?.length) {
768
+ // Send the image URLs to the client
769
+ for (const url of urls) {
770
+ const data = {
771
+ id: `chatcmpl-${crypto.randomUUID()}`,
772
+ object: "chat.completion.chunk",
773
+ created: Math.floor(Date.now() / 1000),
774
+ model: model,
775
+ choices: [
776
+ {
777
+ delta: { content: `![Image](${url})\n` },
778
+ index: 0,
779
+ finish_reason: null,
780
+ },
781
+ ],
782
+ };
783
+ await writer.write(
784
+ new TextEncoder().encode(
785
+ `data: ${JSON.stringify(data)}\n\n`
786
+ )
787
+ );
788
+ }
789
+ imageSent = true;
790
+ break;
791
+ }
792
+ } else if (data.data.status === "FAILED") {
793
+ throw new Error("Image generation failed");
794
+ }
795
+ // No else, we just wait and retry
796
+ } catch (error) {
797
+ console.error(
798
+ `Poll error for task ${taskId} (retry ${retries}):`,
799
+ error
800
+ );
801
+ }
802
+ await new Promise((r) => setTimeout(r, retryInterval));
803
+ retries++;
804
+ }
805
+ if (!imageSent) {
806
+ const errorData = {
807
+ id: `chatcmpl-${crypto.randomUUID()}`,
808
+ object: "chat.completion.chunk",
809
+ created: Math.floor(Date.now() / 1000),
810
+ model: model,
811
+ choices: [
812
+ {
813
+ delta: {
814
+ content: `![Image](Image generation failed for task ${taskId})\n`,
815
+ },
816
+ index: 0,
817
+ finish_reason: null,
818
+ },
819
+ ],
820
+ };
821
+ await writer.write(
822
+ new TextEncoder().encode(
823
+ `data: ${JSON.stringify(errorData)}\n\n`
824
+ )
825
+ );
826
+ }
827
+ }
828
+
829
+ // Send the completion signal
830
+ await writer.write(new TextEncoder().encode("data: [DONE]\n\n"));
831
+ } catch (error) {
832
+ console.error("Stream error:", error);
833
+ await writer.abort(error);
834
+ } finally {
835
+ await writer.close();
836
+ }
837
+ })()
838
+ );
839
+
840
+ return new Response(readable, {
841
+ headers: {
842
+ ...corsHeaders,
843
+ "Content-Type": "text/event-stream",
844
+ "Cache-Control": "no-cache",
845
+ Connection: "keep-alive",
846
+ },
847
+ });
848
+ } else {
849
+ // 非流式处理
850
+ // 发送请求获取任务ID
851
+ const response = await fetch(
852
+ "https://www.genspark.ai/api/copilot/ask",
853
+ {
854
+ method: "POST",
855
+ headers,
856
+ body: JSON.stringify(requestBody),
857
+ }
858
+ );
859
+ if (!response.ok) {
860
+ return createErrorResponse("上游服务请求失败", response.status);
861
+ }
862
+ // 解析响应获取 task_ids
863
+ const text = await response.text();
864
+ const taskIds = [];
865
+ const lines = text.split("\n");
866
+ for (const line of lines) {
867
+ if (line.includes("task_id")) {
868
+ try {
869
+ const content = line.replace("data: ", "");
870
+ if (!content) continue;
871
+ const { content: innerContent } = JSON.parse(content);
872
+ if (!innerContent) continue;
873
+ const { generated_images } = JSON.parse(innerContent);
874
+ if (!generated_images) continue;
875
+ for (const img of generated_images) {
876
+ if (img.task_id) {
877
+ taskIds.push(img.task_id);
878
+ }
879
+ }
880
+ } catch (e) {}
881
+ }
882
+ }
883
+ if (!taskIds.length) {
884
+ return createErrorResponse("获取任务ID失败");
885
+ }
886
+ // 并行轮询任务结果
887
+ const maxRetries = 30;
888
+ const retryInterval = 2000;
889
+
890
+ const taskPromises = taskIds.map((taskId) =>
891
+ pollTaskId(taskId, session_id, maxRetries, retryInterval)
892
+ );
893
+ let imageUrls = [];
894
+ try {
895
+ const results = await Promise.all(taskPromises);
896
+ results.forEach((urls) => {
897
+ imageUrls.push(...urls);
898
+ });
899
+ } catch (error) {
900
+ console.error("Polling error:", error);
901
+ return createErrorResponse(error.message);
902
+ }
903
+
904
+ // 处理 response_format
905
+ if (response_format === "b64_json") {
906
+ // 使用 KV 缓存图像数据
907
+ const imageDatas = await Promise.all(
908
+ imageUrls.map(async (url) => {
909
+ const cachedData = await env.kv.get(url);
910
+ if (cachedData) {
911
+ return { b64_json: cachedData };
912
+ } else {
913
+ const imageResponse = await fetch(url);
914
+ const imageArrayBuffer = await imageResponse.arrayBuffer();
915
+ const base64Data = arrayBufferToBase64(imageArrayBuffer);
916
+ // 存储到 KV
917
+ await env.kv.put(url, base64Data, {
918
+ expirationTtl: CACHE_CONFIG.TTL,
919
+ });
920
+ return { b64_json: base64Data };
921
+ }
922
+ })
923
+ );
924
+
925
+ // 构建聊天回复
926
+ const imageMarkdowns = imageDatas
927
+ .map(
928
+ (imgData, idx) =>
929
+ `![Image](data:image/png;base64,${imgData.b64_json})`
930
+ )
931
+ .join("\n");
932
+
933
+ const messageId = crypto.randomUUID();
934
+
935
+ const result = {
936
+ id: `chatcmpl-${messageId}`,
937
+ object: "chat.completion",
938
+ created: Math.floor(Date.now() / 1000),
939
+ model: model,
940
+ choices: [
941
+ {
942
+ index: 0,
943
+ message: {
944
+ role: "assistant",
945
+ content: imageMarkdowns,
946
+ },
947
+ finish_reason: "stop",
948
+ },
949
+ ],
950
+ usage: {
951
+ prompt_tokens: 0,
952
+ completion_tokens: 0,
953
+ total_tokens: 0,
954
+ },
955
+ };
956
+
957
+ // 缓存结果
958
+ if (!CACHE_CONFIG.EXCLUDED_MODELS.includes(model)) {
959
+ messageCache.set(cacheKey, JSON.stringify(result));
960
+ }
961
+
962
+ return new Response(JSON.stringify(result), {
963
+ headers: {
964
+ ...corsHeaders,
965
+ "Content-Type": "application/json",
966
+ },
967
+ });
968
+ } else {
969
+ // 默认返回 URL,构建聊天回复
970
+ const imageMarkdowns = imageUrls
971
+ .map((url) => `![Image](${url})`)
972
+ .join("\n");
973
+
974
+ const messageId = crypto.randomUUID();
975
+
976
+ const result = {
977
+ id: `chatcmpl-${messageId}`,
978
+ object: "chat.completion",
979
+ created: Math.floor(Date.now() / 1000),
980
+ model: model,
981
+ choices: [
982
+ {
983
+ index: 0,
984
+ message: {
985
+ role: "assistant",
986
+ content: imageMarkdowns,
987
+ },
988
+ finish_reason: "stop",
989
+ },
990
+ ],
991
+ usage: {
992
+ prompt_tokens: 0,
993
+ completion_tokens: 0,
994
+ total_tokens: 0,
995
+ },
996
+ };
997
+
998
+ // 缓存结果
999
+ if (!CACHE_CONFIG.EXCLUDED_MODELS.includes(model)) {
1000
+ messageCache.set(cacheKey, JSON.stringify(result));
1001
+ }
1002
+
1003
+ return new Response(JSON.stringify(result), {
1004
+ headers: {
1005
+ ...corsHeaders,
1006
+ "Content-Type": "application/json",
1007
+ },
1008
+ });
1009
+ }
1010
+ }
1011
+ }
1012
+
1013
+ // 以下是处理非画图模型的逻辑
1014
+
1015
+ let response;
1016
+ if (model === "genspark") {
1017
+ response = await searchModel(session_id, messages);
1018
+ } else {
1019
+ response = await makeRequest(
1020
+ session_id,
1021
+ model,
1022
+ messages,
1023
+ target_lang_code,
1024
+ source_lang_code
1025
+ );
1026
+ }
1027
+
1028
+ if (!response.ok) {
1029
+ return createErrorResponse("上游服务请求失败", response.status);
1030
+ }
1031
+
1032
+ const messageId = crypto.randomUUID();
1033
+ let project_id = null;
1034
+
1035
+ if (stream === true || stream === "true") {
1036
+ const { readable, writable } = new TransformStream();
1037
+ const writer = writable.getWriter();
1038
+ ctx.waitUntil(
1039
+ (async () => {
1040
+ try {
1041
+ const reader = response.body.getReader();
1042
+ for await (const line of streamGenerator(reader)) {
1043
+ if (!project_id) {
1044
+ project_id = extractProjectId(line);
1045
+ }
1046
+ const processed = processChunk(line, messageId, model);
1047
+ if (processed) {
1048
+ await writer.write(new TextEncoder().encode(processed));
1049
+ }
1050
+ }
1051
+ await writer.write(new TextEncoder().encode("data: [DONE]\n\n"));
1052
+ } catch (error) {
1053
+ console.error("Stream error:", error);
1054
+ } finally {
1055
+ await writer.close();
1056
+ if (project_id) {
1057
+ await cleanupProject(session_id, project_id);
1058
+ }
1059
+ }
1060
+ })()
1061
+ );
1062
+ return new Response(readable, {
1063
+ headers: {
1064
+ ...corsHeaders,
1065
+ "Content-Type": "text/event-stream",
1066
+ "Cache-Control": "no-cache",
1067
+ Connection: "keep-alive",
1068
+ },
1069
+ });
1070
+ }
1071
+
1072
+ const chunks = [];
1073
+ const reader = response.body.getReader();
1074
+ try {
1075
+ while (true) {
1076
+ const { done, value } = await reader.read();
1077
+ if (done) break;
1078
+ const text = new TextDecoder().decode(value);
1079
+ chunks.push(text);
1080
+ if (!project_id) {
1081
+ const lines = text.split("\n");
1082
+ for (const line of lines) {
1083
+ project_id = extractProjectId(line);
1084
+ if (project_id) break;
1085
+ }
1086
+ }
1087
+ }
1088
+ } finally {
1089
+ reader.releaseLock();
1090
+ if (project_id) {
1091
+ ctx.waitUntil(cleanupProject(session_id, project_id));
1092
+ }
1093
+ }
1094
+
1095
+ const fullResponse = chunks.join("");
1096
+ for (const line of fullResponse.split("\n")) {
1097
+ if (line.startsWith("data: ")) {
1098
+ try {
1099
+ const content = JSON.parse(line.slice(6));
1100
+ if (model === "deepl") {
1101
+ // 处理翻译模型的响应
1102
+ if (content.field_name === "content" && content.field_value) {
1103
+ const result = JSON.stringify({
1104
+ code: 200,
1105
+ data: {
1106
+ translations: [
1107
+ {
1108
+ detected_source_language:
1109
+ content.session_state?.detected_source_lang || "",
1110
+ text: content.field_value,
1111
+ },
1112
+ ],
1113
+ },
1114
+ msg: "ok",
1115
+ });
1116
+ // 缓存结果
1117
+ if (!CACHE_CONFIG.EXCLUDED_MODELS.includes(model)) {
1118
+ messageCache.set(cacheKey, result);
1119
+ }
1120
+ return new Response(result, {
1121
+ headers: { ...corsHeaders, "Content-Type": "application/json" },
1122
+ });
1123
+ }
1124
+ } else {
1125
+ // 处理其他模型的响应
1126
+ if (
1127
+ content.field_value &&
1128
+ content.field_name !== "session_state.answer_is_finished" &&
1129
+ content.field_name !== "content" &&
1130
+ content.field_name !== "session_state" &&
1131
+ !content.delta &&
1132
+ content.type !== "project_field"
1133
+ ) {
1134
+ const result = JSON.stringify({
1135
+ id: `chatcmpl-${messageId}`,
1136
+ object: "chat.completion",
1137
+ created: Math.floor(Date.now() / 1000),
1138
+ model,
1139
+ choices: [
1140
+ {
1141
+ index: 0,
1142
+ message: {
1143
+ role: "assistant",
1144
+ content: content.field_value,
1145
+ },
1146
+ finish_reason: "stop",
1147
+ },
1148
+ ],
1149
+ usage: {
1150
+ prompt_tokens: 0,
1151
+ completion_tokens: 0,
1152
+ total_tokens: content.field_value.length,
1153
+ },
1154
+ });
1155
+ // 缓存结果
1156
+ if (!CACHE_CONFIG.EXCLUDED_MODELS.includes(model)) {
1157
+ messageCache.set(cacheKey, result);
1158
+ }
1159
+ return new Response(result, {
1160
+ headers: { ...corsHeaders, "Content-Type": "application/json" },
1161
+ });
1162
+ }
1163
+ }
1164
+ } catch (e) {}
1165
+ }
1166
+ }
1167
+ return createErrorResponse("无效的响应数据");
1168
+ } catch (error) {
1169
+ console.error("Request processing error:", error);
1170
+ return createErrorResponse("请求处理失败");
1171
+ }
1172
+ }
1173
+
1174
+ // 添加新的画图路由
1175
+ if (
1176
+ url.pathname === "/v1/images/generations" &&
1177
+ request.method === "POST"
1178
+ ) {
1179
+ try {
1180
+ const requestData = await request.json();
1181
+ // 参数验证
1182
+ if (!requestData.prompt && !requestData.messages) {
1183
+ return new Response(
1184
+ JSON.stringify({ error: "Prompt is required" }),
1185
+ {
1186
+ status: 400,
1187
+ headers: { ...corsHeaders, "Content-Type": "application/json" },
1188
+ }
1189
+ );
1190
+ }
1191
+ // 获取随机session_id
1192
+ const authHeader = request.headers.get("authorization");
1193
+ if (sessionPool.size() === 0) {
1194
+ const sessionString = authHeader?.replace("Bearer ", "");
1195
+ sessionPool.initialize(sessionString);
1196
+ if (sessionPool.size() === 0) {
1197
+ return createErrorResponse("未提供有效的 session_id", 401);
1198
+ }
1199
+ }
1200
+ const session_id = sessionPool.getNext();
1201
+ if (!session_id) {
1202
+ return createErrorResponse("无可用的会话ID", 500);
1203
+ }
1204
+ // 提取 response_format
1205
+ const {
1206
+ response_format = "url",
1207
+ num_images = 1,
1208
+ size = "1024x1024",
1209
+ quality = "standard",
1210
+ style = "auto",
1211
+ image,
1212
+ model = "dall-e-3",
1213
+ aspect_ratio=auto,
1214
+ hd=true,
1215
+ reflection_enabled=false,
1216
+ } = requestData;
1217
+ // 构建请求体
1218
+ let messages = [];
1219
+
1220
+ if (requestData.messages) {
1221
+ messages = requestData.messages;
1222
+ // 处理 messages,处理图片
1223
+ await processMessages(messages, env);
1224
+ } else {
1225
+ // 构建带有 prompt 和 image 的 messages
1226
+ if (image) {
1227
+ // image 可以是 base64 编码的图像数据
1228
+ let base64ImageData = image;
1229
+ if (!base64ImageData.startsWith('data:image')) {
1230
+ // 假设是 base64 编码的数据,包装为数据 URL
1231
+ base64ImageData = `data:image/jpeg;base64,${image}`;
1232
+ }
1233
+ messages.push({
1234
+ role: 'user',
1235
+ content: [
1236
+ { type: 'image_url', image_url: { url: base64ImageData } },
1237
+ { type: 'text', text: requestData.prompt },
1238
+ ],
1239
+ });
1240
+ } else {
1241
+ // 没有图片,仅使用提示词
1242
+ messages.push({ role: 'user', content: requestData.prompt });
1243
+ }
1244
+ }
1245
+
1246
+ await processMessages(messages, env);
1247
+ const user_s_input = extractLastUserInput(messages);
1248
+
1249
+ const requestBody = {
1250
+ type: "COPILOT_MOA_IMAGE",
1251
+ current_query_string: "type=COPILOT_MOA_IMAGE",
1252
+ messages: messages,
1253
+ user_s_input: user_s_input,
1254
+ action_params: {},
1255
+ extra_data: {
1256
+ model_configs: [],
1257
+ llm_model: "gpt-4o",
1258
+ imageModelMap: {},
1259
+ writingContent: null,
1260
+ },
1261
+ };
1262
+ for (let i = 0; i < num_images; i++) {
1263
+ requestBody.extra_data.model_configs.push({
1264
+ model: model === "dall-e-3" ? "dalle-3" : model,
1265
+ aspect_ratio,
1266
+ use_personalized_models:false,
1267
+ fashion_profile_id:null,
1268
+ hd,
1269
+ reflection_enabled,
1270
+ style: style
1271
+ });
1272
+ }
1273
+ // 发送请求获取任务ID
1274
+ const response = await fetch(
1275
+ "https://www.genspark.ai/api/copilot/ask",
1276
+ {
1277
+ method: "POST",
1278
+ headers: {
1279
+ Cookie: `session_id=${session_id}`,
1280
+ "User-Agent": "Apifox/1.0.0 (https://apifox.com)",
1281
+ "Content-Type": "application/json",
1282
+ Accept: "*/*",
1283
+ Host: "www.genspark.ai",
1284
+ Connection: "keep-alive",
1285
+ },
1286
+ body: JSON.stringify(requestBody),
1287
+ }
1288
+ );
1289
+ if (!response.ok) {
1290
+ return createErrorResponse("上游服务请求失败", response.status);
1291
+ }
1292
+ // 解析响应获取task_id
1293
+ const text = await response.text();
1294
+ const taskIds = [];
1295
+ const lines = text.split("\n");
1296
+ for (const line of lines) {
1297
+ if (line.includes("task_id")) {
1298
+ try {
1299
+ const content = line.replace("data: ", "");
1300
+ if (!content) continue;
1301
+ const { content: innerContent } = JSON.parse(content);
1302
+ if (!innerContent) continue;
1303
+ const { generated_images } = JSON.parse(innerContent);
1304
+ if (!generated_images) continue;
1305
+ for (const img of generated_images) {
1306
+ if (img.task_id) {
1307
+ taskIds.push(img.task_id);
1308
+ }
1309
+ }
1310
+ } catch (e) {}
1311
+ }
1312
+ }
1313
+ if (!taskIds.length) {
1314
+ return createErrorResponse("获取任务ID失败");
1315
+ }
1316
+ // 并行轮询任务结果
1317
+ const maxRetries = 300;
1318
+ const retryInterval = 2000;
1319
+
1320
+ const taskPromises = taskIds.map((taskId) =>
1321
+ pollTaskId(taskId, session_id, maxRetries, retryInterval)
1322
+ );
1323
+ let imageUrls = [];
1324
+ try {
1325
+ const results = await Promise.all(taskPromises);
1326
+ results.forEach((urls) => {
1327
+ imageUrls.push(...urls);
1328
+ });
1329
+ } catch (error) {
1330
+ console.error("Polling error:", error);
1331
+ return createErrorResponse(error.message);
1332
+ }
1333
+
1334
+ if (response_format === "b64_json") {
1335
+ // 使用 KV 缓存图像数据
1336
+ const imageDatas = await Promise.all(
1337
+ imageUrls.map(async (url) => {
1338
+ const cachedData = await env.kv.get(url);
1339
+ if (cachedData) {
1340
+ return { b64_json: cachedData };
1341
+ } else {
1342
+ const imageResponse = await fetch(url);
1343
+ const imageArrayBuffer = await imageResponse.arrayBuffer();
1344
+ const base64Data = arrayBufferToBase64(imageArrayBuffer);
1345
+ // 存储到 KV
1346
+ await env.kv.put(url, base64Data, { expirationTtl: CACHE_CONFIG.TTL });
1347
+ return { b64_json: base64Data };
1348
+ }
1349
+ })
1350
+ );
1351
+
1352
+ return new Response(
1353
+ JSON.stringify({
1354
+ created: Date.now(),
1355
+ data: imageDatas,
1356
+ }),
1357
+ { headers: { ...corsHeaders, "Content-Type": "application/json" } }
1358
+ );
1359
+ } else {
1360
+ // 默认返回 URL
1361
+ return new Response(
1362
+ JSON.stringify({
1363
+ created: Date.now(),
1364
+ data: imageUrls.map((url) => ({
1365
+ url,
1366
+ revised_prompt: requestData.prompt,
1367
+ })),
1368
+ }),
1369
+ { headers: { ...corsHeaders, "Content-Type": "application/json" } }
1370
+ );
1371
+ }
1372
+ } catch (error) {
1373
+ console.error("Request processing error:", error);
1374
+ return createErrorResponse("请求处理失败");
1375
+ }
1376
+ }
1377
+
1378
+
1379
+ return new Response("Not Found", {
1380
+ status: 404,
1381
+ headers: corsHeaders,
1382
+ });
1383
+ },
1384
+ };