javaeeduke commited on
Commit
d87d2e8
ยท
verified ยท
1 Parent(s): b78e2db

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +50 -26
main.py CHANGED
@@ -1,5 +1,6 @@
1
  from fastapi import FastAPI, HTTPException, Depends, Header
2
  from fastapi.middleware.cors import CORSMiddleware
 
3
  import uvicorn
4
  import os
5
  import logging
@@ -21,7 +22,6 @@ app.add_middleware(
21
  # ่ฏปๅ–ๅŽŸ้กน็›ฎๅ›บๅฎšๅ‘ฝๅ็š„ Secrets
22
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
23
 
24
- # ๅŽŸ้กน็›ฎ็š„ Provider Key ๆ˜ ๅฐ„่กจ
25
  PROVIDER_MAP = {
26
  "GOOGLE_API_KEY": "google",
27
  "GROQ_API_KEY": "groq",
@@ -38,7 +38,6 @@ PROVIDER_MAP = {
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",
@@ -134,29 +133,34 @@ PROVIDER_CONFIG = {
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
 
@@ -173,11 +177,27 @@ def verify_api_key(authorization: str = Header(...)):
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),
@@ -195,11 +215,8 @@ async def list_models(_: str = Depends(verify_api_key)):
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():
@@ -213,18 +230,25 @@ async def chat_completions(
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__":
 
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
 
22
  # ่ฏปๅ–ๅŽŸ้กน็›ฎๅ›บๅฎšๅ‘ฝๅ็š„ Secrets
23
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
24
 
 
25
  PROVIDER_MAP = {
26
  "GOOGLE_API_KEY": "google",
27
  "GROQ_API_KEY": "groq",
 
38
  "ZHIPU_API_KEY": "zhipu",
39
  }
40
 
 
41
  PROVIDER_CONFIG = {
42
  "google": {
43
  "base_url": "https://generativelanguage.googleapis.com/v1beta/openai",
 
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
 
 
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),
 
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():
 
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__":