Skip to content
Merged
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
8 changes: 6 additions & 2 deletions gittensor/validator/emission_allocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def _collect_repo_pr_scores(
) -> Dict[int, float]:
scores: Dict[int, float] = {}
for uid, evaluation in miner_evaluations.items():
if uid not in miner_uids:
if not _is_scoring_evaluation(uid, evaluation, miner_uids):
continue

score = sum(
Expand All @@ -136,7 +136,7 @@ def _collect_repo_issue_discovery_scores(
) -> Dict[int, float]:
scores: Dict[int, float] = {}
for uid, evaluation in miner_evaluations.items():
if uid not in miner_uids:
if not _is_scoring_evaluation(uid, evaluation, miner_uids):
continue

score = sum(
Expand All @@ -150,6 +150,10 @@ def _collect_repo_issue_discovery_scores(
return scores


def _is_scoring_evaluation(uid: int, evaluation: MinerEvaluation, miner_uids: set[int]) -> bool:
return uid in miner_uids and evaluation.failed_reason is None


def _allocate_scores_to_rewards(
rewards: np.ndarray,
uid_index: Dict[int, int],
Expand Down
1 change: 1 addition & 0 deletions gittensor/validator/oss_contributions/inspections.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def _zero_for_duplicate_penalty(eval_: MinerEvaluation, reason: str) -> None:
eval_.total_valid_solved_issues = 0
eval_.total_closed_issues = 0
eval_.total_open_issues = 0
eval_.issue_discovery_issues = []


def validate_response_and_initialize_miner_evaluation(
Expand Down
34 changes: 34 additions & 0 deletions tests/validator/test_blend_emission_pools.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,40 @@ def test_empty_repo_slice_recycles_without_redistribution(self):
assert rewards[_idx(miner_uids, RECYCLE_UID)] == pytest.approx(0.6 * OSS_EMISSION_SHARE)


class TestFailedEvaluationFiltering:
def test_failed_pr_rows_do_not_consume_repo_slice(self):
repos = {'r/pr': _config(emission_share=1.0, issue_discovery_share=0.0)}
miner_uids = _uids(1, 2)
failed = _evaluation(1, prs=[_scored_pr('r/pr', 100, earned_score=30.0)])
failed.failed_reason = 'penalized'
evaluations = {
1: failed,
2: _evaluation(2, prs=[_scored_pr('r/pr', 200, earned_score=10.0)]),
}

rewards = blend_emission_pools(evaluations, repos, miner_uids)

assert rewards[_idx(miner_uids, 1)] == pytest.approx(0.0)
assert rewards[_idx(miner_uids, 2)] == pytest.approx(OSS_EMISSION_SHARE)
assert rewards[_idx(miner_uids, RECYCLE_UID)] == pytest.approx(0.0)

def test_failed_issue_rows_do_not_consume_repo_slice(self):
repos = {'r/issue': _config(emission_share=1.0, issue_discovery_share=1.0)}
miner_uids = _uids(1, 2)
failed = _evaluation(1, issues=[_discovered_issue('r/issue', 10, earned_score=30.0)])
failed.failed_reason = 'penalized'
evaluations = {
1: failed,
2: _evaluation(2, issues=[_discovered_issue('r/issue', 20, earned_score=10.0)]),
}

rewards = blend_emission_pools(evaluations, repos, miner_uids)

assert rewards[_idx(miner_uids, 1)] == pytest.approx(0.0)
assert rewards[_idx(miner_uids, 2)] == pytest.approx(OSS_EMISSION_SHARE)
assert rewards[_idx(miner_uids, RECYCLE_UID)] == pytest.approx(0.0)


class TestWithinRepoSpill:
def test_issue_side_empty_spills_to_pr_side(self):
repos = {'r/spill-pr': _config(emission_share=0.1, issue_discovery_share=0.6)}
Expand Down
32 changes: 31 additions & 1 deletion tests/validator/test_duplicate_penalty_propagation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import numpy as np

from gittensor.classes import MinerEvaluation
from gittensor.classes import Issue, MinerEvaluation
from gittensor.validator.oss_contributions.inspections import (
detect_and_penalize_miners_sharing_github,
)
Expand All @@ -27,6 +27,17 @@ def __init__(self, scores: np.ndarray, alpha: float = 0.1):
self.config.neuron.moving_average_alpha = alpha


def _discovered_issue(uid: int) -> Issue:
issue = Issue(
number=uid,
pr_number=uid + 100,
repository_full_name='r/issue',
title='cached',
discovery_earned_score=10.0,
)
return issue


def test_detected_duplicates_wipe_prior_ema_via_update_scores():
# Prior round: UID 1 and UID 2 posted PRs under the same GitHub account
# and accumulated EMA weight. UID 0 is honest. On the unfixed call
Expand Down Expand Up @@ -54,3 +65,22 @@ def test_detected_duplicates_wipe_prior_ema_via_update_scores():
assert validator.scores[2] == 0.0
assert validator.scores[0] > 0.0
assert np.isclose(validator.scores.sum(), 1.0)


def test_duplicate_penalty_clears_cached_issue_discovery_rows():
evaluations = {
1: MinerEvaluation(uid=1, hotkey='hotkey_1', github_id='gh_shared'),
2: MinerEvaluation(uid=2, hotkey='hotkey_2', github_id='gh_shared'),
3: MinerEvaluation(uid=3, hotkey='hotkey_3', github_id='gh_honest'),
}
for evaluation in evaluations.values():
evaluation.issue_discovery_score = 10.0
evaluation.issue_discovery_issues = [_discovered_issue(evaluation.uid)]

penalized_uids = detect_and_penalize_miners_sharing_github(evaluations)

assert penalized_uids == {1, 2}
for uid in penalized_uids:
assert evaluations[uid].issue_discovery_score == 0.0
assert evaluations[uid].issue_discovery_issues == []
assert len(evaluations[3].issue_discovery_issues) == 1
Loading