use async_trait::async_trait;
use serde_json::Value;
use crate::error::Result;
use crate::runnables::base::Runnable;
use crate::runnables::config::RunnableConfig;
use super::base::OutputParser;
pub struct CommaSeparatedListOutputParser;
impl OutputParser for CommaSeparatedListOutputParser {
fn parse(&self, text: &str) -> Result<Value> {
let items: Vec<Value> = text
.split(',')
.map(|s| Value::String(s.trim().to_string()))
.filter(|v| v.as_str() != Some(""))
.collect();
Ok(Value::Array(items))
}
fn get_format_instructions(&self) -> Option<String> {
Some(
"Your response should be a list of comma separated values, \
eg: `foo, bar, baz`"
.into(),
)
}
fn parser_type(&self) -> &str {
"comma_separated_list"
}
}
#[async_trait]
impl Runnable for CommaSeparatedListOutputParser {
fn name(&self) -> &str {
"CommaSeparatedListOutputParser"
}
async fn invoke(&self, input: Value, _config: Option<&RunnableConfig>) -> Result<Value> {
let text = match &input {
Value::String(s) => s.clone(),
other => other.to_string(),
};
self.parse(&text)
}
}
pub struct NumberedListOutputParser;
impl OutputParser for NumberedListOutputParser {
fn parse(&self, text: &str) -> Result<Value> {
let items: Vec<Value> = text
.lines()
.filter_map(|line| {
let trimmed = line.trim();
let rest = trimmed
.strip_prefix(|c: char| c.is_ascii_digit())
.and_then(|s| {
let s = s.trim_start_matches(|c: char| c.is_ascii_digit());
s.strip_prefix(". ").or_else(|| s.strip_prefix(") "))
});
rest.map(|s| Value::String(s.trim().to_string()))
})
.collect();
Ok(Value::Array(items))
}
fn get_format_instructions(&self) -> Option<String> {
Some(
"Your response should be a numbered list, eg:\n\
1. foo\n\
2. bar\n\
3. baz"
.into(),
)
}
fn parser_type(&self) -> &str {
"numbered_list"
}
}
#[async_trait]
impl Runnable for NumberedListOutputParser {
fn name(&self) -> &str {
"NumberedListOutputParser"
}
async fn invoke(&self, input: Value, _config: Option<&RunnableConfig>) -> Result<Value> {
let text = match &input {
Value::String(s) => s.clone(),
other => other.to_string(),
};
self.parse(&text)
}
}
pub struct MarkdownListOutputParser;
impl OutputParser for MarkdownListOutputParser {
fn parse(&self, text: &str) -> Result<Value> {
let items: Vec<Value> = text
.lines()
.filter_map(|line| {
let trimmed = line.trim();
trimmed
.strip_prefix("- ")
.or_else(|| trimmed.strip_prefix("* "))
.map(|s| Value::String(s.trim().to_string()))
})
.collect();
Ok(Value::Array(items))
}
fn get_format_instructions(&self) -> Option<String> {
Some(
"Your response should be a markdown list, eg:\n\
- foo\n\
- bar\n\
- baz"
.into(),
)
}
fn parser_type(&self) -> &str {
"markdown_list"
}
}
#[async_trait]
impl Runnable for MarkdownListOutputParser {
fn name(&self) -> &str {
"MarkdownListOutputParser"
}
async fn invoke(&self, input: Value, _config: Option<&RunnableConfig>) -> Result<Value> {
let text = match &input {
Value::String(s) => s.clone(),
other => other.to_string(),
};
self.parse(&text)
}
}