Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions gittensor/validator/oss_contributions/label_resolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@ def get_default_label_multiplier(repo_config: Optional[RepositoryConfig]) -> flo


def get_label_multiplier(label: str, repo_config: Optional[RepositoryConfig]) -> Optional[float]:
"""Return the highest configured multiplier matching a label, or None."""
"""Return the configured multiplier matching a label, or None if unmatched.

A configured multiplier of ``0.0`` is a veto: repos use it to mark rejection
/ void labels (``eval:REJECT``, ``invalid-pr``, ``duplicate-pr``, ``slop``,
``kata:invalid``, ...). So a label matching any ``0.0`` pattern resolves to
``0.0`` even when it also matches a higher-valued pattern — the veto is not
overridden by a co-matching reward pattern. Among purely positive matches the
highest wins, preserving the "best classification" behavior.
"""
if repo_config is None or not repo_config.label_multipliers:
return None

Expand All @@ -28,14 +36,25 @@ def get_label_multiplier(label: str, repo_config: Optional[RepositoryConfig]) ->
for pattern, multiplier in repo_config.label_multipliers.items()
if fnmatch(label_lower, pattern.lower())
]
return max(matches) if matches else None
if not matches:
return None
if any(multiplier == 0.0 for multiplier in matches):
return 0.0
return max(matches)


def resolve_highest_label_multiplier(
labels: Iterable[str],
repo_config: Optional[RepositoryConfig],
) -> tuple[Optional[str], float]:
"""Resolve the highest-multiplier label from unordered candidate labels."""
"""Resolve the scoring multiplier from unordered candidate labels.

A label configured to ``0.0`` is a veto (rejection / void marker) and must
not be overridden by a co-applied higher-value label: if any candidate
resolves to ``0.0`` the result is ``0.0``. Otherwise the highest positive
multiplier wins. Ties (including ties among vetoes) break deterministically
by label name so the result is independent of mirror response ordering.
"""
default_multiplier = get_default_label_multiplier(repo_config)
candidates = []
for label in labels:
Expand All @@ -46,6 +65,10 @@ def resolve_highest_label_multiplier(
if not candidates:
return None, default_multiplier

vetoes = [candidate for candidate in candidates if candidate[1] == 0.0]
if vetoes:
return min(vetoes, key=lambda candidate: candidate[0])

label, multiplier = max(candidates, key=lambda candidate: (candidate[1], candidate[0]))
return label, multiplier

Expand Down
118 changes: 118 additions & 0 deletions tests/validator/test_label_veto.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# The MIT License (MIT)
# Copyright © 2025 Entrius

"""Veto-label semantics for repository label multipliers.

A label configured with multiplier ``0.0`` is a rejection / void marker
(``eval:REJECT``, ``invalid-pr``, ``duplicate-pr``, ``slop``, ``kata:invalid``,
...). It must zero the PR's label multiplier even when a higher-valued label is
applied alongside it, otherwise a maintainer can never actually revoke a PR that
already earned a reward label.
"""

import pytest

from gittensor.validator.oss_contributions.label_resolution import (
get_label_multiplier,
resolve_highest_label_multiplier,
)
from gittensor.validator.utils.load_weights import RepositoryConfig


class TestVetoBeatsRewardLabel:
"""A 0.0 label co-applied with a positive label wins (result is 0.0)."""

def test_reject_label_vetoes_eval_reward(self):
# Mirrors the sparkinfer / SparkDistill / cuda-compute-oss registries.
config = RepositoryConfig(
emission_share=1.0,
label_multipliers={'eval:L': 2.5, 'eval:REJECT': 0.0},
)
label, multiplier = resolve_highest_label_multiplier(['eval:L', 'eval:REJECT'], config)
assert multiplier == pytest.approx(0.0)
assert label == 'eval:REJECT'

def test_invalid_pr_vetoes_round_winner(self):
# Mirrors the gt-imagent registry (round-winner 10.0, invalid-pr 0.0).
config = RepositoryConfig(
emission_share=1.0,
label_multipliers={'round-winner': 10.0, 'invalid-pr': 0.0, 'duplicate-pr': 0.0},
)
_, multiplier = resolve_highest_label_multiplier(['round-winner', 'invalid-pr'], config)
assert multiplier == pytest.approx(0.0)

def test_slop_vetoes_priority(self):
# Mirrors the JSONbored registries (gittensor:priority 1.5, slop 0.0).
config = RepositoryConfig(
emission_share=1.0,
label_multipliers={'gittensor:priority': 1.5, 'slop': 0.0},
)
_, multiplier = resolve_highest_label_multiplier(['gittensor:priority', 'slop'], config)
assert multiplier == pytest.approx(0.0)

def test_veto_independent_of_label_order(self):
config = RepositoryConfig(
emission_share=1.0,
label_multipliers={'eval:L': 2.5, 'eval:REJECT': 0.0},
)
forward = resolve_highest_label_multiplier(['eval:L', 'eval:REJECT'], config)
reverse = resolve_highest_label_multiplier(['eval:REJECT', 'eval:L'], config)
assert forward == reverse
assert forward[1] == pytest.approx(0.0)

def test_wildcard_veto_beats_wildcard_reward(self):
# Mirrors the kata registry (kata:winner:* 0.7, kata:invalid 0.0) but
# exercises the wildcard path on both sides.
config = RepositoryConfig(
emission_share=1.0,
label_multipliers={'kata:winner:*': 0.7, 'kata:defeat:*': 0.0},
)
_, multiplier = resolve_highest_label_multiplier(['kata:winner:s1', 'kata:defeat:s1'], config)
assert multiplier == pytest.approx(0.0)


class TestSingleLabelMatchingBothVetoAndReward:
"""One label matching a 0.0 pattern and a positive pattern resolves to 0.0."""

def test_catch_all_reward_plus_specific_veto(self):
# A repo adds an `eval:*` catch-all alongside an explicit `eval:REJECT`
# veto; the single label `eval:REJECT` matches both patterns.
config = RepositoryConfig(
emission_share=1.0,
label_multipliers={'eval:*': 3.0, 'eval:REJECT': 0.0},
)
assert get_label_multiplier('eval:REJECT', config) == pytest.approx(0.0)
# A non-veto label under the same catch-all still scores.
assert get_label_multiplier('eval:L', config) == pytest.approx(3.0)


class TestNonVetoBehaviorUnchanged:
"""Positive-only resolution keeps its previous 'highest wins' semantics."""

def test_highest_positive_still_wins(self):
config = RepositoryConfig(
emission_share=1.0,
label_multipliers={'bug': 1.2, 'enhancement': 1.3, 'feature': 1.0},
)
label, multiplier = resolve_highest_label_multiplier(['bug', 'enhancement'], config)
assert label == 'enhancement'
assert multiplier == pytest.approx(1.3)

def test_low_positive_is_not_a_veto(self):
# 0.05 is a low reward, not a rejection: it must not veto a higher label.
config = RepositoryConfig(
emission_share=1.0,
label_multipliers={'mult:contribution': 0.05, 'perf:l': 2.5},
)
_, multiplier = resolve_highest_label_multiplier(['mult:contribution', 'perf:l'], config)
assert multiplier == pytest.approx(2.5)

def test_default_multiplier_used_when_no_pattern_matches(self):
config = RepositoryConfig(
emission_share=1.0,
label_multipliers={'bug': 1.2},
default_label_multiplier=0.8,
)
label, multiplier = resolve_highest_label_multiplier(['unrelated'], config)
assert label is None
assert multiplier == pytest.approx(0.8)