1extern crate self as llm_toolkit;
12
13pub extern crate tracing;
16
17pub extern crate minijinja;
19
20#[cfg(feature = "derive")]
25pub use llm_toolkit_macros::ToPrompt;
26
27#[cfg(feature = "derive")]
31pub use llm_toolkit_macros::ToPromptSet;
32
33#[cfg(feature = "derive")]
37pub use llm_toolkit_macros::ToPromptFor;
38
39#[cfg(feature = "derive")]
43pub use llm_toolkit_macros::examples_section;
44
45#[cfg(feature = "derive")]
49pub use llm_toolkit_macros::define_intent;
50
51#[cfg(feature = "agent")]
73pub use llm_toolkit_macros::Agent;
74
75#[cfg(feature = "agent")]
79pub use llm_toolkit_macros::agent;
80
81#[cfg(feature = "agent")]
105pub use llm_toolkit_macros::{TypeMarker, type_marker};
106
107pub mod attachment;
108pub mod extract;
109pub mod intent;
110pub mod multimodal;
111pub mod prompt;
112pub mod retrieval;
113
114#[cfg(feature = "agent")]
115pub mod observability;
116
117#[cfg(feature = "agent")]
118pub mod agent;
119
120#[cfg(feature = "agent")]
121pub mod orchestrator;
122
123pub use attachment::{Attachment, AttachmentSchema, ToAttachments};
124pub use extract::{FlexibleExtractor, MarkdownCodeBlockExtractor};
125#[cfg(feature = "agent")]
126pub use intent::expandable::{
127 Expandable, ReActConfig, ReActError, ReActResult, RegistryError, Selectable, SelectionRegistry,
128 react_loop, simple_tag_selector,
129};
130pub use intent::frame::IntentFrame;
131#[allow(deprecated)]
132pub use intent::{IntentError, IntentExtractor, PromptBasedExtractor};
133pub use multimodal::ImageData;
134pub use prompt::{PromptPart, PromptSetError, ToPrompt, ToPromptFor, ToPromptSet};
135pub use retrieval::Document;
136
137#[cfg(feature = "agent")]
138pub use agent::{Agent, AgentError};
139
140#[cfg(feature = "agent")]
141pub use agent::persona::{ContextConfig, Persona, PersonaAgent};
142
143#[cfg(feature = "agent")]
144pub use agent::retrieval::RetrievalAwareAgent;
145
146#[cfg(feature = "agent")]
147pub use orchestrator::{
148 BlueprintWorkflow, Orchestrator, OrchestratorError, StrategyMap, TypeMarker,
149};
150
151use extract::ParseError;
152
153pub fn extract_json(text: &str) -> Result<String, ParseError> {
166 if let Ok(content) = extract_markdown_block_with_lang(text, "json") {
168 return Ok(content);
169 }
170
171 if let Ok(content) = extract_markdown_block(text) {
173 let extractor = FlexibleExtractor::new();
175 if let Ok(json) = extractor.extract(&content) {
176 return Ok(json);
177 }
178 }
179
180 let extractor = FlexibleExtractor::new();
182 extractor.extract(text)
183}
184
185pub fn extract_markdown_block(text: &str) -> Result<String, ParseError> {
195 let extractor = MarkdownCodeBlockExtractor::new();
196 extractor.extract(text)
197}
198
199pub fn extract_markdown_block_with_lang(text: &str, lang: &str) -> Result<String, ParseError> {
214 let extractor = MarkdownCodeBlockExtractor::with_language(lang.to_string());
215 extractor.extract(text)
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn test_json_extraction() {
224 let input = "Some text before {\"key\": \"value\"} and after.";
225 assert_eq!(extract_json(input).unwrap(), "{\"key\": \"value\"}");
226 }
227
228 #[test]
229 fn test_standard_extraction_from_tagged_content() {
230 let text = "<answer>{\"type\": \"success\"}</answer>";
231 let result = extract_json(text);
232 assert!(result.is_ok());
233 assert_eq!(result.unwrap(), "{\"type\": \"success\"}");
234 }
235
236 #[test]
237 fn test_markdown_extraction() {
238 let text1 = "Here is some code:\n```\nlet x = 42;\n```\nAnd some text after.";
240 let result1 = extract_markdown_block(text1);
241 assert!(result1.is_ok());
242 assert_eq!(result1.unwrap(), "let x = 42;");
243
244 let text2 = "Here's Rust code:\n```rust\nfn main() {
246 println!(\"Hello\");
247}
248```";
249 let result2 = extract_markdown_block_with_lang(text2, "rust");
250 assert!(result2.is_ok());
251 assert_eq!(result2.unwrap(), "fn main() {\n println!(\"Hello\");\n}");
252
253 let text3 = r#"\nFirst a JSON block:
255```json
256{"key": "value"}
257```
258
259Then a Rust block:
260```rust
261let data = vec![1, 2, 3];
262```
263"#;
264 let result3 = extract_markdown_block_with_lang(text3, "rust");
265 assert!(result3.is_ok());
266 assert_eq!(result3.unwrap(), "let data = vec![1, 2, 3];");
267
268 let text4 = "This text has no code blocks at all.";
270 let result4 = extract_markdown_block(text4);
271 assert!(result4.is_err());
272
273 let text5 = r#"\nLots of text before...
275
276
277 ```python
278def hello():
279 print("world")
280 return True
281 ```
282
283
284And more text after with various spacing.
285"#;
286 let result5 = extract_markdown_block_with_lang(text5, "python");
287 assert!(result5.is_ok());
288 assert_eq!(
289 result5.unwrap(),
290 "def hello():\n print(\"world\")\n return True"
291 );
292 }
293
294 #[test]
295 fn test_extract_json_from_json_markdown_block() {
296 let text = r#"Here's the response:
298```json
299{"status": "success", "count": 42}
300```
301That's the data you requested."#;
302 let result = extract_json(text);
303 assert!(result.is_ok());
304 assert_eq!(result.unwrap(), r#"{"status": "success", "count": 42}"#);
305 }
306
307 #[test]
308 fn test_extract_json_from_generic_markdown_block() {
309 let text = r#"The output is:
311```
312{"result": "ok", "value": 123}
313```
314End of output."#;
315 let result = extract_json(text);
316 assert!(result.is_ok());
317 assert_eq!(result.unwrap(), r#"{"result": "ok", "value": 123}"#);
318 }
319
320 #[test]
321 fn test_extract_json_priority_json_block_over_inline() {
322 let text = r#"Some inline {"inline": "data"} here.
324```json
325{"block": "data"}
326```
327More text."#;
328 let result = extract_json(text);
329 assert!(result.is_ok());
330 assert_eq!(result.unwrap(), r#"{"block": "data"}"#);
331 }
332
333 #[test]
334 fn test_extract_json_priority_json_block_over_generic_block() {
335 let text = r#"First a generic block:
337```
338{"generic": "block"}
339```
340
341Then a JSON block:
342```json
343{"json": "block"}
344```"#;
345 let result = extract_json(text);
346 assert!(result.is_ok());
347 assert_eq!(result.unwrap(), r#"{"json": "block"}"#);
348 }
349
350 #[test]
351 fn test_extract_json_fallback_from_non_json_markdown_block() {
352 let text = r#"Here's some code:
354```
355This is not JSON at all
356```
357But this is JSON: {"fallback": "value"}"#;
358 let result = extract_json(text);
359 assert!(result.is_ok());
360 assert_eq!(result.unwrap(), r#"{"fallback": "value"}"#);
361 }
362
363 #[test]
364 fn test_extract_json_from_rust_block_fallback() {
365 let text = r#"```rust
367let x = 42;
368```
369The result is {"data": "inline"}"#;
370 let result = extract_json(text);
371 assert!(result.is_ok());
372 assert_eq!(result.unwrap(), r#"{"data": "inline"}"#);
373 }
374
375 #[test]
376 fn test_extract_json_multiline_in_markdown_block() {
377 let text = r#"Response:
379```json
380{
381 "name": "test",
382 "values": [1, 2, 3],
383 "nested": {
384 "key": "value"
385 }
386}
387```"#;
388 let result = extract_json(text);
389 assert!(result.is_ok());
390 let json = result.unwrap();
391 assert!(json.contains("\"name\": \"test\""));
393 assert!(json.contains("\"values\": [1, 2, 3]"));
394 assert!(json.contains("\"nested\""));
395 }
396}