"""Model rules: which model may bring which into a conversation. The instance's layer. A person's own rules and mode are on their settings page (`api/preferences.py`), and `services/talk.py` is where the two are combined. The page ends in a matrix -- every main model against every other -- drawn by `talk.matrix`, which calls the same `decide` that enforces the rules. A preview computed any other way would be a second copy of the logic, and a preview that disagrees with enforcement is worse than none: it is believed. """ from __future__ import annotations from fastapi import APIRouter, Form, Request, Response, status from fastapi.responses import RedirectResponse from lembas.api.deps import AdminUser, Db from lembas.api.pages import describe_verdict from lembas.db.models import ANY_MODEL, EFFECT_ALLOW, EFFECT_DENY from lembas.services import chat as chat_service from lembas.services import settings_store, talk from lembas.web.templating import render router = APIRouter(prefix="/admin/rules", tags=["admin-rules"]) def model_ids(db, user) -> list[str]: """Every model id this person can reach, once each, in the admin's order.""" seen: list[str] = [] for model in chat_service.available_models(db, user): if model.model_id not in seen: seen.append(model.model_id) return seen @router.get("") async def rules_page(request: Request, db: Db, user: AdminUser, saved: str = ""): models = chat_service.available_models(db, user) return render( request, "admin/rules.html", { "mode": settings_store.rules(db)["mode"], "rules": talk.rules_of(db, None), "model_ids": model_ids(db, user), "any_model": ANY_MODEL, "allow": EFFECT_ALLOW, "deny": EFFECT_DENY, "matrix_models": models, # The instance's own view: no person's layer and no override, which # is what somebody without `rules.override` gets unless they narrow. "matrix": talk.matrix(db, None, models), "describe_verdict": describe_verdict, "saved": saved, }, ) @router.post("/mode") async def save_mode(db: Db, user: AdminUser, mode: str = Form(talk.MODE_OPEN)) -> Response: settings_store.update( db, {"mode": talk.MODE_CLOSED if mode == talk.MODE_CLOSED else talk.MODE_OPEN}, key=settings_store.RULES, ) return RedirectResponse("/admin/rules?saved=1", status_code=status.HTTP_303_SEE_OTHER) @router.post("") async def add_rule( db: Db, user: AdminUser, from_model: str = Form(ANY_MODEL), to_model: str = Form(ANY_MODEL), effect: str = Form(EFFECT_DENY), both: bool = Form(False), ) -> Response: talk.set_rule(db, None, from_model, to_model, effect, both=both) return RedirectResponse("/admin/rules?saved=1", status_code=status.HTTP_303_SEE_OTHER) @router.post("/{rule_id}/delete") async def delete_rule(db: Db, user: AdminUser, rule_id: str) -> Response: talk.delete_rule(db, None, rule_id) return RedirectResponse("/admin/rules?saved=1", status_code=status.HTTP_303_SEE_OTHER)