aaxaxax commited on
Commit
674a2d7
·
1 Parent(s): 62782db

Add 3-key failover

Browse files
Files changed (1) hide show
  1. app.py +92 -31
app.py CHANGED
@@ -1,22 +1,65 @@
1
  import os
 
 
2
  import httpx
3
- import traceback
4
  from fastapi import FastAPI, Request
5
  from fastapi.responses import JSONResponse, StreamingResponse, Response
6
 
7
  app = FastAPI()
8
 
9
  BASE_URL = os.getenv("BASE_URL", "https://ollama.com")
10
- TEST_KEY = "37e81a6be4104fbfbfbe2ecf557a2c10.GoIbzpHebdM9ZcHarQ9A12Cp"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  @app.get("/")
13
  def root():
14
- return {"status": "ok", "base_url": BASE_URL}
 
 
 
 
 
15
 
16
- @app.api_route("/v1/{path:path}", methods=["GET", "POST"])
17
  async def proxy(req: Request, path: str):
18
- print(f"[PROXY] path={path}")
19
-
20
  target_url = f"{BASE_URL}/v1/{path}"
21
  body = await req.body()
22
 
@@ -24,29 +67,47 @@ async def proxy(req: Request, path: str):
24
  headers.pop("host", None)
25
  headers.pop("content-length", None)
26
 
27
- # Hardcoded key
28
- headers["Authorization"] = f"Bearer {TEST_KEY}"
29
-
30
- print(f"[PROXY] target={target_url}")
31
- print(f"[PROXY] key={TEST_KEY[:10]}...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- try:
34
- async with httpx.AsyncClient(timeout=60) as client:
35
- resp = await client.request(
36
- method=req.method,
37
- url=target_url,
38
- headers=headers,
39
- content=body,
40
- params=req.query_params
41
- )
42
- print(f"[PROXY] status={resp.status_code}")
43
-
44
- return Response(
45
- content=resp.content,
46
- status_code=resp.status_code,
47
- headers=dict(resp.headers)
48
- )
49
- except Exception as e:
50
- print(f"[ERROR] {e}")
51
- traceback.print_exc()
52
- return JSONResponse({"error": str(e)}, status_code=500)
 
1
  import os
2
+ import time
3
+ import random
4
  import httpx
5
+ from typing import Dict, List
6
  from fastapi import FastAPI, Request
7
  from fastapi.responses import JSONResponse, StreamingResponse, Response
8
 
9
  app = FastAPI()
10
 
11
  BASE_URL = os.getenv("BASE_URL", "https://ollama.com")
12
+
13
+ # 3 API KEYS - FAILOVER
14
+ API_KEYS = [
15
+ "8ca25de51e554c099962b78b7ce0c9e9.Mp5dnqctR2zzq3g-NO_M-cjW",
16
+ "dbd1d0c534964684a6d4678348ab8d30.ieDfmSYVnf0MmTjR-AIdNrW9",
17
+ "37e81a6be4104fbfbfbe2ecf557a2c10.GoIbzpHebdM9ZcHarQ9A12Cp"
18
+ ]
19
+
20
+ # KEY STATUS
21
+ key_status: Dict[str, Dict] = {
22
+ k: {"fail": 0, "cooldown": 0, "status": "active"}
23
+ for k in API_KEYS
24
+ }
25
+
26
+ def pick_key() -> str:
27
+ """Pick an available key, fallback if all fail"""
28
+ now = time.time()
29
+ valid = [k for k, v in key_status.items()
30
+ if v["status"] != "dead" and v["cooldown"] < now]
31
+
32
+ if valid:
33
+ return random.choice(valid)
34
+
35
+ # All failed, reset and try again
36
+ for k in key_status:
37
+ key_status[k]["status"] = "active"
38
+ key_status[k]["fail"] = 0
39
+
40
+ return random.choice(API_KEYS)
41
+
42
+ def mark_fail(key: str):
43
+ key_status[key]["fail"] += 1
44
+ if key_status[key]["fail"] >= 3:
45
+ key_status[key]["cooldown"] = time.time() + 60
46
+ print(f"[KEY] {key[:8]}... -> cooldown 60s")
47
+
48
+ def mark_ok(key: str):
49
+ key_status[key]["fail"] = 0
50
+ key_status[key]["status"] = "active"
51
 
52
  @app.get("/")
53
  def root():
54
+ active = sum(1 for v in key_status.values() if v["status"] == "active")
55
+ return {
56
+ "status": "ok",
57
+ "keys_total": len(API_KEYS),
58
+ "keys_active": active
59
+ }
60
 
61
+ @app.api_route("/v1/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
62
  async def proxy(req: Request, path: str):
 
 
63
  target_url = f"{BASE_URL}/v1/{path}"
64
  body = await req.body()
65
 
 
67
  headers.pop("host", None)
68
  headers.pop("content-length", None)
69
 
70
+ # Try each key until success
71
+ tried = set()
72
+ for _ in range(len(API_KEYS)):
73
+ key = pick_key()
74
+ if key in tried:
75
+ continue
76
+ tried.add(key)
77
+
78
+ headers["Authorization"] = f"Bearer {key}"
79
+
80
+ try:
81
+ async with httpx.AsyncClient(timeout=60) as client:
82
+ resp = await client.request(
83
+ method=req.method,
84
+ url=target_url,
85
+ headers=headers,
86
+ content=body,
87
+ params=req.query_params
88
+ )
89
+
90
+ if resp.status_code < 400:
91
+ mark_ok(key)
92
+ print(f"[OK] key={key[:6]}...")
93
+ return Response(
94
+ content=resp.content,
95
+ status_code=resp.status_code,
96
+ headers=dict(resp.headers)
97
+ )
98
+
99
+ # Error with this key
100
+ mark_fail(key)
101
+ print(f"[FAIL] key={key[:6]}... status={resp.status_code}")
102
+ continue
103
+
104
+ except Exception as e:
105
+ mark_fail(key)
106
+ print(f"[ERROR] key={key[:6]}... {e}")
107
+ continue
108
 
109
+ # All keys failed
110
+ return JSONResponse(
111
+ {"error": "all keys failed", "tried": len(API_KEYS)},
112
+ status_code=500
113
+ )