import { serve } from "https://deno.land/std@0.220.1/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} 返回成功创建的聊天的 ID (string),或在失败时返回 500。 */ async function createChatAndGetId(): Promise { 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 { 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 ) 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 ' 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 });