Spaces:
Sleeping
Sleeping
File size: 9,222 Bytes
ac689ba e7476bb ac689ba e99826b e7476bb ac689ba cb8f7bb ac689ba cb8f7bb ac689ba e7476bb cb8f7bb ac689ba cb8f7bb e7476bb ac689ba cb8f7bb ac689ba e7476bb 6ab460a e7476bb ac689ba 6ab460a cb8f7bb ac689ba cb8f7bb ac689ba cb8f7bb 6ab460a e7476bb ac689ba 6ab460a cb8f7bb ac689ba cb8f7bb ac689ba cb8f7bb ac689ba 6ab460a cb8f7bb ac689ba e7476bb 6ab460a e7476bb ac689ba cb8f7bb 6ab460a ac689ba 6ab460a ac689ba 6ab460a ac689ba 6ab460a ac689ba 6ab460a ac689ba 6ab460a e7476bb ac689ba cb8f7bb ac689ba cb8f7bb ac689ba 351e107 ac689ba 6ab460a ac689ba e7476bb 6ab460a ac689ba e7476bb cb8f7bb 6ab460a ac689ba cb8f7bb ac689ba 6ab460a ac689ba 6ab460a ac689ba cb8f7bb ac689ba cb8f7bb 6ab460a ac689ba 6ab460a ac689ba 6ab460a ac689ba e7476bb 351e107 ac689ba |
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 |
import { serve } from "https://deno.land/std@0.220.1/http/server.ts";
// 定义常量
const BASE_URL = "https://api.codegpt.co/api";
const PORT = 7860;
interface CodegptModel {
id: string;
org_id: string;
name: string;
model: string;
}
// 从请求头中获取API_KEY
function getApiKeyFromRequest(req: Request): string | null {
const authHeader = req.headers.get("authorization");
if (!authHeader) return null;
// 支持 "Bearer sk-xxx" 或直接 "sk-xxx" 格式
const match = authHeader.match(/^(Bearer\s+)?(sk-[a-zA-Z0-9-]+)$/);
return match ? match[2] : null;
}
// 获取模型列表
async function getModels(apiKey: string): Promise<CodegptModel[]> {
try {
const response = await fetch(
`${BASE_URL}/v1/agent`,
{
method: "GET",
headers: {
"Accept": "application/json",
"Authorization": `Bearer ${apiKey}`,
},
},
);
if (!response.ok) {
const errorData = await response.json();
if (errorData.message === "Invalid credentials") {
throw new Error("🚀 ~ Your API key or access token is invalid.!");
}
throw new Error(`Failed to get models: ${response.status}`);
}
const data = await response.json();
// console.log("🚀 ~ codegpt agents 响应:", data);
return data;
} catch (error) {
console.error("Error getting models:", error);
throw error;
}
}
// 将 codegpt agent 转换为 OpenAI model 格式
function transformModelsToOpenAIFormat(models: CodegptModel[]) {
return {
object: "list",
data: models.map((model) => ({
id: model.id,
name: model.model,
object: "model",
created: Date.now(),
owned_by: "codegpt",
permission: [{
id: `modelperm-${model.model}`,
object: "model_permission",
created: Date.now(),
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: model.model,
parent: null,
})),
};
}
// 处理聊天完成请求
async function handleChatCompletions(
apiKey: string,
requestBody: any,
stream: boolean,
) {
const CodegptBody = {
stream: stream,
agentId: requestBody.model,
messages: requestBody.messages,
format: "json",
};
// console.log("🚀 ~ 内部实际请求: ", CodegptBody);
const response = await fetch(
`${BASE_URL}/v1/chat/completions`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`,
"Accept": stream ? "text/event-stream" : "application/json",
},
body: JSON.stringify(CodegptBody),
},
);
// console.log("🚀 ~ 内部请求实际响应: \n", response);
if (!response.ok) {
throw new Error(`Chat completion failed: ${response.status}`);
}
return response;
}
// 转换流式响应
async function* transformStreamResponse(
readableStream: ReadableStream<Uint8Array>,
) {
const reader = readableStream.getReader();
const decoder = new TextDecoder();
let buffer = "";
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop() || "";
for (const line of lines) {
if (line.trim() === "" || !line.startsWith("data:")) continue;
const data = line.substring(5).trim();
if (data === "[DONE]") {
yield "data: [DONE]\n\n";
continue;
}
try {
const CodegptEvent = JSON.parse(data);
// 转换为OpenAI格式的事件
const openAIEvent = {
id: CodegptEvent.agentName || `chatcmpl-${Date.now()}`,
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model: CodegptEvent.model || CodegptEvent.agentName,
choices: [
{
index: 0,
delta: {
reasoning_content: CodegptEvent.choices?.[0]?.delta?.contents?.[0]?.type === "think" ? CodegptEvent.choices[0].delta.contents[0].content || "" : "",
content: CodegptEvent.choices?.[0]?.delta?.content || "",
},
finish_reason: CodegptEvent.choices?.[0]?.finish_reason || null,
},
],
};
yield `data: ${JSON.stringify(openAIEvent)}\n\n`;
} catch (e) {
console.error("Error parsing event:", e, "Line:", line);
}
}
}
} finally {
reader.releaseLock();
}
}
// 转换非流式响应
async function transformNonStreamResponse(response: Response) {
const CodegptResponse = await response.json();
return {
id: CodegptResponse.id || `chatcmpl-${Date.now()}`,
object: "chat.completion",
created: Math.floor(Date.now() / 1000),
model: CodegptResponse.model || CodegptResponse.agentName,
choices: [
{
index: 0,
message: {
role: "assistant",
content: CodegptResponse.choices?.[0]?.message?.content || "",
},
finish_reason: CodegptResponse.choices?.[0]?.finish_reason || "stop",
},
],
usage: CodegptResponse.usage || {
prompt_tokens: 0,
completion_tokens: 0,
total_tokens: 0,
},
};
}
// 主处理函数
async function handler(req: Request): Promise<Response> {
const url = new URL(req.url);
const path = url.pathname;
const apiKey = getApiKeyFromRequest(req);
if (!apiKey) {
return new Response(
// 没有填写 apikey 时返回的错误信息
JSON.stringify({ error: "Unauthorized", message: "Your API key or access token is invalid." }),
{
status: 401,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
}
);
}
// CORS预检请求处理
if (req.method === "OPTIONS") {
return new Response(null, {
status: 204,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
"Access-Control-Max-Age": "86400",
},
});
}
try {
// 模型列表接口
if (path === "/v1/models" && req.method === "GET") {
const models = await getModels(apiKey);
const openAIModels = transformModelsToOpenAIFormat(models);
// console.log("🚀 ~ openAI格式models: ", openAIModels);
return new Response(JSON.stringify(openAIModels), {
status: 200,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
});
}
// 聊天完成接口
else if (path === "/v1/chat/completions" && req.method === "POST") {
const requestBody = await req.json();
// console.log("🚀 ~ 外部客户端请求: ", requestBody);
const stream = requestBody.stream === true;
const codegptResponse = await handleChatCompletions(apiKey, requestBody, stream);
if (stream) {
const transformedStream = new ReadableStream({
async start(controller) {
try {
for await (const chunk of transformStreamResponse(codegptResponse.body!)) {
controller.enqueue(new TextEncoder().encode(chunk));
}
controller.close();
} catch (error) {
console.error("Stream transformation error:", error);
controller.error(error);
}
},
});
return new Response(transformedStream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Access-Control-Allow-Origin": "*",
},
});
} else {
const transformedResponse = await transformNonStreamResponse(codegptResponse);
return new Response(JSON.stringify(transformedResponse), {
status: 200,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
});
}
}
// 未找到路由
else {
return new Response(
JSON.stringify({ error: "Not found", message: "Endpoint not supported" }),
{
status: 404,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
},
);
}
} catch (error) {
console.error("Request handling error:", error);
return new Response(
JSON.stringify({ error: "Internal server error", message: error.message }),
{
status: 500,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
},
);
}
}
console.log(`Starting server on port ${PORT}...`);
serve(handler, { port: PORT }); |