pub struct PhiAgent {
pub config: PhiAgentConfig,
/* private fields */
}Expand description
A built Agent instance.
Wraps AgentRuntime with common operations behind a simpler API.
§Example
let agent = PhiAgent::build(builder, config)?;
let session = agent.create_session().await;
agent.run_turn(session, "Hello!", |event| renderer.render(event)).await?;Fields§
§config: PhiAgentConfigThe configuration this agent was built with.
Implementations§
Source§impl PhiAgent
impl PhiAgent
Sourcepub fn builder(
llm_client: Arc<dyn LlmProvider>,
system_prompt: String,
) -> AgentBuilder
pub fn builder( llm_client: Arc<dyn LlmProvider>, system_prompt: String, ) -> AgentBuilder
Create a pre-configured AgentBuilder.
Equivalent to base_agent_builder(llm_client).system_prompt(system_prompt),
after which you register tools, middleware, and approval handlers,
then call Self::build.
§Example
use phi_agent::PhiAgent;
use phi_agent::build_system_prompt;
use std::sync::Arc;
// Create your LLM provider (e.g., via llm_unified::create_provider)
let llm_client: Arc<dyn agent_base::llm_trait::LlmProvider> = todo!();
let builder = PhiAgent::builder(llm_client, build_system_prompt());Sourcepub fn build(builder: AgentBuilder, config: PhiAgentConfig) -> AgentResult<Self>
pub fn build(builder: AgentBuilder, config: PhiAgentConfig) -> AgentResult<Self>
Build from an AgentBuilder.
§Example
use phi_agent::{PhiAgent, PhiAgentConfig, base_agent_builder, build_system_prompt};
use std::sync::Arc;
// Create your LLM provider (e.g., via llm_unified::create_provider)
let llm_client: Arc<dyn agent_base::llm_trait::LlmProvider> = todo!();
let builder = base_agent_builder(llm_client).system_prompt(build_system_prompt());
let config = PhiAgentConfig {
model: "gpt-4o".into(),
enable_thinking: true,
..Default::default()
};
let agent = PhiAgent::build(builder, config)?;Sourcepub async fn create_session(&self) -> SessionId
pub async fn create_session(&self) -> SessionId
Sourcepub async fn run_turn<F>(
&self,
session_id: SessionId,
query: &str,
on_event: F,
) -> AgentResult<RunOutcome>
pub async fn run_turn<F>( &self, session_id: SessionId, query: &str, on_event: F, ) -> AgentResult<RunOutcome>
Sourcepub async fn run_turn_ephemeral_input<F>(
&self,
session_id: SessionId,
query: &str,
on_event: F,
) -> AgentResult<RunOutcome>
pub async fn run_turn_ephemeral_input<F>( &self, session_id: SessionId, query: &str, on_event: F, ) -> AgentResult<RunOutcome>
Like run_turn, but the query is pushed as an ephemeral user
message: the LLM sees it for this turn only, then turn-end cleanup
removes it from memory and persistence. Used for skill-body
injection — history keeps only the original command.
Sourcepub async fn system_prompt(&self) -> Option<String>
pub async fn system_prompt(&self) -> Option<String>
The pristine build-time system prompt (async — safe inside a runtime). Hosts that bake session state into the prompt (e.g. skill activation) capture this once and append to it, never recompose.
Sourcepub async fn set_system_prompt(
&self,
session_id: &SessionId,
prompt: impl Into<String>,
) -> AgentResult<()>
pub async fn set_system_prompt( &self, session_id: &SessionId, prompt: impl Into<String>, ) -> AgentResult<()>
Replace the session’s system prompt (the first non-ephemeral System message). phimint uses this to re-bake the prompt when a session-scope skill is activated — the body joins an “Active Skills” section.
Sourcepub fn cancel(&self)
pub fn cancel(&self)
Cancel the currently executing turn.
§Example
let agent_clone = agent.clone();
let session = agent.create_session().await;
// Run the turn in a separate task
let handle = tokio::spawn(async move {
agent_clone.run_turn(session, "count to 100", |_| Ok(())).await
});
// Cancel after a short delay
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
agent.cancel();Sourcepub fn is_cancelled(&self) -> bool
pub fn is_cancelled(&self) -> bool
Sourcepub async fn set_reasoning_effort(&self, effort: ReasoningEffort)
pub async fn set_reasoning_effort(&self, effort: ReasoningEffort)
Sourcepub fn runtime(&self) -> &AgentRuntime
pub fn runtime(&self) -> &AgentRuntime
Sourcepub async fn list_tools(&self) -> Vec<ToolMetadata>
pub async fn list_tools(&self) -> Vec<ToolMetadata>
Sourcepub async fn resume_session(
&self,
messages: Vec<ChatMessage>,
) -> AgentResult<SessionId>
pub async fn resume_session( &self, messages: Vec<ChatMessage>, ) -> AgentResult<SessionId>
Create a new session and inject historical messages for resume.
Creates a fresh session (which receives a fresh System prompt), then
replaces the chat messages with [fresh_system] + messages. The
caller must ensure messages contains no System messages — use
crate::session::load_session_messages which filters them out automatically.
§Errors
Returns an error if messages is empty / System-only, or if the
combined sequence fails validate_message_sequence (e.g. dangling
tool calls). Again, crate::session::load_session_messages sanitizes all of this.
Sourcepub async fn switch_to_session(
&self,
picked_session_dir: &Path,
base_dir: &Path,
) -> AgentResult<(SessionId, Vec<ChatMessage>, SessionContext)>
pub async fn switch_to_session( &self, picked_session_dir: &Path, base_dir: &Path, ) -> AgentResult<(SessionId, Vec<ChatMessage>, SessionContext)>
One-step session switch: resolve → load messages → resume.
Given a session directory (from the picker), resolves the session context, loads historical messages, and creates a new agent session with those messages. Returns the new session ID, the loaded messages (for transcript replay), and the resolved context.
Source§impl PhiAgent
impl PhiAgent
Sourcepub async fn attach_mcp(&self, config: McpServerConfig) -> AgentResult<()>
pub async fn attach_mcp(&self, config: McpServerConfig) -> AgentResult<()>
Dynamically attach an MCP server at runtime.
Adds the server config, connects, discovers tools, and registers them
into the agent’s ToolRegistry. Tools are registered with the
mcp.<server_name>.<tool_name> naming convention.
Returns an error if the server cannot be connected or tools cannot be discovered. On failure, the server config is rolled back (removed from the hub) so a partial entry is never left behind.
§Example
// Attach a filesystem MCP server at runtime
agent
.attach_mcp(McpServerConfig {
name: "filesystem".into(),
transport: phi_agent::McpTransport::Stdio {
command: "npx".into(),
args: vec!["-y".into(), "@modelcontextprotocol/server-filesystem".into(), "/tmp".into()],
env: Default::default(),
},
})
.await?;§Performance note
Currently calls hub.register_all() which re-registers all servers’
tools (O(total-servers)). For the common case this is fine because
re-registration is a no-op HashMap insert. A future optimization would
register only the newly attached server’s tools.
Sourcepub async fn detach_mcp(&self, name: &str)
pub async fn detach_mcp(&self, name: &str)
Dynamically detach an MCP server at runtime.
Unregisters all tools belonging to this server from the agent’s
ToolRegistry, disconnects the server, and removes its config
from the hub.
This is a no-op if the server is not attached.
§Example
// Detach the server — tools are unregistered, connection is closed
agent.detach_mcp("filesystem").await;§Concurrency note
There is a TOCTOU window between collecting tool names (read lock) and
removing them (write lock). If another thread re-attaches a server with
the same name during this window, its tools may be prematurely removed.
In practice this race is harmless: the new attach will re-register tools
on the next turn, and tool calls in flight will fail with a clear error
since hub.remove_server disconnects clients.
Sourcepub fn into_mcp_server(&self, config: McpServeConfig) -> McpServer
pub fn into_mcp_server(&self, config: McpServeConfig) -> McpServer
Convert this agent into an MCP server that external orchestrators can call.
§Example
// Expose the agent as an MCP server via stdio
let mcp_server = agent.into_mcp_server(McpServeConfig {
transport: McpServerTransport::Stdio,
name: "phi-agent".into(),
version: "1.0.0".into(),
});
// External orchestrators (LangGraph, CrewAI, etc.) can now call
// the agent's tools through the MCP protocol