llm_toolkit/
lib.rs

1//! 'llm-toolkit' - A low-level Rust toolkit for the LLM last mile problem.
2//!
3//! This library provides a set of sharp, reliable, and unopinionated "tools"
4//! for building robust LLM-powered applications in Rust. It focuses on solving
5//! the common and frustrating problems that occur at the boundary between a
6//! strongly-typed Rust application and the unstructured, often unpredictable
7//! string-based responses from LLM APIs.
8
9/// A derive macro to implement the `ToPrompt` trait for structs.
10///
11/// This macro is available only when the `derive` feature is enabled.
12/// See the [crate-level documentation](index.html#2-structured-prompts-with-derivetoprompt) for usage examples.
13#[cfg(feature = "derive")]
14pub use llm_toolkit_macros::ToPrompt;
15
16/// A derive macro to implement the `ToPromptSet` trait for structs.
17///
18/// This macro is available only when the `derive` feature is enabled.
19#[cfg(feature = "derive")]
20pub use llm_toolkit_macros::ToPromptSet;
21
22/// A derive macro to implement the `ToPromptFor` trait for structs.
23///
24/// This macro is available only when the `derive` feature is enabled.
25#[cfg(feature = "derive")]
26pub use llm_toolkit_macros::ToPromptFor;
27
28/// A macro for creating examples sections in prompts.
29///
30/// This macro is available only when the `derive` feature is enabled.
31#[cfg(feature = "derive")]
32pub use llm_toolkit_macros::examples_section;
33
34/// A procedural attribute macro for defining intent enums with automatic prompt and extractor generation.
35///
36/// This macro is available only when the `derive` feature is enabled.
37#[cfg(feature = "derive")]
38pub use llm_toolkit_macros::define_intent;
39
40pub mod extract;
41pub mod intent;
42pub mod multimodal;
43pub mod prompt;
44
45pub use extract::{FlexibleExtractor, MarkdownCodeBlockExtractor};
46pub use intent::frame::IntentFrame;
47#[allow(deprecated)]
48pub use intent::{IntentError, IntentExtractor, PromptBasedExtractor};
49pub use multimodal::ImageData;
50pub use prompt::{PromptPart, PromptSetError, ToPrompt, ToPromptFor, ToPromptSet};
51
52use extract::ParseError;
53
54/// Extracts a JSON string from a raw LLM response string.
55///
56/// This function uses a `FlexibleExtractor` with its standard strategies
57/// to find and extract a JSON object from a string that may contain extraneous
58/// text, such as explanations or Markdown code blocks.
59///
60/// For more advanced control over extraction strategies, see the `extract::FlexibleExtractor` struct.
61///
62/// # Returns
63///
64/// A `Result` containing the extracted JSON `String` on success, or a `ParseError`
65/// if no JSON could be extracted.
66pub fn extract_json(text: &str) -> Result<String, ParseError> {
67    let extractor = FlexibleExtractor::new();
68    // Note: The standard strategies in the copied code are TaggedContent("answer"), JsonBrackets, FirstJsonObject.
69    // We will add a markdown strategy later during refactoring.
70    extractor.extract(text)
71}
72
73/// Extracts content from any Markdown code block in the text.
74///
75/// This function searches for the first code block (delimited by triple backticks)
76/// and returns its content. The code block can have any language specifier or none at all.
77///
78/// # Returns
79///
80/// A `Result` containing the extracted code block content on success, or a `ParseError`
81/// if no code block is found.
82pub fn extract_markdown_block(text: &str) -> Result<String, ParseError> {
83    let extractor = MarkdownCodeBlockExtractor::new();
84    extractor.extract(text)
85}
86
87/// Extracts content from a Markdown code block with a specific language.
88///
89/// This function searches for a code block with the specified language hint
90/// (e.g., ```rust, ```python) and returns its content.
91///
92/// # Arguments
93///
94/// * `text` - The text containing the markdown code block
95/// * `lang` - The language specifier to match (e.g., "rust", "python")
96///
97/// # Returns
98///
99/// A `Result` containing the extracted code block content on success, or a `ParseError`
100/// if no code block with the specified language is found.
101pub fn extract_markdown_block_with_lang(text: &str, lang: &str) -> Result<String, ParseError> {
102    let extractor = MarkdownCodeBlockExtractor::with_language(lang.to_string());
103    extractor.extract(text)
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn test_json_extraction() {
112        let input = "Some text before {\"key\": \"value\"} and after.";
113        assert_eq!(extract_json(input).unwrap(), "{\"key\": \"value\"}");
114    }
115
116    #[test]
117    fn test_standard_extraction_from_tagged_content() {
118        let text = "<answer>{\"type\": \"success\"}</answer>";
119        let result = extract_json(text);
120        assert!(result.is_ok());
121        assert_eq!(result.unwrap(), "{\"type\": \"success\"}");
122    }
123
124    #[test]
125    fn test_markdown_extraction() {
126        // Test simple code block with no language
127        let text1 = "Here is some code:\n```\nlet x = 42;\n```\nAnd some text after.";
128        let result1 = extract_markdown_block(text1);
129        assert!(result1.is_ok());
130        assert_eq!(result1.unwrap(), "let x = 42;");
131
132        // Test code block with specific language (rust)
133        let text2 = "Here's Rust code:\n```rust\nfn main() {
134    println!(\"Hello\");
135}
136```";
137        let result2 = extract_markdown_block_with_lang(text2, "rust");
138        assert!(result2.is_ok());
139        assert_eq!(result2.unwrap(), "fn main() {\n    println!(\"Hello\");\n}");
140
141        // Test extracting rust block when json block is also present
142        let text3 = r#"\nFirst a JSON block:
143```json
144{"key": "value"}
145```
146
147Then a Rust block:
148```rust
149let data = vec![1, 2, 3];
150```
151"#;
152        let result3 = extract_markdown_block_with_lang(text3, "rust");
153        assert!(result3.is_ok());
154        assert_eq!(result3.unwrap(), "let data = vec![1, 2, 3];");
155
156        // Test case where no code block is found
157        let text4 = "This text has no code blocks at all.";
158        let result4 = extract_markdown_block(text4);
159        assert!(result4.is_err());
160
161        // Test with messy surrounding text and newlines
162        let text5 = r#"\nLots of text before...
163
164
165   ```python
166def hello():
167    print("world")
168    return True
169   ```   
170
171
172And more text after with various spacing.
173"#;
174        let result5 = extract_markdown_block_with_lang(text5, "python");
175        assert!(result5.is_ok());
176        assert_eq!(
177            result5.unwrap(),
178            "def hello():\n    print(\"world\")\n    return True"
179        );
180    }
181}