1extern crate self as llm_toolkit;
12
13pub extern crate tracing;
16
17#[cfg(feature = "derive")]
22pub use llm_toolkit_macros::ToPrompt;
23
24#[cfg(feature = "derive")]
28pub use llm_toolkit_macros::ToPromptSet;
29
30#[cfg(feature = "derive")]
34pub use llm_toolkit_macros::ToPromptFor;
35
36#[cfg(feature = "derive")]
40pub use llm_toolkit_macros::examples_section;
41
42#[cfg(feature = "derive")]
46pub use llm_toolkit_macros::define_intent;
47
48#[cfg(feature = "agent")]
70pub use llm_toolkit_macros::Agent;
71
72#[cfg(feature = "agent")]
76pub use llm_toolkit_macros::agent;
77
78#[cfg(feature = "agent")]
102pub use llm_toolkit_macros::{TypeMarker, type_marker};
103
104pub mod attachment;
105pub mod extract;
106pub mod intent;
107pub mod multimodal;
108pub mod prompt;
109pub mod retrieval;
110
111#[cfg(feature = "agent")]
112pub mod observability;
113
114#[cfg(feature = "agent")]
115pub mod agent;
116
117#[cfg(feature = "agent")]
118pub mod orchestrator;
119
120pub use attachment::{Attachment, AttachmentSchema, ToAttachments};
121pub use extract::{FlexibleExtractor, MarkdownCodeBlockExtractor};
122pub use intent::frame::IntentFrame;
123#[allow(deprecated)]
124pub use intent::{IntentError, IntentExtractor, PromptBasedExtractor};
125pub use multimodal::ImageData;
126pub use prompt::{PromptPart, PromptSetError, ToPrompt, ToPromptFor, ToPromptSet};
127pub use retrieval::Document;
128
129#[cfg(feature = "agent")]
130pub use agent::{Agent, AgentError};
131
132#[cfg(feature = "agent")]
133pub use agent::persona::{Persona, PersonaAgent};
134
135#[cfg(feature = "agent")]
136pub use agent::retrieval::RetrievalAwareAgent;
137
138#[cfg(feature = "agent")]
139pub use orchestrator::{
140 BlueprintWorkflow, Orchestrator, OrchestratorError, StrategyMap, TypeMarker,
141};
142
143use extract::ParseError;
144
145pub fn extract_json(text: &str) -> Result<String, ParseError> {
158 if let Ok(content) = extract_markdown_block_with_lang(text, "json") {
160 return Ok(content);
161 }
162
163 if let Ok(content) = extract_markdown_block(text) {
165 let extractor = FlexibleExtractor::new();
167 if let Ok(json) = extractor.extract(&content) {
168 return Ok(json);
169 }
170 }
171
172 let extractor = FlexibleExtractor::new();
174 extractor.extract(text)
175}
176
177pub fn extract_markdown_block(text: &str) -> Result<String, ParseError> {
187 let extractor = MarkdownCodeBlockExtractor::new();
188 extractor.extract(text)
189}
190
191pub fn extract_markdown_block_with_lang(text: &str, lang: &str) -> Result<String, ParseError> {
206 let extractor = MarkdownCodeBlockExtractor::with_language(lang.to_string());
207 extractor.extract(text)
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn test_json_extraction() {
216 let input = "Some text before {\"key\": \"value\"} and after.";
217 assert_eq!(extract_json(input).unwrap(), "{\"key\": \"value\"}");
218 }
219
220 #[test]
221 fn test_standard_extraction_from_tagged_content() {
222 let text = "<answer>{\"type\": \"success\"}</answer>";
223 let result = extract_json(text);
224 assert!(result.is_ok());
225 assert_eq!(result.unwrap(), "{\"type\": \"success\"}");
226 }
227
228 #[test]
229 fn test_markdown_extraction() {
230 let text1 = "Here is some code:\n```\nlet x = 42;\n```\nAnd some text after.";
232 let result1 = extract_markdown_block(text1);
233 assert!(result1.is_ok());
234 assert_eq!(result1.unwrap(), "let x = 42;");
235
236 let text2 = "Here's Rust code:\n```rust\nfn main() {
238 println!(\"Hello\");
239}
240```";
241 let result2 = extract_markdown_block_with_lang(text2, "rust");
242 assert!(result2.is_ok());
243 assert_eq!(result2.unwrap(), "fn main() {\n println!(\"Hello\");\n}");
244
245 let text3 = r#"\nFirst a JSON block:
247```json
248{"key": "value"}
249```
250
251Then a Rust block:
252```rust
253let data = vec![1, 2, 3];
254```
255"#;
256 let result3 = extract_markdown_block_with_lang(text3, "rust");
257 assert!(result3.is_ok());
258 assert_eq!(result3.unwrap(), "let data = vec![1, 2, 3];");
259
260 let text4 = "This text has no code blocks at all.";
262 let result4 = extract_markdown_block(text4);
263 assert!(result4.is_err());
264
265 let text5 = r#"\nLots of text before...
267
268
269 ```python
270def hello():
271 print("world")
272 return True
273 ```
274
275
276And more text after with various spacing.
277"#;
278 let result5 = extract_markdown_block_with_lang(text5, "python");
279 assert!(result5.is_ok());
280 assert_eq!(
281 result5.unwrap(),
282 "def hello():\n print(\"world\")\n return True"
283 );
284 }
285
286 #[test]
287 fn test_extract_json_from_json_markdown_block() {
288 let text = r#"Here's the response:
290```json
291{"status": "success", "count": 42}
292```
293That's the data you requested."#;
294 let result = extract_json(text);
295 assert!(result.is_ok());
296 assert_eq!(result.unwrap(), r#"{"status": "success", "count": 42}"#);
297 }
298
299 #[test]
300 fn test_extract_json_from_generic_markdown_block() {
301 let text = r#"The output is:
303```
304{"result": "ok", "value": 123}
305```
306End of output."#;
307 let result = extract_json(text);
308 assert!(result.is_ok());
309 assert_eq!(result.unwrap(), r#"{"result": "ok", "value": 123}"#);
310 }
311
312 #[test]
313 fn test_extract_json_priority_json_block_over_inline() {
314 let text = r#"Some inline {"inline": "data"} here.
316```json
317{"block": "data"}
318```
319More text."#;
320 let result = extract_json(text);
321 assert!(result.is_ok());
322 assert_eq!(result.unwrap(), r#"{"block": "data"}"#);
323 }
324
325 #[test]
326 fn test_extract_json_priority_json_block_over_generic_block() {
327 let text = r#"First a generic block:
329```
330{"generic": "block"}
331```
332
333Then a JSON block:
334```json
335{"json": "block"}
336```"#;
337 let result = extract_json(text);
338 assert!(result.is_ok());
339 assert_eq!(result.unwrap(), r#"{"json": "block"}"#);
340 }
341
342 #[test]
343 fn test_extract_json_fallback_from_non_json_markdown_block() {
344 let text = r#"Here's some code:
346```
347This is not JSON at all
348```
349But this is JSON: {"fallback": "value"}"#;
350 let result = extract_json(text);
351 assert!(result.is_ok());
352 assert_eq!(result.unwrap(), r#"{"fallback": "value"}"#);
353 }
354
355 #[test]
356 fn test_extract_json_from_rust_block_fallback() {
357 let text = r#"```rust
359let x = 42;
360```
361The result is {"data": "inline"}"#;
362 let result = extract_json(text);
363 assert!(result.is_ok());
364 assert_eq!(result.unwrap(), r#"{"data": "inline"}"#);
365 }
366
367 #[test]
368 fn test_extract_json_multiline_in_markdown_block() {
369 let text = r#"Response:
371```json
372{
373 "name": "test",
374 "values": [1, 2, 3],
375 "nested": {
376 "key": "value"
377 }
378}
379```"#;
380 let result = extract_json(text);
381 assert!(result.is_ok());
382 let json = result.unwrap();
383 assert!(json.contains("\"name\": \"test\""));
385 assert!(json.contains("\"values\": [1, 2, 3]"));
386 assert!(json.contains("\"nested\""));
387 }
388}