Skip to main content

ainl_failure_learning/
search.rs

1//! FTS-backed search over `Failure` nodes, mapped to a small hit struct for prompts.
2
3use ainl_memory::{AinlMemoryNode, AinlNodeType, GraphMemory};
4use uuid::Uuid;
5
6/// One failure match (newest-first order preserved from the store when possible).
7#[derive(Debug, Clone, PartialEq)]
8pub struct FailureRecallHit {
9    /// Graph node id.
10    pub id: Uuid,
11    pub source: String,
12    pub message: String,
13    pub tool_name: Option<String>,
14    /// Optional MCP-style namespace (e.g. `ainl`) when the host recorded structured provenance.
15    pub source_namespace: Option<String>,
16    /// Optional fully-qualified tool name when the host recorded structured provenance.
17    pub source_tool: Option<String>,
18    /// Best-effort ranking score (1.0 today; room for rankers later).
19    pub score: f32,
20}
21
22fn hit_from_node(node: AinlMemoryNode) -> Option<FailureRecallHit> {
23    let AinlNodeType::Failure { failure } = node.node_type else {
24        return None;
25    };
26    Some(FailureRecallHit {
27        id: node.id,
28        source: failure.source,
29        message: failure.message,
30        tool_name: failure.tool_name,
31        source_namespace: failure.source_namespace,
32        source_tool: failure.source_tool,
33        score: 1.0,
34    })
35}
36
37/// Search persisted failures for one agent (wraps `GraphMemory::search_failures_for_agent`).
38pub fn search_failures_for_agent(
39    memory: &GraphMemory,
40    agent_id: &str,
41    query: &str,
42    limit: usize,
43) -> Result<Vec<FailureRecallHit>, String> {
44    let nodes = memory.search_failures_for_agent(agent_id, query, limit)?;
45    Ok(nodes.into_iter().filter_map(hit_from_node).collect())
46}