Bring Your Own LLM: per-user LLM config with system default fallback

This commit is contained in:
2026-04-13 14:13:02 +00:00
parent f57dd9621f
commit d28143ec00
3 changed files with 143 additions and 0 deletions
+76
View File
@@ -224,13 +224,89 @@ def verify_magic_link(token: str, response: Response, db: Session = Depends(get_
@app.get("/api/me")
def me(user: dict = Depends(require_auth), db: Session = Depends(get_db)):
u = db.query(User).filter(User.id == user["user_id"]).first()
llm = u.llm_config or {}
return {
"id": u.id, "username": u.username, "email": u.email or "",
"display_name": u.display_name, "role": u.role,
"has_llm": bool(llm.get("api_key")),
"llm_provider": llm.get("provider_type", ""),
"llm_model": llm.get("default_model", ""),
"created_at": u.created_at.isoformat() if u.created_at else None,
}
class UserLLMConfig(BaseModel):
provider_type: str = "" # anthropic, openai, litellm, ollama
api_url: str = ""
api_key: str = ""
default_model: str = ""
@app.get("/api/me/llm")
def get_my_llm(user: dict = Depends(require_auth), db: Session = Depends(get_db)):
"""Get current user's LLM config."""
u = db.query(User).filter(User.id == user["user_id"]).first()
llm = u.llm_config or {}
return {
"provider_type": llm.get("provider_type", ""),
"api_url": llm.get("api_url", ""),
"api_key": "***" if llm.get("api_key") else "",
"default_model": llm.get("default_model", ""),
"configured": bool(llm.get("api_key")),
}
@app.put("/api/me/llm")
def update_my_llm(data: UserLLMConfig, user: dict = Depends(require_auth), db: Session = Depends(get_db)):
"""Update current user's LLM config (bring your own LLM)."""
u = db.query(User).filter(User.id == user["user_id"]).first()
current = u.llm_config or {}
update = data.model_dump()
# Only update api_key if a real value was provided (not "***")
if update.get("api_key") == "***" or not update.get("api_key"):
update["api_key"] = current.get("api_key", "")
u.llm_config = update
db.commit()
return {"status": "updated"}
@app.delete("/api/me/llm")
def delete_my_llm(user: dict = Depends(require_auth), db: Session = Depends(get_db)):
"""Remove user's LLM config (fall back to system default)."""
u = db.query(User).filter(User.id == user["user_id"]).first()
u.llm_config = {}
db.commit()
return {"status": "removed"}
@app.get("/api/users/{user_id}/llm")
def get_user_llm(user_id: int, db: Session = Depends(get_db)):
"""Internal: resolve LLM config for a user. Returns user's own config if set, otherwise system default."""
u = db.query(User).filter(User.id == user_id).first()
if not u:
raise HTTPException(status_code=404)
user_llm = u.llm_config or {}
if user_llm.get("api_key"):
return {
"source": "user",
"provider_type": user_llm.get("provider_type", ""),
"api_url": user_llm.get("api_url", ""),
"api_key": user_llm["api_key"],
"default_model": user_llm.get("default_model", ""),
}
# Fall back to system default
default = db.query(LLMProvider).filter(LLMProvider.is_default == True).first()
if default:
return {
"source": "system",
"provider_type": default.provider_type,
"api_url": default.api_url,
"api_key": default.api_key,
"default_model": default.default_model,
}
return {"source": "none", "provider_type": "", "api_url": "", "api_key": "", "default_model": ""}
# --- Health ---
@app.get("/api/health")