javaeeduke commited on
Commit
493506d
·
verified ·
1 Parent(s): 5519468

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +93 -244
main.py CHANGED
@@ -1,6 +1,5 @@
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
@@ -14,369 +13,219 @@ app = FastAPI(title="FreeLLMAPI")
14
  app.add_middleware(
15
  CORSMiddleware,
16
  allow_origins=["*"],
17
- allow_credentials=True,
18
  allow_methods=["*"],
19
  allow_headers=["*"],
20
  )
21
 
22
- # ============================================================
23
- # Provider Secrets
24
- # ============================================================
25
 
 
26
  PROVIDER_MAP = {
27
- "GOOGLE_API_KEY": "google",
28
- "GROQ_API_KEY": "groq",
29
- "GITHUB_TOKEN": "github",
30
- "OPENROUTER_API_KEY": "openrouter",
31
- "MISTRAL_API_KEY": "mistral",
32
- "TOGETHER_API_KEY": "together",
33
- "NVIDIA_API_KEY": "nvidia",
34
- "COHERE_API_KEY": "cohere",
35
- "HF_TOKEN": "huggingface",
36
- "CEREBRAS_API_KEY": "cerebras",
37
- "SAMBANOVA_API_KEY": "sambanova",
38
- "CLOUDFLARE_API_TOKEN": "cloudflare",
39
- "ZHIPU_API_KEY": "zhipu",
40
  }
41
 
 
42
  PROVIDER_CONFIG = {
43
  "google": {
44
  "base_url": "https://generativelanguage.googleapis.com/v1beta/openai",
45
  "models": [
46
- "gemini-2.0-flash",
47
- "gemini-2.0-flash-lite",
48
- "gemini-1.5-flash",
49
- "gemini-1.5-pro",
50
  ],
51
  },
52
  "groq": {
53
  "base_url": "https://api.groq.com/openai/v1",
54
  "models": [
55
- "llama-3.3-70b-versatile",
56
- "llama-3.1-8b-instant",
57
- "mixtral-8x7b-32768",
58
- "gemma2-9b-it",
59
  ],
60
  },
61
  "github": {
62
  "base_url": "https://models.inference.ai.azure.com",
63
  "models": [
64
- "gpt-4o",
65
- "gpt-4o-mini",
66
- "Phi-4",
67
- "DeepSeek-R1",
68
  ],
69
  },
70
  "openrouter": {
71
  "base_url": "https://openrouter.ai/api/v1",
72
  "models": [
 
 
 
73
  "deepseek/deepseek-r1:free",
74
- "google/gemma-3-27b-it:free",
75
- "meta-llama/llama-3.3-70b-instruct:free",
76
  ],
77
  },
78
  "mistral": {
79
  "base_url": "https://api.mistral.ai/v1",
80
  "models": [
81
- "mistral-small-latest",
82
- "mistral-large-latest",
83
  ],
84
  },
85
  "together": {
86
  "base_url": "https://api.together.xyz/v1",
87
  "models": [
 
88
  "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
89
- "deepseek-ai/DeepSeek-R1",
90
  ],
91
  },
92
  "nvidia": {
93
  "base_url": "https://integrate.api.nvidia.com/v1",
94
  "models": [
95
  "meta/llama-3.1-70b-instruct",
96
- "deepseek-ai/deepseek-r1",
 
97
  ],
98
  },
99
  "cohere": {
100
- "base_url": "https://api.cohere.com/compatibility/v1",
101
- "models": [
102
- "command-r-plus",
103
- "command-r",
104
- ],
105
  },
106
  "huggingface": {
107
- "base_url": "https://huggingface.co/api/inference-proxy/together",
108
  "models": [
109
- "meta-llama/Llama-3.3-70B-Instruct",
110
- "Qwen/Qwen2.5-72B-Instruct",
111
  ],
112
  },
113
  "cerebras": {
114
  "base_url": "https://api.cerebras.ai/v1",
115
- "models": [
116
- "llama3.1-8b",
117
- "llama3.3-70b",
118
- ],
119
  },
120
  "sambanova": {
121
  "base_url": "https://api.sambanova.ai/v1",
122
  "models": [
123
- "Meta-Llama-3.1-405B-Instruct",
 
 
124
  ],
125
  },
126
  "cloudflare": {
127
  "base_url": "https://api.cloudflare.com/client/v4/accounts/{}/ai/v1",
128
  "models": [
129
  "@cf/meta/llama-3.1-8b-instruct",
 
130
  ],
131
  },
132
  "zhipu": {
133
  "base_url": "https://open.bigmodel.cn/api/paas/v4",
134
- "models": [
135
- "glm-4-flash",
136
- "glm-4",
137
- ],
138
  },
139
  }
140
 
141
 
142
- # ============================================================
143
- # Load Config
144
- # ============================================================
145
-
146
  def load_config():
 
147
 
148
- raw_keys = os.getenv("API_KEYS", "").strip()
149
-
150
- api_keys = set(
151
- k.strip()
152
- for k in raw_keys.split(",")
153
- if k.strip()
154
- )
155
 
 
156
  providers = {}
157
-
158
- cf_account_id = os.getenv(
159
- "CLOUDFLARE_ACCOUNT_ID",
160
- ""
161
- )
162
-
163
  for env_name, provider_name in PROVIDER_MAP.items():
 
 
 
164
 
165
- key = os.getenv(env_name, "").strip()
166
-
167
- if not key:
168
- continue
169
-
170
- cfg = PROVIDER_CONFIG[provider_name]
171
-
172
- base_url = cfg["base_url"]
173
-
174
- if provider_name == "cloudflare":
175
- if cf_account_id:
176
- base_url = base_url.format(cf_account_id)
177
-
178
  providers[provider_name] = {
179
- "api_key": key,
180
- "base_url": base_url,
181
- "models": cfg["models"],
182
  }
 
183
 
184
- logger.info(f"✅ Provider loaded: {provider_name}")
185
-
186
- logger.info(
187
- f"🚀 API Keys={len(api_keys)} Providers={len(providers)}"
188
- )
189
 
190
  return api_keys, providers
191
 
192
 
193
  API_KEYS, PROVIDERS = load_config()
194
 
195
- # ============================================================
196
- # Auth
197
- # ============================================================
198
-
199
- def verify_api_key(
200
- authorization: str = Header(...)
201
- ):
202
- token = authorization.replace(
203
- "Bearer ",
204
- ""
205
- ).strip()
206
 
 
 
207
  if token not in API_KEYS:
208
- raise HTTPException(
209
- status_code=401,
210
- detail="Invalid API key"
211
- )
212
-
213
  return token
214
 
215
- # ============================================================
216
- # Routes
217
- # ============================================================
218
-
219
- @app.get("/", response_class=HTMLResponse)
220
- async def root():
221
-
222
- return f"""
223
- <html>
224
- <body style="font-family:Arial;padding:40px">
225
- <h1>🚀 FreeLLMAPI</h1>
226
- <p>Status: ONLINE</p>
227
- <p>Providers: {len(PROVIDERS)}</p>
228
- <p>User Keys: {len(API_KEYS)}</p>
229
- </body>
230
- </html>
231
- """
232
-
233
 
234
  @app.get("/health")
235
  async def health():
236
  return {
237
- "status": "ok",
 
238
  "providers": list(PROVIDERS.keys()),
239
- "provider_count": len(PROVIDERS),
240
- "user_keys": len(API_KEYS),
241
- }
242
-
243
-
244
- @app.get("/keys")
245
- async def keys():
246
-
247
- result = {}
248
-
249
- for env_name, provider in PROVIDER_MAP.items():
250
- result[provider] = bool(
251
- os.getenv(env_name)
252
- )
253
-
254
- return {
255
- "providers": result,
256
- "provider_count": len(PROVIDERS),
257
- "user_api_keys": len(API_KEYS),
258
- }
259
-
260
-
261
- @app.get("/debug")
262
- async def debug():
263
- return {
264
- "providers_loaded": list(PROVIDERS.keys()),
265
- "provider_count": len(PROVIDERS),
266
- "user_api_keys": len(API_KEYS),
267
- "total_models": sum(
268
- len(x["models"])
269
- for x in PROVIDERS.values()
270
- ),
271
  }
272
 
273
 
274
  @app.get("/v1/models")
275
- async def models(
276
- _: str = Depends(verify_api_key)
277
- ):
278
  data = []
279
-
280
- for provider in PROVIDERS.values():
281
- for model in provider["models"]:
282
- data.append({
283
- "id": model,
284
- "object": "model"
285
- })
286
-
287
- return {
288
- "object": "list",
289
- "data": data
290
- }
291
 
292
 
293
  @app.post("/v1/chat/completions")
294
- async def chat(
295
  body: dict,
296
  _: str = Depends(verify_api_key)
297
  ):
298
-
299
- model = body.get("model")
300
-
301
  provider = None
302
 
303
- for p in PROVIDERS.values():
304
- if model in p["models"]:
305
- provider = p
306
  break
307
 
308
  if not provider:
309
  raise HTTPException(
310
  status_code=404,
311
- detail=f"Model not found: {model}"
312
  )
313
 
314
- target_url = (
315
- provider["base_url"]
316
- + "/chat/completions"
317
- )
318
-
319
- headers = {
320
- "Authorization":
321
- f"Bearer {provider['api_key']}",
322
- "Content-Type":
323
- "application/json"
324
- }
325
-
326
- if provider["base_url"].startswith(
327
- "https://openrouter.ai"
328
- ):
329
- headers["HTTP-Referer"] = (
330
- "https://huggingface.co"
331
- )
332
- headers["X-Title"] = (
333
- "HF FreeLLMAPI"
334
- )
335
-
336
- if body.get("stream", False):
337
-
338
- async def generate():
339
-
340
- async with httpx.AsyncClient(
341
- timeout=120
342
- ) as client:
343
-
344
- async with client.stream(
345
- "POST",
346
- target_url,
347
- headers=headers,
348
- json=body,
349
- ) as response:
350
-
351
- async for chunk in response.aiter_bytes():
352
- yield chunk
353
-
354
- return StreamingResponse(
355
- generate(),
356
- media_type="text/event-stream"
357
- )
358
-
359
- async with httpx.AsyncClient(
360
- timeout=120
361
- ) as client:
362
-
363
- response = await client.post(
364
- target_url,
365
- headers=headers,
366
  json=body,
367
  )
 
368
 
369
- try:
370
- return response.json()
371
- except Exception:
372
- return {
373
- "raw": response.text
374
- }
375
 
376
 
377
  if __name__ == "__main__":
378
- uvicorn.run(
379
- app,
380
- host="0.0.0.0",
381
- port=7860
382
- )
 
1
  from fastapi import FastAPI, HTTPException, Depends, Header
2
  from fastapi.middleware.cors import CORSMiddleware
 
3
  import uvicorn
4
  import os
5
  import logging
 
13
  app.add_middleware(
14
  CORSMiddleware,
15
  allow_origins=["*"],
 
16
  allow_methods=["*"],
17
  allow_headers=["*"],
18
  )
19
 
20
+ # ──────────────────────────────────────────
21
+ # 读取原项目固定命名的 Secrets
22
+ # ──────────────────────────────────────────
23
 
24
+ # 原项目的 Provider Key 映射表
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 对应的 base_url 和支持的模型
42
  PROVIDER_CONFIG = {
43
  "google": {
44
  "base_url": "https://generativelanguage.googleapis.com/v1beta/openai",
45
  "models": [
46
+ "gemini-2.0-flash","gemini-2.0-flash-lite",
47
+ "gemini-1.5-pro","gemini-1.5-flash","gemini-1.5-flash-8b"
 
 
48
  ],
49
  },
50
  "groq": {
51
  "base_url": "https://api.groq.com/openai/v1",
52
  "models": [
53
+ "llama-3.3-70b-versatile","llama-3.1-8b-instant",
54
+ "llama3-70b-8192","llama3-8b-8192",
55
+ "mixtral-8x7b-32768","gemma2-9b-it"
 
56
  ],
57
  },
58
  "github": {
59
  "base_url": "https://models.inference.ai.azure.com",
60
  "models": [
61
+ "gpt-4o","gpt-4o-mini",
62
+ "Phi-3.5-mini-instruct","Phi-3.5-MoE-instruct",
63
+ "Meta-Llama-3.1-70B-Instruct","Meta-Llama-3.1-405B-Instruct"
 
64
  ],
65
  },
66
  "openrouter": {
67
  "base_url": "https://openrouter.ai/api/v1",
68
  "models": [
69
+ "mistralai/mistral-7b-instruct:free",
70
+ "meta-llama/llama-3.2-3b-instruct:free",
71
+ "google/gemma-3-1b-it:free",
72
  "deepseek/deepseek-r1:free",
 
 
73
  ],
74
  },
75
  "mistral": {
76
  "base_url": "https://api.mistral.ai/v1",
77
  "models": [
78
+ "mistral-small-latest","mistral-large-latest",
79
+ "open-mistral-7b","open-mixtral-8x7b"
80
  ],
81
  },
82
  "together": {
83
  "base_url": "https://api.together.xyz/v1",
84
  "models": [
85
+ "meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo",
86
  "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
87
+ "mistralai/Mixtral-8x7B-Instruct-v0.1"
88
  ],
89
  },
90
  "nvidia": {
91
  "base_url": "https://integrate.api.nvidia.com/v1",
92
  "models": [
93
  "meta/llama-3.1-70b-instruct",
94
+ "meta/llama-3.1-8b-instruct",
95
+ "mistralai/mixtral-8x7b-instruct"
96
  ],
97
  },
98
  "cohere": {
99
+ "base_url": "https://api.cohere.com/v2",
100
+ "models": ["command-r-plus","command-r","command"],
 
 
 
101
  },
102
  "huggingface": {
103
+ "base_url": "https://api-inference.huggingface.co/v1",
104
  "models": [
105
+ "meta-llama/Llama-3.2-3B-Instruct",
106
+ "mistralai/Mistral-7B-Instruct-v0.3"
107
  ],
108
  },
109
  "cerebras": {
110
  "base_url": "https://api.cerebras.ai/v1",
111
+ "models": ["llama3.1-8b","llama3.1-70b"],
 
 
 
112
  },
113
  "sambanova": {
114
  "base_url": "https://api.sambanova.ai/v1",
115
  "models": [
116
+ "Meta-Llama-3.1-8B-Instruct",
117
+ "Meta-Llama-3.1-70B-Instruct",
118
+ "Meta-Llama-3.1-405B-Instruct"
119
  ],
120
  },
121
  "cloudflare": {
122
  "base_url": "https://api.cloudflare.com/client/v4/accounts/{}/ai/v1",
123
  "models": [
124
  "@cf/meta/llama-3.1-8b-instruct",
125
+ "@cf/mistral/mistral-7b-instruct-v0.1"
126
  ],
127
  },
128
  "zhipu": {
129
  "base_url": "https://open.bigmodel.cn/api/paas/v4",
130
+ "models": ["glm-4-flash","glm-4","glm-3-turbo"],
 
 
 
131
  },
132
  }
133
 
134
 
 
 
 
 
135
  def load_config():
136
+ """从环境变量读取所有配置"""
137
 
138
+ # ── 读取用户 API Keys(用于鉴权) ──
139
+ raw_keys = os.getenv("API_KEYS", "")
140
+ api_keys = set(k.strip() for k in raw_keys.split(",") if k.strip())
 
 
 
 
141
 
142
+ # ── 读取 Provider Keys ──
143
  providers = {}
 
 
 
 
 
 
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 # 没配置该 Provider,跳过
148
 
149
+ cfg = PROVIDER_CONFIG.get(provider_name, {})
 
 
 
 
 
 
 
 
 
 
 
 
150
  providers[provider_name] = {
151
+ "api_key": key_value,
152
+ "base_url": cfg.get("base_url", ""),
153
+ "models": cfg.get("models", []),
154
  }
155
+ logger.info(f"✅ 加载 Provider: {provider_name}")
156
 
157
+ logger.info(f"✅ 加载了 {len(api_keys)} 个 API Key")
158
+ logger.info(f"✅ 加载了 {len(providers)} 个 Provider: {list(providers.keys())}")
 
 
 
159
 
160
  return api_keys, providers
161
 
162
 
163
  API_KEYS, PROVIDERS = load_config()
164
 
165
+ # ──────────────────────────────────────────
166
+ # 鉴权
167
+ # ──────────────────────────────────────────
 
 
 
 
 
 
 
 
168
 
169
+ def verify_api_key(authorization: str = Header(...)):
170
+ token = authorization.removeprefix("Bearer ").strip()
171
  if token not in API_KEYS:
172
+ raise HTTPException(status_code=401, detail="Invalid API key")
 
 
 
 
173
  return token
174
 
175
+ # ──────────────────────────────────────────
176
+ # 路由
177
+ # ──────────────────────────────────────────
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
  @app.get("/health")
180
  async def health():
181
  return {
182
+ "status": "ok",
183
+ "keys": len(API_KEYS),
184
  "providers": list(PROVIDERS.keys()),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  }
186
 
187
 
188
  @app.get("/v1/models")
189
+ async def list_models(_: str = Depends(verify_api_key)):
 
 
190
  data = []
191
+ for p_cfg in PROVIDERS.values():
192
+ for m in p_cfg["models"]:
193
+ data.append({"id": m, "object": "model"})
194
+ return {"object": "list", "data": data}
 
 
 
 
 
 
 
 
195
 
196
 
197
  @app.post("/v1/chat/completions")
198
+ async def chat_completions(
199
  body: dict,
200
  _: str = Depends(verify_api_key)
201
  ):
202
+ model = body.get("model", "")
 
 
203
  provider = None
204
 
205
+ for p_cfg in PROVIDERS.values():
206
+ if model in p_cfg["models"]:
207
+ provider = p_cfg
208
  break
209
 
210
  if not provider:
211
  raise HTTPException(
212
  status_code=404,
213
+ detail=f"没有 Provider 支持模型: {model}"
214
  )
215
 
216
+ async with httpx.AsyncClient(timeout=60) as client:
217
+ resp = await client.post(
218
+ f"{provider['base_url']}/chat/completions",
219
+ headers={"Authorization": f"Bearer {provider['api_key']}"},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  json=body,
221
  )
222
+ return resp.json()
223
 
224
+
225
+ @app.get("/health")
226
+ async def health():
227
+ return {"status": "ok"}
 
 
228
 
229
 
230
  if __name__ == "__main__":
231
+ uvicorn.run(app, host="0.0.0.0", port=7860)