feat: sqlite-backed embedding cache -- 2min -> <5sec rebuilds #60
No reviewers
Labels
No labels
Agent
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
joshtronic/igor!60
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/rag-embedding-cache"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Full re-embed every tick (860 entries / ~2min) was a tax that
didn't earn its keep. Embeddings are deterministic derived data --
same model + same text always produces the same vector -- so
caching them is safe as long as we invalidate when either changes.
New sqlite cache at $IGOR_STATE_DIR/rag-embeddings.sqlite:
CREATE TABLE embeddings (
content_hash TEXT PRIMARY KEY, -- sha256(model_name|text)
embedding BLOB NOT NULL,
created_at TEXT NOT NULL
)
The model name is included in the hash, so switching embedders
auto-invalidates the cache (old hashes become orphans that just
never match again).
Flow on each tick:
memories)
Self-healing intact:
file and the next tick rebuilds from journal/memories/git
Expected performance after merge:
usually 0-3 per tick)
Added IGOR_RAG_CACHE_PATH knob in .env.example for visibility.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com
Full re-embed every tick (860 entries / ~2min) was a tax that didn't earn its keep. Embeddings are deterministic derived data -- same model + same text always produces the same vector -- so caching them is safe as long as we invalidate when either changes. New sqlite cache at $IGOR_STATE_DIR/rag-embeddings.sqlite: CREATE TABLE embeddings ( content_hash TEXT PRIMARY KEY, -- sha256(model_name|text) embedding BLOB NOT NULL, created_at TEXT NOT NULL ) The model name is included in the hash, so switching embedders auto-invalidates the cache (old hashes become orphans that just never match again). Flow on each tick: 1. Collect all source rows (journal + memory + commit), same as before 2. Hash each row's text + model -> content_hash 3. SELECT cached embeddings by hash 4. Embed only the misses (new journal entries, new commits, edited memories) 5. INSERT new embeddings into cache 6. Push all rows (cached + new) into Redis (still flush + rebuild) Self-healing intact: - Redis: still fully ephemeral, flushed every tick - sqlite cache: durable but trivially reconstructible -- delete the file and the next tick rebuilds from journal/memories/git - Both layers can be nuked independently without breaking anything Expected performance after merge: - First tick: same ~2min (rebuilds the cache from cold) - Steady-state ticks: <5sec (only a few new entries to embed, usually 0-3 per tick) - Log line "rag: cache hit N/M, need to embed K new" tells the story Added IGOR_RAG_CACHE_PATH knob in .env.example for visibility. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>