pub struct CortexdbMemory { /* private fields */ }Expand description
A Memory backed by CortexDB’s MCP memory_save / memory_search tools.
Implementations§
Source§impl CortexdbMemory
impl CortexdbMemory
Sourcepub async fn connect_stdio(program: &str, args: &[&str]) -> Result<Self>
pub async fn connect_stdio(program: &str, args: &[&str]) -> Result<Self>
Spawn program args... as CortexDB’s MCP stdio server and adapt it.
(e.g. connect_stdio("cortexdb-mcp-stdio", &[]).)
Sourcepub async fn connect_http(url: &str) -> Result<Self>
pub async fn connect_http(url: &str) -> Result<Self>
Connect to a remote MCP server over Streamable HTTP.
This is an MCP endpoint, not CortexDB’s own API. The distinction is easy
to lose because both are written as a URL: a CortexDB started the way the
openclaw sidecar starts one serves gRPC on host:47821 and answers
nothing at all to HTTP, so pointing this at it fails at the transport.
For that deployment use CortexdbGrpcMemory
(grpc feature), which speaks the protocol the server actually serves.
Use this one when the CortexDB in front of you exposes MCP over HTTP.
Requires the http feature of harness-rs-mcp-client (on by default,
with rustls). For an untrusted URL use
McpClient::connect_http_with_client and from_client,
which leaves the redirect and DNS policy with the caller.
let mem = CortexdbMemory::connect_http("https://cortex.internal/mcp").await?;Sourcepub fn from_client(client: McpClient) -> Result<Self>
pub fn from_client(client: McpClient) -> Result<Self>
Adapt an already-connected CortexDB MCP client — stdio, HTTP, or any
transport McpClient grows later.
Sourcepub fn with_scope(self, scope: impl Into<String>) -> Self
pub fn with_scope(self, scope: impl Into<String>) -> Self
CortexDB memory scope: global (default, shared), user, or session.
Sourcepub fn with_namespace(self, ns: impl Into<String>) -> Self
pub fn with_namespace(self, ns: impl Into<String>) -> Self
CortexDB namespace (default harness). Isolates this app’s memories.
Sourcepub fn with_user_id(self, user: impl Into<String>) -> Self
pub fn with_user_id(self, user: impl Into<String>) -> Self
Scope memories to a user id (for scope = user).
Sourcepub fn can_consolidate(&self) -> bool
pub fn can_consolidate(&self) -> bool
Whether the connected CortexDB exposes the graph-distillation tool.
Sourcepub async fn consolidate(&self, extra_args: Value) -> Result<Value, MemoryError>
pub async fn consolidate(&self, extra_args: Value) -> Result<Value, MemoryError>
Distill accumulated memories into CortexDB’s knowledge graph
(entities + relations), server-side, via knowledge_memory_consolidate.
CortexDB does the extraction — harness just triggers it, so this costs no
tokens on our side. Run it periodically (e.g. from harness-scheduler)
after a batch of turns has been recorded.
Operates on this instance’s scope/namespace/user_id. The server
requires a reflect.recall.query; this method supplies a broad
default plus promote_to_knowledge: true, so consolidate(Value::Null)
works out of the box. Override either via extra_args, e.g.:
mem.consolidate(serde_json::json!({
"reflect": { "recall": { "query": "past tasks and tools", "max_memory_items": 30 } }
})).await?;Quality note: in pure-lexical mode (no embedder configured on the
CortexDB side) entity extraction is poor — configure an embedder for a
clean graph. Returns the raw tool result. Errors if the server doesn’t
expose the tool (check can_consolidate).
Trait Implementations§
Source§impl Memory for CortexdbMemory
impl Memory for CortexdbMemory
Source§fn write<'life0, 'async_trait>(
&'life0 self,
entry: MemoryEntry,
) -> Pin<Box<dyn Future<Output = Result<(), MemoryError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn write<'life0, 'async_trait>(
&'life0 self,
entry: MemoryEntry,
) -> Pin<Box<dyn Future<Output = Result<(), MemoryError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
entry. The backend assigns the id field; callers may
leave it empty. Implementations must be safe to call concurrently
from multiple tasks.Source§fn recall<'life0, 'life1, 'async_trait>(
&'life0 self,
query: &'life1 str,
k: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<MemoryEntry>, MemoryError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn recall<'life0, 'life1, 'async_trait>(
&'life0 self,
query: &'life1 str,
k: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<MemoryEntry>, MemoryError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
k entries most relevant to query, ordered by
descending relevance. The query is typically the current task
description; backends choose how to score (keyword, embedding, BM25…).
Returning an empty Vec is fine and must not be treated as an error.