// freeai_proxy.ts import { serve } from "https://deno.land/std@0.190.0/http/server.ts"; const FREEAI_API_BASE = "https://freeaichatplayground.com/api/v1"; const DEFAULT_MODEL = "Deepseek R1"; // 获取可用模型列表 async function fetchModels() { try { const response = await fetch(`${FREEAI_API_BASE}/models`, { method: "POST", headers: { "Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:137.0) Gecko/20100101 Firefox/137.0", "Origin": "https://freeaichatplayground.com", "Referer": "https://freeaichatplayground.com/chat", }, body: JSON.stringify({ type: "text" }), }); if (!response.ok) { throw new Error(`Failed to fetch models: ${response.status}`); } const models = await response.json(); return models; } catch (error) { console.error("Error fetching models:", error); return []; } } // 转换为 OpenAI 格式的模型列表 function transformModelsToOpenAIFormat(models) { return { object: "list", data: models.map(model => ({ id: model.name, object: "model", created: new Date(model.createdAt).getTime() / 1000, owned_by: model.provider, permission: [], root: model.name, parent: null, })), }; } // 解析 SSE 格式的响应 async function parseSSEResponse(response) { const reader = response.body.getReader(); let content = ""; let id = `chatcmpl-${Date.now()}`; let finishReason = "stop"; try { while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = new TextDecoder().decode(value); content += chunk; } // 解析所有 SSE 消息 const messages = content.split('\n\n') .filter(msg => msg.trim().startsWith('data:')) .map(msg => { const jsonStr = msg.replace('data:', '').trim(); try { return JSON.parse(jsonStr); } catch (e) { console.warn("Failed to parse SSE message:", jsonStr); return null; } }) .filter(Boolean); // 找到最后一条完整消息 const lastCompleteMessage = messages.findLast(msg => msg.choices && msg.choices[0] && msg.choices[0].message && msg.choices[0].message.content ); if (lastCompleteMessage) { id = lastCompleteMessage.id || id; if (lastCompleteMessage.choices && lastCompleteMessage.choices[0] && lastCompleteMessage.choices[0].finish_reason) { finishReason = lastCompleteMessage.choices[0].finish_reason; } return { id, content: lastCompleteMessage.choices[0].message.content, finish_reason: finishReason, usage: lastCompleteMessage.usage || null }; } // 如果没有找到完整消息,尝试从所有消息中提取内容 let combinedContent = ""; for (const msg of messages) { if (msg.choices && msg.choices[0] && msg.choices[0].delta && msg.choices[0].delta.content) { combinedContent += msg.choices[0].delta.content; } else if (msg.choices && msg.choices[0] && msg.choices[0].message && msg.choices[0].message.content) { combinedContent += msg.choices[0].message.content; } } return { id, content: combinedContent || "No content found in response", finish_reason: finishReason, usage: null }; } catch (error) { console.error("Error parsing SSE response:", error); return { id, content: "Error parsing response: " + error.message, finish_reason: "error", usage: null }; } } // 发送聊天请求到 freeaichatplayground async function sendChatRequest(modelName, messages) { try { const formattedMessages = messages.map((msg, index) => ({ id: `${Date.now() + index}`, role: msg.role, content: msg.content, model: { id: "", // 这个ID会在下面被填充 name: modelName, icon: "", provider: "", contextWindow: 63920 } })); // 获取模型列表以找到正确的ID const models = await fetchModels(); const selectedModel = models.find(m => m.name === modelName); if (!selectedModel) { throw new Error(`Model "${modelName}" not found`); } // 填充模型信息 formattedMessages.forEach(msg => { if (msg.model) { msg.model.id = selectedModel.id; msg.model.icon = selectedModel.icon; msg.model.provider = selectedModel.provider; } }); const response = await fetch(`${FREEAI_API_BASE}/chat/completions`, { method: "POST", headers: { "Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:137.0) Gecko/20100101 Firefox/137.0", "Origin": "https://freeaichatplayground.com", "Referer": "https://freeaichatplayground.com/chat", }, body: JSON.stringify({ model: modelName, messages: formattedMessages, }), }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Chat completion failed: ${response.status} - ${errorText}`); } // 处理 SSE 流式响应 const parsedResponse = await parseSSEResponse(response); return parsedResponse; } catch (error) { console.error("Error in chat completion:", error); throw error; } } // 转换为 OpenAI 格式的聊天响应 function transformChatResponseToOpenAIFormat(response, modelName) { return { id: response.id || `chatcmpl-${Date.now()}`, object: "chat.completion", created: Math.floor(Date.now() / 1000), model: modelName, choices: [ { index: 0, message: { role: "assistant", content: response.content, }, finish_reason: response.finish_reason || "stop", }, ], usage: response.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, }, }; } // 处理流式响应请求 async function handleStreamRequest(request, modelName, messages) { const encoder = new TextEncoder(); const formattedMessages = messages.map((msg, index) => ({ id: `${Date.now() + index}`, role: msg.role, content: msg.content, model: { id: "", // 这个ID会在下面被填充 name: modelName, icon: "", provider: "", contextWindow: 63920 } })); // 获取模型列表以找到正确的ID const models = await fetchModels(); const selectedModel = models.find(m => m.name === modelName); if (!selectedModel) { throw new Error(`Model "${modelName}" not found`); } // 填充模型信息 formattedMessages.forEach(msg => { if (msg.model) { msg.model.id = selectedModel.id; msg.model.icon = selectedModel.icon; msg.model.provider = selectedModel.provider; } }); const response = await fetch(`${FREEAI_API_BASE}/chat/completions`, { method: "POST", headers: { "Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:137.0) Gecko/20100101 Firefox/137.0", "Origin": "https://freeaichatplayground.com", "Referer": "https://freeaichatplayground.com/chat", }, body: JSON.stringify({ model: modelName, messages: formattedMessages, stream: true, }), }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Chat completion failed: ${response.status} - ${errorText}`); } const stream = new ReadableStream({ async start(controller) { const reader = response.body.getReader(); const chatId = `chatcmpl-${Date.now()}`; // 发送初始消息 const initialChunk = { id: chatId, object: "chat.completion.chunk", created: Math.floor(Date.now() / 1000), model: modelName, choices: [{ index: 0, delta: { role: "assistant" }, finish_reason: null }] }; controller.enqueue(encoder.encode(`data: ${JSON.stringify(initialChunk)}\n\n`)); try { let buffer = ""; while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = new TextDecoder().decode(value); buffer += chunk; // 处理缓冲区中的所有完整 SSE 消息 const messages = buffer.split('\n\n'); buffer = messages.pop() || ""; // 保留最后一个可能不完整的消息 for (const msg of messages) { if (!msg.trim().startsWith('data:')) continue; try { const jsonStr = msg.replace('data:', '').trim(); const data = JSON.parse(jsonStr); if (data.choices && data.choices[0]) { // 转换为 OpenAI 流式格式 const openAIChunk = { id: chatId, object: "chat.completion.chunk", created: Math.floor(Date.now() / 1000), model: modelName, choices: [{ index: 0, delta: {}, finish_reason: data.choices[0].finish_reason || null }] }; // 提取内容 if (data.choices[0].delta && data.choices[0].delta.content) { openAIChunk.choices[0].delta.content = data.choices[0].delta.content; } else if (data.choices[0].message && data.choices[0].message.content) { openAIChunk.choices[0].delta.content = data.choices[0].message.content; } controller.enqueue(encoder.encode(`data: ${JSON.stringify(openAIChunk)}\n\n`)); // 如果是最后一条消息,发送 [DONE] if (data.choices[0].finish_reason) { controller.enqueue(encoder.encode("data: [DONE]\n\n")); } } } catch (e) { console.warn("Failed to parse SSE message:", msg); continue; } } } // 确保发送最终的 [DONE] 消息 controller.enqueue(encoder.encode("data: [DONE]\n\n")); controller.close(); } catch (error) { console.error("Stream processing error:", error); controller.error(error); } } }); return new Response(stream, { headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", "Connection": "keep-alive", "Access-Control-Allow-Origin": "*", } }); } // 处理请求 async function handleRequest(request) { const url = new URL(request.url); const path = url.pathname; // CORS 预检请求处理 if (request.method === "OPTIONS") { return new Response(null, { status: 200, headers: { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization", }, }); } // 设置通用响应头 const headers = { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*", }; try { // 模型列表接口 if (path === "/v1/models" && request.method === "GET") { const models = await fetchModels(); const openAIModels = transformModelsToOpenAIFormat(models); return new Response(JSON.stringify(openAIModels), { headers }); } // 聊天完成接口 else if (path === "/v1/chat/completions" && request.method === "POST") { const requestData = await request.json(); const modelName = requestData.model || DEFAULT_MODEL; const messages = requestData.messages || []; const stream = requestData.stream || false; // 处理流式响应 if (stream) { return handleStreamRequest(request, modelName, messages); } // 处理普通响应 const chatResponse = await sendChatRequest(modelName, messages); const openAIResponse = transformChatResponseToOpenAIFormat(chatResponse, modelName); return new Response(JSON.stringify(openAIResponse), { headers }); } // 未知路径 else { return new Response(JSON.stringify({ error: { message: "Not found", type: "invalid_request_error", code: "path_not_found", } }), { status: 404, headers }); } } catch (error) { console.error("Error handling request:", error); return new Response(JSON.stringify({ error: { message: error.message, type: "server_error", code: "internal_server_error", } }), { status: 500, headers }); } } // 启动服务器 const port = parseInt(Deno.env.get("PORT") || "8000"); console.log(`Starting server on port ${port}...`); serve(handleRequest, { port });