Model rules: who a chat's model may bring into a conversation

Rules read from the main model decide who it is offered as a crowd member,
a friend and on its roster; any-to-any with denies by default, or
none-to-none with allows. The crowd picker names what it holds back and
why, and a model held back only by a person's own rule -- or by anything,
with the new rules.override -- can still be added by hand. Another data
group is now a deny that an explicit rule opens. Admin -> Model rules and
a card in Settings, each with a matrix drawn by the enforcing function.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-29 17:20:40 +00:00
co-authored by Claude Opus 5.5
parent 9970bb43c6
commit c27fe47d4d
24 changed files with 1324 additions and 29 deletions
+5 -5
View File
@@ -1546,15 +1546,15 @@ def _apply_crowd(db: DBSession, chat: Chat, user: User, values: list[str]) -> No
and a model offered by two connections is two rows with different capabilities.
"""
from lembas.db.models import CrowdMember
from lembas.services import talk
settings = settings_store.crowd(db)
# Only models in the chat's own data group: a member is sent the whole
# conversation, so one from another group would carry it to that provider.
# What this person may add by hand, by the talk rules from the chat's main
# model. A member is sent the whole conversation, so one in another data
# group comes in only when a rule explicitly lets it.
reachable = {
model.model_id: model
for model in chat_service.available_models(
db, user, data_groups.for_chat(db, chat)
)
for model in talk.addable(db, user, chat.model_id, data_groups.for_chat(db, chat))
}
wanted: list[str] = []
for value in values: