aimem_core/lib.rs
1//! # aimem-core
2//!
3//! **Give your AI a memory — reusable core library.**
4//!
5//! `aimem-core` is a pure-Rust library that stores, retrieves, and searches
6//! text memories using [Turso](https://github.com/tursodatabase/turso) (an in-process
7//! SQLite engine with native vector support) as the sole storage backend.
8//! No external vector database, no cloud service, no API key.
9//!
10//! ## Feature overview
11//!
12//! | Feature | Implementation |
13//! |---------|---------------|
14//! | Vector storage | Turso `vector32()` BLOB column |
15//! | Semantic search | `vector_distance_cos()` — SimSIMD-accelerated cosine |
16//! | Full-text search | SQL `LIKE` scan (Turso FTS index optional) |
17//! | Embedding generation | [`fastembed`](https://crates.io/crates/fastembed) — `all-MiniLM-L6-v2` locally |
18//! | Knowledge graph | Temporal entity-relationship triples in SQLite |
19//! | Graph traversal | BFS over wing/room adjacency — no graph DB needed |
20//! | MCP protocol | Implemented in `aimem-mcp` (separate crate) |
21//!
22//! ## Architecture: 4-Layer Memory Stack
23//!
24//! ```text
25//! ┌─────────────────────────────────────────────────────────┐
26//! │ L0 Identity text ~100 tokens always loaded │
27//! │ L1 Essential story ~500-800 tok always loaded │
28//! │ L2 On-demand recall ~200-500 tok loaded on ask │
29//! │ L3 Deep semantic search unlimited full memory │
30//! └─────────────────────────────────────────────────────────┘
31//! ```
32//!
33//! ## Storage model
34//!
35//! Everything lives in a **single Turso DB file** (default: `~/.aimem/aimem.db`):
36//!
37//! ```text
38//! aimem.db
39//! ├── drawers — verbatim text chunks + vector32 embeddings
40//! ├── entities — knowledge graph nodes
41//! └── triples — temporal (subject → predicate → object) edges
42//! ```
43//!
44//! ## Concepts
45//!
46//! | Term | Meaning |
47//! |------|---------|
48//! | **Store** | The entire AiMem memory store (one DB file) |
49//! | **Wing** | A named project or domain (e.g. `"my_app"`, `"journal"`) |
50//! | **Room** | A topic within a wing (e.g. `"backend"`, `"decisions"`) |
51//! | **Drawer** | One verbatim text chunk (800 chars) with its embedding |
52//! | **Tunnel** | A room that spans multiple wings — a conceptual bridge |
53//!
54//! ## Quick start
55//!
56//! ```toml
57//! # Cargo.toml
58//! [dependencies]
59//! tokio = { version = "1", features = ["full"] }
60//! ```
61//!
62//! ```bash
63//! cargo add aimem-core
64//! ```
65//!
66//! ```rust,no_run
67//! use aimem_core::prelude::*;
68//!
69//! #[tokio::main]
70//! async fn main() -> anyhow::Result<()> {
71//! // 1. Load config (env > ~/.aimem/config.json > defaults)
72//! let cfg = Config::load()?;
73//!
74//! // 2. Open the AiMem DB (creates DB + schema on first run)
75//! let db = AimemDb::open(&cfg.db_path).await?;
76//!
77//! // 3. Load local embedding model (downloads ~23 MB on first use)
78//! let embedder = Embedder::new()?;
79//!
80//! // 4. Build the memory stack
81//! let stack = MemoryStack::new(db, embedder, &cfg);
82//!
83//! // 5. Wake-up prompt: L0 (identity) + L1 (top memories) — inject into LLM context
84//! println!("{}", stack.wake_up(None).await?);
85//!
86//! // 6. Semantic search — uses Turso vector_distance_cos() natively
87//! println!("{}", stack.search("why did we choose Rust?", None, None).await?);
88//!
89//! Ok(())
90//! }
91//! ```
92//!
93//! ## Mining a project
94//!
95//! ```rust,no_run
96//! use aimem_core::prelude::*;
97//!
98//! #[tokio::main]
99//! async fn main() -> anyhow::Result<()> {
100//! let cfg = Config::load()?;
101//! let db = AimemDb::open(&cfg.db_path).await?;
102//! let embedder = Embedder::new()?;
103//!
104//! let miner = Miner::new(db, Some(embedder));
105//!
106//! // Reads aimem.yaml in the project directory for wing/room config
107//! let stats = miner.mine("~/projects/my_app", None, "claude", 0, false).await?;
108//!
109//! println!(
110//! "Mined {} drawers from {} files",
111//! stats.drawers_added,
112//! stats.files_scanned - stats.files_skipped,
113//! );
114//! Ok(())
115//! }
116//! ```
117//!
118//! ## Keyword-only search without embeddings
119//!
120//! If you mine with `Miner::new(db, None)` or `ConvoMiner::new(db, None)`,
121//! drawers still participate in SQL keyword search even though they have no embeddings.
122//!
123//! ```rust,no_run
124//! use aimem_core::prelude::*;
125//!
126//! #[tokio::main]
127//! async fn main() -> anyhow::Result<()> {
128//! let db = AimemDb::memory().await?;
129//! let drawer = Drawer {
130//! id: "drawer_kw_001".into(),
131//! wing: "demo".into(),
132//! room: "backend".into(),
133//! content: "Turso keeps the backend local and simple.".into(),
134//! source_file: None,
135//! chunk_index: 0,
136//! added_by: "example".into(),
137//! filed_at: chrono::Utc::now().to_rfc3339(),
138//! };
139//! db.insert_drawer(&drawer, None).await?;
140//!
141//! let searcher = Searcher::keyword_only(db);
142//! let hits = searcher.keyword_search("Turso", Some("demo"), None, 5).await?;
143//! assert_eq!(hits.len(), 1);
144//! Ok(())
145//! }
146//! ```
147//!
148//! ## Knowledge graph
149//!
150//! ```rust,no_run
151//! use aimem_core::prelude::*;
152//!
153//! #[tokio::main]
154//! async fn main() -> anyhow::Result<()> {
155//! let db = AimemDb::memory().await?; // in-memory for demo
156//! let kg = KnowledgeGraph::new(db);
157//!
158//! // Store a temporal fact
159//! kg.add_triple("Max", "does", "swimming", Some("2025-01-01"), None).await?;
160//! kg.add_triple("Max", "loves", "chess", Some("2025-10-01"), None).await?;
161//!
162//! // Query all outgoing facts for "Max"
163//! let facts = kg.query_entity("Max", None, "outgoing").await?;
164//! for f in &facts {
165//! println!("{} → {} → {}", f.subject, f.predicate, f.object);
166//! }
167//!
168//! // Invalidate a fact (set valid_to = today)
169//! kg.invalidate("Max", "does", "swimming", None).await?;
170//!
171//! Ok(())
172//! }
173//! ```
174//!
175//! ## Low-level storage operations
176//!
177//! ```rust,no_run
178//! use aimem_core::prelude::*;
179//!
180//! #[tokio::main]
181//! async fn main() -> anyhow::Result<()> {
182//! let db = AimemDb::memory().await?;
183//! let embedder = Embedder::new()?;
184//!
185//! let content = "We decided to use Rust for performance and memory safety.";
186//! let embedding = embedder.embed_one(content)?;
187//!
188//! let drawer = Drawer {
189//! id: "drawer_test_decision_001".to_string(),
190//! wing: "my_project".to_string(),
191//! room: "decisions".to_string(),
192//! content: content.to_string(),
193//! source_file: Some("DECISIONS.md".to_string()),
194//! chunk_index: 0,
195//! added_by: "claude".to_string(),
196//! filed_at: chrono::Utc::now().to_rfc3339(),
197//! };
198//!
199//! let inserted = db.insert_drawer(&drawer, Some(&embedding)).await?;
200//! println!("Inserted: {inserted}");
201//! println!("Total drawers: {}", db.drawer_count().await?);
202//!
203//! Ok(())
204//! }
205//! ```
206//!
207//! ## Crate feature flags
208//!
209//! `aimem-core` has no optional features itself — the embedding model
210//! is always available. The `hf-hub` feature of `fastembed` controls whether
211//! `Embedder::new()` can download models automatically.
212//!
213//! ## Reusing in other projects
214//!
215//! Because this is a library crate with no binary, any project can depend on it:
216//!
217//! ```toml
218//! [dependencies]
219//! aimem-core = { git = "https://github.com/v1cc0/aimem" }
220//! # or local path:
221//! aimem-core = { path = "/path/to/aimem/crates/aimem-core" }
222//! ```
223//!
224//! The public re-exports in this file form the stable API surface.
225//! Consumers should prefer `aimem_core::{...}` root imports over deep module
226//! paths. The root re-exports are the intended stable integration surface.
227
228pub mod config;
229pub mod convo;
230pub mod db;
231pub mod embedder;
232pub mod extractor;
233pub mod graph;
234pub mod knowledge;
235pub mod layers;
236pub mod miner;
237pub mod search;
238pub mod types;
239
240pub use config::Config;
241pub use convo::{ConvoMineStats, ConvoMiner};
242pub use db::AimemDb;
243pub use embedder::Embedder;
244pub use graph::{AimemGraph, TraversalNode};
245pub use knowledge::KnowledgeGraph;
246pub use layers::MemoryStack;
247pub use miner::Miner;
248pub use search::Searcher;
249pub use types::{Drawer, DrawerMeta, Entity, RoomNode, SearchResult, Triple, Tunnel};
250
251/// Curated high-level imports for typical AiMem integrations.
252///
253/// This is the easiest way to get the common building blocks without pulling
254/// in deeper module paths.
255///
256/// ```rust
257/// use aimem_core::prelude::*;
258///
259/// fn _accepts_common_types(
260/// _cfg: Config,
261/// _db: AimemDb,
262/// _drawer: Drawer,
263/// _searcher: Searcher,
264/// _miner: Miner,
265/// _convo: ConvoMiner,
266/// _graph: AimemGraph,
267/// _kg: KnowledgeGraph,
268/// _stack: MemoryStack,
269/// _result: SearchResult,
270/// _triple: Triple,
271/// ) {
272/// }
273/// ```
274pub mod prelude {
275 pub use crate::{
276 AimemDb, AimemGraph, Config, ConvoMineStats, ConvoMiner, Drawer, DrawerMeta, Embedder,
277 Entity, KnowledgeGraph, MemoryStack, Miner, RoomNode, SearchResult, Searcher, Triple,
278 Tunnel,
279 };
280}