Skip to content

Commit f46ce4f

Browse files
committed
dev
1 parent 9ca7573 commit f46ce4f

23 files changed

Lines changed: 678 additions & 295 deletions

.github/workflows/cd.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: CD
2+
3+
on:
4+
push:
5+
tags: ["v*.*.*"]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
call-cd:
13+
uses: qntx/workflows/.github/workflows/rust-cd.yml@main
14+
with:
15+
bin: meme
16+
package: meme-cli

Cargo.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ default-members = ["meme"]
44
resolver = "3"
55

66
[workspace.package]
7-
version = "0.2.0"
7+
version = "0.2.1"
88
edition = "2024"
99
license = "MIT OR Apache-2.0"
1010
repository = "https://github.com/qntx/meme"

README.md

Lines changed: 161 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,166 @@
11
# meme
22

3-
Long term memory for AI agents.
3+
[![CI][ci-badge]][ci-url]
4+
[![License][license-badge]][license-url]
5+
[![Rust][rust-badge]][rust-url]
6+
7+
[ci-badge]: https://github.com/qntx/meme/actions/workflows/rust.yml/badge.svg
8+
[ci-url]: https://github.com/qntx/meme/actions/workflows/rust.yml
9+
[license-badge]: https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg
10+
[license-url]: LICENSE-MIT
11+
[rust-badge]: https://img.shields.io/badge/rust-edition%202024-orange.svg
12+
[rust-url]: https://doc.rust-lang.org/edition-guide/
13+
14+
**High-performance long-term memory for AI agents — three-stage pipeline with semantic compression, hybrid retrieval, and cross-session persistence, written in Rust.**
15+
16+
meme implements the [SimpleMem](3rdparty/SimpleMem/) three-stage memory pipeline with a production-grade Rust core: (1) **Semantic Structured Compression** extracts lossless, disambiguated memory entries from dialogues via LLM, (2) **Online Semantic Synthesis** deduplicates at write time, and (3) **Intent-Aware Retrieval Planning** combines semantic, lexical (FTS), and structured metadata search with LLM-driven reflection. A full cross-session system persists memory across independent conversations.
17+
18+
## Crates
19+
20+
| Crate | | Description |
21+
| --- | --- | --- |
22+
| **[`meme`](meme/)** | [![crates.io][meme-crate]][meme-crate-url] [![docs.rs][meme-doc]][meme-doc-url] | Core library — pipeline, vector store, cross-session orchestrator |
23+
| **[`meme-cli`](meme-cli/)** | [![crates.io][cli-crate]][cli-crate-url] | CLI tool — add dialogues, ask questions, manage sessions |
24+
25+
[meme-crate]: https://img.shields.io/crates/v/meme.svg
26+
[meme-crate-url]: https://crates.io/crates/meme
27+
[meme-doc]: https://img.shields.io/docsrs/meme.svg
28+
[meme-doc-url]: https://docs.rs/meme
29+
[cli-crate]: https://img.shields.io/crates/v/meme-cli.svg
30+
[cli-crate-url]: https://crates.io/crates/meme-cli
31+
32+
## Quick Start
33+
34+
### Install the CLI
35+
36+
**Shell** (macOS / Linux):
37+
38+
```sh
39+
curl -fsSL https://sh.qntx.fun/meme | sh
40+
```
41+
42+
**PowerShell** (Windows):
43+
44+
```powershell
45+
irm https://sh.qntx.fun/meme/ps | iex
46+
```
47+
48+
### CLI
49+
50+
```bash
51+
# Initialize configuration
52+
meme init
53+
54+
# Add dialogues
55+
meme add -s Alice "I'll be in Tokyo next Monday for the conference."
56+
meme add -s Bob "Let's meet at Shibuya station at 3pm."
57+
58+
# Import from JSONL file
59+
meme add --file conversation.jsonl
60+
61+
# Ask questions
62+
meme ask "Where will Alice and Bob meet?"
63+
64+
# List stored memories
65+
meme list
66+
meme list --json
67+
68+
# Cross-session memory
69+
meme session start conv-001 -m "Refactor the auth module"
70+
meme session stop <session-id>
71+
meme session list
72+
73+
# Memory consolidation (decay/merge/prune)
74+
meme consolidate
75+
76+
# Export / import
77+
meme export -o memories.json
78+
```
79+
80+
### Library
81+
82+
```rust
83+
use meme::MemeBuilder;
84+
85+
let meme = MemeBuilder::new()
86+
.api_key("sk-...")
87+
.model("gpt-4.1-mini")
88+
.build()
89+
.await?;
90+
91+
// Add dialogues — automatically extracted into structured memory entries.
92+
meme.add_dialogue("Alice", "Let's meet at 2pm tomorrow", None).await?;
93+
meme.add_dialogue("Bob", "Sure, I'll bring the Q3 report", None).await?;
94+
meme.finalize().await?;
95+
96+
// Ask questions — hybrid retrieval + LLM answer generation.
97+
let answer = meme.ask("When will Alice meet?").await?;
98+
```
99+
100+
See [`examples/`](meme/examples/) for more: [basic](meme/examples/basic.rs), [cross-session](meme/examples/cross_session.rs), [batch import](meme/examples/batch_import.rs).
101+
102+
## Architecture
103+
104+
```text
105+
Dialogues ──► MemoryBuilder ──► VectorStore (LanceDB)
106+
(Stage 1+2) │
107+
├─ Semantic search (dense vectors)
108+
Query ──► HybridRetriever ────────►├─ Keyword search (FTS / Tantivy)
109+
(Stage 3) ├─ Structured search (metadata filters)
110+
│ │
111+
▼ │
112+
LLM Planning ◄───────────┘
113+
+ Reflection
114+
115+
116+
Answer Generation
117+
```
118+
119+
- **`meme`** — Core library. `Meme` facade wraps the three-stage pipeline behind `add_dialogue()` / `ask()`. `VectorStore` uses LanceDB for embedded vector + FTS indexing. `Embedder` supports API and local ONNX backends via enum dispatch (zero-cost). `LlmClient` is an OpenAI-compatible HTTP client with retry + exponential backoff.
120+
- **`meme-cli`** — Interactive CLI. TOML + env var configuration, JSONL import, table/JSON output, cross-session management.
121+
- **Cross-session**`CrossOrchestrator` manages session lifecycle (start → record events → stop → extract observations → inject context). SQLite stores sessions, events, observations, and summaries. Context injection fills a token-budgeted bundle at session start.
122+
123+
## Three-Stage Pipeline
124+
125+
### Stage 1: Semantic Structured Compression
126+
127+
Dialogues are windowed and sent to an LLM to extract **atomic, self-contained memory entries**. Each entry contains:
128+
129+
- **Lossless restatement** — complete sentence with no pronouns, no relative time
130+
- **Keywords** — for BM25-style lexical matching
131+
- **Structured metadata** — timestamp, location, persons, entities, topic
132+
133+
### Stage 2: Online Semantic Synthesis
134+
135+
Previous-window entries are passed as context during extraction to avoid duplicating information across overlapping windows.
136+
137+
### Stage 3: Intent-Aware Retrieval Planning
138+
139+
A single LLM call analyzes the query to produce a **unified plan** containing:
140+
141+
- Extracted keywords, persons, entities, time expressions
142+
- Targeted search queries for semantic retrieval
143+
- Required information types for completeness assessment
144+
145+
The plan drives parallel execution of three search views (semantic, keyword, structured), followed by optional **reflection** rounds that assess completeness and issue additional targeted queries.
146+
147+
## Feature Flags
148+
149+
| Feature | Description |
150+
| --- | --- |
151+
| `api-embedding` | Remote API-based embedding (enabled by default) |
152+
| `onnx` | Local ONNX Runtime embedding via `ort` + `tokenizers` |
153+
154+
## Configuration
155+
156+
Configuration is loaded from `~/.meme/config.toml` with environment variable overrides:
157+
158+
| Env Var | Description |
159+
| --- | --- |
160+
| `MEME_LLM_API_KEY` | OpenAI-compatible API key |
161+
| `MEME_LLM_BASE_URL` | API base URL |
162+
| `MEME_LLM_MODEL` | Model name (default: `gpt-4.1-mini`) |
163+
| `MEME_EMBEDDING_PROVIDER` | `api` or `onnx` |
4164

5165
## License
6166

install.ps1

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# CLI installer — downloads the latest release binary from GitHub.
2+
# Configure $Repo and $Bin below. Everything else is derived automatically.
3+
#
4+
# Environment (upper-cased $Bin prefix):
5+
# <BIN>_VERSION Override version (default: latest)
6+
# <BIN>_INSTALL_DIR Override install directory (default: %LOCALAPPDATA%\<bin>)
7+
8+
$ErrorActionPreference = "Stop"
9+
$InformationPreference = "Continue"
10+
$Repo = "qntx/meme"
11+
$Bin = "meme"
12+
13+
$BinUpper = $Bin.ToUpper()
14+
$VerEnv = "${BinUpper}_VERSION"
15+
$DirEnv = "${BinUpper}_INSTALL_DIR"
16+
17+
function Get-TargetArch {
18+
try {
19+
$a = [System.Reflection.Assembly]::LoadWithPartialName("System.Runtime.InteropServices.RuntimeInformation")
20+
switch ($a.GetType("System.Runtime.InteropServices.RuntimeInformation").GetProperty("OSArchitecture").GetValue($null).ToString()) {
21+
"X64" { return "x86_64-pc-windows-msvc" }
22+
"Arm64" { return "aarch64-pc-windows-msvc" }
23+
}
24+
}
25+
catch {}
26+
if ([Environment]::Is64BitOperatingSystem) { return "x86_64-pc-windows-msvc" }
27+
throw "32-bit Windows is not supported"
28+
}
29+
30+
function Get-LatestVersion {
31+
$tag = (Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest").tag_name
32+
if ($tag.StartsWith("v")) { $tag = $tag.Substring(1) }
33+
return $tag
34+
}
35+
36+
function Add-ToUserPath($Dir) {
37+
$reg = 'registry::HKEY_CURRENT_USER\Environment'
38+
$current = (Get-Item -LiteralPath $reg).GetValue('Path', '', 'DoNotExpandEnvironmentNames') -split ';' -ne ''
39+
if ($Dir -in $current) { return }
40+
41+
Set-ItemProperty -Type ExpandString -LiteralPath $reg Path ((@($Dir) + $current) -join ';')
42+
# Broadcast WM_SETTINGCHANGE so Explorer picks up the new PATH
43+
$k = "$Bin-" + [guid]::NewGuid().ToString()
44+
[Environment]::SetEnvironmentVariable($k, "1", "User")
45+
[Environment]::SetEnvironmentVariable($k, [NullString]::value, "User")
46+
47+
Write-Information " Added $Dir to PATH (restart your shell to apply)"
48+
}
49+
50+
try {
51+
$target = Get-TargetArch
52+
$envVer = [Environment]::GetEnvironmentVariable($VerEnv)
53+
$envDir = [Environment]::GetEnvironmentVariable($DirEnv)
54+
$ver = if ($envVer) { $envVer } else { Get-LatestVersion }
55+
$dir = if ($envDir) { $envDir } else { Join-Path $env:LOCALAPPDATA $Bin }
56+
57+
Write-Information "Installing $Bin v$ver ($target)"
58+
59+
$url = "https://github.com/$Repo/releases/download/v$ver/$Bin-$ver-$target.zip"
60+
$tmp = New-Item -ItemType Directory -Path (Join-Path ([IO.Path]::GetTempPath()) ([Guid]::NewGuid()))
61+
62+
try {
63+
Invoke-WebRequest -Uri $url -OutFile "$tmp\archive.zip" -UseBasicParsing
64+
Expand-Archive "$tmp\archive.zip" -DestinationPath $tmp -Force
65+
66+
$null = New-Item -ItemType Directory -Force -Path $dir
67+
Copy-Item "$tmp\$Bin.exe" -Destination $dir -Force
68+
Write-Information " -> $(Join-Path $dir "$Bin.exe")"
69+
}
70+
finally {
71+
Remove-Item $tmp -Recurse -Force -ErrorAction SilentlyContinue
72+
}
73+
74+
# Add to PATH if needed
75+
if ($env:GITHUB_PATH) {
76+
$dir | Out-File $env:GITHUB_PATH -Encoding utf8 -Append
77+
}
78+
elseif (-not ($env:Path -split ';' | Where-Object { $_ -eq $dir })) {
79+
Add-ToUserPath $dir
80+
}
81+
82+
Write-Information "`n$Bin v$ver installed successfully!"
83+
}
84+
catch {
85+
Write-Error $_
86+
exit 1
87+
}

0 commit comments

Comments
 (0)