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> {
44 let mut random = [0_u8; 8];
45 getrandom::fill(&mut random)
46 .map_err(|error| format!("generate reviewer generation: {error}"))?;
47 let generation = u64::from_le_bytes(random);
48 if generation == 0 {
49 return Err("generate reviewer generation: random nonce was zero".to_owned());
50 }
51 Ok(generation)
52}
53
54#[must_use]
56pub fn prompt_refusal(session_id: &str) -> Option<&'static str> {
57 PROMPT_LOCK
58 .lock()
59 .unwrap_or_else(std::sync::PoisonError::into_inner)
60 .contains_key(session_id)
61 .then_some(PROMPT_HELD_MESSAGE)
62}
63
64pub(super) fn hold_prompts(session_id: &str) {
65 PROMPT_LOCK
66 .lock()
67 .unwrap_or_else(std::sync::PoisonError::into_inner)
68 .insert(session_id.to_owned(), PromptHold::default());
69}
70
71pub(super) fn release_prompts(session_id: &str) {
72 PROMPT_LOCK
73 .lock()
74 .unwrap_or_else(std::sync::PoisonError::into_inner)
75 .remove(session_id);
76}
77
78pub(super) fn admit_review_delivery(
82 session_id: &str,
83 epoch: u64,
84 command_id: &str,
85) -> Option<ReviewDeliveryAdmission> {
86 let mut locks = PROMPT_LOCK
87 .lock()
88 .unwrap_or_else(std::sync::PoisonError::into_inner);
89 let hold = locks.get_mut(session_id)?;
90 match (hold.delivery_epoch, hold.delivery_command_id.as_deref()) {
91 (Some(existing_epoch), Some(existing_command))
92 if existing_epoch != epoch || existing_command != command_id =>
93 {
94 None
95 }
96 _ => {
97 hold.delivery_epoch = Some(epoch);
98 hold.delivery_command_id = Some(command_id.to_owned());
99 Some(ReviewDeliveryAdmission::new(
100 session_id.to_owned(),
101 epoch,
102 command_id.to_owned(),
103 ))
104 }
105 }
106}
107
108pub(crate) fn review_delivery_admitted(
112 session_id: &str,
113 admission: &ReviewDeliveryAdmission,
114) -> bool {
115 let locks = PROMPT_LOCK
116 .lock()
117 .unwrap_or_else(std::sync::PoisonError::into_inner);
118 locks.get(session_id).is_some_and(|hold| {
119 admission.session_id() == session_id
120 && hold.delivery_epoch == Some(admission.epoch())
121 && hold.delivery_command_id.as_deref() == Some(admission.command_id())
122 })
123}
124
125pub type ReviewConfigSource = Arc<dyn Fn(&str) -> ReviewConfig + Send + Sync>;