1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! Tool execution engine.
//!
//! This module provides the runtime layer for executing tools that LLMs
//! invoke during generation. It builds on the foundational types from
//! [`chat`](crate::chat) ([`ToolCall`](crate::chat::ToolCall), [`ToolResult`](crate::chat::ToolResult)) and
//! [`provider`](crate::provider) ([`ToolDefinition`](crate::provider::ToolDefinition), [`JsonSchema`](crate::JsonSchema)).
//!
//! # Architecture
//!
//! ```text
//! ToolHandler — defines a single tool (schema + execute fn)
//! │
//! ToolRegistry — stores handlers by name, validates & dispatches
//! │
//! tool_loop() — automates generate → execute → feedback cycle
//! tool_loop_stream() — unified LoopEvent stream (LLM deltas + loop lifecycle)
//! ToolLoopHandle — caller-driven resumable variant (borrowed refs)
//! OwnedToolLoopHandle — caller-driven resumable variant (Arc, Send + 'static)
//! ```
//!
//! # Example
//!
//! ```rust,no_run
//! use llm_stack::tool::{ToolRegistry, tool_fn, ToolLoopConfig, tool_loop};
//! use llm_stack::{ChatParams, ChatMessage, JsonSchema, ToolDefinition};
//! use serde_json::{json, Value};
//!
//! # async fn example(provider: &dyn llm_stack::DynProvider) -> Result<(), llm_stack::LlmError> {
//! let mut registry: ToolRegistry<()> = ToolRegistry::new();
//! registry.register(tool_fn(
//! ToolDefinition {
//! name: "add".into(),
//! description: "Add two numbers".into(),
//! parameters: JsonSchema::new(json!({
//! "type": "object",
//! "properties": {
//! "a": {"type": "number"},
//! "b": {"type": "number"}
//! },
//! "required": ["a", "b"]
//! })),
//! retry: None,
//! },
//! |input: Value| async move {
//! let a = input["a"].as_f64().unwrap_or(0.0);
//! let b = input["b"].as_f64().unwrap_or(0.0);
//! Ok(format!("{}", a + b))
//! },
//! ));
//!
//! let params = ChatParams {
//! messages: vec![ChatMessage::user("What is 2 + 3?")],
//! tools: Some(registry.definitions()),
//! ..Default::default()
//! };
//!
//! let result = tool_loop(provider, ®istry, params, ToolLoopConfig::default(), &()).await?;
//! println!("Final answer: {:?}", result.response.text());
//! # Ok(())
//! # }
//! ```
//!
//! # Using Context
//!
//! Tools often need access to shared state like database connections, user identity,
//! or configuration. Use [`tool_fn_with_ctx`] to create tools that receive context:
//!
//! ```rust,no_run
//! use llm_stack::tool::{tool_fn_with_ctx, ToolRegistry, ToolError, ToolOutput, tool_loop, ToolLoopConfig, LoopContext};
//! use llm_stack::{ToolDefinition, JsonSchema, ChatParams, ChatMessage};
//! use serde_json::{json, Value};
//!
//! #[derive(Clone)]
//! struct AppState {
//! user_id: String,
//! api_key: String,
//! }
//!
//! type AppCtx = LoopContext<AppState>;
//!
//! # async fn example(provider: &dyn llm_stack::DynProvider) -> Result<(), llm_stack::LlmError> {
//! let handler = tool_fn_with_ctx(
//! ToolDefinition {
//! name: "get_user_data".into(),
//! description: "Fetch data for the current user".into(),
//! parameters: JsonSchema::new(json!({"type": "object"})),
//! retry: None,
//! },
//! |_input: Value, ctx: &AppCtx| {
//! // Clone data from context before the async block
//! let user_id = ctx.state.user_id.clone();
//! async move {
//! Ok(ToolOutput::new(format!("Data for user: {}", user_id)))
//! }
//! },
//! );
//!
//! let mut registry: ToolRegistry<AppCtx> = ToolRegistry::new();
//! registry.register(handler);
//!
//! let ctx = LoopContext::new(AppState {
//! user_id: "user123".into(),
//! api_key: "secret".into(),
//! });
//!
//! let params = ChatParams {
//! messages: vec![ChatMessage::user("Get my data")],
//! tools: Some(registry.definitions()),
//! ..Default::default()
//! };
//!
//! let result = tool_loop(provider, ®istry, params, ToolLoopConfig::default(), &ctx).await?;
//! # Ok(())
//! # }
//! ```
//!
//! **Note on lifetimes**: The closure passed to `tool_fn_with_ctx` uses higher-ranked
//! trait bounds (`for<'c> Fn(Value, &'c Ctx) -> Fut`). This means the future returned
//! by your closure must be `'static` — it cannot borrow from the context reference.
//! Clone any data you need from the context before creating the async block.
pub
// Re-export all public types
pub use ;
pub use ;
pub use ;
pub use ToolError;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use tool_loop_stream;
pub use tool_loop;
pub use ToolOutput;
pub use ;
pub use ToolRegistry;