klieo_core/runtime/
review.rs1use async_trait::async_trait;
4
5use crate::error::Error;
6use crate::llm::Message;
7
8#[async_trait]
12pub trait ReviewPolicy: Send + Sync {
13 async fn should_pause_for_approval(
15 &self,
16 step: u32,
17 message: &Message,
18 ) -> Result<Option<String>, Error>;
19
20 fn is_never(&self) -> bool {
24 false
25 }
26}
27
28pub struct NeverReview;
31
32#[async_trait]
33impl ReviewPolicy for NeverReview {
34 async fn should_pause_for_approval(
35 &self,
36 _step: u32,
37 _message: &Message,
38 ) -> Result<Option<String>, Error> {
39 Ok(None)
40 }
41
42 fn is_never(&self) -> bool {
43 true
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use crate::llm::{Message, Role};
51
52 #[tokio::test]
53 async fn never_review_never_pauses() {
54 let policy = NeverReview;
55 let msg = Message {
56 role: Role::Assistant,
57 content: "hi".into(),
58 tool_calls: vec![],
59 tool_call_id: None,
60 };
61 assert_eq!(
62 policy.should_pause_for_approval(1, &msg).await.unwrap(),
63 None
64 );
65 }
66
67 #[test]
68 fn never_review_reports_is_never() {
69 assert!(NeverReview.is_never());
70 }
71
72 #[test]
73 fn custom_policy_is_not_never_by_default() {
74 struct Gating;
75 #[async_trait]
76 impl ReviewPolicy for Gating {
77 async fn should_pause_for_approval(
78 &self,
79 _step: u32,
80 _message: &Message,
81 ) -> Result<Option<String>, Error> {
82 Ok(Some("hold".into()))
83 }
84 }
85 assert!(!Gating.is_never());
86 }
87}