feat: hook-based knowledge backend registry + backend contract doc (depends on #280) - #406
Closed
esafwan wants to merge 4 commits into
Closed
Conversation
…+ backend contract doc
This was referenced Jul 19, 2026
…drop dead sqlite_vec adapter
frappe.get_hooks() merges dict-valued hooks into a single dict of lists
({'redis': ['path.to.Class']}), not a list of dicts as _discover_backends()
assumed — so no hooked backend ever registered on a real site. Add
_iter_hook_backends() to normalise both shapes; first declaration of a
type key wins. Document the merge behaviour in BACKEND_CONTRACT.md and
update registry tests to mock the real hook shape.
This was referenced Jul 25, 2026
This was referenced Jul 25, 2026
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #280
This PR builds directly on #280 (
feat/pgvector-knowledge-doctypes) and is intentionally based on that branch — do not merge before #280 lands. After #280 merges to develop, this PR's base should be retargeted todevelop.What this adds
huf_knowledge_backendshook + dynamic registry — replaces the hardcoded backend dict inhuf/ai/knowledge/backends/__init__.py::get_backend()with hook-based discovery, mirroring the existinghuf_toolspattern. External Frappe apps can now register their own knowledge backend:Validation: registered classes must subclass
KnowledgeBackend; collisions with built-in type keys are logged and skipped (external apps cannot shadow built-ins).BACKEND_CONTRACT.md— documents the backend contract and the two sanctioned implementation paths:LlamaIndexBackend(added in feat: add PGVector knowledge source #280) for stores LlamaIndex supports — recommended, ~50–100 lines per backend;KnowledgeBackenddirectly for portable/unsupported stores (the sqlite_vec exception).Also covers: embeddings stay HUF-side, core connection fields vs
advanced_configtuning params, and the live-test bar for new backends.Tests —
test_backend_registry.py: hook merging, collision handling, subclass validation (mockedfrappe.get_hooks), run green in-bench.Why
Audited across all knowledge backends: the hardcoded registry was the single blocker to external-app pluggability. This unlocks the standardization plan the historical vector-store PRs (#234, #235, #236, #238, #149, #168, #230, #231, #232) are being re-scoped against — each carries a TODO comment pointing here.
🤖 Generated with Claude Code
Update (2026-07-25): validated end-to-end on Frappe 16 + fix
Live-tested on a disposable bench (
16_kbreg, Frappe 16.27, Python 3.14) with pgvector (built-in) and the new Redis backend (#434, registered via this hook — huf dogfooding its own registry).Bug found & fixed (
fix: normalise Frappe dict-of-lists hook shape in backend discovery):frappe.get_hooks()merges dict-valued hooks into a single dict of lists ({"redis": ["path.to.Class"]}), not a list of dicts as discovery assumed — so no hooked backend ever registered on a real site._iter_hook_backends()now normalises both shapes; first declaration of a type key wins.BACKEND_CONTRACT.mddocuments the merge behaviour; registry tests mock the real shape.E2E (real Gemini
gemini-embedding-001embeddings): registry discovery ✅ · advanced-config schema round-trip ✅ · pgvector full cycle (index → search → stats/health → delete) ✅ · redis full cycle ✅. Unit tests 28/30 (2 pre-existing standalone-context errors).ruffclean.Follow-up: #434 (Redis backend via this hook) is stacked on this PR — merge order: #280 → #406 → #434.