Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ exclude = [
# one dependency rather than two, and so `tinymemory::MemoryProvider` and
# `tinymemory_api::provider::MemoryProvider` are the same type.
tinymemory-api = { path = "api" }
# The engines, each behind its own feature (#18 §D1). Optional, so the default
# build is still the contract, the registry and the mandatory composition and
# nothing that links C.
#
# This direction only became legal once `mandatory` and the reserved driver ids
# moved into `tinymemory-api`: the adapters used to depend on this crate for
# them, and a facade depending back on the adapters is a package cycle cargo
# forbids.
tinymemory-tinycortex = { path = "adapters/tinycortex", optional = true }
tinymemory-remote = { path = "adapters/remote", optional = true }
# The mandatory capability families are `async fn`s on object-safe traits.
async-trait = "0.1"
# `Memory` is anyhow-typed; `mandatory::engine_error` maps it onto `MemoryError`.
Expand Down Expand Up @@ -85,8 +95,23 @@ tinymemory-conformance = { path = "conformance" }
tinymemory-sync = { path = "sync" }

[features]
# Nothing by default. A host that names no engine links no engine.
default = []

# --- Engines ---------------------------------------------------------------
# Each pulls exactly the adapter that serves it. `supermemory`, `mem0` and
# `cognee` share one adapter crate, so enabling several costs one dependency
# rather than three.
tinycortex = ["dep:tinymemory-tinycortex"]
supermemory = ["dep:tinymemory-remote"]
mem0 = ["dep:tinymemory-remote"]
cognee = ["dep:tinymemory-remote"]

# --- Capability add-ons ----------------------------------------------------
# Each requires the engine that serves it, so asking for a capability cannot
# produce a build where nothing implements it.
memory-git = ["tinycortex", "tinymemory-tinycortex/memory-git"]

# Lints apply to this package only, deliberately. `api/` is contract code moved
# verbatim from `tinycortex-api` and is held byte-identical; subjecting it to a
# stricter lint set than it was written under would force cosmetic edits through
Expand Down
1 change: 0 additions & 1 deletion adapters/remote/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ repository = "https://github.com/tinyhumansai/tinymemory"

[dependencies]
# The engine-neutral contract and mandatory-family composition.
tinymemory = { path = "../.." }
tinymemory-api = { path = "../../api" }
# Memory is an object-safe async trait and each native HTTP dialect is async.
async-trait = "0.1"
Expand Down
2 changes: 1 addition & 1 deletion adapters/remote/src/cognee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use tinymemory_api::types::MemoryTaint;
use crate::common::{stable_id, Dialect, HttpClient, RemoteMemory, StoredEntry};

/// Stable driver id used by configuration and status output.
pub use tinymemory::registry::COGNEE_DRIVER_ID;
pub use tinymemory_api::drivers::COGNEE_DRIVER_ID;

/// Default base URL for Cognee's managed API.
pub const COGNEE_API_ENDPOINT: &str = "https://api.cognee.ai";
Expand Down
4 changes: 2 additions & 2 deletions adapters/remote/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! The adapters preserve TinyMemory's exact `(namespace, key)` upsert contract
//! in backend metadata while delegating semantic recall to each engine's native
//! search API. They advertise Core, Recall, and Portability through
//! [`tinymemory::mandatory::MemoryTraitProvider`].
//! [`tinymemory_api::mandatory::MemoryTraitProvider`].
//!
//! Credentials are accepted only at construction and are never exposed by
//! `Debug` implementations or error messages.
Expand All @@ -19,7 +19,7 @@ pub use supermemory::{SupermemoryMemory, SUPERMEMORY_API_ENDPOINT, SUPERMEMORY_D

use std::sync::Arc;

use tinymemory::mandatory::MemoryTraitProvider;
use tinymemory_api::mandatory::MemoryTraitProvider;

/// Wrap a Supermemory HTTP backend as a bound TinyMemory provider.
#[must_use]
Expand Down
2 changes: 1 addition & 1 deletion adapters/remote/src/mem0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use tinymemory_api::types::MemoryTaint;
use crate::common::{category, Dialect, HttpClient, RemoteMemory, StoredEntry};

/// Stable driver id used by configuration and status output.
pub use tinymemory::registry::MEM0_DRIVER_ID;
pub use tinymemory_api::drivers::MEM0_DRIVER_ID;

/// A self-hosted Mem0 server exposed through TinyMemory's storage contract.
#[derive(Debug)]
Expand Down
2 changes: 1 addition & 1 deletion adapters/remote/src/supermemory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use tinymemory_api::types::MemoryTaint;
use crate::common::{category, stable_id, Dialect, HttpClient, RemoteMemory, StoredEntry};

/// Stable driver id used by configuration and status output.
pub use tinymemory::registry::SUPERMEMORY_DRIVER_ID;
pub use tinymemory_api::drivers::SUPERMEMORY_DRIVER_ID;

/// Default base URL for Supermemory's managed API.
pub const SUPERMEMORY_API_ENDPOINT: &str = "https://api.supermemory.ai";
Expand Down
1 change: 0 additions & 1 deletion adapters/tinycortex/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ repository = "https://github.com/tinyhumansai/tinymemory"

[dependencies]
# The contract this adapter targets.
tinymemory = { path = "../.." }
tinymemory-api = { path = "../../api" }
# The engine being adapted. A version requirement rather than a path, so a host
# that already pins its own TinyCortex checkout unifies both onto one copy
Expand Down
2 changes: 1 addition & 1 deletion adapters/tinycortex/src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ use std::sync::Arc;
use crate::TinycortexMemory;
use async_trait::async_trait;
use chrono::Utc;
use tinymemory::mandatory::MemoryTraitProvider;
use tinymemory_api::capabilities::Capabilities;
use tinymemory_api::chunks::Chunk;
use tinymemory_api::error::MemoryError;
Expand All @@ -31,6 +30,7 @@ use tinymemory_api::host::{
CloudProviderCreds, ComposioMode, LocalAiConfig, MemoryConfig, MemoryHostConfig,
MemoryTreeConfig, SchedulerGateConfig,
};
use tinymemory_api::mandatory::MemoryTraitProvider;
use tinymemory_api::provider::types::{
EntityHit, EntityRef, ExportPage, ExportRecord, ImportOutcome, IngestItem, IngestOutcome,
MaintenanceReport, SourceItem, SourceScope,
Expand Down
6 changes: 3 additions & 3 deletions adapters/tinycortex/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,14 @@ pub use memory::TinycortexMemory;

use std::sync::Arc;

use tinymemory::mandatory::MemoryTraitProvider;
use tinymemory_api::mandatory::MemoryTraitProvider;

/// The driver id this adapter binds under.
///
/// Matches [`tinymemory::registry::TINYCORTEX_DRIVER_ID`], which is where
/// Matches [`tinymemory_api::drivers::TINYCORTEX_DRIVER_ID`], which is where
/// admission reserves it — the constant lives there so a host that compiles
/// this adapter out still refuses to bind something else under the name.
pub use tinymemory::registry::TINYCORTEX_DRIVER_ID;
pub use tinymemory_api::drivers::TINYCORTEX_DRIVER_ID;

/// Wrap a TinyCortex backend as a bound memory driver.
///
Expand Down
4 changes: 4 additions & 0 deletions api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ uuid = { version = "1", features = ["v4"] }
# The moved `host::` config sections are parsed from TOML in their own tests,
# exactly as the host parses them from `config.toml`.
toml = "0.9"
# The mandatory-composition tests are async — they drive a `MemoryProvider`.
# Dev-only, so it does not touch the forbidden-dependency rule this crate's
# manifest enforces for its normal graph (#18 §D4).
tokio = { version = "1", features = ["macros", "rt"] }

[features]
default = []
Expand Down
44 changes: 44 additions & 0 deletions api/src/drivers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//! The reserved driver ids.
//!
//! These name the engines this workspace ships. They live in the contract crate
//! rather than in the facade's registry for the same reason
//! [`crate::null::NULL_DRIVER_ID`] already did: an adapter has to spell the id
//! it binds under, and reaching into the facade for it made every adapter
//! depend on the facade — which in turn made the facade unable to depend on the
//! adapters, a package cycle cargo forbids. That cycle is what blocked #18
//! §D1's per-engine features.
//!
//! Reserving them here does **not** mean the contract knows about these
//! engines. It knows their *names*, so that admission can refuse something else
//! binding under one — a host that compiles an adapter out must still reject an
//! impostor claiming its id. The class each id is admitted under stays in the
//! facade's registry, where the trust decision belongs.
//!
//! `tinymemory::registry` re-exports all of these, so existing paths resolve
//! unchanged.

/// The driver id of the bundled TinyCortex embedded engine.
pub const TINYCORTEX_DRIVER_ID: &str = "tinycortex";

/// The driver id of `tinymemory-core`'s own in-process store.
///
/// Distinct from [`TINYCORTEX_DRIVER_ID`], and the distinction is the point:
/// that one names the bundled TinyCortex engine, this one names
/// `tinymemory_core::store::UnifiedMemory` — a separate SQLite store this
/// workspace implements itself. Both are `Embedded`; they are not the same
/// engine, and a host that binds one has not bound the other.
///
/// Named for what `create_memory` has always called this backend
/// (`effective_memory_backend_name` returns `"namespace"`), so the id an
/// operator sees in status matches the name already in the logs rather than
/// introducing a third vocabulary for one store.
pub const NAMESPACE_DRIVER_ID: &str = "namespace";

/// Driver id of the native Supermemory HTTP adapter.
pub const SUPERMEMORY_DRIVER_ID: &str = "supermemory";

/// Driver id of the native Mem0 HTTP adapter.
pub const MEM0_DRIVER_ID: &str = "mem0";

/// Driver id of the native Cognee HTTP adapter.
pub const COGNEE_DRIVER_ID: &str = "cognee";
12 changes: 12 additions & 0 deletions api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,22 @@

pub mod capabilities;
pub mod chunks;
pub mod drivers;
pub mod error;
pub mod goals;
pub mod health;
pub mod host;
/// The mandatory-family composition: wrap any [`traits::Memory`] backend as a
/// complete [`provider::MemoryProvider`].
///
/// Lives here rather than in the `tinymemory` facade because every adapter
/// needs it, and an adapter that reached for it in the facade made the facade
/// unable to depend on adapters in turn — a package cycle cargo forbids, and
/// the reason #18 §D1's engine features could not be declared. It costs this
/// crate nothing: the module names only `async_trait`, `std`, and this crate's
/// own contract types. The facade re-exports it, so `tinymemory::mandatory`
/// keeps resolving.
pub mod mandatory;
pub mod null;
pub mod provider;
pub mod recall;
Expand Down
40 changes: 20 additions & 20 deletions src/mandatory/mod.rs → api/src/mandatory/mod.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,49 @@
//! The three mandatory capability families, composed over the storage trait.
//!
//! [`MemoryCore`](tinymemory_api::provider::MemoryCore), [`MemoryRecall`](tinymemory_api::provider::MemoryRecall) and [`MemoryPortability`](tinymemory_api::provider::MemoryPortability) are supertraits of
//! [`MemoryProvider`](tinymemory_api::provider::MemoryProvider): a driver
//! [`MemoryCore`](crate::provider::MemoryCore), [`MemoryRecall`](crate::provider::MemoryRecall) and [`MemoryPortability`](crate::provider::MemoryPortability) are supertraits of
//! [`MemoryProvider`](crate::provider::MemoryProvider): a driver
//! missing any of them cannot be constructed at all. For a backend that already
//! implements [`Memory`], almost all three are mechanical — and the parts that
//! implements [`Memory`](crate::traits::Memory), almost all three are mechanical — and the parts that
//! are *not* mechanical are the parts every such backend gets wrong the same
//! way. So they live here once rather than in each driver.
//!
//! ## The four things that are not a straight delegation
//!
//! 1. **`store` maps onto [`Memory::store_with_taint`], never [`Memory::store`].**
//! The contract's `store` always carries a [`MemoryTaint`](tinymemory_api::types::MemoryTaint), because
//! 1. **`store` maps onto [`Memory::store_with_taint`](crate::traits::Memory::store_with_taint), never [`Memory::store`](crate::traits::Memory::store).**
//! The contract's `store` always carries a [`MemoryTaint`](crate::types::MemoryTaint), because
//! provenance is stamped by the host's policy layer *before* the call.
//! [`Memory::store`] hard-codes [`MemoryTaint::Internal`](tinymemory_api::types::MemoryTaint::Internal), so routing through
//! [`Memory::store`](crate::traits::Memory::store) hard-codes [`MemoryTaint::Internal`](crate::types::MemoryTaint::Internal), so routing through
//! it would launder externally-sourced content into internal-trust content —
//! the one failure mode a provenance guard exists to prevent. Note
//! [`Memory::store_with_taint`]'s *trait default* also silently drops the
//! [`Memory::store_with_taint`](crate::traits::Memory::store_with_taint)'s *trait default* also silently drops the
//! taint, so a backend that does not override it is unsafe here; that is a
//! backend bug, not something this layer can paper over.
//!
//! 2. **`list(None, ..)` spans every namespace.** The contract says an
//! all-`None` list returns everything the driver holds. A typical [`Memory`]
//! all-`None` list returns everything the driver holds. A typical [`Memory`](crate::traits::Memory)
//! implementation normalises a `None` namespace to
//! [`GLOBAL_NAMESPACE`], so a naive delegation returns one namespace and
//! calls it "everything". [`list_everything`] composes `namespace_summaries`
//! [`GLOBAL_NAMESPACE`](crate::types::GLOBAL_NAMESPACE), so a naive delegation returns one namespace and
//! calls it "everything". [`list_everything`](crate::mandatory::list_everything) composes `namespace_summaries`
//! with a per-namespace `list` instead.
//!
//! 3. **A scoped recall is refused, not ignored.** See [`recall`].
//!
//! 4. **Import must not re-stamp provenance.** See [`import_records`].
//! 4. **Import must not re-stamp provenance.** See [`import_records`](crate::mandatory::import_records).
//!
//! ## Why free functions rather than a blanket impl
//!
//! A blanket `impl<T: SomeHandleTrait> MemoryCore for T` would collide with any
//! driver that wants to override one method, and would force every driver to
//! resolve its handle through one shape. These are plain functions taking
//! `&dyn Memory`, so a driver delegates the parts it wants and keeps its own
//! logging, laziness, and error context. [`MemoryTraitProvider`] is the
//! logging, laziness, and error context. [`MemoryTraitProvider`](crate::mandatory::MemoryTraitProvider) is the
//! batteries-included alternative for a backend that wants all three whole.

use tinymemory_api::error::MemoryError;
use tinymemory_api::provider::types::{ExportPage, ExportRecord, ImportOutcome, SourceScope};
use tinymemory_api::recall::OwnedRecallOpts;
use tinymemory_api::traits::Memory;
use tinymemory_api::types::{MemoryCategory, MemoryEntry, RecallOpts, GLOBAL_NAMESPACE};
use crate::error::MemoryError;
use crate::provider::types::{ExportPage, ExportRecord, ImportOutcome, SourceScope};
use crate::recall::OwnedRecallOpts;
use crate::traits::Memory;
use crate::types::{MemoryCategory, MemoryEntry, RecallOpts, GLOBAL_NAMESPACE};

mod provider;

Expand Down Expand Up @@ -168,7 +168,7 @@ fn parse_cursor(cursor: Option<&str>) -> Result<(usize, usize), MemoryError> {

/// Renders one entry as an export record.
///
/// A record round-trips the five fields [`MemoryCore`](tinymemory_api::provider::MemoryCore)
/// A record round-trips the five fields [`MemoryCore`](crate::provider::MemoryCore)
/// owns — `key`, `content`, `category`, `session_id`, `taint` — plus its
/// namespace and timestamp. Document-tier attributes (`title`, `tags`,
/// `metadata`, `source_type`, `priority`) belong to the `Documents` family and
Expand Down Expand Up @@ -332,10 +332,10 @@ pub async fn export_page(

/// `MemoryPortability::import_records` over a [`Memory`] backend.
///
/// Each record is stored with its **own** [`MemoryTaint`](tinymemory_api::types::MemoryTaint) via
/// Each record is stored with its **own** [`MemoryTaint`](crate::types::MemoryTaint) via
/// [`Memory::store_with_taint`]: an importing driver must persist the
/// provenance it is given and must not re-stamp it. [`Memory::store`] would
/// stamp [`MemoryTaint::Internal`](tinymemory_api::types::MemoryTaint::Internal), quietly upgrading the trust of every
/// stamp [`MemoryTaint::Internal`](crate::types::MemoryTaint::Internal), quietly upgrading the trust of every
/// externally-sourced record in a restore.
///
/// Per-record rejection is reported in [`ImportOutcome`], never fatal: a
Expand Down
28 changes: 14 additions & 14 deletions src/mandatory/provider.rs → api/src/mandatory/provider.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
//! [`MemoryTraitProvider`] — a complete, mandatory-only
//! [`MemoryProvider`](tinymemory_api::provider::MemoryProvider) over any
//! [`Memory`] backend.
//! [`MemoryTraitProvider`](crate::mandatory::MemoryTraitProvider) — a complete, mandatory-only
//! [`MemoryProvider`](crate::provider::MemoryProvider) over any
//! [`Memory`](crate::traits::Memory) backend.
//!
//! ## What this is for
//!
//! Two things, and it is worth being clear which is which.
//!
//! **A real driver for a simple backend.** A store that implements [`Memory`]
//! **A real driver for a simple backend.** A store that implements [`Memory`](crate::traits::Memory)
//! becomes a bindable memory driver by wrapping it here — no capability
//! plumbing, no export format to invent. It advertises exactly the three
//! mandatory families, so a host binding it gets a memory subsystem whose
Expand All @@ -21,7 +21,7 @@
//! No optional families. Every `as_*` accessor keeps the contract's `None`
//! default, and [`capabilities`](MemoryTraitProvider::capabilities) reports the
//! mandatory three — so the two halves agree and
//! [`audit_provider`](tinymemory_api::provider::audit_provider) passes. A driver
//! [`audit_provider`](crate::provider::audit_provider) passes. A driver
//! that wants documents, trees, or a diff ledger implements those families over
//! its own engine and delegates only the mandatory three here.
//!
Expand All @@ -30,19 +30,19 @@

use std::sync::Arc;

use crate::capabilities::{Capabilities, Capability};
use crate::error::MemoryError;
use crate::health::MemoryHealth;
use crate::provider::types::{ExportPage, ExportRecord, ImportOutcome, SourceScope};
use crate::provider::{MemoryCore, MemoryPortability, MemoryProvider, MemoryRecall};
use crate::recall::OwnedRecallOpts;
use crate::traits::Memory;
use crate::types::{MemoryCategory, MemoryEntry, MemoryTaint, NamespaceSummary};
use async_trait::async_trait;
use tinymemory_api::capabilities::{Capabilities, Capability};
use tinymemory_api::error::MemoryError;
use tinymemory_api::health::MemoryHealth;
use tinymemory_api::provider::types::{ExportPage, ExportRecord, ImportOutcome, SourceScope};
use tinymemory_api::provider::{MemoryCore, MemoryPortability, MemoryProvider, MemoryRecall};
use tinymemory_api::recall::OwnedRecallOpts;
use tinymemory_api::traits::Memory;
use tinymemory_api::types::{MemoryCategory, MemoryEntry, MemoryTaint, NamespaceSummary};

use super::{engine_error, export_page, import_records, list_everything, recall};

/// A mandatory-only memory driver over an [`Memory`] backend.
/// A mandatory-only memory driver over an [`Memory`](crate::traits::Memory) backend.
#[derive(Clone)]
pub struct MemoryTraitProvider {
memory: Arc<dyn Memory>,
Expand Down
Loading
Loading