1use std::sync::Arc;
2use anyhow::{Result, anyhow};
3
4use praxis_llm::LLMClient;
5use praxis_mcp::MCPToolExecutor;
6use crate::types::GraphConfig;
7
8use crate::graph::Graph;
9
10pub struct PersistenceConfig {
12 pub client: Arc<dyn praxis_persist::PersistenceClient>,
13}
14
15#[cfg(feature = "observability")]
17pub struct ObserverConfig {
18 pub observer: Arc<dyn praxis_observability::Observer>,
19}
20
21pub struct GraphBuilder {
23 llm_client: Option<Arc<dyn LLMClient>>,
24 reasoning_client: Option<Arc<dyn praxis_llm::ReasoningClient>>,
25 mcp_executor: Option<Arc<MCPToolExecutor>>,
26 config: GraphConfig,
27 persistence_config: Option<PersistenceConfig>,
28 #[cfg(feature = "observability")]
29 observer_config: Option<ObserverConfig>,
30}
31
32impl GraphBuilder {
33 pub fn new() -> Self {
34 Self {
35 llm_client: None,
36 reasoning_client: None,
37 mcp_executor: None,
38 config: GraphConfig::default(),
39 persistence_config: None,
40 #[cfg(feature = "observability")]
41 observer_config: None,
42 }
43 }
44
45 pub fn llm_client(mut self, client: Arc<dyn LLMClient>) -> Self {
47 self.llm_client = Some(client);
48 self
49 }
50
51 pub fn reasoning_client(mut self, client: Arc<dyn praxis_llm::ReasoningClient>) -> Self {
53 self.reasoning_client = Some(client);
54 self
55 }
56
57 pub fn mcp_executor(mut self, executor: Arc<MCPToolExecutor>) -> Self {
59 self.mcp_executor = Some(executor);
60 self
61 }
62
63 pub fn config(mut self, config: GraphConfig) -> Self {
65 self.config = config;
66 self
67 }
68
69 pub fn with_persistence(mut self, client: Arc<dyn praxis_persist::PersistenceClient>) -> Self {
71 self.persistence_config = Some(PersistenceConfig { client });
72 self
73 }
74
75 #[cfg(feature = "observability")]
77 pub fn with_observer(mut self, observer: Arc<dyn praxis_observability::Observer>) -> Self {
78 self.observer_config = Some(ObserverConfig { observer });
79 self
80 }
81
82 pub fn build(self) -> Result<Graph> {
84 let llm_client = self.llm_client
85 .ok_or_else(|| anyhow!("LLM client is required"))?;
86 let mcp_executor = self.mcp_executor
87 .ok_or_else(|| anyhow!("MCP executor is required"))?;
88
89 Ok(Graph::new_with_config(
90 llm_client,
91 self.reasoning_client,
92 mcp_executor,
93 self.config,
94 self.persistence_config,
95 #[cfg(feature = "observability")]
96 self.observer_config,
97 ))
98 }
99}
100
101impl Default for GraphBuilder {
102 fn default() -> Self {
103 Self::new()
104 }
105}
106