Releases: Rushour0/fabri
Release list
v0.11.1 — Global Memory
Cross-collection memory tier
Adds memory.global_collection: str | None to the memory config. When set, retrieval opens a second Qdrant collection alongside the primary per-session/per-project one, and merges its candidates into the same pre-fusion pool — before the existing RRF fuse, temporal-decay, domain-boost, guaranteed-slot reservation, and MMR pipeline runs. That pipeline still computes its top_k budget once, over one merged pool, exactly as before.
Fully additive and backward compatible:
global_collectiondefaults toNone— unset configs are byte-identical to prior behavior.build_memory_store's single-store return contract is untouched (used byreplay,inspect-memory,memory show/list/diff, the MCP server, ingest, and benchmarks) — the second store is constructed only inside the retrieval path itself.- Any failure opening or querying the global store (unreachable Qdrant, missing collection) degrades cleanly to primary-only results and is logged — never raised.
866 → 871 tests, 5 new (merge respects top_k without inflating guaranteed-slot counts; empty/unreachable global store degrades cleanly; existing single-store configs stay byte-identical), zero regressions.
This is the seam a multi-project deployment can use to give every agent run access to lessons learned anywhere, not just within its own session or project history.
v0.10.0 — Measured Retrieval
Turns memory retrieval from unmeasured into measured, gated, observable, and better by default.
Highlights
- Offline retrieval eval + CI gate (M4) —
python -m fabri.benchmarks.retrieval_evaldrives the real retrieval path over a labeled fixture (recall@k / MRR / precision@k), zero API credits; a pytest gate locks the shipped defaults atmeasured − 0.05. - Default flipped
dense→hybrid(M5/D3) — eval-backed, degrades gracefully to dense where BM25 is unavailable, so never worse. - Two eval-driven quality fixes make hybrid the best strategy on every metric: RRF
k60→20 (memory.rrf_k; recall@3 0.60→0.90) andsuccess_patternslot back-load (recall@1 0.13→0.58, MRR 0.45→0.84 — relevance owns rank 1, the guarantee fills reserved tail slots). - BM25 FTS5 no-op fixed — space-join → implicit-AND meant
sparse/hybridsilently ran asdense; found by the eval. - Retrieval-decision observability (M3) — one structured
retrievaltrace event per call (strategy, pool sizes, BM25-fired-or-fell-back, per-candidateinclusion_reason). Trace-only, zero prompt cost. - Memory-health section in
fabri report(M6) across md/json/html, offline-safe.
Fixed
- Root
startevent now emits before retrieval (nesting invariant). - Per-test Qdrant isolation kills the order-dependent CI flake.
New tuning knob memory.rrf_k. First-user tuning guide: docs/retrieval-tuning.md. Full details in CHANGELOG.md.
v0.9.1 — docs patch
Docs-only patch for v0.9.0.\n\n- TODO.md: v0.9.0 retrieval items marked done; open follow-ups tracked (query expansion, reranking, agent namespacing, TTL/eviction).\n- docs/ROADMAP.md: M2 card added; Track M description and mermaid diagram updated.
v0.9.0 — Hybrid & Advanced Retrieval
Hybrid & Advanced Retrieval — BM25+Vector fusion, temporal decay, MMR, domain routing
This release introduces a fully configurable, multi-strategy retrieval pipeline for fabri's memory system. All changes are backward-compatible — existing configs and databases work without modification.
Retrieval strategies
Controlled by memory.retrieval_strategy:
| Strategy | Description |
|---|---|
dense |
Original cosine vector similarity (default, unchanged) |
sparse |
BM25-only via SQLite FTS5 (built-in) or rank_bm25 for Qdrant |
hybrid |
RRF fusion of dense + sparse |
hybrid+mmr |
Hybrid + Maximal Marginal Relevance diversification |
Reciprocal Rank Fusion (RRF) — entries appearing in both dense and sparse results get double credit. MMR — diversifies the final pool by balancing relevance vs redundancy (memory.mmr_lambda, default 0.7).
Scoring pipeline (all opt-in)
- Temporal decay (
memory.temporal_decay: true) —score *= exp(-ln(2) * age_days / half_life_days). Default half-life: 30 days. - Importance boost (
memory.importance_weight: 0.2) —min(1, hit_count/10 + 0.3 if strategic). - Domain routing (
memory.domain_routing: true) — zero-latency keyword heuristic; matching entries get a 1.15× boost, never hard-filters.
SQLite FTS5 index (zero extra install)
FTS5 is Python built-in. Porter tokenizer. Synced on every upsert/delete. Auto-migration: existing DBs are bulk-populated from guidelines on first upgrade — no manual step needed.
Memory schema enrichment
MemoryEntry gains four new optional fields (all default-safe for old payloads): domain, outcome, agent_id, task_embedding_hash. Deterministic ID hash unchanged — no DB migration needed.
New config keys (all default to pre-v0.9.0 behavior)
memory:
retrieval_strategy: dense # dense | sparse | hybrid | hybrid+mmr
temporal_decay: false
temporal_half_life_days: 30.0
mmr_lambda: 0.7
domain_routing: false
importance_weight: 0.2
query_expansion: false # reservedOptional dependency
pip install 'fabri[bm25]' # client-side BM25 for Qdrant hybrid retrieval
SQLite users get full hybrid retrieval with zero extra installs.
Bug fix
agent_runner_tool.py hardcoded QdrantMemoryStore; now uses build_memory_store(mem_cfg) so SQLite users get hybrid retrieval in sub-agent runs too.
Quick start
# agent.yaml
memory:
backend: sqlite
retrieval_strategy: hybrid+mmr
temporal_decay: true
domain_routing: truev0.6.0 — Business Source License 1.1
License change: Apache-2.0 → Business Source License 1.1.
fabri v0.6.0 and every later release is licensed under the Business Source License 1.1. The TL;DR:
- Free for individuals, for personal/educational/non-commercial use, and for any organization with ≤ US $1M annual gross revenue.
- Free for internal evaluation pilots (up to 90 days), at any scale.
- Commercial license required for organizations above the US $1M revenue threshold, or for anyone embedding fabri into a hosted/distributed product they sell to third parties (regardless of revenue).
- Auto-converts to Apache 2.0 on 2030-06-23. Every BSL-licensed version of fabri becomes Apache-2.0 on that date — written into the LICENSE itself, not a promise.
See COMMERCIAL.md for who needs a license and how to get one. Honest "does this apply to me?" questions are welcome — pataderushikesh@gmail.com.
Prior versions
- Versions ≤ 0.4.6 were released under Apache 2.0 and remain Apache 2.0 forever.
- Versions 0.5.0 and 0.5.1 were withdrawn from PyPI prior to general availability; their functionality is rolled into v0.6.0 (see CHANGELOG).
What's in this release (carried from withdrawn 0.5.x)
- Per-run USD cost (COGS) with sub-agent rollup.
LLMUsagegainedmodel;fabri.pricingprices token usage per model (Sonnet 4.6, Haiku 4.5, Opus tier, gpt-4o; cache-write 1.25×, cache-read 0.10×).run_agent'susageevent and return dict now carrycost_usd,cost_by_model,subagent_cost_usd,total_cost_usd. - Cache pre-warm via
AnthropicLLMBackend.prewarm(system)— writes the static system+tools prefix into Anthropic's ephemeral cache before a burst of same-prefix runs. - Frugal-by-default base prompt —
DEFAULT_AGENT_IDENTITYis now deliberation-first;FRUGALITY_POLICYis appended to every run, with registry-gatedDELEGATION_POLICYandCODE_ACTION_POLICY. - Truncation retry — both backends now retry once at a higher cap on a
max_tokenstruncation before failing the run. QDRANT_URLenv override — propagates the reachable qdrant address across the subprocess boundary in containerized hosts.- +105 tests. Suite 246 → 351.
Full changelog: CHANGELOG.md
v0.1.0
First release of fabri — a local memory + orchestration framework for custom LLM agents.
- SCOPE-style tactical/strategic memory over execution traces (Qdrant + local MiniLM embeddings)
- Polyglot subprocess tools behind a uniform contract; agent-as-tool composition
- Token-efficient TOON encoding of tool results
fabri initscaffolds a runnable starter project;builtintools token; project-local.fabri/state- Anthropic by default; OpenAI via the
[openai]extra
pip install fabri
fabri init demo && cd demo
fabri --config agent.yaml run "greet Ada with the hello tool"Apache-2.0.