Repository navigation
Conversation
…ion Table)
1. **`drizzle/schemas/kb-document-schema.ts`**:
- Extended [`kbDocument`](./drizzle/schemas/kb-document-schema.ts) with four metadata columns:
- `documentType`: `text("document_type").$type<KbDocumentType>().notNull().default("general")`
- `summary`: `text("summary").notNull().default("")`
- `keyEntities`: `jsonb("key_entities").$type<string[]>().notNull().default([])`
- `tableOfContents`: `jsonb("table_of_contents").$type<KbTocEntry[]>().notNull().default([])`
- Preserved non-null defaults to guarantee backwards compatibility with existing rows and upload actions.
2. **`drizzle/schemas/kb-document-relation-schema.ts`**:
- Created [`kbDocumentRelation`](./drizzle/schemas/kb-document-relation-schema.ts) table tracking directed and bidirectional graph edges between knowledge base documents:
- Foreign keys on `kbId`, `sourceDocId`, and `targetDocId` referencing [`knowledgebase`](./drizzle/schemas/knowledgebase-schema.ts) and [`kbDocument`](./drizzle/schemas/kb-document-schema.ts) with `onDelete: "cascade"`.
- Constraints: unique index `kb_doc_rel_edge_idx` on `(sourceDocId, targetDocId, relationType)`, indexes on `kbId` and `targetDocId`.
- Check constraints: `kb_doc_rel_type_check` and `kb_doc_rel_no_self_loop_check` (`sourceDocId <> targetDocId`).
- Exported `KB_RELATION_TYPES = ["references", "supersedes", "supplements", "contradicts", "depicts"] as const` and `KbRelationType`.
3. **`drizzle/db.ts`**:
- Registered `kbDocumentRelationSchema` into the main Drizzle ORM client schema.
4. **`types/knowledgebase/kb-document-row.ts`**:
- Exported [`KbTocEntry`](./types/knowledgebase/kb-document-row.ts#L8-L12) and [`KbDocumentType`](./types/knowledgebase/kb-document-row.ts#L17-L23).
- Extended [`KbDocumentRow`](./types/knowledgebase/kb-document-row.ts#L33-L37) with optional `relationCount?: number`.
1. **Created [`extract-document-metadata.ts`](./lib/rag/extract-document-metadata.ts)** (279 lines, under 300-line limit):
- Implemented `extractDocumentMetadata({ documentName, chunks, userId })` to query the user's default chat model via `resolveDefaultChatProvider` with strict JSON parsing and fence-stripping.
- Implemented `buildDeterministicToc(chunks)` extracting markdown headings (`^#{1,4}\s+(.+)`) and numbered headings (`^\d+(\.\d+)*\.?\s+[A-Z].+`), with fallback to chunk-window ranges (`Section 1 (Chunks 0–4)`).
- Added heuristic `inferDocumentType` classifying documents into `KbDocumentType` (`technical_spec`, `manual`, `report`, `policy`, `reference`, `general`).
- Added `sanitizeTocEntries` ensuring all indices clamp to `[0, chunks.length - 1]` with swapped inverted bounds and non-empty title defaults.
- Built resilient fallback to deterministic TOC and excerpt summary when the chat model is not configured, quota is exceeded, or JSON parsing fails.
2. **Created [`extract-document-metadata.test.ts`](./__tests__/lib/rag/extract-document-metadata.test.ts)**:
- 17 test cases covering all execution paths and edge cases (empty chunks, code fences, malformed JSON, out-of-bounds/inverted TOC ranges, missing model, non-Error exceptions, whitespace chunks, all 6 heuristic document categories, character budget cap).
- Achieved **100% full test coverage** across statements, branches, functions, and lines on `lib/rag/extract-document-metadata.ts`.
1. **Edge Generation & Two-Stage Pruning** ([`lib/rag/generate-document-relations.ts`](./lib/rag/generate-document-relations.ts)): - **Stale Edge Cleanup**: Deletes existing edges in [`kbDocumentRelation`](./drizzle/schemas/kb-document-relation-schema.ts) touching `doc.id` (`sourceDocId === doc.id OR targetDocId === doc.id`) before insertion so re-indexing starts fresh. - **Early Return**: Safely returns `0` without making an LLM call when no other ready documents exist in the knowledge base. - **Candidate Scoring & Pruning**: Evaluates candidate documents against the target document, awarding `+3` points per matching `keyEntities` (case-insensitive) and `+1` point per overlapping non-stopword token from titles and summaries, selecting the top 15 candidates. - **LLM Relationship Extraction**: Invokes `generateText` via [`resolveDefaultChatProvider`](./lib/chat/resolve-default-chat-provider.ts) with zero temperature to identify directed edges (`references`, `supersedes`, `supplements`, `contradicts`, `depicts`). - **Sanitization & Edge Validation**: Filters out self-loops (`sourceDocId === targetDocId`), edges referencing unknown document IDs not in the candidate set, edges where neither endpoint is `doc.id`, invalid relationship types, and duplicate edges. - **Resilience**: Catches missing provider configurations, provider timeouts, network failures, or malformed JSON responses, logging a warning and returning `0` without blocking document ingestion. 2. **Automated Unit & Coverage Tests** ([`__tests__/lib/rag/generate-document-relations.test.ts`](./__tests__/lib/rag/generate-document-relations.test.ts)): - 17 test cases covering early return, candidate ranking, candidate name tie-breaking, bidirectional edge creation, edge sanitization, malformed JSON, and error fallbacks. - **100% test coverage** maintained across statements, branches, functions, and lines. 3. **Knowledge Base Documentation**: - Updated [`.agents/learnings.md`](./.agents/learnings.md) with invariants for document relationship edge generation, candidate pruning, and ingestion resilience. - Re-indexed knowledge graph via `graphify update .`.
…Qdrant Range Queries
1. **Qdrant Client (`lib/rag/qdrant-client.ts`)**:
- Registered integer payload index for `chunkIndex` (`field_name: "chunkIndex", field_schema: "integer"`) in `ensureQdrantCollection`.
- Added `getDocumentChunksByRange(documentId, kbIds, startIndex?, endIndex?)`: scrolls `kb_chunks_*` collections, filters by range, and returns chunks sorted ascending by `chunkIndex`.
- Added optional `documentIds?: string[]` scoping filter to `searchSemanticVectors` and `searchKeywordChunks`.
- Kept file clean and under 300 lines limit (299 lines).
2. **Hybrid Search (`lib/rag/hybrid-search.ts`)**:
- Added optional 5th parameter `documentIds?: string[]` to `hybridSearch`.
- Forwarded `documentIds` to `searchSemanticVectors` and `searchKeywordChunks` while preserving 4-argument call signature when omitted.
3. **Ingestion Pipeline (`lib/rag/ingest-pipeline.ts`)**:
- Integrated `extractDocumentMetadata({ documentName: doc.name, chunks, userId })` during document ingestion.
- Persisted `documentType`, `summary`, `keyEntities`, and `tableOfContents` on `kbDocument` upon marking document `ready`.
- Invoked `generateDocumentRelations` after marking document `ready` to establish bidirectional graph edges.
1. **Context Window Guard (`guardKbContextBudget`)**
- [`lib/rag/guard-kb-context-budget.ts`](./lib/rag/guard-kb-context-budget.ts): Evaluates whether reading a full document or chunk slice will stay within 75% of the model's usable context window budget (`usableBudget = Math.floor(contextWindow * 0.75) - usedTokens`).
- If reading exceeds the budget, it returns `safeToRead: false` and provides the document's structured `tableOfContents` as an outline fallback so the subagent can inspect sections and read targeted slice indices.
2. **Graph Explorer (`exploreKbGraph`)**
- [`lib/rag/explore-kb-graph.ts`](./lib/rag/explore-kb-graph.ts): Queries `kbDocument` and `kbDocumentRelation` within the active knowledge bases.
- Supports 1–3 hop undirected BFS neighborhood traversals from a `startDocId`, keyword matching across title/summary/documentType/keyEntities with 1-hop neighbor expansion, or Tier 1 catalog overview (up to 30 documents with their interconnecting edges).
3. **Isolated KB Reader Subagent (`runKbSubagent`)**
- [`lib/rag/run-kb-subagent.ts`](./lib/rag/run-kb-subagent.ts): Runs an isolated subagent via `generateText` using internal tools:
- `explore_kb_graph`: Traversing or querying document relations.
- `search_kb_chunks`: Scoped hybrid search by query and document IDs.
- `read_kb_document`: Context-guarded document chunk reader leveraging `getDocumentChunksByRange`.
- Protects the main chat history from multi-page document dumps while tracking citations and discovered relationship edges.
- Provides fast-path return when no chat model is passed or when no matching documents exist, and gracefully catches provider failures by returning collected hybrid retrieval hits.
4. **Upgraded `registerKnowledgebaseTool` & Orchestrator Integration**
- [`lib/chat/register-knowledgebase-tool.ts`](./lib/chat/register-knowledgebase-tool.ts): Delegates `search_knowledge_base` to `runKbSubagent`, returning normalized citation results (`content`, `relevanceScore`, `documentId`, `documentName`, `kbId`, `kbName`) and synthesized `answer`.
- Adds `toModelOutput` returning formatted syntheses (`KNOWLEDGE BASE SYNTHESIS`, `DOCUMENT RELATIONSHIPS`, `SOURCES`) to the main model while keeping raw tool result arrays intact for UI citation extractors.
- [`lib/inngest/chat-response/tool-set-builder.ts`](./lib/inngest/chat-response/tool-set-builder.ts): Connects `workerResolved ?? resolved` model and context window configuration into `registerKnowledgebaseTool`.
- [`actions/knowledgebases/list-documents.ts`](./actions/knowledgebases/list-documents.ts):
- Added query to [`kbDocumentRelation`](./drizzle/schemas/kb-document-relation-schema.ts) matching `kbId`.
- Computed bidirectional `relationCount` (inbound + outbound edges) per document.
- Attached `relationCount` to each returned [`KbDocumentRow`](./types/knowledgebase/kb-document-row.ts).
- [`components/knowledgebase/document-list.tsx`](./components/knowledgebase/document-list.tsx):
- Rendered inline metadata for ready documents:
- `documentType` badge when not `"general"` (e.g. `technical spec`).
- `relationCount` badge when `relationCount > 0` (`1 link` / `2 links`).
- `summary` text clamped to 2 lines (`text-[11px]`).
- `keyEntities` badges (up to 4 tags + `+N` overflow badge).
- Maintained existing layout for pending, processing, and failed documents.
- [`__tests__/actions/knowledgebases/list-documents.test.ts`](./__tests__/actions/knowledgebases/list-documents.test.ts):
- Unit tests verifying `NOT_FOUND` authorization check, bidirectional `relationCount` computation, and empty relation fallback.
- [`__tests__/components/knowledgebase/document-list.test.tsx`](./__tests__/components/knowledgebase/document-list.test.tsx):
- 10 unit tests verifying empty states, badges, summaries, entity tags with overflow, pending/processing/failed states, and document deletion transitions.
- [`__tests__/actions/knowledgebases/document-actions.test.ts`](./__tests__/actions/knowledgebases/document-actions.test.ts):
- Updated mock chaining to verify `relationCount: 0` without regressions.
Owner
Author
1. Does it default to subagents reading files directly, or does it still use vector semantic search?It uses both in a hybrid multi-tier pipeline:
2. Does it use the graphs to explore related data?Yes, extensively at both ingestion and query time:
3. What the Subagent Receives First (Without Reading Files)When
4. When the Subagent Decides to Call
|
… and allowing creating from projects
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.
Goal: Upgrade the knowledge base RAG system into a Unified 3-Tier Hybrid architecture combining upload-time LLM metadata and a PostgreSQL document relationship graph with Qdrant chunk retrieval, scoped document reading, and an isolated KB reader subagent that protects the main chat context window.
Architecture: During document ingestion (
ingestDocumentPipeline), extracted text and chunks are analyzed by the user's default chat model (with deterministic fallback) to generate a high-density summary, document classification, key entities, a chunk-indexed Table of Contents (TOC), and bidirectional relationship edges (kb_document_relation) against top-K candidate documents in the knowledge base. At query time,search_knowledge_baseremains the single abstracted tool exposed to the main chat model; internally, it runs an isolated KB Reader Subagent (ToolLoopAgent) equipped with the Tier 1 document/graph map,explore_kb_graph,search_kb_chunks, and context-guardedread_kb_documenttools, returning a synthesized answer and citations without polluting the main thread with raw document dumps.1. High-Level Overview
1.1 High-Level Architecture
flowchart TD subgraph Ingestion ["1. Upload & Ingestion Pipeline (Inngest)"] Upload["S3 Upload + kb_document (pending)"] --> Extract["Extract Text & Recursive Chunks"] Extract --> Embed["Embed Chunks -> Qdrant (with chunkIndex)"] Extract --> MetaLLM["LLM Metadata & TOC Extractor\n(Deterministic Fallback)"] MetaLLM --> Prune["Top-15 Candidate Pruning\n(Entity + Keyword Overlap)"] Prune --> GraphLLM["LLM Bidirectional Edge Generator"] GraphLLM --> PG[("PostgreSQL\nkb_document + kb_document_relation")] end subgraph Runtime ["2. Query-Time Isolated Subagent Retrieval"] MainLLM["Main Chat Model"] -->|"Calls search_knowledge_base(query)"| KBTool["search_knowledge_base Tool"] KBTool --> PreFetch["Initial Hybrid Search (Vector + FTS RRF)\n+ Tier 1 Catalog & Graph Map"] PreFetch --> Subagent["Isolated KB Reader Subagent\n(workerResolved ?? resolved)"] Subagent --> T1["explore_kb_graph\n(1-3 hop undirected traversal)"] Subagent --> T2["search_kb_chunks\n(Qdrant RRF + docId filter)"] Subagent --> T3["read_kb_document\n(Qdrant ordered chunks + Context Guard)"] Subagent -->|"Distilled Answer + Top Citations"| MainLLM end1.2 Global Constraints
git commit,git add,git push, orgit stash. Only read-only Git commands (status,diff,log) are permitted.knowledgebaseandkb_documentusetext(UUID strings), NOT PostgreSQL nativeuuidcolumn types. New tables (kb_document_relation) must usetextfor foreign keys to maintain type compatibility.@/drizzle/schemas/*; never create or import from barrel files.vitest.config.tsenforces 100% statements, branches, functions, and lines. Never use/* v8 ignore */.1.3 Review Focus
extractDocumentMetadataandgenerateDocumentRelationsmust catch the error and fall back to deterministic heading/excerpt metadata and zero graph edges without failingingestDocumentPipeline.sourceDocId === targetDocId). Edge sanitization must filter against the candidate ID allowlist and enforcesourceDocId !== targetDocIdand(sourceDocId === doc.id || targetDocId === doc.id).read_kb_document: When a subagent running on an 8k/16k model attempts to read a 40-page document (docTokens > usableBudget),guardContextBudgetmust block the full read and returnsafeToReadFull: falsewith the document'stableOfContentsandchunkCountso the subagent reads bounded[chunkStartIndex, chunkEndIndex]slices.kb_document_relationrows wheresourceDocId = doc.id OR targetDocId = doc.idbefore inserting fresh edges, and abort ifkbDocumentwas deleted concurrently during LLM generation.search_knowledge_baseCallers & UI Citations:extractCitationsin the chat UI expectsresult.resultsto be an array of{ content, relevanceScore, documentId, documentName, s3Key, kbId?, kbName? }.search_knowledge_basemust preserve this exactresultsarray structure alongside the newanswerandrelatedDocumentsfields, whiletoModelOutputformats a compact context representation for the main model.2. Alternatives Evaluated
search_knowledge_basevia an isolated subagent.explore_kb_graph,search_knowledge_base, andread_kb_documentdirectly on the main chat model and rely ondelegate_taskfor isolation.read_kb_documentdumps tens of thousands of tokens directly into the main chat history.ON DELETE CASCADEhandle 1–3 hop traversals in <1ms without extra containers or split-brain delete bugs.3. Edge Cases & Failure Modes
extractDocumentMetadatacatchesProviderNotConfiguredErrorfromresolveDefaultChatProvider(userId)and returns deterministic TOC + excerpt summary.generateDocumentRelationscatches and returns0edges. Vector indexing in Qdrant succeeds normally.json_schemaor Emits Markdown Code FencesgenerateTextwith explicit JSON instructions and a fence-stripping parser (stripJsonFences+JSON.parse+schema.safeParse) rather than brittle provider-dependentresponse_formatschemas.sanitizeTocEntriesclampschunkStartIndexandchunkEndIndexto[0, chunks.length - 1]and swaps or adjusts ifchunkStartIndex > chunkEndIndex.targetDocIdor Self-Loop in GraphgenerateDocumentRelationsvalidates every edge againstvalidCandidateIds.has(peerId), enforcessourceDocId !== targetDocId, and requires one endpoint to bedoc.id. DatabaseCHECK (source_doc_id <> target_doc_id)and Foreign Keys provide a second hard barrier.guardKbContextBudgetchecksestimatedSliceTokens > usableBudget(0.75 * contextWindow - usedTokens). If exceeded,read_kb_documentreturnssafeToRead: false, the document'stableOfContents, and instructions to request a bounded[chunkStartIndex, chunkEndIndex]range.ingestDocumentPipelinealready checks ifkbDocumentstill exists before Qdrant upsert;generateDocumentRelationscatches foreign key errors or empty document checks cleanly, andON DELETE CASCADEonkb_document_relationpurges any edges if deleted mid-flight.generateDocumentRelationsdeletes all existingkb_document_relationrows wheresourceDocId = doc.id OR targetDocId = doc.idbefore inserting newly discovered edges, preventing stale or duplicate edges.