Skip to content

Commit 789af1a

Browse files
feat: Convert per-repo weight
1 parent 73c9d03 commit 789af1a

23 files changed

Lines changed: 290 additions & 144 deletions

File tree

gittensor/classes.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ class Issue:
150150
discovery_base_score: float = 0.0
151151
discovery_earned_score: float = 0.0
152152
discovery_review_quality_multiplier: float = 1.0
153-
discovery_repo_weight_multiplier: float = 1.0
154153
discovery_time_decay_multiplier: float = 1.0
155154
discovery_credibility_multiplier: float = 1.0
156155
discovery_open_issue_spam_multiplier: float = 1.0
@@ -182,7 +181,6 @@ class PullRequest:
182181
pr_state: PRState
183182

184183
# Score fields
185-
repo_weight_multiplier: float = 1.0
186184
base_score: float = 0.0
187185
issue_multiplier: float = 1.0
188186
open_pr_spam_multiplier: float = 1.0
@@ -234,7 +232,6 @@ def is_pioneer_eligible(self) -> bool:
234232
def calculate_final_earned_score(self) -> float:
235233
"""Combine base score with all multipliers. Pioneer dividend is added separately after."""
236234
multipliers = {
237-
'repo': self.repo_weight_multiplier,
238235
'issue': self.issue_multiplier,
239236
'label': self.label_multiplier,
240237
'spam': self.open_pr_spam_multiplier,
@@ -289,6 +286,7 @@ class MinerEvaluation:
289286
total_valid_solved_issues: int = 0 # solved issues where solving PR has token_score >= 5
290287
total_closed_issues: int = 0
291288
total_open_issues: int = 0 # mirror-tracked open issues in lookback window (set by issue_discovery.scan)
289+
issue_discovery_scores_by_repo: Dict[str, float] = field(default_factory=dict)
292290

293291
@property
294292
def total_prs(self) -> int:

gittensor/cli/miner_commands/score.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ async def _run() -> Dict[str, Any]:
280280
issue_rewards = await issue_discovery(
281281
miner_evaluations, master_repositories, programming_languages, token_config, miner_uids
282282
)
283-
rewards = blend_emission_pools(oss_rewards, issue_rewards, miner_uids)
283+
rewards = blend_emission_pools(miner_evaluations, miner_uids, master_repositories)
284284

285285
return {
286286
'success': True,

gittensor/constants.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
# =============================================================================
7575
# Repository & PR Scoring
7676
# =============================================================================
77-
DEFAULT_REPO_WEIGHT = 0.01 # fallback weight for repos not in master_repositories.json
77+
DEFAULT_REPO_EMISSION_SHARE = 0.01 # fallback share for repos not in master_repositories.json
7878
PR_LOOKBACK_DAYS = 35 # rolling window for scoring
7979
MERGED_PR_BASE_SCORE = 25
8080
MIN_TOKEN_SCORE_FOR_BASE_SCORE = 5 # PRs below this get 0 base score
@@ -155,10 +155,8 @@
155155
RECYCLE_UID = 0
156156

157157
# Hardcoded emission splits per competition (replaces dynamic emissions)
158-
OSS_EMISSION_SHARE = 0.30 # 30% to OSS contributions (PR scoring)
159-
ISSUE_DISCOVERY_EMISSION_SHARE = 0.10 # 10% to issue discovery
160-
RECYCLE_EMISSION_SHARE = 0.45 # 45% to recycle UID 0
161-
# ISSUES_TREASURY_EMISSION_SHARE = 0.15 defined below (15% to smart contract treasury)
158+
OSS_EMISSION_SHARE = 0.90 # 90% scoring pool allocated by per-repo emission_share
159+
# ISSUES_TREASURY_EMISSION_SHARE = 0.10 defined below (10% to smart contract treasury)
162160

163161
# =============================================================================
164162
# Spam & Gaming Mitigation
@@ -187,5 +185,5 @@
187185
# =============================================================================
188186
CONTRACT_ADDRESS = '5FWNdk8YNtNcHKrAx2krqenFrFAZG7vmsd2XN2isJSew3MrD'
189187
ISSUES_TREASURY_UID = 111 # UID of the smart contract neuron, if set to RECYCLE_UID then it's disabled
190-
ISSUES_TREASURY_EMISSION_SHARE = 0.15 # % of emissions allocated to funding issues treasury
188+
ISSUES_TREASURY_EMISSION_SHARE = 0.10 # % of emissions allocated to funding issues treasury
191189
MAX_ISSUE_ID = 1_000_000 # sanity-check upper bound for any real deployment

gittensor/validator/forward.py

Lines changed: 68 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@
99

1010
from gittensor.classes import MinerEvaluation, MinerEvaluationCache
1111
from gittensor.constants import (
12-
ISSUE_DISCOVERY_EMISSION_SHARE,
1312
ISSUES_TREASURY_EMISSION_SHARE,
1413
ISSUES_TREASURY_UID,
1514
OSS_EMISSION_SHARE,
16-
RECYCLE_EMISSION_SHARE,
1715
RECYCLE_UID,
1816
)
1917
from gittensor.utils.uids import get_all_uids
@@ -62,12 +60,12 @@ async def forward(self: 'Validator') -> None:
6260
token_config = load_token_config()
6361

6462
# 1. Score OSS contributions
65-
oss_rewards, miner_evaluations, cached_uids, penalized_uids = await oss_contributions(
63+
_, miner_evaluations, cached_uids, penalized_uids = await oss_contributions(
6664
self, miner_uids, master_repositories, programming_languages, token_config
6765
)
6866

6967
# 2. Score issue discovery
70-
issue_rewards = await issue_discovery(
68+
_ = await issue_discovery(
7169
miner_evaluations,
7270
master_repositories,
7371
programming_languages,
@@ -86,7 +84,7 @@ async def forward(self: 'Validator') -> None:
8684
await self.bulk_store_evaluation(miner_evaluations, skip_uids=cached_uids)
8785

8886
# 5. Blend 4 emission pools into final rewards
89-
rewards = blend_emission_pools(oss_rewards, issue_rewards, miner_uids)
87+
rewards = blend_emission_pools(miner_evaluations, miner_uids, master_repositories)
9088

9189
self.update_scores(rewards, miner_uids, blacklisted_uids=sorted(penalized_uids))
9290

@@ -150,49 +148,84 @@ async def issue_discovery(
150148

151149

152150
def blend_emission_pools(
153-
oss_rewards: np.ndarray,
154-
issue_rewards: np.ndarray,
151+
miner_evaluations: Dict[int, MinerEvaluation],
155152
miner_uids: set[int],
153+
master_repositories: Dict[str, RepositoryConfig],
156154
) -> np.ndarray:
157-
"""Blend 4 emission pools into a single rewards array.
155+
"""Blend the round emission pools using per-repo emission shares.
158156
159-
- OSS contributions: 30%
160-
- Issue discovery: 30%
161-
- Issue treasury: 15% (flat to UID 111)
162-
- Recycle: 25% (flat to UID 0)
157+
- OSS scoring pool: 90% distributed by repo emission_share
158+
- Treasury: 10% flat to UID 111
159+
- Recycle: natural slack from unallocated registry share + unclaimed repo slices
163160
"""
164161
sorted_uids = sorted(miner_uids)
165162
rewards = np.zeros(len(sorted_uids))
163+
uid_to_index = {uid: idx for idx, uid in enumerate(sorted_uids)}
166164
recycle_extra = 0.0
167165

168-
# Pool 1: OSS contributions (30%)
169-
oss_total = float(oss_rewards.sum())
170-
if oss_total > 0:
171-
rewards += oss_rewards * OSS_EMISSION_SHARE
172-
else:
173-
recycle_extra += OSS_EMISSION_SHARE
174-
175-
# Pool 2: Issue discovery (30%)
176-
issue_total = float(issue_rewards.sum())
177-
if issue_total > 0:
178-
rewards += issue_rewards * ISSUE_DISCOVERY_EMISSION_SHARE
179-
else:
180-
recycle_extra += ISSUE_DISCOVERY_EMISSION_SHARE
181-
182-
# Pool 3: Issue treasury (15% flat to UID 111)
166+
total_registry_share = sum(repo.emission_share for repo in master_repositories.values())
167+
if total_registry_share < 1.0:
168+
recycle_extra += OSS_EMISSION_SHARE * (1.0 - total_registry_share)
169+
170+
for repo_name, repo_config in master_repositories.items():
171+
repo_slice = OSS_EMISSION_SHARE * repo_config.emission_share
172+
if repo_slice <= 0.0:
173+
continue
174+
175+
pr_scores: Dict[int, float] = {}
176+
issue_scores: Dict[int, float] = {}
177+
for uid, evaluation in miner_evaluations.items():
178+
pr_score = sum(
179+
pr.earned_score
180+
for pr in evaluation.merged_prs
181+
if pr.repository_full_name == repo_name and pr.earned_score > 0
182+
)
183+
if pr_score > 0:
184+
pr_scores[uid] = pr_score
185+
186+
issue_score = evaluation.issue_discovery_scores_by_repo.get(repo_name, 0.0)
187+
if issue_score > 0:
188+
issue_scores[uid] = issue_score
189+
190+
pr_total = sum(pr_scores.values())
191+
issue_total = sum(issue_scores.values())
192+
193+
if pr_total <= 0.0 and issue_total <= 0.0:
194+
recycle_extra += repo_slice
195+
continue
196+
197+
issue_subslice = repo_slice * repo_config.issue_discovery_share
198+
pr_subslice = repo_slice * (1.0 - repo_config.issue_discovery_share)
199+
200+
if pr_total <= 0.0:
201+
issue_subslice += pr_subslice
202+
pr_subslice = 0.0
203+
elif issue_total <= 0.0:
204+
pr_subslice += issue_subslice
205+
issue_subslice = 0.0
206+
207+
if pr_subslice > 0.0 and pr_total > 0.0:
208+
for uid, score in pr_scores.items():
209+
idx = uid_to_index.get(uid)
210+
if idx is not None:
211+
rewards[idx] += pr_subslice * (score / pr_total)
212+
213+
if issue_subslice > 0.0 and issue_total > 0.0:
214+
for uid, score in issue_scores.items():
215+
idx = uid_to_index.get(uid)
216+
if idx is not None:
217+
rewards[idx] += issue_subslice * (score / issue_total)
218+
219+
# Treasury pool (10% flat to UID 111)
183220
if ISSUES_TREASURY_UID > 0 and ISSUES_TREASURY_UID in miner_uids:
184221
treasury_idx = sorted_uids.index(ISSUES_TREASURY_UID)
185222
rewards[treasury_idx] += ISSUES_TREASURY_EMISSION_SHARE
186-
bt.logging.info(
187-
f'Treasury allocation: UID {ISSUES_TREASURY_UID} receives '
188-
f'{ISSUES_TREASURY_EMISSION_SHARE * 100:.0f}% of emissions'
189-
)
223+
else:
224+
recycle_extra += ISSUES_TREASURY_EMISSION_SHARE
190225

191-
# Pool 4: Recycle (25% + unclaimed from empty pools)
226+
# Recycle pool: unclaimed repo slices + registry slack (+ treasury fallback)
192227
if RECYCLE_UID in miner_uids:
193228
recycle_idx = sorted_uids.index(RECYCLE_UID)
194-
rewards[recycle_idx] += RECYCLE_EMISSION_SHARE + recycle_extra
195-
if recycle_extra > 0:
196-
bt.logging.info(f'Recycling {recycle_extra * 100:.0f}% unclaimed emissions from empty pools')
229+
rewards[recycle_idx] += recycle_extra
197230

198231
return rewards

gittensor/validator/issue_discovery/scan.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@
6060
LanguageConfig,
6161
RepositoryConfig,
6262
TokenConfig,
63-
resolve_repo_weight,
6463
)
6564

6665

@@ -224,6 +223,7 @@ def _clear_issue_discovery_fields(evaluation: MinerEvaluation) -> None:
224223
evaluation.total_valid_solved_issues = 0
225224
evaluation.total_closed_issues = 0
226225
evaluation.total_open_issues = 0
226+
evaluation.issue_discovery_scores_by_repo = {}
227227

228228

229229
def _copy_issue_discovery_fields(target: MinerEvaluation, source: MinerEvaluation) -> None:
@@ -235,6 +235,7 @@ def _copy_issue_discovery_fields(target: MinerEvaluation, source: MinerEvaluatio
235235
target.total_valid_solved_issues = source.total_valid_solved_issues
236236
target.total_closed_issues = source.total_closed_issues
237237
target.total_open_issues = source.total_open_issues
238+
target.issue_discovery_scores_by_repo = dict(source.issue_discovery_scores_by_repo)
238239

239240

240241
def _restore_issue_discovery_from_cache(
@@ -339,6 +340,7 @@ async def _score_miner_issues(
339340
valid_solved_count = 0
340341
closed_count = 0
341342
issue_token_score = 0.0
343+
repo_issue_scores: Dict[str, float] = {}
342344
score_fetch_failed = False
343345
scored_issues: List[Issue] = []
344346

@@ -456,16 +458,20 @@ async def _score_miner_issues(
456458
issue.discovery_open_issue_spam_multiplier = spam_mult
457459
issue.discovery_earned_score = round(
458460
issue.discovery_base_score
459-
* issue.discovery_repo_weight_multiplier
460461
* issue.discovery_time_decay_multiplier
461462
* issue.discovery_review_quality_multiplier
462463
* issue.discovery_credibility_multiplier
463464
* issue.discovery_open_issue_spam_multiplier,
464465
2,
465466
)
466467
total_discovery_score += issue.discovery_earned_score
468+
if issue.discovery_earned_score > 0:
469+
repo_issue_scores[issue.repository_full_name] = (
470+
repo_issue_scores.get(issue.repository_full_name, 0.0) + issue.discovery_earned_score
471+
)
467472

468473
evaluation.issue_discovery_score = round(total_discovery_score, 2)
474+
evaluation.issue_discovery_scores_by_repo = repo_issue_scores
469475

470476
bt.logging.info(
471477
f'├─ UID {evaluation.uid}: {solved_count} solved ({valid_solved_count} valid) | '
@@ -620,7 +626,6 @@ def _mirror_issue_for_scoring(
620626
)
621627

622628
adapted.discovery_base_score = base_score
623-
adapted.discovery_repo_weight_multiplier = resolve_repo_weight(repo_config)
624629
adapted.discovery_time_decay_multiplier = round(calculate_time_decay(solving_pr.merged_at), 2)
625630
adapted.discovery_review_quality_multiplier = round(
626631
calculate_issue_review_quality_multiplier(solving_pr.review_summary.maintainer_changes_requested_count),

gittensor/validator/oss_contributions/mirror/adapters.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ def mirror_scored_pr_to_legacy_pull_request(
116116
merged_at=pr.merged_at,
117117
created_at=pr.created_at,
118118
pr_state=PRState(pr.state),
119-
repo_weight_multiplier=scored.repo_weight_multiplier,
120119
base_score=scored.base_score,
121120
issue_multiplier=scored.issue_multiplier,
122121
open_pr_spam_multiplier=scored.open_pr_spam_multiplier,

gittensor/validator/oss_contributions/mirror/scored_pr.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ class ScoredPR:
2727
pr: MirrorPullRequest
2828

2929
# Multipliers (default 1.0 — neutral if not yet computed)
30-
repo_weight_multiplier: float = 1.0
3130
issue_multiplier: float = 1.0
3231
open_pr_spam_multiplier: float = 1.0
3332
time_decay_multiplier: float = 1.0
@@ -88,7 +87,6 @@ def is_pioneer_eligible(self) -> bool:
8887
def calculate_final_earned_score(self) -> float:
8988
"""Combine base score with all multipliers. Pioneer dividend is added separately after."""
9089
multipliers = {
91-
'repo': self.repo_weight_multiplier,
9290
'issue': self.issue_multiplier,
9391
'label': self.label_multiplier,
9492
'spam': self.open_pr_spam_multiplier,

gittensor/validator/oss_contributions/mirror/scoring.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
LanguageConfig,
5555
RepositoryConfig,
5656
TokenConfig,
57-
resolve_repo_weight,
5857
)
5958
from gittensor.validator.utils.tree_sitter_scoring import calculate_token_score_from_file_changes
6059

@@ -338,16 +337,14 @@ def calculate_base_score_for_pr_files(
338337

339338

340339
def _calculate_pr_multipliers(scored: ScoredPR, repo_config: RepositoryConfig) -> None:
341-
"""Compute repo_weight, time_decay, review_quality, label, issue multipliers.
340+
"""Compute time_decay, review_quality, label, issue multipliers.
342341
343342
Spam and credibility multipliers are deferred to ``finalize_miner_scores``
344343
— they depend on per-miner aggregate counts.
345344
"""
346345
pr = scored.pr
347346
is_merged = pr.state == 'MERGED'
348347

349-
scored.repo_weight_multiplier = resolve_repo_weight(repo_config)
350-
351348
chosen_label, label_multiplier = _resolve_trusted_scoring_label(pr, repo_config)
352349
scored.label = chosen_label
353350
scored.label_multiplier = label_multiplier

gittensor/validator/oss_contributions/scoring.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,14 +283,13 @@ def calculate_open_pr_collateral_score(pr: 'ScoredPR') -> float:
283283
284284
Collateral = base_score * applicable_multipliers * OPEN_PR_COLLATERAL_PERCENT
285285
286-
Applicable multipliers: repo_weight, issue, label, review_collateral
286+
Applicable multipliers: issue, label, review_collateral
287287
NOT applicable: time_decay (merge-based), credibility_multiplier (merge-based),
288288
open_pr_spam (not for collateral)
289289
"""
290290
from math import prod
291291

292292
multipliers = {
293-
'repo_weight': pr.repo_weight_multiplier,
294293
'issue': pr.issue_multiplier,
295294
'label': pr.label_multiplier,
296295
'review_collateral': calculate_review_collateral_multiplier(pr.changes_requested_count, pr.number),

gittensor/validator/storage/queries.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
INSERT INTO pull_requests (
4646
number, repository_full_name, uid, hotkey, github_id, title, author_login,
4747
merged_at, pr_created_at, pr_state,
48-
repo_weight_multiplier, base_score, issue_multiplier,
48+
base_score, issue_multiplier,
4949
open_pr_spam_multiplier, pioneer_dividend, pioneer_rank, time_decay_multiplier,
5050
credibility_multiplier, review_quality_multiplier, label_multiplier, label,
5151
earned_score, collateral_score,
@@ -54,7 +54,7 @@
5454
code_density, token_score, structural_count, structural_score, leaf_count, leaf_score
5555
) VALUES (
5656
%s, %s, %s, %s, %s, %s, %s,
57-
%s, %s, %s,
57+
%s, %s,
5858
%s, %s, %s,
5959
%s, %s, %s, %s,
6060
%s, %s, %s, %s,
@@ -71,7 +71,6 @@
7171
author_login = EXCLUDED.author_login,
7272
merged_at = EXCLUDED.merged_at,
7373
pr_state = EXCLUDED.pr_state,
74-
repo_weight_multiplier = EXCLUDED.repo_weight_multiplier,
7574
base_score = EXCLUDED.base_score,
7675
issue_multiplier = EXCLUDED.issue_multiplier,
7776
open_pr_spam_multiplier = EXCLUDED.open_pr_spam_multiplier,
@@ -117,14 +116,14 @@
117116
author_login, state, author_association,
118117
author_github_id, is_transferred, updated_at,
119118
discovery_base_score, discovery_earned_score,
120-
discovery_review_quality_multiplier, discovery_repo_weight_multiplier,
119+
discovery_review_quality_multiplier,
121120
discovery_time_decay_multiplier, discovery_credibility_multiplier,
122121
discovery_open_issue_spam_multiplier
123122
) VALUES (
124123
%s, %s, %s, %s, %s, %s,
125124
%s, %s, %s,
126125
%s, %s, %s,
127-
%s, %s,
126+
%s,
128127
%s, %s,
129128
%s, %s,
130129
%s
@@ -142,7 +141,6 @@
142141
discovery_base_score = EXCLUDED.discovery_base_score,
143142
discovery_earned_score = EXCLUDED.discovery_earned_score,
144143
discovery_review_quality_multiplier = EXCLUDED.discovery_review_quality_multiplier,
145-
discovery_repo_weight_multiplier = EXCLUDED.discovery_repo_weight_multiplier,
146144
discovery_time_decay_multiplier = EXCLUDED.discovery_time_decay_multiplier,
147145
discovery_credibility_multiplier = EXCLUDED.discovery_credibility_multiplier,
148146
discovery_open_issue_spam_multiplier = EXCLUDED.discovery_open_issue_spam_multiplier

0 commit comments

Comments
 (0)