Skip to main content

funera_orchestrate/
lib.rs

1//! # funera-orchestrate
2//!
3//! **Easy-to-use orchestration layer for [funera-core].**
4//!
5//! This crate provides a high-level agent API (`Agent`) and a runtime container
6//! (`AgentRuntime`) that together let you integrate funera's LLM agent runtime
7//! into your own projects with minimal boilerplate.
8//!
9//! ## Features
10//!
11//! | Feature | Default | Description |
12//! |---------|---------|-------------|
13//! | `deepseek` | ✅ | DeepSeek provider |
14//! | `openai` | ❌ | OpenAI provider |
15//! | `tool` | ✅ | Tool system (trait, registry, executor) |
16//! | `funera-builtin-tools` | ❌ | Built-in tools (Read, Write, Edit, Shell) |
17//! | `security` | ❌ | Tool policy enforcement |
18//! | `middleware` | ❌ | Event interception pipeline (Inspector + Mutator) |
19//! | `skill` | ❌ | Skill loading and prompt injection |
20//! | `sandbox` | ❌ | Kernel-level subprocess isolation |
21//!
22//! ---
23//!
24//! ## Quick Start
25//!
26//! ```rust,no_run
27//! use funera_orchestrate::{Agent, AgentRuntime, DeepSeekProvider};
28//!
29//! #[tokio::main]
30//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
31//!     let runtime = AgentRuntime::<DeepSeekProvider>::builder()
32//!         .api_key(std::env::var("DEEPSEEK_API_KEY")?)
33//!         .model("deepseek-v4-flash")
34//!         .build()?;
35//!
36//!     let agent = Agent::builder()
37//!         .system_prompt("You are a helpful assistant.")
38//!         .build();
39//!
40//!     let resp = agent.fire("Hello!", &runtime).await?;
41//!     println!("{}", resp.content);
42//!     Ok(())
43//! }
44//! ```
45//!
46//! ## Core Concepts
47//!
48//! | Concept | Type | Description |
49//! |---------|------|-------------|
50//! | **Runtime** | [`AgentRuntime`] | Shared infrastructure + conversation session |
51//! | **Agent** | [`Agent`] | Behavioural config (system prompt, callbacks) |
52//! | **One-shot** | [`Agent::fire`] | Temporary session, discarded after call |
53//! | **Multi-turn** | [`Agent::send`] | Persistent session across calls |
54//! | **Streaming** | [`fire_stream`](Agent::fire_stream) / [`send_stream`](Agent::send_stream) | Token-by-token streaming |
55//! | **Approval** | [`ApprovalHandle`] | Lightweight cloneable handle for tool-call approval |
56//!
57//! ## Examples
58//!
59//! ### One-shot query with stream
60//!
61//! ```rust,no_run
62//! # use funera_orchestrate::{Agent, AgentEvent, AgentRuntime, DeepSeekProvider};
63//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
64//! let runtime = AgentRuntime::<DeepSeekProvider>::builder()
65//!     .api_key(std::env::var("DEEPSEEK_API_KEY")?)
66//!     .model("deepseek-v4-flash")
67//!     .build()?;
68//!
69//! let agent = Agent::builder()
70//!     .on_token(|t| print!("{t}"))
71//!     .build();
72//!
73//! let mut rx = agent.fire_stream("Explain Rust's ownership model", &runtime).await?;
74//! while let Some(event) = rx.recv().await {
75//!     if let AgentEvent::Text(t) = event {
76//!         print!("{t}");
77//!     }
78//! }
79//! # Ok(())
80//! # }
81//! ```
82//!
83//! ### Multi-turn conversation with callbacks
84//!
85//! ```rust,no_run
86//! # use funera_orchestrate::{Agent, AgentRuntime, DeepSeekProvider};
87//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
88//! let runtime = AgentRuntime::<DeepSeekProvider>::builder()
89//!     .api_key(std::env::var("DEEPSEEK_API_KEY")?)
90//!     .model("deepseek-v4-flash")
91//!     .build()?;
92//!
93//! let agent = Agent::builder()
94//!     .system_prompt("You are helpful.")
95//!     .on_tool_call(|name, _| eprintln!("[tool] {name}"))
96//!     .on_turn_start(|| eprintln!("--- turn ---"))
97//!     .build();
98//!
99//! let handle = agent.send("Hi, I'm Alice.", runtime).await?;
100//! let (runtime, _resp) = handle.await?;
101//! let handle = agent.send("What's my name?", runtime).await?;
102//! let (_runtime, _resp) = handle.await?;
103//! # Ok(())
104//! # }
105//! ```
106//!
107//! ### Switching models on the same provider
108//!
109//! ```rust,no_run
110//! # use funera_orchestrate::{Agent, AgentRuntime, DeepSeekProvider};
111//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
112//! let fast = AgentRuntime::<DeepSeekProvider>::builder()
113//!     .api_key(std::env::var("DEEPSEEK_API_KEY")?)
114//!     .model("deepseek-v4-flash")
115//!     .build()?;
116//!
117//! let powerful = AgentRuntime::<DeepSeekProvider>::builder()
118//!     .api_key(std::env::var("DEEPSEEK_API_KEY")?)
119//!     .model("deepseek-r1")
120//!     .build()?;
121//!
122//! let agent = Agent::builder().build();
123//!
124//! let (fast, _) = agent.send("Hello", fast).await?.await?;              // fast model
125//! agent.fire("What is Rust?", &powerful).await?;                        // powerful model (temp)
126//! let (_fast, _) = agent.send("Tell me more", fast).await?.await?;       // back to fast
127//! # Ok(())
128//! # }
129//! ```
130//!
131//! ### Security with tool-call approval
132//!
133//! Requires the `security` feature (and optionally `funera-builtin-tools`, `sandbox`).
134//! Use [`ApprovalHandle`] to approve or reject tool calls from a spawned task
135//! while the agent is running — works with `fire()`, `send()`, and `send_stream()`.
136//!
137//! ```rust,no_run
138//! # use funera_orchestrate::{Agent, AgentRuntime, ApprovalHandle, DeepSeekProvider};
139//! # use std::time::Duration;
140//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
141//! let (approval_tx, mut approval_rx) = tokio::sync::mpsc::unbounded_channel();
142//!
143//! let runtime = AgentRuntime::<DeepSeekProvider>::builder()
144//!     .api_key(std::env::var("DEEPSEEK_API_KEY")?)
145//!     .model("deepseek-v4-flash")
146//!     .on_approval_required(move |call_id, tool, reason| {
147//!         eprintln!("[{tool}] needs approval: {reason}");
148//!         let _ = approval_tx.send(call_id.to_string());
149//!     })
150//!     .with_approval_timeout(Duration::from_secs(30))
151//!     .build()?;
152//!
153//! // Clone the ApprovalHandle *before* send() consumes the runtime.
154//! let approver: ApprovalHandle = runtime.approval_handle();
155//! tokio::spawn(async move {
156//!     while let Some(call_id) = approval_rx.recv().await {
157//!         approver.approve_tool_call(&call_id, true).await.ok();
158//!     }
159//! });
160//!
161//! let agent = Agent::builder().build();
162//! let (_runtime, resp) = agent.send("do something", runtime).await?.await?;
163//! println!("{}", resp.content);
164//! # Ok(())
165//! # }
166//! ```
167//!
168//! ## Module Structure
169//!
170//! - [`runtime`] — [`AgentRuntimeBuilder`] and [`AgentRuntime`]
171//! - [`agent`] — [`AgentBuilder`] and [`Agent`]
172//! - [`send_handle`] — [`SendHandle`], [`SendStreamHandle`], [`FireStreamHandle`], [`ApprovalHandle`]
173//! - [`dispatcher`] — Event bus subscription and callback dispatch
174//! - [`event`] — [`AgentEvent`] enum
175//! - [`response`] — [`ChatResponse`] and [`ToolCallInfo`]
176//! - [`error`] — [`OrchestrateError`]
177
178pub mod agent;
179pub mod dispatcher;
180pub mod error;
181pub mod event;
182pub mod response;
183pub mod runtime;
184pub mod send_handle;
185
186#[cfg(feature = "middleware")]
187pub mod middleware_bundle;
188
189pub use agent::{Agent, AgentBuilder};
190pub use dispatcher::CallbackRegistry;
191pub use error::OrchestrateError;
192pub use event::{AgentEvent, RawAgentEvent};
193#[cfg(feature = "deepseek")]
194pub use funera_core::provider::deepseek::DeepSeekProvider;
195#[cfg(feature = "openai")]
196pub use funera_core::provider::openai::OpenAIProvider;
197pub use response::{ChatResponse, ToolCallInfo};
198pub use runtime::{Acquired, AgentRuntime, AgentRuntimeBuilder, Idle};
199#[cfg(all(feature = "tool", feature = "security"))]
200pub use send_handle::ApprovalHandle;
201pub use send_handle::{FireStreamHandle, SendHandle, SendStreamHandle};
202
203// Re-export security policy types for convenience.
204#[cfg(feature = "security")]
205pub use funera_core::security::audit::{AuditBus, AuditEvent};
206#[cfg(feature = "security")]
207pub use funera_core::security::policy::{PolicyError, ShellPolicy, ToolPolicy};
208
209// Re-export core event types for direct access
210pub use funera_core::event_bus::env_state_bus::EnvStateEvent;
211pub use funera_core::event_bus::react_bus::{
212    ReactEvent, ToolCallErrorInfo, ToolCallRequest, ToolCallResponse,
213};
214pub use funera_core::event_bus::token_bus::TokenEvent;
215
216/// Middleware 相关的类型和 trait。
217///
218/// 该模块提供了 [`InspectorMiddleware`]、[`MutatorMiddleware`] 等核心 trait,
219/// 以及 [`MiddlewareChain`]、[`MiddlewareBundle`] 等构建管道所需的类型。
220///
221/// ## Feature gate
222///
223/// 需要启用 `middleware` feature:
224///
225/// ```toml
226/// funera-orchestrate = { features = ["middleware"] }
227/// ```
228#[cfg(feature = "middleware")]
229pub mod middleware {
230    pub use crate::middleware_bundle::MiddlewareBundle;
231    pub use funera_core::middleware::*;
232}