1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
//! Generates questions and answers from a given text chunk and adds them as metadata.
use anyhow::Result;
use async_trait::async_trait;
use swiftide_core::{indexing::Node, Transformer};
/// This module defines the `MetadataQAText` struct and its associated methods,
/// which are used for generating metadata in the form of questions and answers
/// from a given text. It interacts with a client (e.g., `OpenAI`) to generate
/// these questions and answers based on the text chunk in an `Node`.
/// `MetadataQAText` is responsible for generating questions and answers
/// from a given text chunk. It uses a templated prompt to interact with a client
/// that implements the `SimplePrompt` trait.
#[swiftide_macros::indexing_transformer(
metadata_field_name = "Questions and Answers (text)",
default_prompt_file = "prompts/metadata_qa_text.prompt.md"
)]
pub struct MetadataQAText {
#[builder(default = "5")]
num_questions: usize,
}
#[async_trait]
impl Transformer for MetadataQAText {
/// Transforms an `Node` by generating questions and answers
/// based on the text chunk within the node.
///
/// # Arguments
///
/// * `node` - The `Node` containing the text chunk to process.
///
/// # Returns
///
/// A `Result` containing the transformed `Node` with added metadata,
/// or an error if the transformation fails.
///
/// # Errors
///
/// This function will return an error if the client fails to generate
/// questions and answers from the provided prompt.
#[tracing::instrument(skip_all, name = "transformers.metadata_qa_text")]
async fn transform_node(&self, mut node: Node) -> Result<Node> {
let prompt = self
.prompt_template
.to_prompt()
.with_node(&node)
.with_context_value("questions", self.num_questions);
let response = self.prompt(prompt).await?;
node.metadata.insert(NAME, response);
Ok(node)
}
fn concurrency(&self) -> Option<usize> {
self.concurrency
}
}
#[cfg(test)]
mod test {
use swiftide_core::MockSimplePrompt;
use super::*;
#[tokio::test]
async fn test_template() {
let template = default_prompt();
let prompt = template
.to_prompt()
.with_node(&Node::new("test"))
.with_context_value("questions", 5);
insta::assert_snapshot!(prompt.render().await.unwrap());
}
#[tokio::test]
async fn test_metadata_qacode() {
let mut client = MockSimplePrompt::new();
client
.expect_prompt()
.returning(|_| Ok("Q1: Hello\nA1: World".to_string()));
let transformer = MetadataQAText::builder().client(client).build().unwrap();
let node = Node::new("Some text");
let result = transformer.transform_node(node).await.unwrap();
assert_eq!(
result.metadata.get("Questions and Answers (text)").unwrap(),
"Q1: Hello\nA1: World"
);
}
}