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

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +382 -1
main.py CHANGED
@@ -1 +1,382 @@
1
- 可直ζŽ₯ιƒ¨η½²εˆ° Hugging Face Space ηš„ε•ζ–‡δ»Άη‰ˆζœ¬γ€‚
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_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
+ )