Add TTL expiry for transient facts

This commit is contained in:
Agent Zero
2026-03-24 03:20:10 +00:00
parent 1314015479
commit 5d5c042dd1
12 changed files with 241 additions and 15 deletions

View File

@@ -9,6 +9,7 @@ use std::sync::Arc;
use tracing::info;
use crate::embedding::extract_keywords;
use crate::ttl::expires_at_from_ttl;
use crate::AppState;
/// Maximum number of entries allowed per batch store call
@@ -18,6 +19,7 @@ const MAX_BATCH_SIZE: usize = 50;
///
/// Accepts:
/// - `agent_id`: Optional agent identifier (defaults to "default")
/// - `ttl`: Optional default TTL string applied to entries without their own ttl
/// - `entries`: Array of 1-50 entries, each with `content` (required) and `metadata` (optional)
///
/// Returns:
@@ -41,6 +43,7 @@ pub async fn execute(state: &Arc<AppState>, arguments: Value) -> Result<String>
.get("entries")
.and_then(|v| v.as_array())
.context("Missing required parameter: entries")?;
let default_ttl = arguments.get("ttl").and_then(|v| v.as_str());
// 3. Validate batch size
if entries.is_empty() {
@@ -79,6 +82,12 @@ pub async fn execute(state: &Arc<AppState>, arguments: Value) -> Result<String>
.get("metadata")
.cloned()
.unwrap_or(serde_json::json!({}));
let ttl = entry
.get("ttl")
.and_then(|v| v.as_str())
.or(default_ttl);
let expires_at = expires_at_from_ttl(ttl)
.with_context(|| format!("Invalid ttl for entry at index {}", idx))?;
// Generate embedding for this entry
let embedding = embedding_engine
@@ -88,7 +97,7 @@ pub async fn execute(state: &Arc<AppState>, arguments: Value) -> Result<String>
// Extract keywords
let keywords = extract_keywords(content, 10);
processed_entries.push((content.to_string(), metadata, embedding, keywords));
processed_entries.push((content.to_string(), metadata, embedding, keywords, expires_at));
}
// 5. Batch DB insert (single transaction for atomicity)

View File

@@ -30,6 +30,10 @@ pub fn get_tool_definitions() -> Vec<Value> {
"metadata": {
"type": "object",
"description": "Optional metadata to attach to the memory"
},
"ttl": {
"type": "string",
"description": "Optional time-to-live for transient facts, like 30s, 15m, 1h, 7d, or 2w"
}
},
"required": ["content"]
@@ -45,6 +49,10 @@ pub fn get_tool_definitions() -> Vec<Value> {
"type": "string",
"description": "Unique identifier for the agent storing the memories (default: 'default')"
},
"ttl": {
"type": "string",
"description": "Optional default time-to-live applied to entries without their own ttl"
},
"entries": {
"type": "array",
"description": "Array of 1-50 memory entries to store atomically",
@@ -58,6 +66,10 @@ pub fn get_tool_definitions() -> Vec<Value> {
"metadata": {
"type": "object",
"description": "Optional metadata to attach to the memory"
},
"ttl": {
"type": "string",
"description": "Optional per-entry time-to-live override like 30s, 15m, 1h, 7d, or 2w"
}
},
"required": ["content"]

View File

@@ -81,7 +81,8 @@ pub async fn execute(state: &Arc<AppState>, arguments: Value) -> Result<String>
"hybrid_score": m.hybrid_score,
"keywords": m.record.keywords,
"metadata": m.record.metadata,
"created_at": m.record.created_at.to_rfc3339()
"created_at": m.record.created_at.to_rfc3339(),
"expires_at": m.record.expires_at.as_ref().map(|ts| ts.to_rfc3339())
})
})
.collect();

View File

@@ -6,6 +6,7 @@ use std::sync::Arc;
use tracing::info;
use crate::embedding::extract_keywords;
use crate::ttl::expires_at_from_ttl;
use crate::AppState;
/// Execute the store tool
@@ -32,6 +33,9 @@ pub async fn execute(state: &Arc<AppState>, arguments: Value) -> Result<String>
.cloned()
.unwrap_or(serde_json::json!({}));
let ttl = arguments.get("ttl").and_then(|v| v.as_str());
let expires_at = expires_at_from_ttl(ttl).context("Invalid ttl")?;
info!(
"Storing memory for agent '{}': {} chars",
agent_id,
@@ -49,7 +53,14 @@ pub async fn execute(state: &Arc<AppState>, arguments: Value) -> Result<String>
// Store in database
let id = state
.db
.store_memory(agent_id, content, &embedding, &keywords, metadata)
.store_memory(
agent_id,
content,
&embedding,
&keywords,
metadata,
expires_at.clone(),
)
.await
.context("Failed to store memory")?;
@@ -60,7 +71,9 @@ pub async fn execute(state: &Arc<AppState>, arguments: Value) -> Result<String>
"id": id.to_string(),
"agent_id": agent_id,
"keywords": keywords,
"embedding_dimension": embedding.len()
"embedding_dimension": embedding.len(),
"ttl": ttl,
"expires_at": expires_at.as_ref().map(|ts| ts.to_rfc3339())
})
.to_string())
}