pub mod search;
#[cfg(test)]
mod test;
pub mod types;
pub use search::search;
pub use types::{RecallEntry, RecallOutput, RecallSearchInput};
use crate::ToolDescription;
pub struct Recall {
pub description_search: ToolDescription,
}
impl Default for Recall {
fn default() -> Self {
Self::new()
}
}
const DEFAULT_DESCRIPTION: &str = concat!(
"Search an active knowledge base for entries semantically similar to your query. ",
"Returns matching entries with their IDs and content. ",
"Read-only — never writes to the database. ",
"Use this to retrieve relevant context from stored knowledge. ",
"Only the database name and query text are needed."
);
impl Recall {
#[must_use]
pub fn new() -> Self {
Self::build(DEFAULT_DESCRIPTION)
}
#[must_use]
pub fn with_description(self, suffix: impl Into<String>) -> Self {
let extra = suffix.into();
if extra.is_empty() {
return Self::build(DEFAULT_DESCRIPTION);
}
let full = format!("{DEFAULT_DESCRIPTION}\n\n{extra}");
Self::build(&full)
}
pub fn rebuild_description(&mut self, suffix: impl Into<String>) {
let extra = suffix.into();
let full = if extra.is_empty() {
DEFAULT_DESCRIPTION.to_string()
} else {
format!("{DEFAULT_DESCRIPTION}\n\n{extra}")
};
self.description_search = Self::make_tool_description(&full);
}
fn build(description: &str) -> Self {
Self {
description_search: Self::make_tool_description(description),
}
}
fn make_tool_description(description: &str) -> ToolDescription {
serde_json::json!({
"name": "recall_search",
"description": description,
"inputSchema": {
"type": "object",
"properties": {
"db_name": {
"type": "string",
"description": "Name of the active database to search (e.g. one listed in the session's knowledge-base context, if provided)"
},
"query": {
"type": "string",
"description": "The search query text"
},
"limit": {
"type": "integer",
"description": "Maximum number of results to return (default: 5)",
"default": 5
}
},
"required": ["db_name", "query"]
}
})
}
pub async fn search(&self, input: &RecallSearchInput) -> Result<RecallOutput, String> {
search::search(input).await
}
}