pub trait Prompt: Clone {
async fn prompt(&self, input: String) -> Result<String, PromptError>;
}
#[derive(Debug, thiserror::Error)]
pub enum PromptError {
#[error("Prompt error: {0}")]
Error(String),
}
use super::memory_ops::Op;
use crate::memory::{MemoryManager, MemoryNode, MemoryType, Error as MemoryError};
use super::memory_ops::{self, StoreMemory, RetrieveMemories, SearchMemories};
mod workflow {
use super::*;
pub fn new() -> WorkflowBuilder {
WorkflowBuilder
}
pub struct WorkflowBuilder;
impl WorkflowBuilder {
pub fn chain<O>(self, op: O) -> O {
op
}
}
}
fn passthrough<T: Clone + Send + Sync + 'static>() -> impl Op<Input = T, Output = T> {
struct PassthroughOp<T> {
_phantom: std::marker::PhantomData<T>,
}
impl<T: Clone + Send + Sync + 'static> Op for PassthroughOp<T> {
type Input = T;
type Output = T;
async fn call(&self, input: Self::Input) -> Self::Output {
input
}
}
PassthroughOp { _phantom: std::marker::PhantomData }
}
fn run_both<I, O1, O2, Op1, Op2>(op1: Op1, op2: Op2) -> impl Op<Input = I, Output = (O1, O2)>
where
I: Clone + Send + Sync + 'static,
O1: Send + Sync + 'static,
O2: Send + Sync + 'static,
Op1: Op<Input = I, Output = O1>,
Op2: Op<Input = I, Output = O2>,
{
struct BothOp<Op1, Op2> {
op1: Op1,
op2: Op2,
}
impl<I, O1, O2, Op1, Op2> Op for BothOp<Op1, Op2>
where
I: Clone + Send + Sync + 'static,
O1: Send + Sync + 'static,
O2: Send + Sync + 'static,
Op1: Op<Input = I, Output = O1>,
Op2: Op<Input = I, Output = O2>,
{
type Input = I;
type Output = (O1, O2);
async fn call(&self, input: Self::Input) -> Self::Output {
let result1 = self.op1.call(input.clone()).await;
let result2 = self.op2.call(input).await;
(result1, result2)
}
}
BothOp { op1, op2 }
}
struct MemoryWorkflowOp<M, P> {
memory_manager: M,
prompt_model: P,
context_limit: usize,
}
impl<M, P> Op for MemoryWorkflowOp<M, P>
where
M: MemoryManager + Clone,
P: Prompt,
{
type Input = String;
type Output = Result<String, WorkflowError>;
async fn call(&self, input: Self::Input) -> Self::Output {
let _ = memory_ops::store_memory(self.memory_manager.clone(), MemoryType::Episodic)
.call(input.clone())
.await;
let memories = memory_ops::search_memories(self.memory_manager.clone())
.call(input.clone())
.await
.unwrap_or_default();
let context = memories
.iter()
.take(self.context_limit)
.map(|m| format!("- {}", m.content))
.collect::<Vec<_>>()
.join("\n");
let formatted_input = if context.is_empty() {
input
} else {
format!("Previous context:\n{}\n\nCurrent query: {}", context, input)
};
let response = self.prompt_model
.prompt(formatted_input)
.await
.map_err(WorkflowError::Prompt)?;
let _ = memory_ops::store_memory(self.memory_manager.clone(), MemoryType::Semantic)
.call(response.clone())
.await;
Ok(response)
}
}
pub struct MemoryEnhancedWorkflow<M, P> {
memory_manager: M,
prompt_model: P,
context_limit: usize,
}
impl<M, P> MemoryEnhancedWorkflow<M, P>
where
M: MemoryManager + Clone,
P: Prompt,
{
pub fn new(memory_manager: M, prompt_model: P) -> Self {
Self {
memory_manager,
prompt_model,
context_limit: 5,
}
}
pub fn with_context_limit(mut self, limit: usize) -> Self {
self.context_limit = limit;
self
}
pub fn build(self) -> impl Op<Input = String, Output = Result<String, WorkflowError>> {
let memory_manager = self.memory_manager;
let prompt_model = self.prompt_model;
let context_limit = self.context_limit;
MemoryWorkflowOp {
memory_manager,
prompt_model,
context_limit,
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum WorkflowError {
#[error("Memory error: {0}")]
Memory(#[from] MemoryError),
#[error("Prompt error: {0}")]
Prompt(#[from] PromptError),
#[error("Workflow error: {0}")]
Other(String),
}
pub fn conversation_workflow<M, P>(
memory_manager: M,
prompt_model: P,
) -> impl Op<Input = String, Output = Result<String, WorkflowError>>
where
M: MemoryManager + Clone,
P: Prompt,
{
MemoryEnhancedWorkflow::new(memory_manager, prompt_model)
.with_context_limit(10)
.build()
}
pub struct AdaptiveWorkflow<M, B> {
memory_manager: M,
base_op: B,
}
impl<M, B> AdaptiveWorkflow<M, B>
where
M: MemoryManager + Clone,
B: Op,
{
pub fn new(memory_manager: M, base_op: B) -> Self {
Self {
memory_manager,
base_op,
}
}
}
impl<M, B> Op for AdaptiveWorkflow<M, B>
where
M: MemoryManager + Clone,
B: Op,
B::Input: Clone + serde::Serialize,
B::Output: Clone + serde::Serialize,
{
type Input = B::Input;
type Output = (B::Output, String);
async fn call(&self, input: Self::Input) -> Self::Output {
let output = self.base_op.call(input.clone()).await;
let memory_content = serde_json::json!({
"input": input,
"output": output,
"timestamp": std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0),
}).to_string();
let memory = MemoryNode::new(memory_content, MemoryType::Episodic)
.with_importance(0.5);
let stored_memory = self.memory_manager
.create_memory(memory)
.await
.expect("Failed to store memory");
(output, stored_memory.id)
}
}
pub async fn apply_feedback<M: MemoryManager>(
memory_manager: M,
memory_id: String,
feedback: f32,
) -> Result<(), MemoryError> {
if let Some(mut memory) = memory_manager.get_memory(&memory_id).await? {
memory.metadata.importance = feedback.clamp(0.0, 1.0);
memory_manager.update_memory(memory).await?;
}
Ok(())
}
pub fn rag_workflow<M, P>(
memory_manager: M,
prompt_model: P,
retrieval_limit: usize,
) -> impl Op<Input = String, Output = Result<String, WorkflowError>>
where
M: MemoryManager + Clone,
P: Prompt,
{
RagWorkflowOp {
memory_manager,
prompt_model,
retrieval_limit,
}
}
struct RagWorkflowOp<M, P> {
memory_manager: M,
prompt_model: P,
retrieval_limit: usize,
}
impl<M, P> Op for RagWorkflowOp<M, P>
where
M: MemoryManager + Clone,
P: Prompt,
{
type Input = String;
type Output = Result<String, WorkflowError>;
async fn call(&self, query: Self::Input) -> Self::Output {
let memories = memory_ops::search_memories(self.memory_manager.clone())
.call(query.clone())
.await
.unwrap_or_default();
let documents = memories
.iter()
.take(self.retrieval_limit)
.enumerate()
.map(|(i, m)| format!("Document {}: {}", i + 1, m.content))
.collect::<Vec<_>>()
.join("\n\n");
let prompt = format!(
"Using the following documents as context:\n\n{}\n\nAnswer the question: {}",
documents, query
);
self.prompt_model
.prompt(prompt)
.await
.map_err(WorkflowError::Prompt)
}
}