File size: 4,637 Bytes
b1ddb4f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
from fastapi import FastAPI, HTTPException, Depends, Header
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
import os
import logging
import httpx

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = FastAPI(title="FreeLLMAPI")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# ไปŽ็Žฏๅขƒๅ˜้‡่ฏปๅ–ๆ‰€ๆœ‰้…็ฝฎ๏ผˆ้‡ๅฏๆฐธไธไธขๅคฑ๏ผ‰
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

def load_config():
    """
    Secrets ๆ ผๅผ็บฆๅฎš๏ผš

    API Keys๏ผˆๅ…่ฎธ่ฎฟ้—ฎ็š„็”จๆˆท๏ผ‰:
      API_KEYS = "key1,key2,key3"

    Providers๏ผˆไธŠๆธธ LLM ๆœๅŠก๏ผ‰:
      PROVIDER_1_NAME    = "openai"
      PROVIDER_1_APIKEY  = "sk-xxxx"
      PROVIDER_1_BASEURL = "https://api.openai.com/v1"
      PROVIDER_1_MODELS  = "gpt-4o,gpt-4o-mini"

      PROVIDER_2_NAME    = "deepseek"
      PROVIDER_2_APIKEY  = "sk-yyyy"
      PROVIDER_2_BASEURL = "https://api.deepseek.com/v1"
      PROVIDER_2_MODELS  = "deepseek-chat,deepseek-coder"

      # ๆœ€ๅคšๆ”ฏๆŒ 20 ไธช Provider
    """

    # โ”€โ”€ ่ฏปๅ– API Keys โ”€โ”€
    raw_keys = os.getenv("API_KEYS", "")
    api_keys = set(
        k.strip() for k in raw_keys.split(",") if k.strip()
    )

    # โ”€โ”€ ่ฏปๅ– Providers โ”€โ”€
    providers = {}
    for i in range(1, 21):
        name    = os.getenv(f"PROVIDER_{i}_NAME")
        api_key = os.getenv(f"PROVIDER_{i}_APIKEY")
        baseurl = os.getenv(f"PROVIDER_{i}_BASEURL")
        models  = os.getenv(f"PROVIDER_{i}_MODELS", "")

        if not name or not api_key or not baseurl:
            continue  # ่ฏฅ็ผ–ๅทๆœช้…็ฝฎ๏ผŒ่ทณ่ฟ‡

        providers[name] = {
            "api_key":  api_key,
            "base_url": baseurl.rstrip("/"),
            "models":   [m.strip() for m in models.split(",") if m.strip()],
        }

    logger.info(f"โœ… ๅŠ ่ฝฝไบ† {len(api_keys)} ไธช API Key")
    logger.info(f"โœ… ๅŠ ่ฝฝไบ† {len(providers)} ไธช Provider: {list(providers.keys())}")

    return api_keys, providers


# ๅฏๅŠจๆ—ถๅŠ ่ฝฝไธ€ๆฌก
API_KEYS, PROVIDERS = load_config()

# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# ้‰ดๆƒ
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

def verify_api_key(authorization: str = Header(...)):
    token = authorization.removeprefix("Bearer ").strip()
    if token not in API_KEYS:
        raise HTTPException(status_code=401, detail="Invalid API key")
    return token

# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# ่ทฏ็”ฑ
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

@app.get("/health")
async def health():
    return {
        "status":    "ok",
        "keys":      len(API_KEYS),
        "providers": list(PROVIDERS.keys()),
    }


@app.get("/v1/models")
async def list_models(_: str = Depends(verify_api_key)):
    data = []
    for p_cfg in PROVIDERS.values():
        for m in p_cfg["models"]:
            data.append({"id": m, "object": "model"})
    return {"object": "list", "data": data}


@app.post("/v1/chat/completions")
async def chat_completions(
    body: dict,
    _: str = Depends(verify_api_key)
):
    model    = body.get("model", "")
    provider = None

    # ๆ‰พๅˆฐๆ”ฏๆŒ่ฏฅ model ็š„ provider
    for p_cfg in PROVIDERS.values():
        if model in p_cfg["models"]:
            provider = p_cfg
            break

    if not provider:
        raise HTTPException(
            status_code=404,
            detail=f"ๆฒกๆœ‰ Provider ๆ”ฏๆŒๆจกๅž‹: {model}"
        )

    # ่ฝฌๅ‘ๅˆฐไธŠๆธธ
    async with httpx.AsyncClient(timeout=60) as client:
        resp = await client.post(
            f"{provider['base_url']}/chat/completions",
            headers={"Authorization": f"Bearer {provider['api_key']}"},
            json=body,
        )
        return resp.json()


# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# ๅ…ฅๅฃ
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=7860)