mj_controller/review_host/
prompts.rs1use super::*;
2
3pub(super) const ROLE_POLL_IDLE_INTERVAL: Duration = Duration::from_millis(200);
7
8#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct StartRefusal(pub String);
12
13impl std::fmt::Display for StartRefusal {
14 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 formatter.write_str(&self.0)
16 }
17}
18
19pub const PROMPT_HELD_MESSAGE: &str =
21 "a review of the last turn is open; forward, dismiss or cancel it first";
22
23pub(super) static PROMPT_LOCK: LazyLock<Mutex<BTreeMap<String, PromptHold>>> =
33 LazyLock::new(Mutex::default);
34
35#[derive(Debug, Default)]
36pub(super) struct PromptHold {
37 pub(super) delivery_epoch: Option<u64>,
38 pub(super) delivery_command_id: Option<String>,
39}
40
41pub(crate) fn next_review_generation() -> Result<u64, String> {
46 let mut random = [0_u8; 8];
47 getrandom::fill(&mut random)
48 .map_err(|error| format!("generate reviewer generation: {error}"))?;
49 let generation = u64::from_le_bytes(random);
50 if generation == 0 {
51 return Err("generate reviewer generation: random nonce was zero".to_owned());
52 }
53 Ok(generation)
54}
55
56#[must_use]
58pub fn prompt_refusal(session_id: &str) -> Option<&'static str> {
59 PROMPT_LOCK
60 .lock()
61 .unwrap_or_else(std::sync::PoisonError::into_inner)
62 .contains_key(session_id)
63 .then_some(PROMPT_HELD_MESSAGE)
64}
65
66pub(super) fn hold_prompts(session_id: &str) {
67 PROMPT_LOCK
68 .lock()
69 .unwrap_or_else(std::sync::PoisonError::into_inner)
70 .insert(session_id.to_owned(), PromptHold::default());
71}
72
73pub(super) fn release_prompts(session_id: &str) {
74 PROMPT_LOCK
75 .lock()
76 .unwrap_or_else(std::sync::PoisonError::into_inner)
77 .remove(session_id);
78}
79
80pub(super) fn admit_review_delivery(
84 session_id: &str,
85 epoch: u64,
86 command_id: &str,
87) -> Option<ReviewDeliveryAdmission> {
88 let mut locks = PROMPT_LOCK
89 .lock()
90 .unwrap_or_else(std::sync::PoisonError::into_inner);
91 let hold = locks.get_mut(session_id)?;
92 match (hold.delivery_epoch, hold.delivery_command_id.as_deref()) {
93 (Some(existing_epoch), Some(existing_command))
94 if existing_epoch != epoch || existing_command != command_id =>
95 {
96 None
97 }
98 _ => {
99 hold.delivery_epoch = Some(epoch);
100 hold.delivery_command_id = Some(command_id.to_owned());
101 Some(ReviewDeliveryAdmission::new(
102 session_id.to_owned(),
103 epoch,
104 command_id.to_owned(),
105 ))
106 }
107 }
108}
109
110pub(crate) fn review_delivery_admitted(
114 session_id: &str,
115 admission: &ReviewDeliveryAdmission,
116) -> bool {
117 let locks = PROMPT_LOCK
118 .lock()
119 .unwrap_or_else(std::sync::PoisonError::into_inner);
120 locks.get(session_id).is_some_and(|hold| {
121 admission.session_id() == session_id
122 && hold.delivery_epoch == Some(admission.epoch())
123 && hold.delivery_command_id.as_deref() == Some(admission.command_id())
124 })
125}
126
127pub type ReviewConfigSource = Arc<dyn Fn() -> ReviewConfig + Send + Sync>;