Skip to main content

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    /// React-loop iteration cap for a single run (one user input).
28    /// `None` means use the builder default (200 in [`base_agent_builder`]).
29    pub max_turns: Option<u32>,
30}
31
32/// A built Agent instance.
33///
34/// Wraps [`AgentRuntime`] with common operations behind a simpler API.
35///
36/// ## Example
37///
38/// ```ignore
39/// let agent = PhiAgent::build(builder, config)?;
40/// let session = agent.create_session().await;
41/// agent.run_turn(session, "Hello!", |event| renderer.render(event)).await?;
42/// ```
43#[derive(Clone)]
44pub struct PhiAgent {
45    runtime: AgentRuntime,
46    /// The configuration this agent was built with.
47    pub config: PhiAgentConfig,
48}
49
50impl PhiAgent {
51    /// Create a pre-configured AgentBuilder.
52    ///
53    /// Equivalent to `base_agent_builder(llm_client).system_prompt(system_prompt)`,
54    /// after which you register tools, middleware, and approval handlers,
55    /// then call `Self::build`.
56    pub fn builder(llm_client: Arc<dyn agent_base::LlmClient>, system_prompt: String) -> AgentBuilder {
57        base_agent_builder(llm_client).system_prompt(system_prompt)
58    }
59
60    /// Build from an AgentBuilder.
61    pub fn build(builder: AgentBuilder, config: PhiAgentConfig) -> Result<Self> {
62        let runtime = builder.build()?;
63        Ok(Self { runtime, config })
64    }
65
66    /// Create an agent session.
67    pub async fn create_session(&self) -> SessionId {
68        self.runtime.create_session().await
69    }
70
71    /// Execute one turn.
72    pub async fn run_turn<F>(&self, session_id: SessionId, query: &str, on_event: F) -> AgentResult<RunOutcome>
73    where
74        F: FnMut(RuntimeEvent) -> AgentResult<()> + Send,
75    {
76        self.runtime.run_turn(session_id, query, on_event).await
77    }
78
79    /// Cancel the currently executing turn.
80    pub fn cancel(&self) {
81        self.runtime.cancel();
82    }
83
84    /// Check whether the agent has been cancelled.
85    pub fn is_cancelled(&self) -> bool {
86        self.runtime.is_cancelled()
87    }
88
89    /// Set the reasoning effort.
90    pub async fn set_reasoning_effort(&self, effort: ReasoningEffort) {
91        self.runtime.set_reasoning_effort(effort).await;
92    }
93
94    /// Access the underlying runtime (for advanced use like hook registration).
95    pub fn runtime(&self) -> &AgentRuntime {
96        &self.runtime
97    }
98
99    /// List all registered tools with their metadata, sorted by name.
100    pub async fn list_tools(&self) -> Vec<agent_base::ToolMetadata> {
101        let tools = self.runtime.tools_mut();
102        let registry = tools.read().await;
103        registry.metadatas()
104    }
105}