codegpt / main.ts
hongshi-files's picture
Update main.ts
e99826b verified
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 });