Skip to main content

hh_cli/core/
traits.rs

1use crate::core::types::{Message, ProviderRequest, ProviderResponse, ProviderStreamEvent};
2use async_trait::async_trait;
3use serde_json::Value;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum ApprovalDecision {
7    Allow,
8    Ask,
9    Deny,
10}
11
12pub trait AgentEvents: Send + Sync {
13    fn on_thinking(&self, _text: &str) {}
14    fn on_tool_start(&self, _name: &str, _args: &Value) {}
15    fn on_tool_end(&self, _name: &str, _result: &crate::tool::ToolResult) {}
16    fn on_todo_items_changed(&self, _items: &[crate::core::TodoItem]) {}
17    fn on_assistant_delta(&self, _delta: &str) {}
18    fn on_context_usage(&self, _tokens: usize) {}
19    fn on_assistant_done(&self) {}
20}
21
22#[derive(Debug, Default, Clone, Copy)]
23pub struct NoopEvents;
24
25impl AgentEvents for NoopEvents {}
26
27#[async_trait]
28pub trait Provider: Send + Sync {
29    async fn complete(&self, req: ProviderRequest) -> anyhow::Result<ProviderResponse>;
30
31    async fn complete_stream<F>(
32        &self,
33        req: ProviderRequest,
34        mut on_event: F,
35    ) -> anyhow::Result<ProviderResponse>
36    where
37        F: FnMut(ProviderStreamEvent) + Send,
38    {
39        let response = self.complete(req).await?;
40        if let Some(thinking) = &response.thinking {
41            on_event(ProviderStreamEvent::ThinkingDelta(thinking.clone()));
42        }
43        if !response.assistant_message.content.is_empty() {
44            on_event(ProviderStreamEvent::AssistantDelta(
45                response.assistant_message.content.clone(),
46            ));
47        }
48        Ok(response)
49    }
50}
51
52#[async_trait]
53pub trait ToolExecutor: Send + Sync {
54    fn schemas(&self) -> Vec<crate::tool::schema::ToolSchema>;
55    async fn execute(&self, name: &str, args: Value) -> crate::tool::ToolResult;
56    fn is_non_blocking(&self, _name: &str) -> bool {
57        false
58    }
59}
60
61pub trait ApprovalPolicy: Send + Sync {
62    fn decision_for_tool(&self, tool_name: &str) -> ApprovalDecision;
63}
64
65pub trait SessionSink: Send + Sync {
66    fn append(&self, event: &crate::session::SessionEvent) -> anyhow::Result<()>;
67}
68
69pub trait SessionReader: Send + Sync {
70    fn replay_messages(&self) -> anyhow::Result<Vec<Message>>;
71    fn replay_events(&self) -> anyhow::Result<Vec<crate::session::SessionEvent>>;
72}