diff --git a/adapters/remote/examples/conformance.rs b/adapters/remote/examples/conformance.rs index 3321b5b..bfa0378 100644 --- a/adapters/remote/examples/conformance.rs +++ b/adapters/remote/examples/conformance.rs @@ -11,11 +11,13 @@ use tinymemory_remote::{ SupermemoryMemory, }; +/// Builds the command-line usage error returned for invalid arguments. fn usage() -> anyhow::Error { anyhow::anyhow!("usage: conformance [credential]") } #[tokio::main] +/// Exercises mandatory capabilities against a live remote backend. async fn main() -> anyhow::Result<()> { let mut args = std::env::args().skip(1); let engine = args.next().ok_or_else(usage)?; diff --git a/adapters/remote/src/cognee.rs b/adapters/remote/src/cognee.rs index 75012da..6362325 100644 --- a/adapters/remote/src/cognee.rs +++ b/adapters/remote/src/cognee.rs @@ -39,9 +39,11 @@ impl CogneeMemory { #[async_trait] impl Memory for CogneeMemory { + /// Returns the Cognee driver identifier. fn name(&self) -> &str { self.inner.name() } + /// Stores an internally sourced record through the shared contract. async fn store( &self, n: &str, @@ -52,6 +54,7 @@ impl Memory for CogneeMemory { ) -> anyhow::Result<()> { self.inner.store(n, k, c, cat, s).await } + /// Stores a record while preserving its provenance taint. async fn store_with_taint( &self, n: &str, @@ -63,6 +66,7 @@ impl Memory for CogneeMemory { ) -> anyhow::Result<()> { self.inner.store_with_taint(n, k, c, cat, s, t).await } + /// Runs native Cognee recall and applies TinyMemory filters. async fn recall( &self, q: &str, @@ -71,6 +75,7 @@ impl Memory for CogneeMemory { ) -> anyhow::Result> { self.inner.recall(q, l, o).await } + /// Fetches one exact namespace/key record. async fn get( &self, n: &str, @@ -78,6 +83,7 @@ impl Memory for CogneeMemory { ) -> anyhow::Result> { self.inner.get(n, k).await } + /// Lists records matching the supplied TinyMemory filters. async fn list( &self, n: Option<&str>, @@ -86,41 +92,50 @@ impl Memory for CogneeMemory { ) -> anyhow::Result> { self.inner.list(n, c, s).await } + /// Deletes one exact namespace/key record. async fn forget(&self, n: &str, k: &str) -> anyhow::Result { self.inner.forget(n, k).await } + /// Summarizes every namespace visible through this adapter. async fn namespace_summaries( &self, ) -> anyhow::Result> { self.inner.namespace_summaries().await } + /// Counts every record visible through this adapter. async fn count(&self) -> anyhow::Result { self.inner.count().await } + /// Checks whether the configured Cognee service is reachable. async fn health_check(&self) -> bool { self.inner.health_check().await } } #[derive(Debug)] +/// Cognee-specific REST operations and wire-format conversion. struct CogneeDialect { client: HttpClient, } #[derive(Debug, Clone)] +/// Identity of a Cognee dataset used for one TinyMemory namespace. struct Dataset { id: String, name: String, } impl CogneeDialect { + /// Encodes a TinyMemory namespace as a collision-free Cognee dataset name. fn dataset_name(namespace: &str) -> String { format!("tinymemory__{}", encode(namespace)) } + /// Encodes a TinyMemory key as the uploaded envelope's filename. fn filename(key: &str) -> String { format!("{}.tinymemory.json", encode(key)) } + /// Discovers only datasets owned by the TinyMemory adapter. async fn datasets(&self) -> anyhow::Result> { let response: Value = self .client @@ -140,6 +155,7 @@ impl CogneeDialect { .collect()) } + /// Downloads and decodes every TinyMemory envelope in one dataset. async fn dataset_entries(&self, dataset: &Dataset) -> anyhow::Result> { let response: Value = self .client @@ -187,6 +203,7 @@ impl CogneeDialect { Ok(entries) } + /// Resolves the dataset assigned to a namespace. async fn find_dataset(&self, namespace: &str) -> anyhow::Result> { let name = Self::dataset_name(namespace); Ok(self @@ -196,6 +213,7 @@ impl CogneeDialect { .find(|dataset| dataset.name == name)) } + /// Deletes a stored envelope using its composite remote identifier. async fn delete_entry(&self, entry: &StoredEntry) -> anyhow::Result<()> { let (dataset_id, data_id) = entry .remote_id @@ -214,10 +232,12 @@ impl CogneeDialect { #[async_trait] impl Dialect for CogneeDialect { + /// Returns the stable Cognee driver identifier. fn name(&self) -> &'static str { COGNEE_DRIVER_ID } + /// Replaces an existing envelope and uploads the new exact record. async fn upsert(&self, entry: StoredEntry) -> anyhow::Result<()> { if let Some(existing) = self .entries() @@ -252,6 +272,7 @@ impl Dialect for CogneeDialect { Ok(()) } + /// Enumerates records across all TinyMemory-owned Cognee datasets. async fn entries(&self) -> anyhow::Result> { let mut entries = Vec::new(); for dataset in self.datasets().await? { @@ -260,6 +281,7 @@ impl Dialect for CogneeDialect { Ok(entries) } + /// Executes Cognee's native chunk recall and decodes returned envelopes. async fn search( &self, query: &str, @@ -309,6 +331,7 @@ impl Dialect for CogneeDialect { Ok(entries) } + /// Finds and deletes an exact TinyMemory logical record. async fn delete(&self, namespace: &str, key: &str) -> anyhow::Result { let Some(dataset) = self.find_dataset(namespace).await? else { return Ok(false); @@ -325,6 +348,7 @@ impl Dialect for CogneeDialect { Ok(true) } + /// Checks Cognee's aggregate health endpoint. async fn health(&self) -> bool { self.client.healthy("health").await } diff --git a/adapters/remote/src/common.rs b/adapters/remote/src/common.rs index 62eea35..6fe3a92 100644 --- a/adapters/remote/src/common.rs +++ b/adapters/remote/src/common.rs @@ -13,6 +13,9 @@ use tinymemory_api::types::{ }; #[derive(Clone)] +/// HTTP transport shared by every remote-engine dialect. +/// +/// Authentication material is deliberately omitted from its `Debug` output. pub(crate) struct HttpClient { inner: reqwest::Client, endpoint: Url, @@ -20,6 +23,7 @@ pub(crate) struct HttpClient { } #[derive(Clone)] +/// Authentication scheme applied to every request for one backend. enum Auth { None, Bearer(String), @@ -27,6 +31,7 @@ enum Auth { } impl std::fmt::Debug for HttpClient { + /// Renders endpoint origin and authentication presence without credentials. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("HttpClient") .field("endpoint", &self.endpoint.origin().ascii_serialization()) @@ -36,6 +41,7 @@ impl std::fmt::Debug for HttpClient { } impl HttpClient { + /// Builds a client that optionally authenticates with a bearer token. pub(crate) fn bearer(endpoint: &str, credential: Option<&str>) -> anyhow::Result { Self::new( endpoint, @@ -43,6 +49,7 @@ impl HttpClient { ) } + /// Builds a client that optionally authenticates with `X-API-Key`. pub(crate) fn api_key(endpoint: &str, credential: Option<&str>) -> anyhow::Result { Self::new( endpoint, @@ -50,6 +57,7 @@ impl HttpClient { ) } + /// Validates and normalizes an endpoint before constructing the transport. fn new(endpoint: &str, auth: Auth) -> anyhow::Result { let mut endpoint = Url::parse(endpoint).context("memory endpoint is not a valid URL")?; if !matches!(endpoint.scheme(), "http" | "https") { @@ -68,6 +76,7 @@ impl HttpClient { }) } + /// Resolves a relative API path and attaches the configured authentication. fn request(&self, method: Method, path: &str) -> anyhow::Result { let url = self .endpoint @@ -81,6 +90,7 @@ impl HttpClient { }) } + /// Sends a JSON request and decodes a successful JSON response. pub(crate) async fn json( &self, method: Method, @@ -102,6 +112,7 @@ impl HttpClient { .with_context(|| format!("memory API {path} returned invalid JSON")) } + /// Sends a request and returns a successful response body as text. pub(crate) async fn text(&self, method: Method, path: &str) -> anyhow::Result { let response = self.request(method, path)?.send().await?; let status = response.status(); @@ -114,6 +125,7 @@ impl HttpClient { .context("memory API response was unreadable") } + /// Sends a request whose successful response body is not needed. pub(crate) async fn empty( &self, method: Method, @@ -132,10 +144,12 @@ impl HttpClient { Ok(status) } + /// Starts an authenticated multipart POST request. pub(crate) fn multipart(&self, path: &str) -> anyhow::Result { self.request(Method::POST, path) } + /// Reports whether a GET endpoint responds successfully. pub(crate) async fn healthy(&self, path: &str) -> bool { let Ok(request) = self.request(Method::GET, path) else { return false; @@ -148,6 +162,7 @@ impl HttpClient { } #[derive(Debug, Clone, Serialize, Deserialize)] +/// Lossless TinyMemory record stored in backend-native metadata or content. pub(crate) struct StoredEntry { #[serde(default)] pub(crate) remote_id: String, @@ -166,6 +181,7 @@ pub(crate) struct StoredEntry { } impl StoredEntry { + /// Creates an unstored record; the dialect fills in the remote identifier. pub(crate) fn new( namespace: &str, key: &str, @@ -187,6 +203,7 @@ impl StoredEntry { } } + /// Converts the transport envelope into the public TinyMemory record type. pub(crate) fn into_memory_entry(self) -> MemoryEntry { MemoryEntry { id: if self.remote_id.is_empty() { @@ -206,6 +223,7 @@ impl StoredEntry { } } +/// Derives a deterministic fallback identifier from a logical record key. pub(crate) fn stable_id(namespace: &str, key: &str) -> String { let mut digest = Sha256::new(); digest.update(namespace.as_bytes()); @@ -214,6 +232,7 @@ pub(crate) fn stable_id(namespace: &str, key: &str) -> String { format!("tm_{}", encode(&digest.finalize()[..20])) } +/// Encodes arbitrary bytes as lowercase hexadecimal text safe for remote names. pub(crate) fn encode(value: impl AsRef<[u8]>) -> String { let value = value.as_ref(); value.iter().fold( @@ -226,32 +245,42 @@ pub(crate) fn encode(value: impl AsRef<[u8]>) -> String { ) } +/// Parses a stored category, preserving unknown or absent values as remote data. pub(crate) fn category(raw: Option<&str>) -> MemoryCategory { raw.and_then(|value| value.parse().ok()) .unwrap_or_else(|| MemoryCategory::Custom("remote".into())) } #[async_trait] +/// Backend-specific operations needed by the shared TinyMemory implementation. pub(crate) trait Dialect: Send + Sync + std::fmt::Debug { + /// Returns the stable driver identifier. fn name(&self) -> &'static str; + /// Creates or replaces one exact logical record. async fn upsert(&self, entry: StoredEntry) -> anyhow::Result<()>; + /// Enumerates every record owned by this adapter. async fn entries(&self) -> anyhow::Result>; + /// Runs the backend's native recall operation. async fn search( &self, query: &str, limit: usize, opts: RecallOpts<'_>, ) -> anyhow::Result>; + /// Deletes one exact logical record and reports whether it existed. async fn delete(&self, namespace: &str, key: &str) -> anyhow::Result; + /// Checks whether the backend is available for requests. async fn health(&self) -> bool; } #[derive(Debug)] +/// TinyMemory's exact-record contract composed over a native backend dialect. pub(crate) struct RemoteMemory { dialect: D, } impl RemoteMemory { + /// Wraps a backend dialect with shared filtering and conversion behavior. pub(crate) fn new(dialect: D) -> Self { Self { dialect } } @@ -259,10 +288,12 @@ impl RemoteMemory { #[async_trait] impl Memory for RemoteMemory { + /// Returns the wrapped dialect's stable driver identifier. fn name(&self) -> &str { self.dialect.name() } + /// Stores a record with the default internal provenance. async fn store( &self, namespace: &str, @@ -282,6 +313,7 @@ impl Memory for RemoteMemory { .await } + /// Validates identity fields and delegates a provenance-preserving upsert. async fn store_with_taint( &self, namespace: &str, @@ -301,6 +333,7 @@ impl Memory for RemoteMemory { .await } + /// Runs native search, enforces remaining filters, and caps the result set. async fn recall( &self, query: &str, @@ -323,6 +356,7 @@ impl Memory for RemoteMemory { .collect()) } + /// Locates one record by its exact logical namespace and key. async fn get(&self, namespace: &str, key: &str) -> anyhow::Result> { Ok(self .dialect @@ -333,6 +367,7 @@ impl Memory for RemoteMemory { .map(StoredEntry::into_memory_entry)) } + /// Enumerates records and applies exact category and session filters. async fn list( &self, namespace: Option<&str>, @@ -351,10 +386,12 @@ impl Memory for RemoteMemory { .collect()) } + /// Delegates exact logical deletion to the backend dialect. async fn forget(&self, namespace: &str, key: &str) -> anyhow::Result { self.dialect.delete(namespace, key).await } + /// Aggregates record counts and latest timestamps by namespace. async fn namespace_summaries(&self) -> anyhow::Result> { let mut summaries: BTreeMap = BTreeMap::new(); for entry in self.dialect.entries().await? { @@ -379,15 +416,18 @@ impl Memory for RemoteMemory { Ok(summaries.into_values().collect()) } + /// Counts all records owned by the adapter. async fn count(&self) -> anyhow::Result { Ok(self.dialect.entries().await?.len()) } + /// Delegates availability checking to the backend dialect. async fn health_check(&self) -> bool { self.dialect.health().await } } +/// Applies TinyMemory recall filters that a backend may not support natively. fn matches_filters(entry: &StoredEntry, opts: &RecallOpts<'_>) -> bool { opts.namespace.is_none_or(|value| entry.namespace == value) && opts diff --git a/adapters/remote/src/mem0.rs b/adapters/remote/src/mem0.rs index 67071de..53221d9 100644 --- a/adapters/remote/src/mem0.rs +++ b/adapters/remote/src/mem0.rs @@ -39,9 +39,11 @@ impl Mem0Memory { #[async_trait] impl Memory for Mem0Memory { + /// Returns the Mem0 driver identifier. fn name(&self) -> &str { self.inner.name() } + /// Stores an internally sourced record through the shared contract. async fn store( &self, n: &str, @@ -52,6 +54,7 @@ impl Memory for Mem0Memory { ) -> anyhow::Result<()> { self.inner.store(n, k, c, cat, s).await } + /// Stores a record while preserving its provenance taint. async fn store_with_taint( &self, n: &str, @@ -63,6 +66,7 @@ impl Memory for Mem0Memory { ) -> anyhow::Result<()> { self.inner.store_with_taint(n, k, c, cat, s, t).await } + /// Runs native Mem0 semantic search and applies TinyMemory filters. async fn recall( &self, q: &str, @@ -71,6 +75,7 @@ impl Memory for Mem0Memory { ) -> anyhow::Result> { self.inner.recall(q, l, o).await } + /// Fetches one exact namespace/key record. async fn get( &self, n: &str, @@ -78,6 +83,7 @@ impl Memory for Mem0Memory { ) -> anyhow::Result> { self.inner.get(n, k).await } + /// Lists records matching the supplied TinyMemory filters. async fn list( &self, n: Option<&str>, @@ -86,28 +92,34 @@ impl Memory for Mem0Memory { ) -> anyhow::Result> { self.inner.list(n, c, s).await } + /// Deletes one exact namespace/key record. async fn forget(&self, n: &str, k: &str) -> anyhow::Result { self.inner.forget(n, k).await } + /// Summarizes every namespace visible through this adapter. async fn namespace_summaries( &self, ) -> anyhow::Result> { self.inner.namespace_summaries().await } + /// Counts every record visible through this adapter. async fn count(&self) -> anyhow::Result { self.inner.count().await } + /// Checks whether the configured Mem0 service is reachable. async fn health_check(&self) -> bool { self.inner.health_check().await } } #[derive(Debug)] +/// Mem0-specific REST operations and wire-format conversion. struct Mem0Dialect { client: HttpClient, } impl Mem0Dialect { + /// Fetches Mem0's administrative memory listing. async fn values(&self) -> anyhow::Result> { let response: Value = self .client @@ -120,6 +132,7 @@ impl Mem0Dialect { .unwrap_or_default()) } + /// Decodes a Mem0 result containing TinyMemory-owned metadata. fn decode(value: &Value) -> Option { let metadata = value.get("metadata")?.as_object()?; let namespace = metadata.get("tinymemory_namespace")?.as_str()?.to_owned(); @@ -153,6 +166,7 @@ impl Mem0Dialect { }) } + /// Encodes TinyMemory identity, classification, session, and provenance. fn metadata(entry: &StoredEntry) -> Value { let mut value = json!({ "tinymemory_namespace": entry.namespace, @@ -169,10 +183,12 @@ impl Mem0Dialect { #[async_trait] impl Dialect for Mem0Dialect { + /// Returns the stable Mem0 driver identifier. fn name(&self) -> &'static str { MEM0_DRIVER_ID } + /// Replaces an existing exact record or creates it with inference disabled. async fn upsert(&self, entry: StoredEntry) -> anyhow::Result<()> { let existing = self .entries() @@ -206,6 +222,7 @@ impl Dialect for Mem0Dialect { Ok(()) } + /// Enumerates and decodes TinyMemory-owned Mem0 records. async fn entries(&self) -> anyhow::Result> { Ok(self .values() @@ -215,6 +232,7 @@ impl Dialect for Mem0Dialect { .collect()) } + /// Executes Mem0's native vector search. async fn search( &self, query: &str, @@ -243,6 +261,7 @@ impl Dialect for Mem0Dialect { Ok(values.iter().filter_map(Self::decode).collect()) } + /// Finds and deletes an exact TinyMemory logical record. async fn delete(&self, namespace: &str, key: &str) -> anyhow::Result { let Some(entry) = self .entries() @@ -263,6 +282,7 @@ impl Dialect for Mem0Dialect { Ok(true) } + /// Accepts either Mem0's health endpoint or its redirected root page. async fn health(&self) -> bool { self.client.healthy("api/health").await || self.client.healthy("").await } diff --git a/adapters/remote/src/supermemory.rs b/adapters/remote/src/supermemory.rs index 38afce1..cc8df37 100644 --- a/adapters/remote/src/supermemory.rs +++ b/adapters/remote/src/supermemory.rs @@ -35,9 +35,11 @@ impl SupermemoryMemory { #[async_trait] impl Memory for SupermemoryMemory { + /// Returns the Supermemory driver identifier. fn name(&self) -> &str { self.inner.name() } + /// Stores an internally sourced record through the shared contract. async fn store( &self, n: &str, @@ -48,6 +50,7 @@ impl Memory for SupermemoryMemory { ) -> anyhow::Result<()> { self.inner.store(n, k, c, cat, s).await } + /// Stores a record while preserving its provenance taint. async fn store_with_taint( &self, n: &str, @@ -59,6 +62,7 @@ impl Memory for SupermemoryMemory { ) -> anyhow::Result<()> { self.inner.store_with_taint(n, k, c, cat, s, t).await } + /// Runs native Supermemory search and applies TinyMemory filters. async fn recall( &self, q: &str, @@ -67,6 +71,7 @@ impl Memory for SupermemoryMemory { ) -> anyhow::Result> { self.inner.recall(q, l, o).await } + /// Fetches one exact namespace/key record. async fn get( &self, n: &str, @@ -74,6 +79,7 @@ impl Memory for SupermemoryMemory { ) -> anyhow::Result> { self.inner.get(n, k).await } + /// Lists records matching the supplied TinyMemory filters. async fn list( &self, n: Option<&str>, @@ -82,28 +88,34 @@ impl Memory for SupermemoryMemory { ) -> anyhow::Result> { self.inner.list(n, c, s).await } + /// Deletes one exact namespace/key record. async fn forget(&self, n: &str, k: &str) -> anyhow::Result { self.inner.forget(n, k).await } + /// Summarizes every namespace visible through this adapter. async fn namespace_summaries( &self, ) -> anyhow::Result> { self.inner.namespace_summaries().await } + /// Counts every record visible through this adapter. async fn count(&self) -> anyhow::Result { self.inner.count().await } + /// Checks whether the configured Supermemory service is reachable. async fn health_check(&self) -> bool { self.inner.health_check().await } } #[derive(Debug)] +/// Supermemory-specific REST operations and wire-format conversion. struct SupermemoryDialect { client: HttpClient, } impl SupermemoryDialect { + /// Encodes TinyMemory identity, classification, session, and provenance. fn metadata(entry: &StoredEntry) -> Value { let mut metadata = serde_json::Map::from_iter([ ("tinymemory_namespace".into(), json!(entry.namespace)), @@ -120,6 +132,7 @@ impl SupermemoryDialect { Value::Object(metadata) } + /// Decodes a Supermemory result containing TinyMemory-owned metadata. fn decode(value: &Value) -> Option { let metadata = value.get("metadata")?.as_object()?; Some(StoredEntry { @@ -152,6 +165,7 @@ impl SupermemoryDialect { }) } + /// Enumerates memories separately for each discovered container tag. async fn memories(&self) -> anyhow::Result> { let tags: Value = self .client @@ -221,10 +235,12 @@ impl SupermemoryDialect { #[async_trait] impl Dialect for SupermemoryDialect { + /// Returns the stable Supermemory driver identifier. fn name(&self) -> &'static str { SUPERMEMORY_DRIVER_ID } + /// Replaces an existing exact record or creates a direct v4 memory. async fn upsert(&self, entry: StoredEntry) -> anyhow::Result<()> { let existing = self .memories() @@ -263,10 +279,12 @@ impl Dialect for SupermemoryDialect { Ok(()) } + /// Enumerates TinyMemory-owned Supermemory records. async fn entries(&self) -> anyhow::Result> { self.memories().await } + /// Executes Supermemory's native v4 search. async fn search( &self, query: &str, @@ -299,6 +317,7 @@ impl Dialect for SupermemoryDialect { .collect()) } + /// Finds and deletes an exact TinyMemory logical record. async fn delete(&self, namespace: &str, key: &str) -> anyhow::Result { let Some(entry) = self .memories() @@ -322,6 +341,7 @@ impl Dialect for SupermemoryDialect { Ok(true) } + /// Checks the local server root, its stable availability endpoint. async fn health(&self) -> bool { self.client.healthy("").await }