Spaces:
Running
Running
File size: 17,818 Bytes
720fe41 c10af0f 720fe41 c10af0f 720fe41 105e682 c10af0f 720fe41 105e682 720fe41 430c991 720fe41 430c991 105e682 720fe41 430c991 720fe41 430c991 105e682 720fe41 430c991 720fe41 73cb3e1 430c991 720fe41 105e682 720fe41 73cb3e1 2ff0f55 |
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 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
import { serve } from "https://deno.land/[email protected]/http/server.ts";
// --- 配置常量 ---
const AUTH_KEY = Deno.env.get("AUTH_KEY") ?? "default_api_key_value"; //API密钥在Environment Variables中添加,否则默认是default_api_key_value
const TARGET_URL = 'https://assistant.on.adaptive.ai/api/sendMessage';
const PROXY_MODEL_NAME = "gpt-4o"; // 代理服务返回的模型名称
const TARGET_HEADERS = {
'content-type': 'application/json',
'x-channel-id': "0"
};
// --- 辅助函数:创建 SSE 数据块 ---
function createSSEChunk(id: string, model: string, content: string | null, role: string | null, finish_reason: string | null): string {
const now = Math.floor(Date.now() / 1000);
const chunk: any = {
id: id,
object: "chat.completion.chunk",
created: now,
model: model,
choices: [
{
index: 0,
delta: {},
finish_reason: finish_reason,
logprobs: null,
}
],
// system_fingerprint: null, // 可选
};
if (role) {
chunk.choices[0].delta.role = role;
}
if (content) {
chunk.choices[0].delta.content = content;
}
// 如果 delta 为空且有 finish_reason,确保 delta 是空对象
if (!role && !content && finish_reason) {
chunk.choices[0].delta = {};
}
return `data: ${JSON.stringify(chunk)}\n\n`;
}
/**
* 向 Adaptive AI 发送创建新聊天的请求。
* 成功时返回新聊天的 ID (字符串)。
* 如果在过程中发生任何错误,则捕获错误并返500。
*
* @returns {Promise<string | 500>} 返回成功创建的聊天的 ID (string),或在失败时返回 500。
*/
async function createChatAndGetId(): Promise<string> {
const url = 'https://assistant.on.adaptive.ai/api/createChat';
// 定义请求头 (省略了 cookie 和 priority)
const headers = {
'content-type': 'application/json',
};
// 构建请求体,使用当前时间戳作为请求 ID
const payload = {
json: {
jsonrpc: "2.0",
id: Date.now(), // 使用动态 ID
method: "createChat",
params: []
}
};
console.log("正在发送创建聊天请求...");
try {
const response = await fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(payload) // 将 payload 对象转换为 JSON 字符串
});
// 检查 HTTP 响应状态码是否表示成功
if (!response.ok) {
let errorBody = "无法读取响应体";
try {
errorBody = await response.text(); // 尝试读取错误响应体
} catch (readError) {
console.warn("读取错误响应体失败:", readError);
}
throw new Error(`创建聊天请求失败,HTTP 状态码: ${response.status}. 响应: ${errorBody}`);
}
// 解析 JSON 响应体
let responseData: any;
try {
responseData = await response.json();
} catch (parseError) {
// 如果响应不是有效的 JSON,则抛出错误
console.error("解析创建聊天响应 JSON 时出错:", parseError);
throw new Error(`无法将响应解析为 JSON: ${parseError.message}`);
}
// 提取并验证 ID
// ID 预期在 responseData.json.result.id
// 使用可选链操作符 (?.) 来安全地访问嵌套属性,防止因中间属性不存在而报错
const chatId = responseData?.json?.result?.id;
// 检查提取到的 ID 是否是一个有效的、非空的字符串
if (typeof chatId === 'string' && chatId.length > 0) {
console.log(`成功创建聊天,获取到 ID: ${chatId}`);
return chatId; // 返回提取到的 ID
} else {
// 如果 ID 不存在或格式不正确,则抛出错误
console.error("从响应中未能提取有效的聊天 ID。响应数据:", JSON.stringify(responseData));
throw new Error("创建聊天的响应格式无效或缺少 'json.result.id' 字段。");
}
} catch (error) {
// 捕获 try 块中抛出的任何错误,或 fetch 本身的网络错误
const errorMessage = error instanceof Error ? error.message : String(error);
console.error("执行 createChatAndGetId 时发生错误:", errorMessage); // 记录详细错误信息
// 创建并返回一个 Response 对象
// 状态码为 500 (Internal Server Error)
// 响应体为 JSON 格式,包含错误信息
return new Response(
JSON.stringify({ error: `创建聊天会话失败: ${errorMessage}` }), // 将错误信息包装在 JSON 对象中
{
status: 500, // 设置 HTTP 状态码为 500
headers: {
"Content-Type": "application/json", // 设置响应内容类型为 JSON
"Access-Control-Allow-Origin": "*" // 如果需要跨域,添加此头
}
}
);
}
}
// --- 主处理函数 ---
async function handler(req: Request): Promise<Response> {
const url = new URL(req.url);
// --- CORS 预检请求处理 ---
if (req.method === "OPTIONS") {
return new Response(null, {
status: 204,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
"Access-Control-Max-Age": "86400",
},
});
}
// 模型列表接口
if (url.pathname === "/v1/models" && req.method === "GET") {
return new Response(
JSON.stringify({
object: "list",
data: [
{
id: "gpt-4o",
object: "model",
created: 0,
owned_by: "unlimitedai",
permission: [{
id: "modelperm-gpt-4o",
object: "model_permission",
created: 0,
allow_create_engine: false,
allow_sampling: true,
allow_logprobs: false,
allow_search_indices: false,
allow_view: true,
allow_fine_tuning: false,
organization: "*",
group: null,
is_blocking: false,
}],
root: "gpt-4o",
parent: null,
},
],
}),
{
status: 200,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
}
);
}
// --- 路径和方法检查 ---
if (url.pathname !== "/v1/chat/completions" || req.method !== "POST") {
return new Response(JSON.stringify({ error: "Not Found or Method Not Allowed" }), {
status: 404,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
});
}
// --- 添加认证检查 ---
const authHeader = req.headers.get("Authorization");
let providedKey = "";
// 检查 Authorization header 是否存在且格式正确 (Bearer <key>)
if (!authHeader || !authHeader.toLowerCase().startsWith("bearer ")) {
console.warn(`认证失败: 缺少或格式错误的 Authorization header`);
return new Response(JSON.stringify({
error: {
message: "Unauthorized: Missing or invalid Authorization header. Use 'Bearer <YOUR_API_KEY>' format.",
type: "invalid_request_error",
param: null,
code: "missing_or_invalid_header"
}
}), {
status: 401, // Unauthorized
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"WWW-Authenticate": 'Bearer realm="API Access"'
}
});
}
// 提取 key 部分
providedKey = authHeader.substring(7); // "Bearer ".length is 7
console.log("providedKey:" + providedKey);
// 直接比较提供的 key 和硬编码的 key
if (providedKey !== AUTH_KEY) {
console.warn(`认证失败: 无效的 API Key 提供`);
return new Response(JSON.stringify({
error: {
message: "Unauthorized: Invalid API Key provided.",
type: "invalid_request_error",
param: null,
code: "inAUTH_KEY"
}
}), {
status: 401, // Unauthorized
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"WWW-Authenticate": 'Bearer realm="API Access"'
}
});
}
// --- 处理 POST 请求 ---
try {
// 1. 解析入站请求体
let requestBody: any;
try {
requestBody = await req.json();
console.log(requestBody)
} catch (e) {
console.error("Failed to parse request JSON:", e);
return new Response(JSON.stringify({ error: "Invalid JSON in request body" }), {
status: 400, headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" },
});
}
// 2. 检查是否请求流式响应
const isStream = requestBody.stream === true;
// 3. 提取用户输入的内容 - 将 messages 数组转换为字符串
let userContent: string | undefined;
if (Array.isArray(requestBody.messages) && requestBody.messages.length > 0) {
try {
// 直接将整个 messages 数组转换为 JSON 字符串
userContent = JSON.stringify(requestBody.messages);
} catch (e) {
console.error("Failed to stringify 'messages' array:", e);
// 如果 JSON.stringify 失败 (虽然对数组不太可能,但以防万一)
return new Response(JSON.stringify({ error: "Failed to process 'messages' array." }), {
status: 400, headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" },
});
}
}
// 检查 userContent 是否成功生成
// 如果 requestBody.messages 不存在、不是数组、为空,或者转换出错,userContent 会是 undefined
if (!userContent) {
console.error("Request body must contain a non-empty 'messages' array.");
return new Response(JSON.stringify({ error: "Request body must contain a non-empty 'messages' array." }), {
status: 400, headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" },
});
}
// 现在 userContent 包含了整个对话历史的字符串表示
console.log("Formatted user content:", userContent); // 可以取消注释来调试输出
const CHAT_ID = await createChatAndGetId();//获取新的聊天ID
// 4. 构建目标 API Payload
const payload = {
json: {
jsonrpc: "2.0", id: Date.now(), method: "sendMessage",
params: [{ chatId: CHAT_ID, content: userContent, fileId: null, fileIds: [] }]
},
meta: { values: { "params.0.fileId": ["undefined"] } }
};
// 5. 发送请求到目标 API (无论是否流式,都需要先获取完整响应)
console.log("Forwarding request to:", TARGET_URL);
const targetResponse = await fetch(TARGET_URL, {
method: 'POST', headers: TARGET_HEADERS, body: JSON.stringify(payload),
});
// 6. 处理目标 API 的响应
if (!targetResponse.ok) {
const errorBody = await targetResponse.text();
console.error(`Target API Error (${targetResponse.status}):`, errorBody);
// 即使是流式请求失败,也返回 JSON 错误
return new Response(JSON.stringify({ error: `Upstream API request failed with status ${targetResponse.status}`, details: errorBody }), {
status: 502, headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" },
});
}
// 7. 解析目标 API 的 JSON 响应
let targetData: any;
try {
targetData = await targetResponse.json();
} catch (e) {
console.error("Failed to parse target API response JSON:", e);
// 即使是流式请求失败,也返回 JSON 错误
return new Response(JSON.stringify({ error: "Failed to parse upstream API response" }), {
status: 500, headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" },
});
}
// 8. 从目标响应中提取内容
const assistantContent = targetData?.json?.result?.content;
if (typeof assistantContent !== 'string') {
console.error("Could not extract 'content' from target API response:", JSON.stringify(targetData));
// 即使是流式请求失败,也返回 JSON 错误
return new Response(JSON.stringify({ error: "Invalid response format from upstream API" }), {
status: 500, headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" },
});
}
// 9. 根据 isStream 决定返回格式
const chatCompletionId = `chatcmpl-${crypto.randomUUID()}`; // 为本次交互生成唯一 ID
const modelName = requestBody.model || PROXY_MODEL_NAME; // 确定模型名称
if (isStream) {
// --- 返回模拟的流式响应 ---
console.log("Simulating stream response...");
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
try {
// 模拟发送块
// 块 1: 发送角色信息
controller.enqueue(encoder.encode(
createSSEChunk(chatCompletionId, modelName, null, "assistant", null)
));
await new Promise(resolve => setTimeout(resolve, 10)); // 短暂延迟,模拟处理
// 块 2: 发送完整内容
controller.enqueue(encoder.encode(
createSSEChunk(chatCompletionId, modelName, assistantContent, null, null)
));
await new Promise(resolve => setTimeout(resolve, 10)); // 短暂延迟
// 块 3: 发送结束信号
controller.enqueue(encoder.encode(
createSSEChunk(chatCompletionId, modelName, null, null, "stop")
));
// 发送 [DONE] 标记
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
// 关闭流
controller.close();
} catch (error) {
console.error("Error during stream simulation:", error);
controller.error(error); // 通知流出错了
}
}
});
return new Response(stream, {
status: 200,
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive', // 建议 SSE 使用
'Access-Control-Allow-Origin': '*'
},
});
} else {
// --- 返回完整的 JSON 响应 ---
console.log("Returning non-stream response.");
const finalResponse = {
id: chatCompletionId,
object: "chat.completion",
created: Math.floor(Date.now() / 1000),
model: modelName,
choices: [
{
index: 0,
message: {
role: "assistant",
content: assistantContent,
},
finish_reason: "stop",
logprobs: null,
}
],
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
};
return new Response(JSON.stringify(finalResponse), {
status: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
});
}
} catch (error) {
// --- 全局错误处理 ---
console.error("Unhandled error in handler:", error);
// 即使请求流式,也返回 JSON 错误
return new Response(JSON.stringify({ error: "Internal Server Error" }), {
status: 500,
headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" },
});
}
}
serve(handler, { port: 7860 }); |