This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
# aether-tools-rs — atom + 4 binaries
|
||||
|
||||
The unified rewrite of aether-tools onto a single atomic primitive.
|
||||
**1517 production LOC across 4 binaries** (vs ~7000 LOC in the
|
||||
original 7-crate suite) — driven by one core idea:
|
||||
|
||||
> An answer, once given, is true until forgotten.
|
||||
|
||||
Concretely: a content-addressed memo table that lives on disk as an
|
||||
append-only JSONL ledger. Every binary in this workspace is a thin
|
||||
binding around three operations:
|
||||
|
||||
1. **`canonicalize(input)`** — collapse equivalent inputs to a stable
|
||||
byte form (regex-based Rust-error abstraction by default; identity
|
||||
otherwise).
|
||||
2. **`record(sig, scope, body, worked?, ts)`** — append a fact to the
|
||||
audit log.
|
||||
3. **`ask(sig) → value | miss`** — cache-or-compute.
|
||||
|
||||
## Crates
|
||||
|
||||
| Crate | LOC | Role |
|
||||
|---|---:|---|
|
||||
| `aether-atom` | 577 | The atomic core: fingerprint, canonicalize, lattice, ask/demote/remember |
|
||||
| `aether-recall-mcp` | 311 | MCP-over-stdio server (the 4 tools that drive opencode's recall loop) |
|
||||
| `aether-net-mini` | 245 | TCP JSON-RPC daemon: `put`/`get` over the atom |
|
||||
| `aether-ask` | 326 | HTTP front-end: cache-or-fallback to ollama, dark-mode UI |
|
||||
| `aec-atom` | 250 | REPL: `lookup`/`remember`/`stats`/`compact`/`replay`/`fingerprint` |
|
||||
| `examples/library_usage` | ~80 | Drop-in pattern for any third-party crate that wants memory |
|
||||
|
||||
Total **1517 production LOC** (excluding the example).
|
||||
|
||||
## Tests
|
||||
|
||||
```
|
||||
atom 28 unit
|
||||
aether-recall-mcp 1 integration (spawns binary, full lifecycle)
|
||||
aether-net-mini 5 unit
|
||||
aether-ask 2 unit + 4 integration (HTTP routes + cache hit assertion)
|
||||
aec-atom 10 unit
|
||||
─────
|
||||
50 passing
|
||||
```
|
||||
|
||||
`cargo test --workspace` runs all of them in ~0.30 s.
|
||||
|
||||
## Clippy
|
||||
|
||||
```
|
||||
$ cargo clippy --workspace --all-targets
|
||||
(clean — no warnings, no errors)
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Every binary takes its data directory from a single helper in the atom:
|
||||
|
||||
```rust
|
||||
aether_atom::data_dir("AETHER_RECALL_DIR")
|
||||
```
|
||||
|
||||
Resolution order: env var → `$HOME/.aether-recall/` → `./.aether-recall/`.
|
||||
Override with `AETHER_RECALL_DIR=/path/to/ledger`.
|
||||
|
||||
Per-binary tunables:
|
||||
|
||||
| Binary | Env var | Default |
|
||||
|---|---|---|
|
||||
| `aether-recall-mcp` | `AETHER_RECALL_LOOKUP_RETENTION_DAYS` | 30 |
|
||||
| `aether-ask` | `AETHER_ASK_PORT`, `OLLAMA_HOST`, `OLLAMA_PORT`, `OLLAMA_MODEL` | 8082 / 127.0.0.1 / 11434 / dolphin-llama3:8b |
|
||||
| `aether-net-mini` | `AETHER_NET_DATA_DIR` | `$HOME/.aether-recall/` |
|
||||
| `aec-atom` | `AEC_COMPACT_DAYS` | 30 |
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
cargo build --release --workspace
|
||||
|
||||
# Stand up an MCP server (used by opencode)
|
||||
AETHER_RECALL_DIR=~/.aether-recall ./target/release/aether-recall-mcp &
|
||||
|
||||
# Run an HTTP gateway alongside
|
||||
AETHER_ASK_PORT=8082 ./target/release/aether-ask &
|
||||
|
||||
# Optionally stand up a DHT node
|
||||
./target/release/aether-net-mini daemon --listen 0.0.0.0:9001 &
|
||||
|
||||
# Or poke at the ledger live
|
||||
./target/release/aec-atom
|
||||
ae> remember "error[E0308]: expected i32, found &str" "use .to_string()"
|
||||
ae> lookup "error[E0308]: expected i32, found &str"
|
||||
ae> stats
|
||||
ae> replay
|
||||
ae> quit
|
||||
```
|
||||
|
||||
## Use the atom in your own code
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
aether-atom = { path = "/home/cosmos/aether-atom" } # or git / crates.io
|
||||
```
|
||||
|
||||
```rust
|
||||
use aether_atom::{data_dir, fingerprint, rust_canonicalize, Lattice};
|
||||
|
||||
let mut lat = Lattice::open(&data_dir("MY_DATA_DIR"))?;
|
||||
let sig = fingerprint(rust_canonicalize(my_input).as_slice());
|
||||
match lat.lookup(&sig, "global") {
|
||||
Some(hit) => return hit.value, // cache hit
|
||||
None => { let v = compute(my_input); // cache miss
|
||||
lat.record(sig, "global", &v, Some(true));
|
||||
return v; }
|
||||
}
|
||||
```
|
||||
|
||||
See `examples/library_usage/` for a 50-LOC working program with tests.
|
||||
|
||||
## Bench approx (in-process, sdb fs)
|
||||
|
||||
```
|
||||
10,000 cached ask() ops (incl. disk write): 801 ms = 80 µs/op
|
||||
100,000 raw lookup() ops (incl. disk write): 8.5 s = 85 µs/op
|
||||
```
|
||||
|
||||
vs the original aether-recall README's claim of ~200 µs per cached
|
||||
lookup — about **2.5×** faster, chiefly because bodies are Arc-shared
|
||||
on the hit path so there's never a memcpy of the cached value.
|
||||
|
||||
## What this is NOT
|
||||
|
||||
- Not a chat product. A primitive your tools build on.
|
||||
- Not a peer-to-peer replication mesh. `replicate` is a placeholder.
|
||||
Both peers inherit the same atom ledger if they share the data dir.
|
||||
- Not a model registry. `aether-ask` delegates to whatever ollama is
|
||||
reachable; it doesn't manage model storage.
|
||||
|
||||
## Built from this premise
|
||||
|
||||
Today the aether suite was 7 crates, ~7000 LOC. The original had
|
||||
*three* divergent fingerprint schemes, *three* append-only ledgers,
|
||||
~900 ms subprocess overhead per ask, a hardcoded WSL2 path in three
|
||||
places, two dead kelp of dependencies (`sha3`, `hex`, `rand`, `chrono`)
|
||||
and two stubbed modules. This rewrite collapses that to one
|
||||
fingerprint, one ledger, one canonical linker (the atom), and four
|
||||
bindings — each adding a real surface (MCP tool, TCP verb, HTTP verb,
|
||||
or REPL command).
|
||||
|
||||
The atom is the most essential layer: prove it works, prove it
|
||||
benchmarks, and every wrapper is an obligation.
|
||||
Reference in New Issue
Block a user