phi_agent/bridge/
server.rs1use std::collections::HashMap;
8use std::sync::Arc;
9
10use agent_base::{
11 AgentResult, AgentRuntime, RunOutcome, RuntimeEvent, SessionId, Tool, ToolContext, ToolControlFlow, ToolMetadata,
12 ToolOutput,
13};
14use agent_works::AgentBuilder;
15use async_trait::async_trait;
16use serde_json::Value;
17use tokio::sync::{Mutex, mpsc};
18
19#[derive(Clone)]
26pub struct ProtocolServer {
27 runtime: AgentRuntime,
28 slot: Arc<Mutex<Option<mpsc::UnboundedReceiver<AgentResult<ToolOutput>>>>>,
31 sessions: Arc<Mutex<HashMap<String, SessionId>>>,
34}
35
36impl ProtocolServer {
37 pub fn new(runtime: AgentRuntime) -> Self {
39 Self { runtime, slot: Arc::new(Mutex::new(None)), sessions: Arc::new(Mutex::new(HashMap::new())) }
40 }
41
42 pub fn from_builder(builder: AgentBuilder) -> Result<Self, agent_base::AgentError> {
44 let runtime = builder.build()?;
45 Ok(Self::new(runtime))
46 }
47
48 pub async fn register_tool(&self, name: String, description: String, parameters: Value) {
53 let proxy = ProxyTool { name, description, parameters, slot: self.slot.clone() };
54 let tools_arc = self.runtime.tools_mut();
55 let mut tools = tools_arc.write().await;
56 tools.register(proxy);
57 }
58
59 pub async fn prepare_tool_call(&self) -> mpsc::UnboundedSender<AgentResult<ToolOutput>> {
62 let (tx, rx) = mpsc::unbounded_channel();
63 *self.slot.lock().await = Some(rx);
64 tx
65 }
66
67 pub async fn create_session(&self, external_id: Option<String>) -> (SessionId, Option<String>) {
69 let sid = self.runtime.create_session().await;
70 let ext = external_id.clone();
75 (sid, ext)
76 }
77
78 pub async fn get_or_create_session(&self, external_id: Option<String>) -> SessionId {
84 if let Some(ref ext) = external_id {
85 let mut sessions = self.sessions.lock().await;
86 if let Some(sid) = sessions.get(ext) {
87 return sid.clone();
88 }
89 let (sid, _) = self.create_session(Some(ext.clone())).await;
91 sessions.insert(ext.clone(), sid.clone());
92 return sid;
93 }
94 self.create_session(None).await.0
96 }
97
98 pub fn subscribe_events(&self) -> tokio::sync::broadcast::Receiver<RuntimeEvent> {
100 self.runtime.subscribe_runtime_events()
101 }
102
103 pub async fn run_turn<F>(&self, sid: &SessionId, input: &str, f: F) -> AgentResult<RunOutcome>
105 where
106 F: FnMut(RuntimeEvent) -> AgentResult<()> + Send,
107 {
108 self.runtime.run_turn(sid.clone(), input, f).await
109 }
110
111 pub fn cancel(&self) {
113 self.runtime.cancel();
114 }
115
116 pub async fn list_tools(&self) -> Vec<ToolMetadata> {
118 let tools = self.runtime.tools_mut();
119 let registry = tools.read().await;
120 registry.metadatas()
121 }
122}
123
124struct ProxyTool {
127 name: String,
128 description: String,
129 parameters: Value,
130 slot: Arc<Mutex<Option<mpsc::UnboundedReceiver<AgentResult<ToolOutput>>>>>,
131}
132
133#[async_trait]
134impl Tool for ProxyTool {
135 fn name(&self) -> &'static str {
136 Box::leak(self.name.clone().into_boxed_str())
137 }
138
139 fn definition(&self) -> Value {
140 serde_json::json!({
141 "type": "function",
142 "function": {
143 "name": self.name,
144 "description": self.description,
145 "parameters": self.parameters,
146 }
147 })
148 }
149
150 async fn call(&self, _args: &Value, _ctx: &ToolContext) -> AgentResult<ToolOutput> {
151 let mut rx = self
152 .slot
153 .lock()
154 .await
155 .take()
156 .ok_or_else(|| agent_base::AgentError::internal("no tool call slot prepared"))?;
157
158 match rx.recv().await {
159 Some(result) => result,
160 None => Ok(ToolOutput {
161 summary: "Tool call cancelled".to_string(),
162 raw: None,
163 control_flow: ToolControlFlow::Break,
164 truncation: None,
165 }),
166 }
167 }
168}