-
-
Notifications
You must be signed in to change notification settings - Fork 193
/
Copy pathcloudflare-worker
361 lines (340 loc) · 13.7 KB
/
cloudflare-worker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
// 在顶部定义CORS头部
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', // 允许的HTTP方法
'Access-Control-Allow-Headers': 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization',
'Access-Control-Max-Age': '86400', // 预检请求结果的缓存时间
};
addEventListener('fetch', event => {
console.log(`收到请求: ${event.request.method} ${event.request.url}`);
const url = new URL(event.request.url);
// 处理 CORS 预检请求
if (event.request.method === 'OPTIONS') {
return event.respondWith(handleOptions());
}
const apiBase = typeof APIBASE !== 'undefined' ? APIBASE : 'https://api.openai.com';
const authHeader = event.request.headers.get('Authorization'); // 从请求的 headers 中获取 Authorization
let apiKey = '';
if (authHeader) {
apiKey = authHeader.split(' ')[1]; // 从 Authorization 中获取 API key
} else {
return event.respondWith(new Response('Authorization header is missing', {status: 400, headers: corsHeaders}));
}
if (url.pathname === '/v1/chat/completions') {
console.log('接收到 fetch 事件');
event.respondWith(handleRequest(event.request, apiBase, apiKey));
} else {
event.respondWith(handleOtherRequest(apiBase, apiKey, event.request, url.pathname).then(response => {
return new Response(response.body, {
status: response.status,
headers: { ...response.headers, ...corsHeaders }
});
}));
}
})
// 处理 OPTIONS 请求
function handleOptions() {
return new Response(null, {
status: 204,
headers: corsHeaders
});
}
async function handleOtherRequest(apiBase, apiKey, request, pathname) {
// 创建一个新的 Headers 对象,复制原始请求的所有头部,但不包括 Host 头部
const headers = new Headers(request.headers);
headers.delete('Host');
headers.set('Authorization', `Bearer ${apiKey}`);
// 对所有请求,直接转发
const response = await fetch(`${apiBase}${pathname}`, {
method: request.method,
headers: headers,
body: request.body
});
let data;
if (pathname.startsWith('/v1/audio/')) {
// 如果路径以 '/v1/audio/' 开头,处理音频文件
data = await response.arrayBuffer();
return new Response(data, {
status: response.status,
headers: { 'Content-Type': 'audio/mpeg', ...corsHeaders }
});
} else {
// 对于其他路径,处理 JSON 数据
data = await response.json();
return new Response(JSON.stringify(data), {
status: response.status,
headers: corsHeaders
});
}
}
// 搜索函数,调用您的搜索服务
async function search(query) {
console.log(`正在使用查询进行自定义搜索: ${JSON.stringify(query)}`);
try {
const response = await fetch('https://search.search2ai.one', {
method: 'POST',
headers: {
"Content-Type": "application/json",
"google_cx": typeof GOOGLE_CX !== 'undefined' ? GOOGLE_CX : '',
"google_key": typeof GOOGLE_KEY !== 'undefined' ? GOOGLE_KEY : '',
"serpapi_key": typeof SERPAPI_KEY !== 'undefined' ? SERPAPI_KEY : '',
"serper_key": typeof SERPER_KEY !== 'undefined' ? SERPER_KEY : '',
"bing_key": typeof BING_KEY !== 'undefined' ? BING_KEY : '',
"apibase": typeof APIBASE !== 'undefined' ? APIBASE : 'https://api.openai.com'
},
body: JSON.stringify({
query: query,
search_service: SEARCH_SERVICE
})
});
if (!response.ok) {
console.error(`API 请求失败, 状态码: ${response.status}`);
return `API 请求失败, 状态码: ${response.status}`;
}
const contentType = response.headers.get("content-type");
if (!contentType || !contentType.includes("application/json")) {
console.error("收到的响应不是有效的 JSON 格式");
return "收到的响应不是有效的 JSON 格式";
}
const data = await response.json();
console.log('自定义搜索服务调用完成');
return JSON.stringify(data);
}catch (error) {
console.error(`在 search 函数中捕获到错误: ${error}`);
return `在 search 函数中捕获到错误: ${error}`;
}
}
// 爬取函数,调用你的爬取服务
async function crawer(url) {
console.log(`正在使用 URL 进行自定义爬取:${JSON.stringify(url)}`);
try {
const response = await fetch('https://crawer.search2ai.one', {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
url: url
})
});
if (!response.ok) {
console.error(`API 请求失败, 状态码: ${response.status}`);
return `API 请求失败, 状态码: ${response.status}`;
}
const contentType = response.headers.get("content-type");
if (!contentType || !contentType.includes("application/json")) {
console.error("收到的响应不是有效的 JSON 格式");
return "收到的响应不是有效的 JSON 格式";
}
const data = await response.json();
console.log('自定义爬取服务调用完成');
return JSON.stringify(data);
} catch (error) {
console.error(`在 crawl 函数中捕获到错误: ${error}`);
return `在 crawer 函数中捕获到错误: ${error}`;
}
}
async function handleRequest(request, apiBase, apiKey) {
console.log(`开始处理请求: ${request.method} ${request.url}`);
// 确保请求是我们可以处理的类型
if (request.method !== 'POST') {
console.log(`不支持的请求方法: ${request.method}`);
return new Response('Method Not Allowed', { status: 405, headers: corsHeaders });
}
const requestData = await request.json();
console.log('请求数据:', requestData);
const stream = requestData.stream || false;
const userMessages = requestData.messages.filter(message => message.role === 'user');
const latestUserMessage = userMessages[userMessages.length - 1];
const model = requestData.model
const isContentArray = Array.isArray(latestUserMessage.content);
const defaultMaxTokens = 3000;
const maxTokens = requestData.max_tokens || defaultMaxTokens; // 使用默认 max_tokens 如果未提供
const body = JSON.stringify({
model: model,
messages: requestData.messages,
max_tokens: maxTokens,
...(isContentArray ? {} : {
tools: [
{
type: "function",
function: {
name: "search",
description: "search for news and factors",
parameters: {
type: "object",
properties: {
query: { type: "string","description": "The query to search."}
},
required: ["query"]
}
}
},
{
type: "function",
function: {
name: "crawer",
description: "Get the content of a specified webpage",
parameters: {
type: "object",
properties: {
url: {
type: "string",
description: "The URL of the webpage"},
},
required: ["url"],
}
}
}
],
tool_choice: "auto"
})
});
console.log('请求体:', body);
const openAIResponse = await fetch(`${apiBase}/v1/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}` // 使用从请求的 headers 中获取的 API key
},
body: body
});
const data = await openAIResponse.json();
console.log('OpenAI API 响应状态码:', openAIResponse.status);
if (!data.choices || data.choices.length === 0) {
console.log('数据中没有选择项');
return new Response('数据中没有选择项', { status: 500 });
}
console.log('OpenAI API 响应接收完成,检查是否需要调用自定义函数');
let messages = requestData.messages;
messages.push(data.choices[0].message);
// 检查是否有函数调用
let calledCustomFunction = false;
if (data.choices[0].message.tool_calls) {
const toolCalls = data.choices[0].message.tool_calls;
const availableFunctions = {
"search": search,
"crawer": crawer
};
for (const toolCall of toolCalls) {
const functionName = toolCall.function.name;
const functionToCall = availableFunctions[functionName];
const functionArgs = JSON.parse(toolCall.function.arguments);
let functionResponse;
if (functionName === 'search') {
functionResponse = await functionToCall(functionArgs.query);
} else if (functionName === 'crawer') {
functionResponse = await functionToCall(functionArgs.url);
}
console.log('工具调用的响应: ', functionResponse);
messages.push({
tool_call_id: toolCall.id,
role: "tool",
name: functionName,
content: functionResponse,
});
if (functionName === "search" || functionName === "crawer") {
calledCustomFunction = true;
}
}
console.log('准备发送第二次 OpenAI API 请求');
const requestBody = {
model: model,
messages: messages,
stream: stream
};
const secondResponse = await fetch(`${apiBase}/v1/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': `Bearer ${apiKey}` // 使用从请求的 headers 中获取的 API key
},
body: JSON.stringify(requestBody)
});
console.log('响应状态码: 200');
if (stream) {
// 使用 SSE 格式
return new Response(secondResponse.body, {
status: secondResponse.status,
headers: {
'Content-Type': 'text/event-stream',
...corsHeaders,
}
});
} else {
// 使用普通 JSON 格式
const data = await secondResponse.json();
return new Response(JSON.stringify(data), {
status: 200,
headers: {
'Content-Type': 'application/json',
...corsHeaders,
}
});
} }
if (!calledCustomFunction) {
// 没有调用自定义函数,直接返回原始回复
console.log('响应状态码: 200');
// 创建一个将 JSON 数据转换为 SSE 格式的流的函数
function jsonToStream(jsonData) {
const encoder = new TextEncoder();
const delay = 10; // 延迟0.01秒
return new ReadableStream({
start(controller) {
const characters = Array.from(jsonData.choices[0].message.content);
let i = 0;
function pushCharacter() {
if (i >= characters.length) {
controller.enqueue(encoder.encode('data: [DONE]\n\n'));
controller.close();
} else {
const character = characters[i];
const newJsonData = {
id: jsonData.id,
object: 'chat.completion.chunk',
created: jsonData.created,
model: jsonData.model,
choices: [
{
index: 0,
delta: {
content: character
},
logprobs: null,
finish_reason: i === characters.length - 1 ? 'stop' : null
}
],
system_fingerprint: jsonData.system_fingerprint
};
controller.enqueue(encoder.encode(`data: ${JSON.stringify(newJsonData)}\n\n`));
i++;
setTimeout(pushCharacter, delay);
}
}
pushCharacter();
}
});
}
if (stream) {
// 使用 SSE 格式
const sseStream = jsonToStream(data);
return new Response(sseStream,{
status: 200,
headers: {
'Content-Type': 'text/event-stream',
...corsHeaders,
}
});
} else {
// 使用普通 JSON 格式
return new Response(JSON.stringify(data), {
status: 200,
headers: {
'Content-Type': 'application/json',
...corsHeaders,
}
});
}
}
}