Spaces:
Sleeping
Sleeping
Upload 4 files
Browse files- app/routers/__init__.py +0 -0
- app/routers/auth_router.py +44 -0
- app/routers/proxy_config_router.py +114 -0
- app/routers/proxy_endpoint_router.py +50 -0
app/routers/__init__.py
ADDED
|
File without changes
|
app/routers/auth_router.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import APIRouter, Depends, HTTPException, status
|
| 2 |
+
from sqlalchemy.orm import Session
|
| 3 |
+
|
| 4 |
+
from ..database import get_db
|
| 5 |
+
from .. import models
|
| 6 |
+
from ..auth import verify_password, hash_password, create_access_token, get_current_user
|
| 7 |
+
from ..schemas import UserCreate, UserOut, Token, LoginRequest
|
| 8 |
+
|
| 9 |
+
router = APIRouter(prefix="/auth", tags=["Authentication"])
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@router.post("/signup", response_model=Token, status_code=status.HTTP_201_CREATED)
|
| 13 |
+
def signup(payload: UserCreate, db: Session = Depends(get_db)):
|
| 14 |
+
if db.query(models.User).filter(models.User.username == payload.username).first():
|
| 15 |
+
raise HTTPException(status_code=400, detail="Username already taken")
|
| 16 |
+
if db.query(models.User).filter(models.User.email == payload.email).first():
|
| 17 |
+
raise HTTPException(status_code=400, detail="Email already registered")
|
| 18 |
+
|
| 19 |
+
user = models.User(
|
| 20 |
+
username=payload.username,
|
| 21 |
+
email=payload.email,
|
| 22 |
+
hashed_password=hash_password(payload.password),
|
| 23 |
+
)
|
| 24 |
+
db.add(user)
|
| 25 |
+
db.commit()
|
| 26 |
+
db.refresh(user)
|
| 27 |
+
|
| 28 |
+
token = create_access_token({"sub": user.username})
|
| 29 |
+
return Token(access_token=token, user=UserOut.model_validate(user))
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
@router.post("/login", response_model=Token)
|
| 33 |
+
def login(payload: LoginRequest, db: Session = Depends(get_db)):
|
| 34 |
+
user = db.query(models.User).filter(models.User.username == payload.username).first()
|
| 35 |
+
if not user or not verify_password(payload.password, user.hashed_password):
|
| 36 |
+
raise HTTPException(status_code=401, detail="Invalid username or password")
|
| 37 |
+
|
| 38 |
+
token = create_access_token({"sub": user.username})
|
| 39 |
+
return Token(access_token=token, user=UserOut.model_validate(user))
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
@router.get("/me", response_model=UserOut)
|
| 43 |
+
def me(current_user: models.User = Depends(get_current_user)):
|
| 44 |
+
return current_user
|
app/routers/proxy_config_router.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import secrets
|
| 3 |
+
from fastapi import APIRouter, Depends, HTTPException, Request
|
| 4 |
+
from sqlalchemy.orm import Session
|
| 5 |
+
|
| 6 |
+
from ..database import get_db
|
| 7 |
+
from .. import models
|
| 8 |
+
from ..auth import get_current_user, encrypt_api_key
|
| 9 |
+
from ..schemas import ProxyCreate, ProxyUpdate, ProxyOut
|
| 10 |
+
|
| 11 |
+
router = APIRouter(prefix="/proxies", tags=["Proxy Management"])
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def _proxy_out_with_url(proxy: models.ProxyConfig, request: Request) -> dict:
|
| 15 |
+
base = str(request.base_url).rstrip("/")
|
| 16 |
+
return {
|
| 17 |
+
**ProxyOut.model_validate(proxy).model_dump(),
|
| 18 |
+
"proxy_url": f"{base}/{proxy.proxy_token}",
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@router.post("", status_code=201)
|
| 23 |
+
def create_proxy(
|
| 24 |
+
payload: ProxyCreate,
|
| 25 |
+
request: Request,
|
| 26 |
+
db: Session = Depends(get_db),
|
| 27 |
+
current_user: models.User = Depends(get_current_user),
|
| 28 |
+
):
|
| 29 |
+
token = secrets.token_urlsafe(24)
|
| 30 |
+
proxy = models.ProxyConfig(
|
| 31 |
+
user_id=current_user.id,
|
| 32 |
+
proxy_token=token,
|
| 33 |
+
name=payload.name,
|
| 34 |
+
openai_base_url=payload.openai_base_url.rstrip("/"),
|
| 35 |
+
encrypted_api_key=encrypt_api_key(payload.openai_api_key),
|
| 36 |
+
model_mapping=json.dumps(payload.model_mapping or {}),
|
| 37 |
+
)
|
| 38 |
+
db.add(proxy)
|
| 39 |
+
db.commit()
|
| 40 |
+
db.refresh(proxy)
|
| 41 |
+
return _proxy_out_with_url(proxy, request)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
@router.get("", response_model=list)
|
| 45 |
+
def list_proxies(
|
| 46 |
+
request: Request,
|
| 47 |
+
db: Session = Depends(get_db),
|
| 48 |
+
current_user: models.User = Depends(get_current_user),
|
| 49 |
+
):
|
| 50 |
+
proxies = db.query(models.ProxyConfig).filter(
|
| 51 |
+
models.ProxyConfig.user_id == current_user.id
|
| 52 |
+
).all()
|
| 53 |
+
return [_proxy_out_with_url(p, request) for p in proxies]
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
@router.get("/{proxy_id}")
|
| 57 |
+
def get_proxy(
|
| 58 |
+
proxy_id: int,
|
| 59 |
+
request: Request,
|
| 60 |
+
db: Session = Depends(get_db),
|
| 61 |
+
current_user: models.User = Depends(get_current_user),
|
| 62 |
+
):
|
| 63 |
+
proxy = db.query(models.ProxyConfig).filter(
|
| 64 |
+
models.ProxyConfig.id == proxy_id,
|
| 65 |
+
models.ProxyConfig.user_id == current_user.id,
|
| 66 |
+
).first()
|
| 67 |
+
if not proxy:
|
| 68 |
+
raise HTTPException(status_code=404, detail="Proxy not found")
|
| 69 |
+
return _proxy_out_with_url(proxy, request)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
@router.patch("/{proxy_id}")
|
| 73 |
+
def update_proxy(
|
| 74 |
+
proxy_id: int,
|
| 75 |
+
payload: ProxyUpdate,
|
| 76 |
+
request: Request,
|
| 77 |
+
db: Session = Depends(get_db),
|
| 78 |
+
current_user: models.User = Depends(get_current_user),
|
| 79 |
+
):
|
| 80 |
+
proxy = db.query(models.ProxyConfig).filter(
|
| 81 |
+
models.ProxyConfig.id == proxy_id,
|
| 82 |
+
models.ProxyConfig.user_id == current_user.id,
|
| 83 |
+
).first()
|
| 84 |
+
if not proxy:
|
| 85 |
+
raise HTTPException(status_code=404, detail="Proxy not found")
|
| 86 |
+
|
| 87 |
+
if payload.name is not None:
|
| 88 |
+
proxy.name = payload.name
|
| 89 |
+
if payload.openai_base_url is not None:
|
| 90 |
+
proxy.openai_base_url = payload.openai_base_url.rstrip("/")
|
| 91 |
+
if payload.openai_api_key is not None:
|
| 92 |
+
proxy.encrypted_api_key = encrypt_api_key(payload.openai_api_key)
|
| 93 |
+
if payload.model_mapping is not None:
|
| 94 |
+
proxy.model_mapping = json.dumps(payload.model_mapping)
|
| 95 |
+
|
| 96 |
+
db.commit()
|
| 97 |
+
db.refresh(proxy)
|
| 98 |
+
return _proxy_out_with_url(proxy, request)
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
@router.delete("/{proxy_id}", status_code=204)
|
| 102 |
+
def delete_proxy(
|
| 103 |
+
proxy_id: int,
|
| 104 |
+
db: Session = Depends(get_db),
|
| 105 |
+
current_user: models.User = Depends(get_current_user),
|
| 106 |
+
):
|
| 107 |
+
proxy = db.query(models.ProxyConfig).filter(
|
| 108 |
+
models.ProxyConfig.id == proxy_id,
|
| 109 |
+
models.ProxyConfig.user_id == current_user.id,
|
| 110 |
+
).first()
|
| 111 |
+
if not proxy:
|
| 112 |
+
raise HTTPException(status_code=404, detail="Proxy not found")
|
| 113 |
+
db.delete(proxy)
|
| 114 |
+
db.commit()
|
app/routers/proxy_endpoint_router.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import APIRouter, Depends, HTTPException, Request
|
| 2 |
+
from fastapi.responses import JSONResponse
|
| 3 |
+
from sqlalchemy.orm import Session
|
| 4 |
+
|
| 5 |
+
from ..database import get_db
|
| 6 |
+
from .. import models
|
| 7 |
+
from ..proxy_handler import handle_messages_request
|
| 8 |
+
|
| 9 |
+
router = APIRouter(tags=["Proxy Endpoint"])
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def _get_proxy(token: str, db: Session) -> models.ProxyConfig:
|
| 13 |
+
proxy = db.query(models.ProxyConfig).filter(
|
| 14 |
+
models.ProxyConfig.proxy_token == token
|
| 15 |
+
).first()
|
| 16 |
+
if not proxy:
|
| 17 |
+
raise HTTPException(status_code=404, detail="Invalid proxy token")
|
| 18 |
+
return proxy
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
@router.post("/{token}/v1/messages")
|
| 22 |
+
async def messages(
|
| 23 |
+
token: str,
|
| 24 |
+
request: Request,
|
| 25 |
+
db: Session = Depends(get_db),
|
| 26 |
+
):
|
| 27 |
+
proxy = _get_proxy(token, db)
|
| 28 |
+
try:
|
| 29 |
+
body = await request.json()
|
| 30 |
+
except Exception:
|
| 31 |
+
raise HTTPException(status_code=400, detail="Invalid JSON body")
|
| 32 |
+
|
| 33 |
+
result = await handle_messages_request(body, proxy)
|
| 34 |
+
if isinstance(result, dict):
|
| 35 |
+
return JSONResponse(content=result)
|
| 36 |
+
return result
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
@router.get("/{token}/v1/models")
|
| 40 |
+
def models_list(token: str, db: Session = Depends(get_db)):
|
| 41 |
+
_get_proxy(token, db)
|
| 42 |
+
return JSONResponse(content={
|
| 43 |
+
"object": "list",
|
| 44 |
+
"data": [
|
| 45 |
+
{"id": "claude-3-opus-20240229", "object": "model"},
|
| 46 |
+
{"id": "claude-3-sonnet-20240229", "object": "model"},
|
| 47 |
+
{"id": "claude-3-haiku-20240307", "object": "model"},
|
| 48 |
+
{"id": "claude-3-5-sonnet-20240620","object": "model"},
|
| 49 |
+
],
|
| 50 |
+
})
|