Skip to content

Commit e5af8ef

Browse files
committed
feat(prism): arch registry, training-only competition, top-model
1 parent ac0bf03 commit e5af8ef

33 files changed

Lines changed: 2472 additions & 186 deletions

File tree

Cargo.lock

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bins/prism-challenge/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ clap = { version = "4", features = ["derive", "env"] }
2121
prism-challenge = { path = "../../crates/prism-challenge" }
2222
prism-lium = { path = "../../crates/prism-lium" }
2323
prism-recipe = { path = "../../crates/prism-recipe" }
24+
prism-registry = { path = "../../crates/prism-registry" }
2425
prism-review = { path = "../../crates/prism-review" }
2526
submission-gating = { path = "../../crates/submission-gating" }
2627
crypto = { path = "../../crates/crypto" }

bins/prism-challenge/src/main.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,16 @@ fn spawn_gating_watcher(
345345
});
346346
}
347347

348+
/// Top-model GitHub publisher: graceful no-op (`None`) unless
349+
/// `PRISM_TOPMODEL_GITHUB_TOKEN_FILE` points at a readable token file.
350+
fn build_topmodel() -> Option<Arc<prism_registry::TopModelPublisher>> {
351+
let p = prism_registry::TopModelPublisher::from_env().map(Arc::new);
352+
if p.is_none() {
353+
tracing::info!("top-model publish disabled (PRISM_TOPMODEL_GITHUB_TOKEN_FILE unset/empty)");
354+
}
355+
p
356+
}
357+
348358
async fn cmd_serve(cli: Cli) -> Result<(), String> {
349359
let path = resolve_sk_path(cli.challenge_sk_file.as_ref())?;
350360
if !path.is_file() {
@@ -415,6 +425,7 @@ async fn cmd_serve(cli: Cli) -> Result<(), String> {
415425
stage_delay,
416426
auto_retry_max: cli.auto_retry_max,
417427
};
428+
let topmodel = build_topmodel();
418429
let mut orchestrator = Orchestrator::new(
419430
oc,
420431
store,
@@ -424,7 +435,8 @@ async fn cmd_serve(cli: Cli) -> Result<(), String> {
424435
gateway,
425436
Arc::new(chain),
426437
sk,
427-
);
438+
)
439+
.with_topmodel(topmodel);
428440
if gating_enabled {
429441
orchestrator = orchestrator.with_gating(Arc::clone(&gating));
430442
spawn_gating_watcher(

crates/challenge-agentic/src/sim.rs

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,23 @@ impl SimAgent {
4646
}
4747
}
4848

49+
/// Primaries in scope for corpus comparison. Prism compares `architecture.py`
50+
/// ONLY (similarity v2: training.py is exempt from corpus/candidate); other
51+
/// domains use every primary.
52+
fn scoped_primaries<'a>(req: &ReviewRequest, primaries: &'a [(String, String)]) -> Vec<&'a str> {
53+
if req.domain_rules.contains("Prism domain") {
54+
let arch: Vec<&str> = primaries
55+
.iter()
56+
.filter(|(p, _)| p.ends_with("architecture.py"))
57+
.map(|(_, s)| s.as_str())
58+
.collect();
59+
if !arch.is_empty() {
60+
return arch;
61+
}
62+
}
63+
primaries.iter().map(|(_, s)| s.as_str()).collect()
64+
}
65+
4966
#[async_trait]
5067
impl AgenticBackend for SimAgent {
5168
async fn review(&self, req: &ReviewRequest) -> Result<AgenticVerdict, AgenticError> {
@@ -66,25 +83,9 @@ impl AgenticBackend for SimAgent {
6683
return Ok(v);
6784
}
6885

69-
// Concatenate primaries for hash / fingerprint (stable order from
70-
// request). Prism compares architecture.py ONLY (similarity v2:
71-
// training.py is exempt from corpus/candidate); other domains use
72-
// every primary.
73-
let scoped: Vec<&str> = if req.domain_rules.contains("Prism domain") {
74-
let arch: Vec<&str> = primaries
75-
.iter()
76-
.filter(|(p, _)| p.ends_with("architecture.py"))
77-
.map(|(_, s)| s.as_str())
78-
.collect();
79-
if arch.is_empty() {
80-
primaries.iter().map(|(_, s)| s.as_str()).collect()
81-
} else {
82-
arch
83-
}
84-
} else {
85-
primaries.iter().map(|(_, s)| s.as_str()).collect()
86-
};
87-
let joined = scoped.join("\n#--\n");
86+
// Concatenate the in-scope primaries for hash / fingerprint (stable
87+
// order from request).
88+
let joined = scoped_primaries(req, &primaries).join("\n#--\n");
8889
let cand_hash = source_hash_hex(&joined);
8990

9091
for entry in &req.corpus {
@@ -185,9 +186,7 @@ fn telemetry_hooks_verdict(
185186
if !req.domain_rules.contains("Prism domain") {
186187
return None;
187188
}
188-
let Some((path, src)) = primaries.iter().find(|(p, _)| p.ends_with("training.py")) else {
189-
return None;
190-
};
189+
let (path, src) = primaries.iter().find(|(p, _)| p.ends_with("training.py"))?;
191190
let imports_shim = src.contains("prism_telemetry")
192191
|| src.contains("ctx[\"telemetry\"]")
193192
|| src.contains("ctx['telemetry']");

crates/db/src/prism_store.rs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -298,30 +298,6 @@ pub async fn prism_stage_events(
298298
Ok(rows)
299299
}
300300

301-
/// Latest final score per miner for `(netuid, epoch)`.
302-
///
303-
/// Rows without `kind` (not scored yet) do not appear.
304-
///
305-
/// # Errors
306-
/// SQL error.
307-
pub async fn prism_scores_for_epoch(
308-
pool: &PgPool,
309-
netuid: i32,
310-
epoch: i64,
311-
) -> Result<Vec<(String, String, Option<i64>, Option<i16>)>, DbError> {
312-
let rows: Vec<(String, String, Option<i64>, Option<i16>)> = sqlx::query_as(
313-
"SELECT DISTINCT ON (miner_hotkey) miner_hotkey, kind, score, absence_reason \
314-
FROM prism_submission \
315-
WHERE netuid = $1 AND epoch = $2 AND kind IS NOT NULL \
316-
ORDER BY miner_hotkey, updated_at DESC",
317-
)
318-
.bind(netuid)
319-
.bind(epoch)
320-
.fetch_all(pool)
321-
.await?;
322-
Ok(rows)
323-
}
324-
325301
/// Stage statuses stuck non-terminal beyond the grace window (restart sweep).
326302
///
327303
/// # Errors

crates/prism-challenge/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ prism-challenge-task = { path = "../prism-challenge-task" }
2323
prism-lium = { path = "../prism-lium" }
2424
prism-pipeline = { path = "../prism-pipeline" }
2525
prism-recipe = { path = "../prism-recipe" }
26+
prism-registry = { path = "../prism-registry" }
2627
prism-review = { path = "../prism-review" }
2728
prism-store = { path = "../prism-store" }
2829
trustroot = { path = "../trustroot" }

crates/prism-challenge/src/agentic.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,20 @@ pub fn build_review_request(
5252
/// Corpus entries are **architecture.py only** (similarity v2): `training.py`
5353
/// is exempt from every copy/similarity comparison — the same training
5454
/// script on two different architectures is legitimate competition behavior.
55+
/// `exempt_arch` drops entries byte-equal to that source (training-only
56+
/// submissions on a registry architecture: the identity is by design).
5557
#[must_use]
5658
pub fn corpus_from_rows(
5759
current_id: &str,
5860
recent: &[prism_store::SubmissionState],
61+
exempt_arch: Option<&str>,
5962
) -> Vec<CorpusEntry> {
6063
let mut v = vec![CorpusEntry {
6164
id: "baseline".into(),
6265
source: BASELINE_ARCHITECTURE_PY.into(),
6366
}];
6467
for r in recent {
65-
if r.id == current_id {
68+
if r.id == current_id || Some(r.architecture_py.as_str()) == exempt_arch {
6669
continue;
6770
}
6871
let label = if r.id.len() >= 8 {

0 commit comments

Comments
 (0)