This commit is contained in:
2026-04-19 00:13:37 +02:00
parent bc089c1a10
commit a78c4c52e2
3 changed files with 126 additions and 1 deletions

View File

@@ -8,7 +8,7 @@ members = [
]
[workspace.package]
version = "0.5.8"
version = "0.5.9"
edition = "2024"
license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobot"

View File

@@ -1286,6 +1286,34 @@ pub async fn run_listener_runtime(
);
},
}
let session_candidates =
session_candidate_tracker.snapshot_sorted_by_score_desc();
tracing::info!(
listener_session_id = session.id,
candidate_count = session_candidates.len(),
"session candidate summary ready"
);
for candidate in &session_candidates {
tracing::info!(
listener_session_id = session.id,
key = %candidate.key,
category = %candidate.category,
pubkey = ?candidate.pubkey,
first_seen_slot = candidate.first_seen_slot,
last_seen_slot = candidate.last_seen_slot,
seen_count = candidate.seen_count,
score = candidate.score,
confidence = ?candidate.confidence,
"session candidate summary entry"
);
}
let high_confidence_candidates = session_candidate_tracker
.snapshot_with_min_confidence(crate::KhbbCandidateConfidence::High);
tracing::info!(
listener_session_id = session.id,
high_confidence_candidate_count = high_confidence_candidates.len(),
"session high confidence candidate summary ready"
);
let status_update_result =
crate::storage::update_listener_session_status(pool, session.id, &final_status).await;
match status_update_result {

View File

@@ -53,6 +53,42 @@ impl KhbbSessionCandidateTracker {
},
}
}
/// Returns all tracked candidates as a vector snapshot.
pub fn snapshot(&self) -> std::vec::Vec<crate::KhbbSessionCandidate> {
let mut values = std::vec::Vec::<crate::KhbbSessionCandidate>::new();
for value in self.candidates.values() {
values.push(value.clone());
}
values
}
/// Returns tracked candidates sorted from highest to lowest score.
pub fn snapshot_sorted_by_score_desc(&self) -> std::vec::Vec<crate::KhbbSessionCandidate> {
let mut values = self.snapshot();
values.sort_by(|left, right| {
right
.score
.cmp(&left.score)
.then_with(|| right.seen_count.cmp(&left.seen_count))
.then_with(|| left.key.cmp(&right.key))
});
values
}
/// Returns tracked candidates whose confidence is at least the requested minimum.
pub fn snapshot_with_min_confidence(
&self,
min_confidence: crate::KhbbCandidateConfidence,
) -> std::vec::Vec<crate::KhbbSessionCandidate> {
let mut values = std::vec::Vec::<crate::KhbbSessionCandidate>::new();
for candidate in self.snapshot_sorted_by_score_desc() {
if confidence_rank(candidate.confidence) >= confidence_rank(min_confidence) {
values.push(candidate);
}
}
values
}
}
fn build_session_candidate_from_correlated_signal(
@@ -136,6 +172,14 @@ fn compute_candidate_confidence(score: u64) -> crate::KhbbCandidateConfidence {
crate::KhbbCandidateConfidence::Low
}
fn confidence_rank(confidence: crate::KhbbCandidateConfidence) -> u8 {
match confidence {
crate::KhbbCandidateConfidence::Low => 1,
crate::KhbbCandidateConfidence::Medium => 2,
crate::KhbbCandidateConfidence::High => 3,
}
}
#[cfg(test)]
mod tests {
#[test]
@@ -208,4 +252,57 @@ mod tests {
assert!(update.is_new);
assert_eq!(update.candidate.confidence, crate::KhbbCandidateConfidence::Medium);
}
#[test]
fn snapshot_sorted_by_score_desc_orders_highest_score_first() {
let mut tracker = super::KhbbSessionCandidateTracker::new();
let token_signal = crate::KhbbCorrelatedSignal::ConfirmedTokenAccountUpdate(
crate::KhbbConfirmedTokenAccountUpdateSignal {
pubkey: std::string::String::from("TokenPubkey"),
context_slot: 100,
owner: Some(crate::ids::SPL_TOKEN_PROGRAM_ID.to_string()),
lamports: Some(123),
},
);
let mint_signal = crate::KhbbCorrelatedSignal::PotentialNewTokenMint(
crate::KhbbPotentialNewTokenMintSignal {
pubkey: std::string::String::from("MintPubkey"),
context_slot: 200,
owner: Some(crate::ids::SPL_TOKEN_PROGRAM_ID.to_string()),
lamports: Some(456),
},
);
tracker.upsert_from_correlated_signal(&token_signal);
tracker.upsert_from_correlated_signal(&mint_signal);
let snapshot = tracker.snapshot_sorted_by_score_desc();
assert_eq!(snapshot.len(), 2);
assert_eq!(snapshot[0].category, "mint");
assert_eq!(snapshot[1].category, "token_account");
}
#[test]
fn snapshot_with_min_confidence_filters_candidates() {
let mut tracker = super::KhbbSessionCandidateTracker::new();
let token_signal = crate::KhbbCorrelatedSignal::ConfirmedTokenAccountUpdate(
crate::KhbbConfirmedTokenAccountUpdateSignal {
pubkey: std::string::String::from("TokenPubkey"),
context_slot: 100,
owner: Some(crate::ids::SPL_TOKEN_PROGRAM_ID.to_string()),
lamports: Some(123),
},
);
let mint_signal = crate::KhbbCorrelatedSignal::PotentialNewTokenMint(
crate::KhbbPotentialNewTokenMintSignal {
pubkey: std::string::String::from("MintPubkey"),
context_slot: 200,
owner: Some(crate::ids::SPL_TOKEN_PROGRAM_ID.to_string()),
lamports: Some(456),
},
);
tracker.upsert_from_correlated_signal(&token_signal);
tracker.upsert_from_correlated_signal(&mint_signal);
let filtered = tracker.snapshot_with_min_confidence(crate::KhbbCandidateConfidence::High);
assert_eq!(filtered.len(), 1);
assert_eq!(filtered[0].category, "mint");
}
}