forge_workspace/mcp/systemone/
facade.rs1use std::sync::Arc;
7
8use forge_system_one::{AskOutcome, Question, SystemOneClient, SystemOneError};
9
10#[async_trait::async_trait]
12pub(crate) trait SystemOneFacade: Send + Sync {
13 async fn ask(
14 &self,
15 state: serde_json::Value,
16 question: Question,
17 ) -> Result<AskOutcome, SystemOneError>;
18}
19
20pub(crate) struct ProdSystemOneFacade {
22 client: Arc<SystemOneClient>,
23}
24
25impl ProdSystemOneFacade {
26 pub(crate) fn new(client: Arc<SystemOneClient>) -> Self {
27 Self { client }
28 }
29
30 pub(crate) fn into_arc(self) -> Arc<dyn SystemOneFacade> {
31 Arc::new(self)
32 }
33}
34
35#[async_trait::async_trait]
36impl SystemOneFacade for ProdSystemOneFacade {
37 async fn ask(
38 &self,
39 state: serde_json::Value,
40 question: Question,
41 ) -> Result<AskOutcome, SystemOneError> {
42 self.client.ask(&state, &question).await
43 }
44}
45
46#[cfg(test)]
47#[derive(Default)]
48pub(crate) struct MockSystemOneFacade {
49 pub result: parking_lot::Mutex<Option<Result<AskOutcome, SystemOneError>>>,
50 pub calls: parking_lot::Mutex<Vec<(serde_json::Value, Question)>>,
51}
52
53#[cfg(test)]
54impl MockSystemOneFacade {
55 pub(crate) fn new() -> Self {
56 Self::default()
57 }
58
59 pub(crate) fn into_arc(self) -> Arc<dyn SystemOneFacade> {
60 Arc::new(self)
61 }
62}
63
64#[cfg(test)]
65#[async_trait::async_trait]
66impl SystemOneFacade for MockSystemOneFacade {
67 async fn ask(
68 &self,
69 state: serde_json::Value,
70 question: Question,
71 ) -> Result<AskOutcome, SystemOneError> {
72 self.calls.lock().push((state, question));
73 self.result.lock().clone().unwrap_or_else(default_outcome)
74 }
75}
76
77#[cfg(test)]
78fn default_outcome() -> Result<AskOutcome, SystemOneError> {
79 Ok(AskOutcome {
80 model: "test-model".to_owned(),
81 answer: forge_system_one::Answer::Noul { noul: 0.5 },
82 usage: Some(forge_system_one::Usage { input_tokens: 1, output_tokens: 1, cost: None }),
83 })
84}