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)