File size: 3,269 Bytes
3f06aeb
674a2d7
 
491498e
ebba60d
 
07bf7b7
3f06aeb
 
 
 
674a2d7
ebba60d
 
 
 
674a2d7
 
 
 
 
 
 
 
 
 
 
 
ebba60d
 
 
 
 
 
 
674a2d7
 
 
 
 
 
 
ebba60d
674a2d7
 
 
 
 
 
 
 
 
 
 
 
 
 
3f06aeb
5904245
 
674a2d7
 
 
ebba60d
674a2d7
 
 
3f06aeb
674a2d7
491498e
ebba60d
 
 
491498e
 
5904245
491498e
 
8f7852e
5904245
ebba60d
674a2d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5904245
ebba60d
012c649
fdb04f0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import os
import time
import random
import httpx
from typing import Dict
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse, StreamingResponse, Response

app = FastAPI()

BASE_URL = os.getenv("BASE_URL", "https://ollama.com")

# CLIENT API KEY (for auth)
MASTER_API_KEY = "ollama-proxy-free"

# BACKEND API KEYS (for Ollama)
API_KEYS = [
    "8ca25de51e554c099962b78b7ce0c9e9.Mp5dnqctR2zzq3g-NO_M-cjW",
    "dbd1d0c534964684a6d4678348ab8d30.ieDfmSYVnf0MmTjR-AIdNrW9",
    "37e81a6be4104fbfbfbe2ecf557a2c10.GoIbzpHebdM9ZcHarQ9A12Cp"
]

# KEY STATUS
key_status: Dict[str, Dict] = {
    k: {"fail": 0, "cooldown": 0, "status": "active"}
    for k in API_KEYS
}

def auth(req: Request):
    """Validate client API key"""
    client_key = req.headers.get("Authorization", "").replace("Bearer ", "")
    if client_key != MASTER_API_KEY:
        raise HTTPException(401, "Unauthorized")

def pick_key():
    now = time.time()
    valid = [k for k, v in key_status.items() 
             if v["status"] != "dead" and v["cooldown"] < now]
    
    if valid:
        return random.choice(valid)
    
    # Reset if all failed
    for k in key_status:
        key_status[k]["status"] = "active"
        key_status[k]["fail"] = 0
    
    return random.choice(API_KEYS)

def mark_fail(key: str):
    key_status[key]["fail"] += 1
    if key_status[key]["fail"] >= 3:
        key_status[key]["cooldown"] = time.time() + 60

def mark_ok(key: str):
    key_status[key]["fail"] = 0
    key_status[key]["status"] = "active"

@app.get("/")
def root():
    active = sum(1 for v in key_status.values() if v["status"] == "active")
    return {
        "status": "ok",
        "mode": "openai-proxy",
        "keys_total": len(API_KEYS),
        "keys_active": active
    }

@app.api_route("/v1/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
async def proxy(req: Request, path: str):
    # Auth first
    auth(req)
    
    target_url = f"{BASE_URL}/v1/{path}"
    body = await req.body()
    
    headers = dict(req.headers)
    headers.pop("host", None)
    headers.pop("content-length", None)
    
    # Try each key
    tried = set()
    for _ in range(len(API_KEYS)):
        key = pick_key()
        if key in tried:
            continue
        tried.add(key)
        
        headers["Authorization"] = f"Bearer {key}"
        
        try:
            async with httpx.AsyncClient(timeout=60) as client:
                resp = await client.request(
                    method=req.method,
                    url=target_url,
                    headers=headers,
                    content=body,
                    params=req.query_params
                )
                
                if resp.status_code < 400:
                    mark_ok(key)
                    return Response(
                        content=resp.content,
                        status_code=resp.status_code,
                        headers=dict(resp.headers)
                    )
                
                mark_fail(key)
                continue
                
        except Exception as e:
            mark_fail(key)
            continue
    
    return JSONResponse({"error": "all keys failed"}, status_code=500)
# rebuild
# Testing build