javaeeduke commited on
Commit
b1ddb4f
ยท
verified ยท
1 Parent(s): a45f14b

Create main.py

Browse files
Files changed (1) hide show
  1. main.py +144 -0
main.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException, Depends, Header
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ import uvicorn
4
+ import os
5
+ import logging
6
+ import httpx
7
+
8
+ logging.basicConfig(level=logging.INFO)
9
+ logger = logging.getLogger(__name__)
10
+
11
+ app = FastAPI(title="FreeLLMAPI")
12
+
13
+ app.add_middleware(
14
+ CORSMiddleware,
15
+ allow_origins=["*"],
16
+ allow_methods=["*"],
17
+ allow_headers=["*"],
18
+ )
19
+
20
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
21
+ # ไปŽ็Žฏๅขƒๅ˜้‡่ฏปๅ–ๆ‰€ๆœ‰้…็ฝฎ๏ผˆ้‡ๅฏๆฐธไธไธขๅคฑ๏ผ‰
22
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
23
+
24
+ def load_config():
25
+ """
26
+ Secrets ๆ ผๅผ็บฆๅฎš๏ผš
27
+
28
+ API Keys๏ผˆๅ…่ฎธ่ฎฟ้—ฎ็š„็”จๆˆท๏ผ‰:
29
+ API_KEYS = "key1,key2,key3"
30
+
31
+ Providers๏ผˆไธŠๆธธ LLM ๆœๅŠก๏ผ‰:
32
+ PROVIDER_1_NAME = "openai"
33
+ PROVIDER_1_APIKEY = "sk-xxxx"
34
+ PROVIDER_1_BASEURL = "https://api.openai.com/v1"
35
+ PROVIDER_1_MODELS = "gpt-4o,gpt-4o-mini"
36
+
37
+ PROVIDER_2_NAME = "deepseek"
38
+ PROVIDER_2_APIKEY = "sk-yyyy"
39
+ PROVIDER_2_BASEURL = "https://api.deepseek.com/v1"
40
+ PROVIDER_2_MODELS = "deepseek-chat,deepseek-coder"
41
+
42
+ # ๆœ€ๅคšๆ”ฏๆŒ 20 ไธช Provider
43
+ """
44
+
45
+ # โ”€โ”€ ่ฏปๅ– API Keys โ”€โ”€
46
+ raw_keys = os.getenv("API_KEYS", "")
47
+ api_keys = set(
48
+ k.strip() for k in raw_keys.split(",") if k.strip()
49
+ )
50
+
51
+ # โ”€โ”€ ่ฏปๅ– Providers โ”€โ”€
52
+ providers = {}
53
+ for i in range(1, 21):
54
+ name = os.getenv(f"PROVIDER_{i}_NAME")
55
+ api_key = os.getenv(f"PROVIDER_{i}_APIKEY")
56
+ baseurl = os.getenv(f"PROVIDER_{i}_BASEURL")
57
+ models = os.getenv(f"PROVIDER_{i}_MODELS", "")
58
+
59
+ if not name or not api_key or not baseurl:
60
+ continue # ่ฏฅ็ผ–ๅทๆœช้…็ฝฎ๏ผŒ่ทณ่ฟ‡
61
+
62
+ providers[name] = {
63
+ "api_key": api_key,
64
+ "base_url": baseurl.rstrip("/"),
65
+ "models": [m.strip() for m in models.split(",") if m.strip()],
66
+ }
67
+
68
+ logger.info(f"โœ… ๅŠ ่ฝฝไบ† {len(api_keys)} ไธช API Key")
69
+ logger.info(f"โœ… ๅŠ ่ฝฝไบ† {len(providers)} ไธช Provider: {list(providers.keys())}")
70
+
71
+ return api_keys, providers
72
+
73
+
74
+ # ๅฏๅŠจๆ—ถๅŠ ่ฝฝไธ€ๆฌก
75
+ API_KEYS, PROVIDERS = load_config()
76
+
77
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
78
+ # ้‰ดๆƒ
79
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
80
+
81
+ def verify_api_key(authorization: str = Header(...)):
82
+ token = authorization.removeprefix("Bearer ").strip()
83
+ if token not in API_KEYS:
84
+ raise HTTPException(status_code=401, detail="Invalid API key")
85
+ return token
86
+
87
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
88
+ # ่ทฏ็”ฑ
89
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
90
+
91
+ @app.get("/health")
92
+ async def health():
93
+ return {
94
+ "status": "ok",
95
+ "keys": len(API_KEYS),
96
+ "providers": list(PROVIDERS.keys()),
97
+ }
98
+
99
+
100
+ @app.get("/v1/models")
101
+ async def list_models(_: str = Depends(verify_api_key)):
102
+ data = []
103
+ for p_cfg in PROVIDERS.values():
104
+ for m in p_cfg["models"]:
105
+ data.append({"id": m, "object": "model"})
106
+ return {"object": "list", "data": data}
107
+
108
+
109
+ @app.post("/v1/chat/completions")
110
+ async def chat_completions(
111
+ body: dict,
112
+ _: str = Depends(verify_api_key)
113
+ ):
114
+ model = body.get("model", "")
115
+ provider = None
116
+
117
+ # ๆ‰พๅˆฐๆ”ฏๆŒ่ฏฅ model ็š„ provider
118
+ for p_cfg in PROVIDERS.values():
119
+ if model in p_cfg["models"]:
120
+ provider = p_cfg
121
+ break
122
+
123
+ if not provider:
124
+ raise HTTPException(
125
+ status_code=404,
126
+ detail=f"ๆฒกๆœ‰ Provider ๆ”ฏๆŒๆจกๅž‹: {model}"
127
+ )
128
+
129
+ # ่ฝฌๅ‘ๅˆฐไธŠๆธธ
130
+ async with httpx.AsyncClient(timeout=60) as client:
131
+ resp = await client.post(
132
+ f"{provider['base_url']}/chat/completions",
133
+ headers={"Authorization": f"Bearer {provider['api_key']}"},
134
+ json=body,
135
+ )
136
+ return resp.json()
137
+
138
+
139
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
140
+ # ๅ…ฅๅฃ
141
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
142
+
143
+ if __name__ == "__main__":
144
+ uvicorn.run(app, host="0.0.0.0", port=7860)