fix(pop): trusted-only requests in an untrusted owner's pop page

The pop queries did not read `trusted_workers`, so a request for trusted-workers-only reached the page of every worker and was refused afterwards in `can_generate` for an owner that is not trusted. Such a request at the front of the queue then cost every untrusted worker a page slot on *every* pop until a trusted worker took it.

- `text_wp_offer_clauses` in `horde/database/text_functions.py` and the image pop query in `horde/database/functions.py` offer a request only when it is not trusted-only or the popping worker's owner is trusted, folded in as the unsafe-IP and NSFW clauses are. The Python check in `can_generate` stays as the guard behind the page
- the text pop's TODO for this filter is removed
- unit tests show a trusted-only request is not paged to a worker whose owner is untrusted and is paged once the owner gains the trusted role, for text and image workers
This commit is contained in:
tazlin committed 2026-11-02 16:37:00 -05:00
1 parent d33285d12e
commit 58a4c141ea
4 files changed
+74 -1

No files matched your search

+6
View File
@@ -1362,6 +1362,12 @@ def get_sorted_wp_filtered_to_worker(worker, models_list=None, blacklist=None, p
ImageWaitingPrompt.nsfw.is_(False),
worker.nsfw is True,
),
# A trusted-only request is refused to an untrusted owner's worker by can_generate as well. Filtering
# here keeps such a request from taking a slot in the page of a worker that cannot serve it.
or_(
ImageWaitingPrompt.trusted_workers.is_(False),
worker.user.trusted is True,
),
or_(
not_(ImageWaitingPrompt.params.has_key("loras")),
and_(
+6 -1
View File
@@ -153,6 +153,12 @@ def text_wp_offer_clauses(worker, models_list):
TextWaitingPrompt.nsfw.is_(False),
worker.nsfw is True,
),
# A trusted-only request is refused to an untrusted owner's worker by can_generate as well. Filtering here keeps
# such a request from taking a slot in the page of a worker that cannot serve it.
or_(
TextWaitingPrompt.trusted_workers.is_(False),
worker.user.trusted is True,
),
or_(
wp_serves_model,
~wp_names_any_model,
@@ -267,7 +273,6 @@ def worker_may_be_speed_gated(worker, models_list) -> bool:
def get_sorted_text_wp_filtered_to_worker(worker, models_list=None, priority_user_ids=None, page=0):
# This is just the top 3 - Adjusted method to send Worker object. Filters to add.
# TODO: Filter by (Worker in WP.workers) __ONLY IF__ len(WP.workers) >=1
# TODO: Filter by WP.trusted_workers == False __ONLY IF__ Worker.user.trusted == False
# TODO: Filter by Worker not in WP.tricked_worker
# TODO: If any word in the prompt is in the WP.blacklist rows, then exclude it (L293 in base.worker.Worker.gan_generate())
PER_PAGE = 3 # how many requests we're picking up to filter further
@@ -85,6 +85,37 @@ def _make_pair(make_user: Any, make_user_role: Any) -> tuple[ImageWaitingPrompt,
return _make_request(user), _make_worker(user)
def _activate(request: ImageWaitingPrompt) -> None:
"""Make ``request`` a pop candidate without the kudos bookkeeping of ``activate()``."""
request.active = True
request.expiry = datetime.utcnow() + timedelta(minutes=10)
db.session.commit()
def test_a_trusted_only_request_is_paged_only_to_a_trusted_owners_worker(
db_session: Any,
make_user: Any,
make_user_role: Any,
) -> None:
"""The pop query of a worker whose owner is not trusted does not page a trusted-only request, so the request takes
no slot in that worker's page. Once the owner is trusted, the same worker pages it."""
from horde.database import functions as database
user = make_user()
worker = _make_worker(user)
request = _make_request(user)
request.trusted_workers = True
_activate(request)
untrusted_popped = database.get_sorted_wp_filtered_to_worker(worker, [_MODEL], [])
make_user_role(user, UserRoleTypes.TRUSTED, value=True)
db.session.expire_all()
trusted_popped = database.get_sorted_wp_filtered_to_worker(worker, [_MODEL], [])
assert [wp.id for wp in untrusted_popped] == []
assert [wp.id for wp in trusted_popped] == [request.id]
@pytest.mark.parametrize(
("request_change", "worker_change", "model_names", "reason"),
[
+31
View File
@@ -919,6 +919,37 @@ def _post_progress(client: FlaskClient, generation: TextProcessingGeneration, se
)
class TestTrustedOnlyRequests:
"""A request for trusted workers only is paged only to workers whose owner is trusted."""
def test_a_trusted_only_request_is_paged_only_to_a_trusted_owners_worker(
self,
db_session: Session,
fake_redis: object,
make_user: MakeUser,
make_user_role: MakeUserRole,
) -> None:
"""The pop query of a worker whose owner is not trusted does not page a trusted-only request, so the request
takes no slot in that worker's page. Once the owner is trusted, the same worker pages and can take it."""
user = make_user()
worker = _make_worker(user)
waiting_prompt = _make_request(user, 80)
waiting_prompt.trusted_workers = True
db.session.commit()
untrusted_popped = text_functions.get_sorted_text_wp_filtered_to_worker(worker, [MODEL_NAME])
untrusted_verdict = worker.can_generate(waiting_prompt)
make_user_role(user, UserRoleTypes.TRUSTED, value=True)
db.session.expire_all()
trusted_popped = text_functions.get_sorted_text_wp_filtered_to_worker(worker, [MODEL_NAME])
trusted_verdict = worker.can_generate(waiting_prompt)
assert [wp.id for wp in untrusted_popped] == []
assert untrusted_verdict == [False, "untrusted"]
assert [wp.id for wp in trusted_popped] == [waiting_prompt.id]
assert trusted_verdict == [True, None]
_FEATURE_CONTAINMENT_CASES = [
pytest.param(["seed", "tools"], ["tools"], True, id="holds_the_required_feature"),
pytest.param(["seed"], ["tools"], False, id="lacks_the_required_feature"),