1extern crate self as llm_toolkit;
12
13#[cfg(feature = "agent")]
16pub extern crate tracing;
17
18pub extern crate minijinja;
20
21#[cfg(feature = "derive")]
23pub extern crate quick_xml;
24
25#[cfg(feature = "derive")]
30pub use llm_toolkit_macros::ToPrompt;
31
32#[cfg(feature = "derive")]
36pub use llm_toolkit_macros::ToPromptSet;
37
38#[cfg(feature = "derive")]
42pub use llm_toolkit_macros::ToPromptFor;
43
44#[cfg(feature = "derive")]
48pub use llm_toolkit_macros::examples_section;
49
50#[cfg(feature = "derive")]
54pub use llm_toolkit_macros::define_intent;
55
56#[cfg(feature = "agent")]
78pub use llm_toolkit_macros::Agent;
79
80#[cfg(feature = "agent")]
84pub use llm_toolkit_macros::agent;
85
86#[cfg(feature = "agent")]
110pub use llm_toolkit_macros::{TypeMarker, type_marker};
111
112pub mod attachment;
113pub mod context;
114pub mod extract;
115pub mod intent;
116pub mod models;
117pub mod multimodal;
118pub mod prompt;
119pub mod retrieval;
120
121#[cfg(feature = "agent")]
122pub mod observability;
123
124#[cfg(feature = "agent")]
125pub mod agent;
126
127#[cfg(feature = "agent")]
128pub mod orchestrator;
129
130pub use attachment::{Attachment, AttachmentSchema, ToAttachments};
131pub use context::{ContextProfile, Priority, TaskHealth};
132pub use extract::{FlexibleExtractor, MarkdownCodeBlockExtractor};
133#[cfg(feature = "agent")]
134pub use intent::expandable::{
135 Expandable, ReActConfig, ReActError, ReActResult, RegistryError, Selectable, SelectionRegistry,
136 react_loop, simple_tag_selector,
137};
138pub use intent::frame::IntentFrame;
139#[allow(deprecated)]
140pub use intent::{IntentError, IntentExtractor, PromptBasedExtractor};
141pub use models::{ClaudeModel, GeminiModel, Model, ModelError, OpenAIModel};
142pub use multimodal::ImageData;
143pub use prompt::{PromptPart, PromptSetError, ToPrompt, ToPromptFor, ToPromptSet};
144pub use retrieval::Document;
145
146#[cfg(feature = "agent")]
147pub use agent::{Agent, AgentError, AnyAgent, ToExpertise};
148
149#[cfg(feature = "agent")]
150pub use agent::persona::{ContextConfig, Persona, PersonaAgent};
151
152#[cfg(feature = "agent")]
153pub use agent::retrieval::RetrievalAwareAgent;
154
155#[cfg(feature = "agent")]
156pub use agent::expertise::{
157 Anchor, ContextualPrompt, Expertise, KnowledgeFragment, RenderContext, WeightedFragment,
158};
159
160#[cfg(feature = "agent")]
161pub use orchestrator::{
162 BlueprintWorkflow, Orchestrator, OrchestratorError, StrategyMap, TypeMarker,
163};
164
165use extract::ParseError;
166
167pub fn extract_json(text: &str) -> Result<String, ParseError> {
180 if let Ok(content) = extract_markdown_block_with_lang(text, "json") {
182 return Ok(content);
183 }
184
185 if let Ok(content) = extract_markdown_block(text) {
187 let extractor = FlexibleExtractor::new();
189 if let Ok(json) = extractor.extract(&content) {
190 return Ok(json);
191 }
192 }
193
194 let extractor = FlexibleExtractor::new();
196 extractor.extract(text)
197}
198
199pub fn extract_markdown_block(text: &str) -> Result<String, ParseError> {
209 let extractor = MarkdownCodeBlockExtractor::new();
210 extractor.extract(text)
211}
212
213pub fn extract_markdown_block_with_lang(text: &str, lang: &str) -> Result<String, ParseError> {
228 let extractor = MarkdownCodeBlockExtractor::with_language(lang.to_string());
229 extractor.extract(text)
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235
236 #[test]
237 fn test_json_extraction() {
238 let input = "Some text before {\"key\": \"value\"} and after.";
239 assert_eq!(extract_json(input).unwrap(), "{\"key\": \"value\"}");
240 }
241
242 #[test]
243 fn test_standard_extraction_from_tagged_content() {
244 let text = "<answer>{\"type\": \"success\"}</answer>";
245 let result = extract_json(text);
246 assert!(result.is_ok());
247 assert_eq!(result.unwrap(), "{\"type\": \"success\"}");
248 }
249
250 #[test]
251 fn test_markdown_extraction() {
252 let text1 = "Here is some code:\n```\nlet x = 42;\n```\nAnd some text after.";
254 let result1 = extract_markdown_block(text1);
255 assert!(result1.is_ok());
256 assert_eq!(result1.unwrap(), "let x = 42;");
257
258 let text2 = "Here's Rust code:\n```rust\nfn main() {
260 println!(\"Hello\");
261}
262```";
263 let result2 = extract_markdown_block_with_lang(text2, "rust");
264 assert!(result2.is_ok());
265 assert_eq!(result2.unwrap(), "fn main() {\n println!(\"Hello\");\n}");
266
267 let text3 = r#"\nFirst a JSON block:
269```json
270{"key": "value"}
271```
272
273Then a Rust block:
274```rust
275let data = vec![1, 2, 3];
276```
277"#;
278 let result3 = extract_markdown_block_with_lang(text3, "rust");
279 assert!(result3.is_ok());
280 assert_eq!(result3.unwrap(), "let data = vec![1, 2, 3];");
281
282 let text4 = "This text has no code blocks at all.";
284 let result4 = extract_markdown_block(text4);
285 assert!(result4.is_err());
286
287 let text5 = r#"\nLots of text before...
289
290
291 ```python
292def hello():
293 print("world")
294 return True
295 ```
296
297
298And more text after with various spacing.
299"#;
300 let result5 = extract_markdown_block_with_lang(text5, "python");
301 assert!(result5.is_ok());
302 assert_eq!(
303 result5.unwrap(),
304 "def hello():\n print(\"world\")\n return True"
305 );
306 }
307
308 #[test]
309 fn test_extract_json_from_json_markdown_block() {
310 let text = r#"Here's the response:
312```json
313{"status": "success", "count": 42}
314```
315That's the data you requested."#;
316 let result = extract_json(text);
317 assert!(result.is_ok());
318 assert_eq!(result.unwrap(), r#"{"status": "success", "count": 42}"#);
319 }
320
321 #[test]
322 fn test_extract_json_from_generic_markdown_block() {
323 let text = r#"The output is:
325```
326{"result": "ok", "value": 123}
327```
328End of output."#;
329 let result = extract_json(text);
330 assert!(result.is_ok());
331 assert_eq!(result.unwrap(), r#"{"result": "ok", "value": 123}"#);
332 }
333
334 #[test]
335 fn test_extract_json_priority_json_block_over_inline() {
336 let text = r#"Some inline {"inline": "data"} here.
338```json
339{"block": "data"}
340```
341More text."#;
342 let result = extract_json(text);
343 assert!(result.is_ok());
344 assert_eq!(result.unwrap(), r#"{"block": "data"}"#);
345 }
346
347 #[test]
348 fn test_extract_json_priority_json_block_over_generic_block() {
349 let text = r#"First a generic block:
351```
352{"generic": "block"}
353```
354
355Then a JSON block:
356```json
357{"json": "block"}
358```"#;
359 let result = extract_json(text);
360 assert!(result.is_ok());
361 assert_eq!(result.unwrap(), r#"{"json": "block"}"#);
362 }
363
364 #[test]
365 fn test_extract_json_fallback_from_non_json_markdown_block() {
366 let text = r#"Here's some code:
368```
369This is not JSON at all
370```
371But this is JSON: {"fallback": "value"}"#;
372 let result = extract_json(text);
373 assert!(result.is_ok());
374 assert_eq!(result.unwrap(), r#"{"fallback": "value"}"#);
375 }
376
377 #[test]
378 fn test_extract_json_from_rust_block_fallback() {
379 let text = r#"```rust
381let x = 42;
382```
383The result is {"data": "inline"}"#;
384 let result = extract_json(text);
385 assert!(result.is_ok());
386 assert_eq!(result.unwrap(), r#"{"data": "inline"}"#);
387 }
388
389 #[test]
390 fn test_extract_json_multiline_in_markdown_block() {
391 let text = r#"Response:
393```json
394{
395 "name": "test",
396 "values": [1, 2, 3],
397 "nested": {
398 "key": "value"
399 }
400}
401```"#;
402 let result = extract_json(text);
403 assert!(result.is_ok());
404 let json = result.unwrap();
405 assert!(json.contains("\"name\": \"test\""));
407 assert!(json.contains("\"values\": [1, 2, 3]"));
408 assert!(json.contains("\"nested\""));
409 }
410}