CCR compression plugin for Hermes Agent - thin Python loader + Rust dylib. Sub-ms tool output compression, 26-type classifier, 13 tools, 9 skills.
Aphrodite intercepts tool output before it reaches the LLM and replaces it with compact, structured previews. The agent sees 15 tokens of metadata instead of 500 tokens of raw text - and retrieves the full content only when it actually needs it. All compression logic runs in the Rust dylib.
git clone https://github.com/PlayForm/Aphrodite-Hermes.git
ln -s "$(pwd)/Aphrodite-Hermes" ~/.hermes/plugins/aphrodite
hermes plugins enable aphrodite
hermesOn first launch, the plugin automatically downloads the aphrodite binary
from releases. No Rust
toolchain required.
Native Windows: run
pwsh ./download.ps1instead ofdownload.sh- no Git Bash/WSL needed. See Windows install for the full walkthrough, and Troubleshooting if the proxy doesn't come up after enabling the plugin.
The aphrodite proxy is an OpenAI-compatible LLM API proxy - it forwards requests upstream - so it needs its own provider credentials even though Hermes already has a provider configured. The plugin cannot read Hermes' provider config, and there is no keyless / compression-only mode: without a key the proxy refuses to start and the plugin is unusable.
export APHRODITE_API_KEY="sk-..." # REQUIRED
export APHRODITE_API_URL="https://api.openai.com" # optional
export APHRODITE_MODEL="default-model" # optionalAlternatives: run aphrodite setup, or add api_key / api_url / model to
~/.hermes/aphrodite/aphrodite.toml. If the proxy fails to start,
~/.hermes/aphrodite/proxy-stderr.log shows the reason - no API key configured means the key is missing.
After installing and launching Hermes once:
~/.hermes/
├── plugins/
│ └── aphrodite → /path/to/Aphrodite-Hermes ← symlink to this repo
├── aphrodite/
│ ├── aphrodite ← auto-downloaded binary (~12 MB)
│ └── ccr.db ← SQLite CCR store (on first run)
└── profiles/<name>/
└── plugins/
└── aphrodite → ~/.hermes/plugins/aphrodite
The plugin also adds to your Hermes config:
# Added automatically on enable
plugins:
enabled:
- aphrodite
# Recommended additions (manual)
context:
engine: aphrodite
engine_threshold_pct: 55
model:
context_length: 1000000Two proxy processes launch on :9797 (cache) and :9798 (token).
# In a Hermes session:
aphrodite_stats
# Or via CLI:
curl http://127.0.0.1:9798/health
# → {"status":"ok","version":"<current aphrodite version - see the badge above>"}hermes plugins disable aphrodite
rm ~/.hermes/plugins/aphrodite
pkill -f "aphrodite/binaries/aphrodite"Python (thin loader) Rust dylib (all logic)
__init__.py 421L libaphrodite_hermes.dylib
↓ ctypes FFI ← universal dispatch (5 hooks)
libaphrodite_hermes.dylib ← 13 tool handlers, delegates into
libaphrodite (core engine): hooks,
resolve, stage2, struct_extract, state,
catalog, session, marker, prefetch,
config_loader
All 5 hooks + 13 tools delegate to Rust. Python serves as fallback. Hot-reload: rebuild dylib → mtime change detected → next call picks up new code.
| Tool | Description |
|---|---|
aphrodite_retrieve |
Resolve <<<CCR:hash|type>>> markers |
aphrodite_compress |
Compress content via CCR with type hint |
aphrodite_stats |
Proxy health, engine status, inline store size |
aphrodite_rebuild |
Report binary/proxy version + a rebuild hint (does not rebuild or restart itself) |
aphrodite_files |
Tracked file references grouped by tool |
aphrodite_diff |
Conversation turn history with summaries |
aphrodite_search |
Search CCR store by keyword or type |
aphrodite_directive |
List/swap/add/remove/reset active behavioral directives |
aphrodite_test |
Smoke test suite: quick (1 sample) or full (3 samples) |
aphrodite_catalog |
Full CCR catalog with hashes, types, sizes, previews |
aphrodite_reclassify |
Retroactive metadata enrichment |
aphrodite_prefetch |
Background file read + compress (markers return instantly) |
aphrodite_prefetch_status |
Live prefetch schedule: loading, ready, errors |
All tuning in aphrodite.toml (searched: CWD → ~/.hermes/aphrodite/ → repo root):
[compression]
engine_threshold_pct = 45 # compress at 45% context fill
engine_protect_first = 2 # messages to keep at start
engine_protect_last = 5 # messages to keep at end
engine_min_msgs = 8 # minimum before activating
tool_threshold_token = 512 # token proxy threshold (bytes)
tool_threshold_cache = 4096 # cache proxy threshold (bytes)
code_multiplier = 3.0 # keep code in context longer
context_engine = true # default-on, no env var needed
[previews]
model_family = "code_first" # compact | code_first | balance
code_structure_map = true # show fn/struct/class sigs
[prompts]
retrieve_guidance = "verbose"
ccr_marker_hint = trueEnv var overrides: APHRODITE_ENGINE_THRESHOLD_PCT, APHRODITE_CONTEXT_ENGINE, etc.
APHRODITE_HOME relocates the plugin's Python-side data (hot-reload dylib
copies, proxy-stderr.log) from the default ~/.hermes/aphrodite; the Rust
binary does not read it - aphrodite.toml / ccr.db lookup stays put.
On registration the plugin probes both health endpoints (:9797, :9798)
and reuses an already-running proxy pair instead of launching a second
instance, so extra Hermes processes no longer pile failed to bind listener
noise into proxy-stderr.log. Set APHRODITE_NO_AUTO_LAUNCH=1 to skip the
auto-launch entirely, e.g. when a cargo watch dev loop runs the proxy
itself.
Custom behavioral directives are name.md files in
~/.hermes/aphrodite/directives/ - an empty file means an intentionally
empty directive. The plugin ships its own directives/ set, auto-exposed
to the dylib via APHRODITE_DIRECTIVES_DIR (override the env var to point
elsewhere). If no directive directory is found, the compiled built-in set
loads as a fallback - its activation is logged. Manage them at runtime with
aphrodite_directive (list/swap/add/load/remove/reset).
git clone https://github.com/PlayForm/Aphrodite.git
cd Aphrodite
cargo build -p aphrodite
# Dylib at target/debug/libaphrodite.dylib - auto-detected by pluginAphrodite-Hermes/
├── __init__.py ← 421-line Python loader (ctypes FFI)
├── plugin.yaml ← 13 tools, 5 hooks, context engine
├── download.sh ← Binary auto-downloader (macOS/Linux/Git Bash/WSL)
├── download.ps1 ← Binary auto-downloader (native Windows PowerShell)
├── binaries/ ← Platform-native dylib + proxy binary
├── README.md ← This file
└── .gitignore