maltose1 commited on
Commit
46a964e
·
verified ·
1 Parent(s): c7620d2

Upload 5 files

Browse files
Files changed (5) hide show
  1. Dockerfile +23 -0
  2. README.md +84 -10
  3. app.js +123 -0
  4. deno.js +85 -0
  5. 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
- title: Open
3
- emoji: 🌍
4
- colorFrom: green
5
- colorTo: blue
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DeepInfra to OpenAI API Adapter
2
+
3
+ [![Deploy to Hugging Face Spaces](https://img.shields.io/badge/🤗%20Deploy%20to%20Hugging%20Face-Spaces-blue)](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
+ };