phi_agent/agent/factory.rs
1use std::sync::Arc;
2
3use agent_base::{
4 AgentBuilder, AgentResult, AgentRuntime, ReasoningEffort, RunOutcome, RuntimeEvent, SafetyConfig, SessionId,
5};
6use anyhow::Result;
7
8use crate::agent::builder::base_agent_builder;
9
10/// phi-agent configuration (tool-agnostic).
11///
12/// This config covers model and safety settings only. Tools are registered
13/// externally on [`AgentBuilder`] — phi-agent itself never bundles tools.
14#[derive(Clone)]
15pub struct PhiAgentConfig {
16 /// Model name passed to the LLM provider (e.g. `"opus"`, `"gpt-4o"`).
17 pub model: String,
18 /// Enable extended thinking / chain-of-thought.
19 pub enable_thinking: bool,
20 /// Token budget for thinking (provider-dependent). `None` means use the
21 /// provider default.
22 pub thinking_budget: Option<u64>,
23 /// Reasoning intensity: Low / Medium / High / XHigh.
24 pub thinking_effort: ReasoningEffort,
25 /// Per-turn safety limits (max tool calls, max consecutive failures, etc.).
26 pub safety: SafetyConfig,
27}
28
29/// A built Agent instance.
30///
31/// Wraps [`AgentRuntime`] with common operations behind a simpler API.
32///
33/// ## Example
34///
35/// ```ignore
36/// let agent = PhiAgent::build(builder, config)?;
37/// let session = agent.create_session().await;
38/// agent.run_turn(session, "Hello!", |event| renderer.render(event)).await?;
39/// ```
40#[derive(Clone)]
41pub struct PhiAgent {
42 runtime: AgentRuntime,
43 /// The configuration this agent was built with.
44 pub config: PhiAgentConfig,
45}
46
47impl PhiAgent {
48 /// Create a pre-configured AgentBuilder.
49 ///
50 /// Equivalent to `base_agent_builder(llm_client).system_prompt(system_prompt)`,
51 /// after which you register tools, middleware, and approval handlers,
52 /// then call `Self::build`.
53 pub fn builder(llm_client: Arc<dyn agent_base::LlmClient>, system_prompt: String) -> AgentBuilder {
54 base_agent_builder(llm_client).system_prompt(system_prompt)
55 }
56
57 /// Build from an AgentBuilder.
58 pub fn build(builder: AgentBuilder, config: PhiAgentConfig) -> Result<Self> {
59 let runtime = builder.build()?;
60 Ok(Self { runtime, config })
61 }
62
63 /// Create an agent session.
64 pub async fn create_session(&self) -> SessionId {
65 self.runtime.create_session().await
66 }
67
68 /// Execute one turn.
69 pub async fn run_turn<F>(&self, session_id: SessionId, query: &str, on_event: F) -> AgentResult<RunOutcome>
70 where
71 F: FnMut(RuntimeEvent) -> AgentResult<()> + Send,
72 {
73 self.runtime.run_turn(session_id, query, on_event).await
74 }
75
76 /// Cancel the currently executing turn.
77 pub fn cancel(&self) {
78 self.runtime.cancel();
79 }
80
81 /// Check whether the agent has been cancelled.
82 pub fn is_cancelled(&self) -> bool {
83 self.runtime.is_cancelled()
84 }
85
86 /// Set the reasoning effort.
87 pub async fn set_reasoning_effort(&self, effort: ReasoningEffort) {
88 self.runtime.set_reasoning_effort(effort).await;
89 }
90
91 /// Access the underlying runtime (for advanced use like hook registration).
92 pub fn runtime(&self) -> &AgentRuntime {
93 &self.runtime
94 }
95}