mirror of
https://github.com/Haidra-Org/AI-Horde.git
synced 2026-11-06 05:17:31 +00:00
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:
4 files changed
+74
-1
No files matched your search
@@ -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_(
|
||||
|
||||
@@ -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"),
|
||||
[
|
||||
|
||||
@@ -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"),
|
||||
|
||||
Reference in new issue
Block a user