use chrono::{DateTime, Utc};
use systemprompt_identifiers::{BrainFactId, GatewayConversationId};
use systemprompt_models::brain::{
BrainFact, BrainOutcome, InjectionPlan, OutcomeObservation, ProposalDraft, RecallCtx,
RecallQuery, ScoredFact, StoredBlock,
};
use super::errors::{BrainAnalysisError, OutcomeSignalError, RecallError};
use super::recall::RecallReceipt;
#[async_trait::async_trait]
pub trait FactRecallSource: Send + Sync {
async fn pinned(&self, ctx: &RecallCtx, limit: u32) -> Result<Vec<BrainFact>, RecallError>;
async fn by_subject(
&self,
query: &RecallQuery,
ctx: &RecallCtx,
) -> Result<Vec<BrainFact>, RecallError>;
async fn by_context(
&self,
query: &RecallQuery,
ctx: &RecallCtx,
) -> Result<Vec<BrainFact>, RecallError>;
async fn full_text(
&self,
query: &RecallQuery,
ctx: &RecallCtx,
) -> Result<Vec<BrainFact>, RecallError>;
async fn stored_block(
&self,
conversation: &GatewayConversationId,
) -> Result<Option<StoredBlock>, RecallError>;
async fn record_recall(&self, receipt: &RecallReceipt) -> Result<(), RecallError>;
}
#[async_trait::async_trait]
pub trait FactRetriever: Send + Sync {
fn name(&self) -> &'static str;
async fn retrieve(
&self,
query: &RecallQuery,
ctx: &RecallCtx,
) -> Result<Vec<BrainFactId>, RecallError>;
}
pub trait InjectionPolicy: Send + Sync {
fn name(&self) -> &'static str;
fn plan(
&self,
ranked: &[ScoredFact],
ctx: &RecallCtx,
budget_tokens: u32,
) -> Option<InjectionPlan>;
}
#[async_trait::async_trait]
pub trait OutcomeSignal: Send + Sync {
fn name(&self) -> &'static str;
async fn observe(
&self,
observation: &OutcomeObservation,
) -> Result<Vec<BrainOutcome>, OutcomeSignalError>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AnalysisWindow {
pub since: DateTime<Utc>,
pub until: DateTime<Utc>,
}
#[async_trait::async_trait]
pub trait BrainAnalysis: Send + Sync {
fn name(&self) -> &'static str;
async fn analyse(
&self,
window: AnalysisWindow,
) -> Result<Vec<ProposalDraft>, BrainAnalysisError>;
}