javaeeduke commited on
Commit
4bea97b
·
verified ·
1 Parent(s): ca6663e

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +1 -255
main.py CHANGED
@@ -1,255 +1 @@
1
- from fastapi import FastAPI, HTTPException, Depends, Header
2
- from fastapi.middleware.cors import CORSMiddleware
3
- from fastapi.responses import StreamingResponse, HTMLResponse
4
- import uvicorn
5
- import os
6
- import logging
7
- import httpx
8
-
9
- logging.basicConfig(level=logging.INFO)
10
- logger = logging.getLogger(__name__)
11
-
12
- app = FastAPI(title="FreeLLMAPI")
13
-
14
- app.add_middleware(
15
- CORSMiddleware,
16
- allow_origins=["*"],
17
- allow_methods=["*"],
18
- allow_headers=["*"],
19
- )
20
-
21
- # ──────────────────────────────────────────
22
- # 读取原项目固定命名的 Secrets
23
- # ──────────────────────────────────────────
24
-
25
- PROVIDER_MAP = {
26
- "GOOGLE_API_KEY": "google",
27
- "GROQ_API_KEY": "groq",
28
- "GITHUB_TOKEN": "github",
29
- "OPENROUTER_API_KEY": "openrouter",
30
- "MISTRAL_API_KEY": "mistral",
31
- "TOGETHER_API_KEY": "together",
32
- "NVIDIA_API_KEY": "nvidia",
33
- "COHERE_API_KEY": "cohere",
34
- "HF_TOKEN": "huggingface",
35
- "CEREBRAS_API_KEY": "cerebras",
36
- "SAMBANOVA_API_KEY": "sambanova",
37
- "CLOUDFLARE_API_TOKEN": "cloudflare",
38
- "ZHIPU_API_KEY": "zhipu",
39
- }
40
-
41
- PROVIDER_CONFIG = {
42
- "google": {
43
- "base_url": "https://generativelanguage.googleapis.com/v1beta/openai",
44
- "models": [
45
- "gemini-2.0-flash","gemini-2.0-flash-lite",
46
- "gemini-1.5-pro","gemini-1.5-flash","gemini-1.5-flash-8b"
47
- ],
48
- },
49
- "groq": {
50
- "base_url": "https://api.groq.com/openai/v1",
51
- "models": [
52
- "llama-3.3-70b-versatile","llama-3.1-8b-instant",
53
- "llama3-70b-8192","llama3-8b-8192",
54
- "mixtral-8x7b-32768","gemma2-9b-it"
55
- ],
56
- },
57
- "github": {
58
- "base_url": "https://models.inference.ai.azure.com",
59
- "models": [
60
- "gpt-4o","gpt-4o-mini",
61
- "Phi-3.5-mini-instruct","Phi-3.5-MoE-instruct",
62
- "Meta-Llama-3.1-70B-Instruct","Meta-Llama-3.1-405B-Instruct"
63
- ],
64
- },
65
- "openrouter": {
66
- "base_url": "https://openrouter.ai/api/v1",
67
- "models": [
68
- "mistralai/mistral-7b-instruct:free",
69
- "meta-llama/llama-3.2-3b-instruct:free",
70
- "google/gemma-3-1b-it:free",
71
- "deepseek/deepseek-r1:free",
72
- ],
73
- },
74
- "mistral": {
75
- "base_url": "https://api.mistral.ai/v1",
76
- "models": [
77
- "mistral-small-latest","mistral-large-latest",
78
- "open-mistral-7b","open-mixtral-8x7b"
79
- ],
80
- },
81
- "together": {
82
- "base_url": "https://api.together.xyz/v1",
83
- "models": [
84
- "meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo",
85
- "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
86
- "mistralai/Mixtral-8x7B-Instruct-v0.1"
87
- ],
88
- },
89
- "nvidia": {
90
- "base_url": "https://integrate.api.nvidia.com/v1",
91
- "models": [
92
- "meta/llama-3.1-70b-instruct",
93
- "meta/llama-3.1-8b-instruct",
94
- "mistralai/mixtral-8x7b-instruct"
95
- ],
96
- },
97
- "cohere": {
98
- "base_url": "https://api.cohere.com/v2",
99
- "models": ["command-r-plus","command-r","command"],
100
- },
101
- "huggingface": {
102
- "base_url": "https://api-inference.huggingface.co/v1",
103
- "models": [
104
- "meta-llama/Llama-3.2-3B-Instruct",
105
- "mistralai/Mistral-7B-Instruct-v0.3"
106
- ],
107
- },
108
- "cerebras": {
109
- "base_url": "https://api.cerebras.ai/v1",
110
- "models": ["llama3.1-8b","llama3.1-70b"],
111
- },
112
- "sambanova": {
113
- "base_url": "https://api.sambanova.ai/v1",
114
- "models": [
115
- "Meta-Llama-3.1-8B-Instruct",
116
- "Meta-Llama-3.1-70B-Instruct",
117
- "Meta-Llama-3.1-405B-Instruct"
118
- ],
119
- },
120
- "cloudflare": {
121
- "base_url": "https://api.cloudflare.com/client/v4/accounts/{}/ai/v1",
122
- "models": [
123
- "@cf/meta/llama-3.1-8b-instruct",
124
- "@cf/mistral/mistral-7b-instruct-v0.1"
125
- ],
126
- },
127
- "zhipu": {
128
- "base_url": "https://open.bigmodel.cn/api/paas/v4",
129
- "models": ["glm-4-flash","glm-4","glm-3-turbo"],
130
- },
131
- }
132
-
133
-
134
- def load_config():
135
- """从环境变量读取所有配置"""
136
- # ── 读取用户 API Keys ──
137
- raw_keys = os.getenv("API_KEYS", "").strip()
138
- api_keys = set(k.strip() for k in raw_keys.split(",") if k.strip())
139
-
140
- # ── 读取 Provider Keys ──
141
- providers = {}
142
- cf_account = os.getenv("CLOUDFLARE_ACCOUNT_ID", "default_id")
143
-
144
- for env_name, provider_name in PROVIDER_MAP.items():
145
- key_value = os.getenv(env_name, "").strip()
146
- if not key_value:
147
- continue
148
-
149
- cfg = PROVIDER_CONFIG.get(provider_name, {})
150
- base_url = cfg.get("base_url", "")
151
-
152
- # 安全修复:动态组装 Cloudflare 的账户 ID 路径
153
- if provider_name == "cloudflare" and "{}" in base_url:
154
- base_url = base_url.format(cf_account)
155
-
156
- providers[provider_name] = {
157
- "api_key": key_value,
158
- "base_url": base_url,
159
- "models": cfg.get("models", []),
160
- }
161
- logger.info(f"✅ 加载 Provider: {provider_name}")
162
-
163
- logger.info(f"🚀 加载成功!用户授权 Key 数量: {len(api_keys)} | 通道网关数量: {len(providers)}")
164
- return api_keys, providers
165
-
166
-
167
- API_KEYS, PROVIDERS = load_config()
168
-
169
- # ──────────────────────────────────────────
170
- # 鉴权
171
- # ──────────────────────────────────────────
172
-
173
- def verify_api_key(authorization: str = Header(...)):
174
- token = authorization.removeprefix("Bearer ").strip()
175
- if token not in API_KEYS:
176
- raise HTTPException(status_code=401, detail="Invalid API key")
177
- return token
178
-
179
- # ──────────────────────────────────────────
180
- # 路由映射
181
- # ──────────────────────────────────────────
182
-
183
- @app.get("/", response_class=HTMLResponse)
184
- async def index():
185
- return """
186
- <html>
187
- <head><title>FreeLLMAPI Gateway</title></head>
188
- <body style="font-family: Arial, sans-serif; text-align: center; margin-top: 100px; background-color: #f4f4f9;">
189
- <h1 style="color: #4f46e5;">🚀 FreeLLMAPI 环境变量极速版</h1>
190
- <p style="color: #6b7280;">云端轻量化无持久化转发网关运行中...</p>
191
- <div style="margin-top: 20px;">
192
- <span style="background-color: #10b981; color: white; padding: 6px 16px; border-radius: 20px; font-size: 0.9em; font-weight: bold;">STATUS: ONLINE</span>
193
- </div>
194
- </body>
195
- </html>
196
- """
197
-
198
- @app.get("/health")
199
- async def health():
200
- # 完美合并后的检查路由
201
- return {
202
- "status": "ok",
203
- "keys": len(API_KEYS),
204
- "providers": list(PROVIDERS.keys()),
205
- }
206
-
207
-
208
- @app.get("/v1/models")
209
- async def list_models(_: str = Depends(verify_api_key)):
210
- data = []
211
- for p_cfg in PROVIDERS.values():
212
- for m in p_cfg["models"]:
213
- data.append({"id": m, "object": "model"})
214
- return {"object": "list", "data": data}
215
-
216
-
217
- @app.post("/v1/chat/completions")
218
- async def chat_completions(body: dict, _: str = Depends(verify_api_key)):
219
- model = body.get("model", "")
220
- provider = None
221
-
222
- for p_cfg in PROVIDERS.values():
223
- if model in p_cfg["models"]:
224
- provider = p_cfg
225
- break
226
-
227
- if not provider:
228
- raise HTTPException(
229
- status_code=404,
230
- detail=f"没有 Provider 支持模型: {model}"
231
- )
232
-
233
- # 完美支持流式输出与普通响应的核心中转逻辑
234
- async def stream_generator(target_url, headers, payload):
235
- async with httpx.AsyncClient(timeout=60.0) as client:
236
- async with client.stream("POST", target_url, headers=headers, json=payload) as response:
237
- async for chunk in response.aiter_bytes():
238
- yield chunk
239
-
240
- target_url = f"{provider['base_url']}/chat/completions"
241
- headers = {"Authorization": f"Bearer {provider['api_key']}"}
242
-
243
- if body.get("stream", False):
244
- return StreamingResponse(
245
- stream_generator(target_url, headers, body),
246
- media_type="text/event-stream"
247
- )
248
- else:
249
- async with httpx.AsyncClient(timeout=60.0) as client:
250
- resp = await client.post(target_url, headers=headers, json=body)
251
- return resp.json()
252
-
253
-
254
- if __name__ == "__main__":
255
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
1
+ 可直接部署到 Hugging Face Space 的单文件版本。