Files
wehub-resource-sync e4dcfc49aa
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:00:43 +08:00

126 lines
4.7 KiB
Python

"""Server-side model grant resolution and redacted model views.
Grants carry LLM assignments only (grant v2): embedding and search always
resolve from the deployment's active profiles, so per-user grants for them
were never enforced and are not stored.
"""
from __future__ import annotations
from typing import Any
from deeptutor.services.config.model_catalog import ModelCatalogService
from deeptutor.services.model_selection import list_llm_options
from .context import get_current_user
from .grants import load_grant
from .paths import get_admin_path_service
def admin_catalog_service() -> ModelCatalogService:
return ModelCatalogService(path=get_admin_path_service().get_settings_file("model_catalog"))
def admin_catalog() -> dict[str, Any]:
return admin_catalog_service().load()
def _profile_by_id(catalog: dict[str, Any], service: str, profile_id: str) -> dict[str, Any] | None:
for profile in catalog.get("services", {}).get(service, {}).get("profiles", []) or []:
if str(profile.get("id") or "") == profile_id:
return profile
return None
def _model_by_id(profile: dict[str, Any], model_id: str) -> dict[str, Any] | None:
for model in profile.get("models", []) or []:
if str(model.get("id") or "") == model_id:
return model
return None
def redacted_model_access(user_id: str | None = None) -> dict[str, list[dict[str, Any]]]:
user = get_current_user()
if user_id is None:
user_id = user.id
grant = load_grant(user_id)
catalog = admin_catalog()
result: dict[str, list[dict[str, Any]]] = {"llm": []}
for item in grant.get("models", {}).get("llm", []) or []:
profile_id = str(item.get("profile_id") or item.get("id") or "")
profile = _profile_by_id(catalog, "llm", profile_id)
if not profile:
result["llm"].append(
{
"profile_id": profile_id,
"name": item.get("name") or profile_id or "Unavailable profile",
"source": "admin",
"available": False,
}
)
continue
for model_id in item.get("model_ids") or []:
model = _model_by_id(profile, str(model_id))
result["llm"].append(
{
"profile_id": profile_id,
"model_id": str(model_id),
"name": (model or {}).get("name") or str(model_id),
"model": (model or {}).get("model") or "",
"source": "admin",
"available": model is not None,
}
)
return result
def allowed_llm_options() -> dict[str, Any]:
user = get_current_user()
if user.is_admin:
return list_llm_options(admin_catalog())
options = [
{
"profile_id": item.get("profile_id"),
"model_id": item.get("model_id"),
"profile_name": item.get("name") or item.get("profile_id") or "LLM",
"model_name": item.get("name") or item.get("model") or item.get("model_id"),
"label": item.get("name") or item.get("model") or item.get("model_id"),
"model": item.get("model") or "",
"provider": "",
"source": "admin",
"is_active_default": False,
}
for item in redacted_model_access(user.id).get("llm", [])
if item.get("available")
]
return {"active": None, "options": options}
def has_capability_access(capability: str, user_id: str | None = None) -> bool:
"""Whether the user has at least one usable model for ``capability``.
Admins are never gated — they manage the catalog directly. For ordinary
users this mirrors exactly what ``redacted_model_access`` exposes to the
frontend, so the server-side gate and the UI lock always agree.
"""
user = get_current_user()
if user.is_admin:
return True
if user_id is None:
user_id = user.id
items = redacted_model_access(user_id).get(capability, []) or []
return any(item.get("available") for item in items)
def apply_allowed_llm_selection(selection: dict[str, Any] | None) -> dict[str, Any] | None:
"""Allow only admin-granted LLM profile/model selections for ordinary users."""
user = get_current_user()
if user.is_admin or not selection:
return selection
profile_id = str(selection.get("profile_id") or "")
model_id = str(selection.get("model_id") or "")
for item in redacted_model_access(user.id).get("llm", []):
if item.get("profile_id") == profile_id and item.get("model_id") == model_id:
return selection
raise PermissionError("This model is not assigned to your account.")