mirror of
https://gitea.ingwaz.work/Ingwaz/openbrain-mcp.git
synced 2026-03-31 14:49:06 +00:00
Add TTL expiry for transient facts
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user