#[cfg(feature = "agent")]
pub mod expandable;
pub mod frame;
use self::frame::IntentFrame;
use std::str::FromStr;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum IntentError {
#[error("Extraction failed: Tag '{tag}' not found in response")]
TagNotFound { tag: String },
#[error("Parsing failed: Could not parse '{value}' into a valid intent")]
ParseFailed { value: String },
#[error(transparent)]
Other(#[from] anyhow::Error),
}
pub type IntentExtractionError = IntentError;
#[derive(Debug, Error)]
pub enum ParseError {
#[error("Failed to parse intent: {0}")]
ParseError(String),
}
pub fn extract_intent_from_response<T>(
response: &str,
tag: &str,
) -> Result<T, IntentExtractionError>
where
T: FromStr,
T::Err: std::fmt::Display,
{
use crate::extract::FlexibleExtractor;
use crate::extract::core::ContentExtractor;
let extractor = FlexibleExtractor::new();
let extracted_str =
extractor
.extract_tagged(response, tag)
.ok_or_else(|| IntentError::TagNotFound {
tag: tag.to_string(),
})?;
T::from_str(&extracted_str).map_err(|e| IntentError::ParseFailed {
value: format!("{}: {}", extracted_str, e),
})
}
pub trait IntentExtractor<T>
where
T: FromStr,
{
fn extract_intent(&self, text: &str) -> Result<T, IntentError>;
}
#[deprecated(
since = "0.8.0",
note = "Please use `IntentFrame` instead for better safety and clarity."
)]
pub struct PromptBasedExtractor {
frame: IntentFrame,
}
#[allow(deprecated)]
impl PromptBasedExtractor {
pub fn new(tag: &str) -> Self {
Self {
frame: IntentFrame::new(tag, tag),
}
}
}
#[allow(deprecated)]
impl<T> IntentExtractor<T> for PromptBasedExtractor
where
T: FromStr,
{
fn extract_intent(&self, text: &str) -> Result<T, IntentError> {
self.frame.extract_intent(text)
}
}
#[cfg(test)]
#[allow(deprecated)]
mod tests {
use super::*;
#[derive(Debug, PartialEq)]
enum TestIntent {
Login,
Logout,
}
impl FromStr for TestIntent {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Login" => Ok(TestIntent::Login),
"Logout" => Ok(TestIntent::Logout),
_ => Err(format!("Unknown intent: {}", s)),
}
}
}
#[test]
fn test_extract_intent_success() {
let extractor = PromptBasedExtractor::new("intent");
let text = "<intent>Login</intent>";
let result: Result<TestIntent, _> = IntentExtractor::extract_intent(&extractor, text);
assert_eq!(result.unwrap(), TestIntent::Login);
}
#[test]
fn test_extract_intent_tag_not_found() {
let extractor = PromptBasedExtractor::new("intent");
let text = "No intent tag here";
let result: Result<TestIntent, _> = IntentExtractor::extract_intent(&extractor, text);
match result {
Err(IntentError::TagNotFound { tag }) => {
assert_eq!(tag, "intent");
}
_ => panic!("Expected TagNotFound error"),
}
}
#[test]
fn test_extract_intent_parse_failed() {
let extractor = PromptBasedExtractor::new("intent");
let text = "<intent>Invalid</intent>";
let result: Result<TestIntent, _> = IntentExtractor::extract_intent(&extractor, text);
match result {
Err(IntentError::ParseFailed { value }) => {
assert_eq!(value, "Invalid");
}
_ => panic!("Expected ParseFailed error"),
}
}
}