Upload 5 files
Browse files- Dockerfile +23 -0
- README.md +84 -10
- app.js +123 -0
- deno.js +85 -0
- index.js +133 -0
Dockerfile
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 使用官方 Deno 镜像
|
| 2 |
+
FROM denoland/deno:latest
|
| 3 |
+
|
| 4 |
+
# 你的应用监听的端口
|
| 5 |
+
# Hugging Face Spaces 会自动设置 PORT 环境变量
|
| 6 |
+
# 默认为 7860
|
| 7 |
+
ENV PORT 7860
|
| 8 |
+
EXPOSE 7860
|
| 9 |
+
|
| 10 |
+
# 应用的工作目录
|
| 11 |
+
WORKDIR /app
|
| 12 |
+
|
| 13 |
+
# 复制应用文件
|
| 14 |
+
COPY app.js .
|
| 15 |
+
|
| 16 |
+
# 在一个单独的层中缓存依赖项
|
| 17 |
+
# 这对这个小脚本作用不大,但是一个好习惯
|
| 18 |
+
RUN deno cache app.js
|
| 19 |
+
|
| 20 |
+
# 运行应用的命令
|
| 21 |
+
# 使用 --allow-net 来允许网络访问 (fetch 和 serve)
|
| 22 |
+
# 使用 --allow-env 来读取环境变量,如 TOKEN 和 PORT
|
| 23 |
+
CMD ["run", "--allow-net", "--allow-env", "app.js"]
|
README.md
CHANGED
|
@@ -1,10 +1,84 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# DeepInfra to OpenAI API Adapter
|
| 2 |
+
|
| 3 |
+
[](https://huggingface.co/new/space?template=deepinfra-openai-adapter)
|
| 4 |
+
|
| 5 |
+
这是一个代理服务,可以将 [DeepInfra](https://deepinfra.com/) 的模型 API 转换为与 OpenAI API 兼容的格式。这使得在各种为 OpenAI API 设计的客户端和工具中使用 DeepInfra 的模型成为可能。
|
| 6 |
+
|
| 7 |
+
## ✨ 功能
|
| 8 |
+
|
| 9 |
+
- **OpenAI 格式兼容**:将 DeepInfra API 无缝转换为 OpenAI Chat Completions API 格式。
|
| 10 |
+
- **模型列表接口**:支持 `/v1/models` 接口,可以获取预设的模型列表。
|
| 11 |
+
- **自定义鉴权**:通过环境变量设置 `TOKEN`,保护你的 API 不被滥用。
|
| 12 |
+
- **轻松部署**:可以一键部署到 Hugging Face Spaces。
|
| 13 |
+
|
| 14 |
+
## 🚀 部署到 Hugging Face Spaces
|
| 15 |
+
|
| 16 |
+
按照以下步骤,你可以轻松地将此服务部署到你自己的 Hugging Face Space。
|
| 17 |
+
|
| 18 |
+
1. **创建 Space**
|
| 19 |
+
- 点击上方的 "Deploy to Hugging Face Spaces" 徽章,或访问 [Hugging Face 新建 Space 页面](https://huggingface.co/new/space)。
|
| 20 |
+
- 给你的 Space 起一个名字(`Space name`)。
|
| 21 |
+
- 在 `Select the Space SDK` 选项中,选择 **Docker**。
|
| 22 |
+
- 在 `Docker template` 中,选择 **Blank**。
|
| 23 |
+
- 点击 `Create Space`。
|
| 24 |
+
|
| 25 |
+
2. **上传文件**
|
| 26 |
+
- 在你的 Space 页面,进入 `Files` 标签页。
|
| 27 |
+
- 点击 `Add file` -> `Upload file`。
|
| 28 |
+
- 将项目中的 `app.js` 和 `Dockerfile` 这两个文件上传。
|
| 29 |
+
|
| 30 |
+
3. **设置 API 密钥**
|
| 31 |
+
- 在你的 Space 页面,进入 `Settings` 标签页。
|
| 32 |
+
- 找到 `Secrets management` 部分,点击 `New secret`。
|
| 33 |
+
- 在 `Name` 字段中输入 `TOKEN`。
|
| 34 |
+
- 在 `Value` 字段中输入一个你自己的密码,例如 `sk-my-secret-key-12345`。这个密码将作为访问你服务的 API Key。
|
| 35 |
+
- 点击 `Save secret`。Hugging Face 会自动重新构建你的 Space 以应用这个密钥。
|
| 36 |
+
|
| 37 |
+
部署完成后,你的 Space 页面应该会显示 `Running` 状态。
|
| 38 |
+
|
| 39 |
+
## 🛠️ 如何使用
|
| 40 |
+
|
| 41 |
+
部署成功后,你就可以在任何支持 OpenAI API 的客户端或代码中配置并使用你的代理了。
|
| 42 |
+
|
| 43 |
+
- **API 地址 (Endpoint / Base URL)**:
|
| 44 |
+
你的 Hugging Face Space 的公开 URL,格式为 `https://<你的用户名>-<你的Space名>.hf.space`。
|
| 45 |
+
**重要**: 请在 URL 后面加上 `/v1`,完整的地址应该是 `https://<你的用户名>-<你的Space名>.hf.space/v1`。
|
| 46 |
+
|
| 47 |
+
- **API 密钥 (API Key)**:
|
| 48 |
+
你在 Hugging Face `TOKEN` 密钥中设置的值。
|
| 49 |
+
|
| 50 |
+
- **模型名称**:
|
| 51 |
+
任何 DeepInfra 支持的模型,或者使用 `/v1/models` 接口中提供的模型,例如:
|
| 52 |
+
- `deepseek-ai/DeepSeek-R1-0528-Turbo`
|
| 53 |
+
- `deepseek-ai/DeepSeek-V3-0324-Turbo`
|
| 54 |
+
- `deepseek-ai/DeepSeek-R1-Distill-Llama-70B`
|
| 55 |
+
|
| 56 |
+
### 使用 cURL 测试
|
| 57 |
+
|
| 58 |
+
你可以使用 `curl` 命令来测试你的服务是否正常工作。
|
| 59 |
+
|
| 60 |
+
#### 获取模型列表
|
| 61 |
+
```bash
|
| 62 |
+
curl https://<你的用户名>-<你的Space名>.hf.space/v1/models \
|
| 63 |
+
-H "Authorization: Bearer <你的TOKEN>"
|
| 64 |
+
```
|
| 65 |
+
|
| 66 |
+
#### 发起聊天请求
|
| 67 |
+
```bash
|
| 68 |
+
curl https://<你的用户名>-<你的Space名>.hf.space/v1/chat/completions \
|
| 69 |
+
-H "Content-Type: application/json" \
|
| 70 |
+
-H "Authorization: Bearer <你的TOKEN>" \
|
| 71 |
+
-d '{
|
| 72 |
+
"model": "deepseek-ai/DeepSeek-R1-0528-Turbo",
|
| 73 |
+
"messages": [
|
| 74 |
+
{
|
| 75 |
+
"role": "user",
|
| 76 |
+
"content": "Hello, how are you?"
|
| 77 |
+
}
|
| 78 |
+
],
|
| 79 |
+
"stream": false
|
| 80 |
+
}'
|
| 81 |
+
```
|
| 82 |
+
> 将 `<你的用户名>-<你的Space名>` 和 `<你的TOKEN>` 替换为你自己的信息。
|
| 83 |
+
|
| 84 |
+
现在你可以随时随地使用 DeepInfra 上的优秀模型了!
|
app.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { serve } from "https://deno.land/std/http/server.ts";
|
| 2 |
+
|
| 3 |
+
const handler = async (request) => {
|
| 4 |
+
const url = new URL(request.url);
|
| 5 |
+
|
| 6 |
+
// Handle CORS preflight requests
|
| 7 |
+
if (request.method === "OPTIONS") {
|
| 8 |
+
return new Response(null, {
|
| 9 |
+
headers: {
|
| 10 |
+
"Access-Control-Allow-Origin": "*",
|
| 11 |
+
"Access-Control-Allow-Methods": "POST, GET, OPTIONS",
|
| 12 |
+
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
| 13 |
+
"Access-Control-Max-Age": "86400",
|
| 14 |
+
},
|
| 15 |
+
});
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
const TOKEN = Deno.env.get("TOKEN");
|
| 19 |
+
|
| 20 |
+
// Authentication check
|
| 21 |
+
if (TOKEN) {
|
| 22 |
+
const authHeader = request.headers.get("Authorization");
|
| 23 |
+
if (!authHeader || authHeader !== `Bearer ${TOKEN}`) {
|
| 24 |
+
return new Response("Unauthorized", {
|
| 25 |
+
status: 401,
|
| 26 |
+
headers: {
|
| 27 |
+
"Content-Type": "application/json",
|
| 28 |
+
"Access-Control-Allow-Origin": "*",
|
| 29 |
+
},
|
| 30 |
+
});
|
| 31 |
+
}
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
// Handle /v1/models endpoint
|
| 35 |
+
if (url.pathname === "/v1/models" && request.method === "GET") {
|
| 36 |
+
const models = {
|
| 37 |
+
object: "list",
|
| 38 |
+
data: [
|
| 39 |
+
{
|
| 40 |
+
id: "deepseek-ai/DeepSeek-R1-0528-Turbo",
|
| 41 |
+
object: "model",
|
| 42 |
+
created: 1624980000,
|
| 43 |
+
owned_by: "deepseek-ai",
|
| 44 |
+
},
|
| 45 |
+
{
|
| 46 |
+
id: "deepseek-ai/DeepSeek-V3-0324-Turbo",
|
| 47 |
+
object: "model",
|
| 48 |
+
created: 1632000000,
|
| 49 |
+
owned_by: "deepseek-ai",
|
| 50 |
+
},
|
| 51 |
+
{
|
| 52 |
+
id: "deepseek-ai/DeepSeek-R1-Distill-Llama-70B",
|
| 53 |
+
object: "model",
|
| 54 |
+
created: 1640000000,
|
| 55 |
+
owned_by: "deepseek-ai",
|
| 56 |
+
},
|
| 57 |
+
],
|
| 58 |
+
};
|
| 59 |
+
|
| 60 |
+
return new Response(JSON.stringify(models), {
|
| 61 |
+
headers: {
|
| 62 |
+
"Content-Type": "application/json",
|
| 63 |
+
"Access-Control-Allow-Origin": "*",
|
| 64 |
+
},
|
| 65 |
+
});
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
// Handle chat completions
|
| 69 |
+
if (url.pathname === "/v1/chat/completions" && request.method === "POST") {
|
| 70 |
+
try {
|
| 71 |
+
const body = await request.json();
|
| 72 |
+
const headers = new Headers({
|
| 73 |
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0",
|
| 74 |
+
"Accept": "text/event-stream",
|
| 75 |
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
| 76 |
+
"Content-Type": "application/json",
|
| 77 |
+
"sec-ch-ua-platform": "Windows",
|
| 78 |
+
"X-Deepinfra-Source": "web-page",
|
| 79 |
+
"sec-ch-ua": '"Not(A:Brand";v="99", "Microsoft Edge";v="133", "Chromium";v="133"',
|
| 80 |
+
"sec-ch-ua-mobile": "?0",
|
| 81 |
+
"Origin": "https://deepinfra.com",
|
| 82 |
+
"Sec-Fetch-Site": "same-site",
|
| 83 |
+
"Sec-Fetch-Mode": "cors",
|
| 84 |
+
"Sec-Fetch-Dest": "empty",
|
| 85 |
+
"Referer": "https://deepinfra.com/",
|
| 86 |
+
});
|
| 87 |
+
|
| 88 |
+
const response = await fetch("https://api.deepinfra.com/v1/openai/chat/completions", {
|
| 89 |
+
method: "POST",
|
| 90 |
+
headers: headers,
|
| 91 |
+
body: JSON.stringify(body),
|
| 92 |
+
});
|
| 93 |
+
|
| 94 |
+
return new Response(response.body, {
|
| 95 |
+
status: response.status,
|
| 96 |
+
statusText: response.statusText,
|
| 97 |
+
headers: {
|
| 98 |
+
"Access-Control-Allow-Origin": "*",
|
| 99 |
+
"Content-Type": response.headers.get("Content-Type") || "application/json",
|
| 100 |
+
},
|
| 101 |
+
});
|
| 102 |
+
} catch (error) {
|
| 103 |
+
return new Response(JSON.stringify({ error: error.message }), {
|
| 104 |
+
status: 500,
|
| 105 |
+
headers: {
|
| 106 |
+
"Content-Type": "application/json",
|
| 107 |
+
"Access-Control-Allow-Origin": "*",
|
| 108 |
+
},
|
| 109 |
+
});
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
return new Response("Not Found", {
|
| 114 |
+
status: 404,
|
| 115 |
+
headers: {
|
| 116 |
+
"Access-Control-Allow-Origin": "*",
|
| 117 |
+
}
|
| 118 |
+
});
|
| 119 |
+
};
|
| 120 |
+
|
| 121 |
+
const port = Deno.env.get("PORT") ? parseInt(Deno.env.get("PORT")) : 7860;
|
| 122 |
+
console.log(`HTTP server running. Access it at: http://localhost:${port}/`);
|
| 123 |
+
serve(handler, { port });
|
deno.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export default {
|
| 2 |
+
async fetch(request: Request): Promise<Response> {
|
| 3 |
+
// Handle CORS preflight request
|
| 4 |
+
if (request.method === "OPTIONS") {
|
| 5 |
+
return new Response(null, {
|
| 6 |
+
headers: {
|
| 7 |
+
"Access-Control-Allow-Origin": "*",
|
| 8 |
+
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
| 9 |
+
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
| 10 |
+
"Access-Control-Max-Age": "86400",
|
| 11 |
+
},
|
| 12 |
+
});
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
// Only handle POST requests
|
| 16 |
+
if (request.method !== "POST") {
|
| 17 |
+
return new Response("Method not allowed", { status: 405 });
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
// Validate Authorization header
|
| 21 |
+
const authHeader = request.headers.get("Authorization");
|
| 22 |
+
const TOKEN = Deno.env.get("TOKEN");
|
| 23 |
+
if (TOKEN) {
|
| 24 |
+
if (!authHeader || authHeader !== `Bearer ${TOKEN}`) {
|
| 25 |
+
return new Response("Unauthorized", {
|
| 26 |
+
status: 401,
|
| 27 |
+
headers: {
|
| 28 |
+
"Content-Type": "application/json",
|
| 29 |
+
"Access-Control-Allow-Origin": "*"
|
| 30 |
+
}
|
| 31 |
+
});
|
| 32 |
+
}
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
try {
|
| 36 |
+
// Clone request body
|
| 37 |
+
const body = await request.json();
|
| 38 |
+
|
| 39 |
+
// Construct new request headers
|
| 40 |
+
const headers = new Headers({
|
| 41 |
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0",
|
| 42 |
+
"Accept": "text/event-stream",
|
| 43 |
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
| 44 |
+
"Content-Type": "application/json",
|
| 45 |
+
"sec-ch-ua-platform": "Windows",
|
| 46 |
+
"X-Deepinfra-Source": "web-page",
|
| 47 |
+
"sec-ch-ua": "\"Not(A:Brand\";v=\"99\", \"Microsoft Edge\";v=\"133\", \"Chromium\";v=\"133\"",
|
| 48 |
+
"sec-ch-ua-mobile": "?0",
|
| 49 |
+
"Origin": "https://deepinfra.com",
|
| 50 |
+
"Sec-Fetch-Site": "same-site",
|
| 51 |
+
"Sec-Fetch-Mode": "cors",
|
| 52 |
+
"Sec-Fetch-Dest": "empty",
|
| 53 |
+
"Referer": "https://deepinfra.com/"
|
| 54 |
+
});
|
| 55 |
+
|
| 56 |
+
// Send request to DeepInfra
|
| 57 |
+
const response = await fetch("https://api.deepinfra.com/v1/openai/chat/completions", {
|
| 58 |
+
method: "POST",
|
| 59 |
+
headers: headers,
|
| 60 |
+
body: JSON.stringify(body)
|
| 61 |
+
});
|
| 62 |
+
|
| 63 |
+
// Construct response
|
| 64 |
+
const newResponse = new Response(response.body, {
|
| 65 |
+
status: response.status,
|
| 66 |
+
statusText: response.statusText,
|
| 67 |
+
headers: {
|
| 68 |
+
"Access-Control-Allow-Origin": "*",
|
| 69 |
+
"Content-Type": response.headers.get("Content-Type") || "application/json",
|
| 70 |
+
}
|
| 71 |
+
});
|
| 72 |
+
|
| 73 |
+
return newResponse;
|
| 74 |
+
|
| 75 |
+
} catch (error) {
|
| 76 |
+
return new Response(JSON.stringify({ error: error.message }), {
|
| 77 |
+
status: 500,
|
| 78 |
+
headers: {
|
| 79 |
+
"Content-Type": "application/json",
|
| 80 |
+
"Access-Control-Allow-Origin": "*"
|
| 81 |
+
}
|
| 82 |
+
});
|
| 83 |
+
}
|
| 84 |
+
}
|
| 85 |
+
};
|
index.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export default {
|
| 2 |
+
async fetch(request, env, ctx) {
|
| 3 |
+
const url = new URL(request.url);
|
| 4 |
+
|
| 5 |
+
// 处理 CORS 预检请求
|
| 6 |
+
if (request.method === "OPTIONS") {
|
| 7 |
+
return new Response(null, {
|
| 8 |
+
headers: {
|
| 9 |
+
"Access-Control-Allow-Origin": "*",
|
| 10 |
+
"Access-Control-Allow-Methods": "POST, GET, OPTIONS",
|
| 11 |
+
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
| 12 |
+
"Access-Control-Max-Age": "86400",
|
| 13 |
+
},
|
| 14 |
+
});
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
// 处理 /v1/models 请求
|
| 18 |
+
if (url.pathname === "/v1/models" && request.method === "GET") {
|
| 19 |
+
// 鉴权验证
|
| 20 |
+
if (env.TOKEN) {
|
| 21 |
+
const authHeader = request.headers.get("Authorization");
|
| 22 |
+
if (!authHeader || authHeader !== `Bearer ${env.TOKEN}`) {
|
| 23 |
+
return new Response("Unauthorized", {
|
| 24 |
+
status: 401,
|
| 25 |
+
headers: {
|
| 26 |
+
"Content-Type": "application/json",
|
| 27 |
+
"Access-Control-Allow-Origin": "*"
|
| 28 |
+
}
|
| 29 |
+
});
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
// 构建模型列表
|
| 34 |
+
const models = {
|
| 35 |
+
object: "list",
|
| 36 |
+
data: [
|
| 37 |
+
{
|
| 38 |
+
id: "deepseek-ai/DeepSeek-R1-0528-Turbo",
|
| 39 |
+
object: "model",
|
| 40 |
+
created: 1624980000,
|
| 41 |
+
owned_by: "deepseek-ai"
|
| 42 |
+
},
|
| 43 |
+
{
|
| 44 |
+
id: "deepseek-ai/DeepSeek-V3-0324-Turbo",
|
| 45 |
+
object: "model",
|
| 46 |
+
created: 1632000000,
|
| 47 |
+
owned_by: "deepseek-ai"
|
| 48 |
+
},
|
| 49 |
+
{
|
| 50 |
+
id: "deepseek-ai/DeepSeek-R1-Distill-Llama-70B",
|
| 51 |
+
object: "model",
|
| 52 |
+
created: 1640000000,
|
| 53 |
+
owned_by: "deepseek-ai"
|
| 54 |
+
}
|
| 55 |
+
]
|
| 56 |
+
};
|
| 57 |
+
|
| 58 |
+
return new Response(JSON.stringify(models), {
|
| 59 |
+
headers: {
|
| 60 |
+
"Content-Type": "application/json",
|
| 61 |
+
"Access-Control-Allow-Origin": "*"
|
| 62 |
+
}
|
| 63 |
+
});
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
// 处理其他 POST 请求
|
| 67 |
+
if (request.method !== "POST") {
|
| 68 |
+
return new Response("Method not allowed", {
|
| 69 |
+
status: 405,
|
| 70 |
+
headers: {
|
| 71 |
+
"Access-Control-Allow-Origin": "*"
|
| 72 |
+
}
|
| 73 |
+
});
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
// 验证鉴权头
|
| 77 |
+
const authHeader = request.headers.get("Authorization");
|
| 78 |
+
if (env.TOKEN) {
|
| 79 |
+
if (!authHeader || authHeader !== `Bearer ${env.TOKEN}`) {
|
| 80 |
+
return new Response("Unauthorized", {
|
| 81 |
+
status: 401,
|
| 82 |
+
headers: {
|
| 83 |
+
"Content-Type": "application/json",
|
| 84 |
+
"Access-Control-Allow-Origin": "*"
|
| 85 |
+
}
|
| 86 |
+
});
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
try {
|
| 91 |
+
// 原始请求处理逻辑
|
| 92 |
+
const body = await request.json();
|
| 93 |
+
const headers = {
|
| 94 |
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0",
|
| 95 |
+
"Accept": "text/event-stream",
|
| 96 |
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
| 97 |
+
"Content-Type": "application/json",
|
| 98 |
+
"sec-ch-ua-platform": "Windows",
|
| 99 |
+
"X-Deepinfra-Source": "web-page",
|
| 100 |
+
"sec-ch-ua": "\"Not(A:Brand\";v=\"99\", \"Microsoft Edge\";v=\"133\", \"Chromium\";v=\"133\"",
|
| 101 |
+
"sec-ch-ua-mobile": "?0",
|
| 102 |
+
"Origin": "https://deepinfra.com",
|
| 103 |
+
"Sec-Fetch-Site": "same-site",
|
| 104 |
+
"Sec-Fetch-Mode": "cors",
|
| 105 |
+
"Sec-Fetch-Dest": "empty",
|
| 106 |
+
"Referer": "https://deepinfra.com/"
|
| 107 |
+
};
|
| 108 |
+
|
| 109 |
+
const response = await fetch("https://api.deepinfra.com/v1/openai/chat/completions", {
|
| 110 |
+
method: "POST",
|
| 111 |
+
headers: headers,
|
| 112 |
+
body: JSON.stringify(body)
|
| 113 |
+
});
|
| 114 |
+
|
| 115 |
+
return new Response(response.body, {
|
| 116 |
+
status: response.status,
|
| 117 |
+
headers: {
|
| 118 |
+
"Access-Control-Allow-Origin": "*",
|
| 119 |
+
"Content-Type": response.headers.get("Content-Type")
|
| 120 |
+
}
|
| 121 |
+
});
|
| 122 |
+
|
| 123 |
+
} catch (error) {
|
| 124 |
+
return new Response(JSON.stringify({ error: error.message }), {
|
| 125 |
+
status: 500,
|
| 126 |
+
headers: {
|
| 127 |
+
"Content-Type": "application/json",
|
| 128 |
+
"Access-Control-Allow-Origin": "*"
|
| 129 |
+
}
|
| 130 |
+
});
|
| 131 |
+
}
|
| 132 |
+
}
|
| 133 |
+
};
|