Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
5a77637
refactor: deepen service and retrieval architecture
suguanYang May 15, 2026
4a5cfd3
refactor: deepen apps/api workflow seams
suguanYang May 15, 2026
a2edca9
refactor: split stripe billing workflows
suguanYang May 15, 2026
3fc99ce
refactor: split api key workflows
suguanYang May 15, 2026
5439a92
refactor: extract stripe refund reconciliation
suguanYang May 15, 2026
84072fe
refactor: split document ingestion workflows
suguanYang May 16, 2026
dd0b178
refactor: split job admission policies
suguanYang May 16, 2026
67418bd
refactor: extract stripe credits settlement
suguanYang May 16, 2026
2bc351c
refactor: package document ingestion workflow
suguanYang May 16, 2026
a6f2631
refactor: package job read workflows
suguanYang May 16, 2026
b783850
refactor: remove GitHub flow test file
suguanYang May 16, 2026
fa25896
refactor: split worker ingestion and parser orchestration
suguanYang May 16, 2026
38fdc88
refactor: consolidate job storage helpers
suguanYang May 16, 2026
559f86f
refactor: align sync state machine payloads
suguanYang May 16, 2026
5109662
refactor: centralize job result delivery
suguanYang May 16, 2026
c452d98
refactor: centralize retrieval asset projection
suguanYang May 16, 2026
96ae384
refactor: deepen job file storage interface
suguanYang May 16, 2026
3c54362
refactor: remove api state machine facade
suguanYang May 16, 2026
11d624e
refactor deepen workflow modules
suguanYang May 17, 2026
d0c039c
refactor deepen retrieval and ingestion modules
suguanYang May 17, 2026
94bf087
refactor deepen job lifecycle and upload handoff
suguanYang May 17, 2026
0d66fe6
refactor deepen workflow and demo projections
suguanYang May 17, 2026
556ebd2
refactor deepen agentic and zip modules
suguanYang May 17, 2026
c8c2eef
refactor deepen webhook delivery modules
suguanYang May 17, 2026
3549310
refactor split retrieval hydration modules
suguanYang May 17, 2026
7519c3a
refactor split retrieval graph modules
suguanYang May 17, 2026
f5d8d3b
refactor extract agentic selection hydration
suguanYang May 17, 2026
0db1874
refactor extract legacy retrieval route
suguanYang May 17, 2026
4517c43
refactor deepen qstash webhook publisher
suguanYang May 17, 2026
5fe4961
refactor extract retrieval lexical ranker
suguanYang May 17, 2026
42f4aec
refactor extract agentic section counts
suguanYang May 17, 2026
e079d6a
refactor extract agentic section prompt projection
suguanYang May 17, 2026
7f4c504
refactor move agentic asset availability
suguanYang May 17, 2026
9a11351
refactor split agentic discovery selection
suguanYang May 17, 2026
b4d2b76
refactor share agentic asset scope loading
suguanYang May 17, 2026
f430026
refactor(worker): deepen parser architecture
suguanYang May 17, 2026
21381a9
refactor(worker): deepen excel parser contract
suguanYang May 17, 2026
eaf78c7
refactor(worker): deepen markdown table parser modules
suguanYang May 17, 2026
8081faf
refactor(api): deepen demo source materialization
suguanYang May 17, 2026
f278e1e
refactor: remove dead code
suguanYang May 17, 2026
c76693c
refactor: deepen workflow architecture
suguanYang May 18, 2026
1c3befd
refactor: deepen terminal workflow seams
suguanYang May 18, 2026
5637794
refactor: deepen workflow policy seams
suguanYang May 18, 2026
231d784
fix: resolve refactor review blockers
suguanYang May 18, 2026
8fd245e
fix: satisfy demo source projection typing
suguanYang May 18, 2026
0711ba5
fix: use explicit protocol stubs
suguanYang May 18, 2026
0164a13
test: remove unit-style contract tests
suguanYang May 18, 2026
5b7585a
test: keep contract suite at behavior boundaries
suguanYang May 18, 2026
dfb5d4f
test: fix contract test lint
suguanYang May 18, 2026
8eef198
chore: remove empty service package modules
suguanYang May 18, 2026
0a3252a
refactor: organize shared utilities by domain
suguanYang May 18, 2026
b15be8b
refactor: put shared quota base in neutral module
suguanYang May 18, 2026
ab188dd
Revert "refactor: put shared quota base in neutral module"
suguanYang May 18, 2026
68c16b7
Revert "refactor: organize shared utilities by domain"
suguanYang May 18, 2026
000b5ba
refactor: organize shared utility ownership seams
suguanYang May 18, 2026
34fb93b
refactor: remove internal compatibility wrappers
suguanYang May 18, 2026
7bb32ae
refactor: group demo services by feature
suguanYang May 18, 2026
04afe2f
refactor: remove empty app package markers
suguanYang May 18, 2026
3ee415b
refactor: remove init-only modules
suguanYang May 18, 2026
f7e4511
refactor: remove unused image client bootstrap
suguanYang May 18, 2026
091ab0c
refactor: move current user auth out of core
suguanYang May 18, 2026
efc0c34
refactor: isolate job admission request adapters
suguanYang May 18, 2026
aa59024
refactor: return qstash callback outcomes from workflow
suguanYang May 18, 2026
b2c72ea
refactor: group document lifecycle service
suguanYang May 18, 2026
d848d3c
refactor: group shared job lifecycle services
suguanYang May 18, 2026
7cf84f2
docs: refresh architecture context map
suguanYang May 18, 2026
0fc8d9d
refactor: group webhook callback services
suguanYang May 18, 2026
2ff56d5
refactor: structure worker document parser modules
suguanYang May 18, 2026
1e3e5e2
refactor: group retrieval internals by ownership
suguanYang May 18, 2026
c5452b7
refactor: structure agentic retrieval modules
suguanYang May 18, 2026
16d605f
refactor: split current user dependency from job admission
suguanYang May 18, 2026
285aaa3
refactor: rename document ingestion worker dispatch
suguanYang May 18, 2026
908b421
refactor: replace parser kb dir with namespace
suguanYang May 18, 2026
d2b8454
refactor: decouple parser paths from namespace
suguanYang May 18, 2026
489ca98
refactor: deepen document processing modules
suguanYang May 18, 2026
f684107
refactor: deepen lifecycle and packaging modules
suguanYang May 18, 2026
88ebee7
fix: address advanced security review comments
suguanYang May 18, 2026
fe111d4
fix: clear remaining codeql alerts
suguanYang May 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
104 changes: 60 additions & 44 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ knowhereapi-main/
│ ├── api/ # FastAPI REST API (port 5005)
│ │ ├── app/
│ │ │ ├── api/v1/routes/ # Endpoint handlers
│ │ │ ├── services/ # Business logic (auth, knowledge, billing)
│ │ │ ├── services/ # Business logic (auth, ingestion, billing)
│ │ │ └── repositories/ # Data access layer
│ │ └── main.py # Entrypoint, runs migrations on start
│ ├── worker/ # Celery worker for async document processing
Expand All @@ -50,7 +50,10 @@ knowhereapi-main/
│ ├── services/retrieval/ # Core retrieval engine
│ ├── services/chunks/ # DataFrame → ChunkPayload conversion
│ ├── services/ai/ # LLM prompt service & AI client
│ └── utils/ # Text, file, and chunk utilities
│ ├── services/http/ # Public URL validation and outbound HTTP
│ ├── services/redis/ # Redis state, key language, and retry policy
│ ├── services/quota/ # Shared token-pool quota primitives
│ └── utils/ # Generic text, chunk, and API helpers
└── deploy/ # Docker Compose & deployment scripts
```

Expand All @@ -70,14 +73,14 @@ flowchart TB
end

subgraph PARSE["② Document Parsing (Worker)"]
Queue --> Router["parse_service.checkerboard_inject_parse"]
Router --> Profiler["doc_profiler.profile_document"]
Profiler --> PDF["pdf_parser → MinerU"]
Profiler --> DOCX["doc_parser.parse_docx"]
Profiler --> PPTX["pptx_parser → iLoveAPI → PDF"]
Profiler --> XLSX["table_parser.parse_xlsx"]
Profiler --> MD["md_parser.parse_md"]
Profiler --> IMG["image_parser.parse_image"]
Queue --> Router["parse_service.checkerboard_parse_output"]
Router --> Profiler["profiling.doc_profiler.profile_document"]
Profiler --> PDF["formats.pdf.parser → MinerU"]
Profiler --> DOCX["formats.docx.parser.parse_docx"]
Profiler --> PPTX["formats.pptx.parser → iLoveAPI → PDF"]
Profiler --> XLSX["formats.excel.table_parser.parse_xlsx"]
Profiler --> MD["formats.markdown.parser.parse_md"]
Profiler --> IMG["formats.image.parser.parse_image"]
PDF --> DF["pd.DataFrame (ALL_DF_COLS)"]
DOCX --> DF
PPTX --> DF
Expand Down Expand Up @@ -117,36 +120,36 @@ flowchart TB
### Entry Point

`apps/worker/app/services/document_parser/parse_service.py` →
`checkerboard_inject_parse()`
`checkerboard_parse_output()`

This is the universal entry for all file types. It:
This is the typed `ParseOutput` entry for all file types. The parser flow:

1. **Profiles** the document via `doc_profiler.profile_document()` to detect
1. **Profiles** the document via `profiling.doc_profiler.profile_document()` to detect
file type, page count, and special categories (e.g. `atlas`).
2. **Routes** to the appropriate parser based on file extension.
3. **Post-processes**: cleans up unreferenced images, compresses PNG→JPG.
4. Returns `(output_dir, parsed_df)` — the parsed DataFrame.
4. Returns typed parse output with task-local artifact paths.

### Parser Routing Table

| Extension | Parser Module | Strategy |
|:---|:---|:---|
| `.pdf` | `pdf_parser.parse_pdfs` | MinerU API → `md_parser` → `layout_parser.pred_titles` |
| `.docx` | `doc_parser.parse_docx` + `convert_doc2dics` | OXML iteration → heading detection → hierarchical tree |
| `.doc` | `legacy_converter.doc_to_docx` → `.docx` pipeline | LibreOffice headless conversion first |
| `.pptx` | `pptx_parser.parse_pptx` | iLoveAPI PPTX→PDF → MinerU pipeline |
| `.xlsx` | `table_parser.parse_xlsx` | Sheet-by-sheet HTML table extraction |
| `.xls` | `legacy_converter.xls_to_xlsx` → `.xlsx` pipeline | LibreOffice conversion first |
| `.md` | `md_parser.parse_md` | Markdown heading parsing + LLM summaries |
| `.txt` | `txt_parser.parse_texts` → `md_parser` | Read lines then route to MD parser |
| `.png/.jpg` | `image_parser.parse_image` | VLM image description + OCR |
| `.fragment` | `fragment_parser.parse_fragment` | Raw text fragment ingestion |

### Heading Detection: `layout_parser.pred_titles()`
| `.pdf` | `formats.pdf.parser.parse_pdfs` | MinerU API → Markdown parser → `structure.layout_parser.pred_titles` |
| `.docx` | `formats.docx.parser.parse_docx` + `convert_doc2dics` | OXML iteration → heading detection → hierarchical tree |
| `.doc` | `conversion.legacy_converter.doc_to_docx` → `.docx` pipeline | LibreOffice headless conversion first |
| `.pptx` | `formats.pptx.parser.parse_pptx` | iLoveAPI PPTX→PDF → MinerU pipeline |
| `.xlsx` | `formats.excel.table_parser.parse_xlsx` | Sheet-by-sheet HTML table extraction |
| `.xls` | `conversion.legacy_converter.xls_to_xlsx` → `.xlsx` pipeline | LibreOffice conversion first |
| `.md` | `formats.markdown.parser.parse_md` | Markdown heading parsing + LLM summaries |
| `.txt` | `formats.text.parser.parse_texts` → Markdown parser | Read lines then route to MD parser |
| `.png/.jpg` | `formats.image.parser.parse_image` | VLM image description + OCR |
| `.fragment` | `formats.fragment.parser.parse_fragment` | Raw text fragment ingestion |

### Heading Detection: `structure.layout_parser.pred_titles()`

The core hierarchical recognition module. Determines heading levels using:

1. **TOC-first**: If a DOCX TOC exists (`toc_parser.build_docx_toc_hierarchies`),
1. **TOC-first**: If a DOCX TOC exists (`structure.toc_parser.build_docx_toc_hierarchies`),
use it as ground truth for heading levels.
2. **Regex patterns**: Match numbered headings like `1.2.3`, `第X章`, `(一)`.
3. **LLM smart parse**: When `smart_title_parse=True`, send candidate headings
Expand All @@ -155,7 +158,7 @@ The core hierarchical recognition module. Determines heading levels using:
4. **Font clustering (PDF)**: K-means on span heights from MinerU `layout.json`
to group headings into 5 discrete tiers.

### DOCX Parsing Deep Dive: `doc_parser.py`
### DOCX Parsing Deep Dive: `formats/docx/parser.py`

```mermaid
flowchart LR
Expand Down Expand Up @@ -188,11 +191,11 @@ Key logic in `parse_docx()`:

```mermaid
flowchart LR
PDF[pdf_parser] --> MinerU[MinerU Cloud API]
PDF[formats.pdf.parser] --> MinerU[MinerU Cloud API]
MinerU --> MDFile[Markdown + layout.json]
MDFile --> MDParser[md_parser.parse_md]
MDFile --> MDParser[formats.markdown.parser.parse_md]
MDParser --> EvalHeadings[eval_md_headings + layout.json]
EvalHeadings --> PredTitles[layout_parser.pred_titles]
EvalHeadings --> PredTitles[structure.layout_parser.pred_titles]
PredTitles --> Chunks[Hierarchical Chunks]
```

Expand All @@ -204,21 +207,21 @@ flowchart LR
| Heading hierarchy recognition | `HIERARCHY_LLM_MODEL` | Falls back to `NORMOL_MODEL` |
| Image description (VLM) | `IMAGE_MODEL` | `qwen3.5-flash` |
| Image OCR / Q&A | `IMAGE_MODEL_MAX` | `qwen3.5-flash` |
| Atlas classification | VLM via `atlas_classifier` | `IMAGE_MODEL` |
| Atlas classification | VLM via `formats.atlas.classifier` | `IMAGE_MODEL` |

---

## Persisted Knowledge Base Schema (On-Disk Output)
## Persisted Document Corpus Schema (On-Disk Output)

After parsing and chunk conversion, results are persisted to `~/.knowhere/{kb_name}/`.
After parsing and chunk conversion, results are persisted to `~/.knowhere/{corpus_name}/`.
This on-disk structure is the **authoritative persisted format** — the intermediate
DataFrame is an internal detail. Below is the complete schema.

### KB-Level Directory Layout
### Corpus-Level Directory Layout

```text
~/.knowhere/{kb_name}/
├── knowledge_graph.json # KB-wide graph: file metadata + cross-doc edges
~/.knowhere/{corpus_name}/
├── knowledge_graph.json # corpus-wide graph: file metadata + cross-doc edges
├── chunk_stats.json # Per-chunk retrieval hit analytics {chunk_id → stats}
├── {source_file_name}/ # One directory per ingested document
│ ├── chunks.json # All parsed chunks for this document
Expand All @@ -233,12 +236,12 @@ DataFrame is an internal detail. Below is the complete schema.
│ └── toc_hierarchies.json # Debug: extracted TOC structure (DOCX only)
```

### `knowledge_graph.json` — KB-Wide Graph
### `knowledge_graph.json` — Corpus-Wide Graph

```json
{
"version": "2.0",
"kb_id": "test_kb",
"corpus_id": "test-corpus",
"stats": { "total_files": 3, "total_chunks": 364, "total_cross_file_edges": 0 },
"files": {
"AI_Security_Report.docx": {
Expand Down Expand Up @@ -422,7 +425,7 @@ Used by agentic retrieval for 2-level section browsing. Structure:
```

The `HIERARCHY` field is a nested dict representing the full heading tree
discovered by `layout_parser.pred_titles()`. Each key is a heading title;
discovered by `structure.layout_parser.pred_titles()`. Each key is a heading title;
its value is a dict of child headings (empty `{}` for leaf nodes).

### Intermediate DataFrame (`ALL_DF_COLS`)
Expand Down Expand Up @@ -527,6 +530,19 @@ debug CSVs (`preds_*.csv`) are saved alongside for troubleshooting.

`shared/services/retrieval/app_service.py` → `run_retrieval_query()`

Core retrieval internals are grouped by ownership:

- `execution/`: request shaping, route selection, legacy route execution, and public response projection.
- `search/`: lexical channels, scoring, section filters, and candidate ranking.
- `hydration/`: row/path/reference hydration, inline assets, and result assembly.
- `graph/`: document graph publication/query support.
- `stats/`: retrieval hit recording.
- `workflow/`: query planning, step execution, synthesis, and wallet state.
- `agentic/core/`: agentic run types, token budgets, runtime config, and traces.
- `agentic/discovery/`: bottom discovery and document selection.
- `agentic/navigation/`: section-tree navigation, selection hydration, and asset tools.
- `agentic/evidence/`: evidence tree rendering and budget trimming.

### Two Retrieval Modes

The system supports two modes, controlled globally by `RETRIEVAL_AGENTIC_ENABLED` and locally via the per-request `use_agentic` toggle.
Expand All @@ -543,7 +559,7 @@ flowchart LR
T --> RRF
RRF --> Graph[Legacy Graph Routing]
Graph --> Rank[Dual-priority ranking]
Rank --> Assemble[assemble_retrieval_results]
Rank --> Assemble[hydration.result_assembly]
```

**Channel weights** (default): path=1.0, content=2.0, term=1.5
Expand Down Expand Up @@ -578,18 +594,18 @@ Unlike legacy retrieval which relied on static `hydrate_mode` tags, hydration is

### Result Assembly

`assemble_retrieval_results()`:
`hydration.result_assembly.assemble_retrieval_results()`:

1. Filters by `exclude_document_ids` and `exclude_sections`
2. Filters by `allowed_chunk_types` (data_type parameter)
3. Hydrates `connect_to` targets (related table chunks inlined into text)
4. Cleans asset path references from content
5. Attaches citation: `{document_id, chunk_id, source_file_name, section_path}`

### Small KB Optimization
### Small Corpus Optimization

When `total_chunks <= top_k`, skips the full pipeline and returns all chunks
directly (router: `small_kb_all`).
directly (router: `small_corpus_all`).

### Caching

Expand Down
Loading
Loading