docugraph 0.1.1__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. docugraph-0.2.0/.github/workflows/bench.yml +79 -0
  2. {docugraph-0.1.1 → docugraph-0.2.0}/PKG-INFO +38 -1
  3. {docugraph-0.1.1 → docugraph-0.2.0}/README.md +37 -0
  4. docugraph-0.2.0/benchmarks/retrieval/MANIFEST.json +13 -0
  5. docugraph-0.2.0/benchmarks/retrieval/baselines/bge-small-en-v1.5.json +2669 -0
  6. docugraph-0.2.0/benchmarks/retrieval/corpus.jsonl +36 -0
  7. docugraph-0.2.0/benchmarks/retrieval/queries.jsonl +21 -0
  8. {docugraph-0.1.1 → docugraph-0.2.0}/pyproject.toml +1 -1
  9. docugraph-0.2.0/src/docugraph/evals/__init__.py +12 -0
  10. docugraph-0.2.0/src/docugraph/evals/arms.py +111 -0
  11. docugraph-0.2.0/src/docugraph/evals/compare.py +106 -0
  12. docugraph-0.2.0/src/docugraph/evals/dataset.py +210 -0
  13. docugraph-0.2.0/src/docugraph/evals/metrics.py +123 -0
  14. docugraph-0.2.0/src/docugraph/evals/runner.py +169 -0
  15. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/interfaces/cli.py +176 -0
  16. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/retrieval/hybrid_search.py +7 -3
  17. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/storage/vector_store.py +122 -21
  18. docugraph-0.2.0/tests/integration/test_bench_runner.py +126 -0
  19. docugraph-0.2.0/tests/unit/test_bench_dataset.py +133 -0
  20. docugraph-0.2.0/tests/unit/test_bench_metrics.py +95 -0
  21. {docugraph-0.1.1 → docugraph-0.2.0}/tests/unit/test_hybrid_graph_status.py +7 -0
  22. {docugraph-0.1.1 → docugraph-0.2.0}/uv.lock +3 -3
  23. {docugraph-0.1.1 → docugraph-0.2.0}/.dockerignore +0 -0
  24. {docugraph-0.1.1 → docugraph-0.2.0}/.github/dependabot.yml +0 -0
  25. {docugraph-0.1.1 → docugraph-0.2.0}/.github/workflows/ci.yml +0 -0
  26. {docugraph-0.1.1 → docugraph-0.2.0}/.github/workflows/docker-publish.yml +0 -0
  27. {docugraph-0.1.1 → docugraph-0.2.0}/.github/workflows/live-provider-smoke.yml +0 -0
  28. {docugraph-0.1.1 → docugraph-0.2.0}/.github/workflows/live-reranker.yml +0 -0
  29. {docugraph-0.1.1 → docugraph-0.2.0}/.github/workflows/release.yml +0 -0
  30. {docugraph-0.1.1 → docugraph-0.2.0}/.gitignore +0 -0
  31. {docugraph-0.1.1 → docugraph-0.2.0}/.pre-commit-config.yaml +0 -0
  32. {docugraph-0.1.1 → docugraph-0.2.0}/.python-version +0 -0
  33. {docugraph-0.1.1 → docugraph-0.2.0}/LICENSE +0 -0
  34. {docugraph-0.1.1 → docugraph-0.2.0}/NOTICE +0 -0
  35. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/README.md +0 -0
  36. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/hooks/docugraph-auto-index.sh +0 -0
  37. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/hooks/docugraph-auto-search.sh +0 -0
  38. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/hooks/docugraph-post-compact.sh +0 -0
  39. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/hooks/docugraph-pre-compact.sh +0 -0
  40. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/hooks/docugraph-rule-check.sh +0 -0
  41. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/hooks/docugraph-session-end.sh +0 -0
  42. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/hooks/docugraph-session-start.sh +0 -0
  43. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/install.sh +0 -0
  44. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/settings.json +0 -0
  45. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/skills/architecture/SKILL.md +0 -0
  46. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/skills/context/SKILL.md +0 -0
  47. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/skills/docs/SKILL.md +0 -0
  48. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/skills/index/SKILL.md +0 -0
  49. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/skills/recall/SKILL.md +0 -0
  50. {docugraph-0.1.1 → docugraph-0.2.0}/claude-integration/skills/remember/SKILL.md +0 -0
  51. {docugraph-0.1.1 → docugraph-0.2.0}/docker/Dockerfile +0 -0
  52. {docugraph-0.1.1 → docugraph-0.2.0}/docker/docker-compose.yml +0 -0
  53. {docugraph-0.1.1 → docugraph-0.2.0}/docker-compose.yml +0 -0
  54. {docugraph-0.1.1 → docugraph-0.2.0}/docs/SECURITY.md +0 -0
  55. {docugraph-0.1.1 → docugraph-0.2.0}/docs/configuration.md +0 -0
  56. {docugraph-0.1.1 → docugraph-0.2.0}/docs/quickstart.md +0 -0
  57. {docugraph-0.1.1 → docugraph-0.2.0}/examples/crawl_and_search.py +0 -0
  58. {docugraph-0.1.1 → docugraph-0.2.0}/examples/index_local_repo.py +0 -0
  59. {docugraph-0.1.1 → docugraph-0.2.0}/examples/mcp_client_example.py +0 -0
  60. {docugraph-0.1.1 → docugraph-0.2.0}/scripts/live_provider_smoke.py +0 -0
  61. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/__init__.py +0 -0
  62. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/_version.py +0 -0
  63. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/agents/__init__.py +0 -0
  64. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/agents/coding_agent.py +0 -0
  65. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/agents/tools/__init__.py +0 -0
  66. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/agents/tools/graph_query.py +0 -0
  67. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/agents/tools/memory_ops.py +0 -0
  68. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/agents/tools/search_code.py +0 -0
  69. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/agents/tools/search_docs.py +0 -0
  70. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/core/__init__.py +0 -0
  71. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/core/config.py +0 -0
  72. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/core/embeddings.py +0 -0
  73. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/core/llm.py +0 -0
  74. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/core/models.py +0 -0
  75. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/ingestion/__init__.py +0 -0
  76. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/ingestion/api_docs.py +0 -0
  77. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/ingestion/chunker.py +0 -0
  78. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/ingestion/crawler.py +0 -0
  79. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/ingestion/git_indexer.py +0 -0
  80. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/ingestion/local_files.py +0 -0
  81. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/interfaces/__init__.py +0 -0
  82. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/interfaces/mcp_server.py +0 -0
  83. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/retrieval/__init__.py +0 -0
  84. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/retrieval/context_builder.py +0 -0
  85. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/retrieval/reranker.py +0 -0
  86. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/storage/__init__.py +0 -0
  87. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/storage/graph_store.py +0 -0
  88. {docugraph-0.1.1 → docugraph-0.2.0}/src/docugraph/storage/memory_store.py +0 -0
  89. {docugraph-0.1.1 → docugraph-0.2.0}/tests/e2e/__init__.py +0 -0
  90. {docugraph-0.1.1 → docugraph-0.2.0}/tests/e2e/test_cli.py +0 -0
  91. {docugraph-0.1.1 → docugraph-0.2.0}/tests/e2e/test_hooks.py +0 -0
  92. {docugraph-0.1.1 → docugraph-0.2.0}/tests/e2e/test_mcp_server.py +0 -0
  93. {docugraph-0.1.1 → docugraph-0.2.0}/tests/e2e/tools_snapshot.json +0 -0
  94. {docugraph-0.1.1 → docugraph-0.2.0}/tests/integration/__init__.py +0 -0
  95. {docugraph-0.1.1 → docugraph-0.2.0}/tests/integration/test_graph_store_init.py +0 -0
  96. {docugraph-0.1.1 → docugraph-0.2.0}/tests/integration/test_memory_store.py +0 -0
  97. {docugraph-0.1.1 → docugraph-0.2.0}/tests/unit/__init__.py +0 -0
  98. {docugraph-0.1.1 → docugraph-0.2.0}/tests/unit/test_chunker.py +0 -0
  99. {docugraph-0.1.1 → docugraph-0.2.0}/tests/unit/test_cloud_provider_sdks.py +0 -0
  100. {docugraph-0.1.1 → docugraph-0.2.0}/tests/unit/test_docs_consistency.py +0 -0
  101. {docugraph-0.1.1 → docugraph-0.2.0}/tests/unit/test_embeddings.py +0 -0
  102. {docugraph-0.1.1 → docugraph-0.2.0}/tests/unit/test_event_loop_acquisition.py +0 -0
  103. {docugraph-0.1.1 → docugraph-0.2.0}/tests/unit/test_graph_embedder_adapter.py +0 -0
  104. {docugraph-0.1.1 → docugraph-0.2.0}/tests/unit/test_graphiti_llm_client.py +0 -0
  105. {docugraph-0.1.1 → docugraph-0.2.0}/tests/unit/test_llm_preflight.py +0 -0
  106. {docugraph-0.1.1 → docugraph-0.2.0}/tests/unit/test_models.py +0 -0
  107. {docugraph-0.1.1 → docugraph-0.2.0}/tests/unit/test_no_model_ids_in_source.py +0 -0
  108. {docugraph-0.1.1 → docugraph-0.2.0}/tests/unit/test_reranker.py +0 -0
  109. {docugraph-0.1.1 → docugraph-0.2.0}/tests/unit/test_vector_store.py +0 -0
  110. {docugraph-0.1.1 → docugraph-0.2.0}/tests/unit/test_version.py +0 -0
@@ -0,0 +1,79 @@
1
+ # The retrieval benchmark, run against the real embedder, weekly and on demand.
2
+ #
3
+ # Fast CI proves the harness with a deterministic fake embedder
4
+ # (tests/integration/test_bench_runner.py) and never downloads weights. What
5
+ # that leaves unexercised is the thing the benchmark exists for: the numbers
6
+ # on the shipped model. This job runs `docugraph bench run` on the real
7
+ # bge-small-en-v1.5 (~130 MB, cached between runs) and holds the result
8
+ # against the committed baseline with `docugraph bench compare`, which exits 1
9
+ # when nDCG@10 -- overall or in any single failure-mode category -- drops by
10
+ # more than the tolerance.
11
+ #
12
+ # The baseline under benchmarks/retrieval/baselines/ is committed, per
13
+ # embedding model, and regenerated deliberately: when the dataset or the
14
+ # retrieval stack changes on purpose, a human reruns the benchmark, reads the
15
+ # delta table, and commits the new baseline with the change that moved it.
16
+ # Until a baseline exists for the model, this job fails -- a comparison
17
+ # against nothing proving nothing would be green that means "checked nothing"
18
+ # (the live-reranker header has the longer form of this argument).
19
+ #
20
+ # Reranking arms are not measured here: the cross-encoder is ~2.3 GB and has
21
+ # its own opt-in workflow; run `docugraph bench run --rerank` locally when a
22
+ # reranker change needs numbers.
23
+ name: Retrieval benchmark
24
+
25
+ on:
26
+ # Manual: Actions -> Retrieval benchmark -> Run workflow.
27
+ workflow_dispatch:
28
+ schedule:
29
+ # Weekly, Monday 06:17 UTC. An off-hour minute, deliberately: scheduled
30
+ # workflows on :00 queue behind everyone else's.
31
+ - cron: "17 6 * * 1"
32
+
33
+ env:
34
+ FASTEMBED_CACHE_PATH: /home/runner/.cache/fastembed
35
+
36
+ jobs:
37
+ bench:
38
+ name: bench run · compare to baseline
39
+ runs-on: ubuntu-latest
40
+ steps:
41
+ - name: Checkout repository
42
+ uses: actions/checkout@v4
43
+
44
+ - name: Set up Python
45
+ uses: actions/setup-python@v5
46
+ with:
47
+ python-version: '3.12'
48
+
49
+ - name: Install uv
50
+ uses: astral-sh/setup-uv@v4
51
+
52
+ - name: Sync dependencies
53
+ run: uv sync --all-extras
54
+
55
+ - name: Cache embedding model
56
+ uses: actions/cache@v4
57
+ with:
58
+ path: /home/runner/.cache/fastembed
59
+ key: fastembed-bge-small-en-v1.5
60
+
61
+ - name: Run benchmark
62
+ run: uv run docugraph bench run --json bench-report.json
63
+
64
+ - name: Compare against committed baseline
65
+ run: |
66
+ baseline="benchmarks/retrieval/baselines/bge-small-en-v1.5.json"
67
+ if [ ! -f "$baseline" ]; then
68
+ echo "::error::No committed baseline at $baseline. Run 'docugraph bench run --json' locally and commit the report as the baseline; a compare against nothing is not a gate."
69
+ exit 1
70
+ fi
71
+ uv run docugraph bench compare "$baseline" bench-report.json
72
+
73
+ - name: Upload report
74
+ if: always()
75
+ uses: actions/upload-artifact@v4
76
+ with:
77
+ name: bench-report
78
+ path: bench-report.json
79
+ if-no-files-found: ignore
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: docugraph
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: Documentation RAG for coding agents: vector search, knowledge graph, agent memory, and an MCP server — torch-free local embeddings via fastembed
5
5
  Project-URL: Homepage, https://github.com/CJ-coding-apps/docugraph
6
6
  Project-URL: Repository, https://github.com/CJ-coding-apps/docugraph
@@ -78,6 +78,7 @@ Code and other MCP clients. The CLI is for out-of-band index bootstrapping
78
78
  - **Agent memory**: persistent, repository/branch-scoped key-value store
79
79
  - **Optional reranking**: local multilingual cross-encoder (BAAI/bge-reranker-v2-m3), opt-in
80
80
  - **Torch-free**: default embeddings and reranking run on ONNX — no PyTorch in the dependency tree
81
+ - **Measured retrieval**: a committed adversarial benchmark scores every search mode per failure category, with a regression gate (`docugraph bench`)
81
82
 
82
83
  ## Quick start
83
84
 
@@ -308,6 +309,42 @@ required**. However, Graphiti performs entity extraction with an **LLM**, so
308
309
  OpenAI/Anthropic key). Without one, the graph tools degrade gracefully with a
309
310
  clear message; vector and keyword search are unaffected.
310
311
 
312
+ ## Retrieval benchmark
313
+
314
+ Retrieval quality is measured, not asserted. `benchmarks/retrieval/` commits a
315
+ hand-authored documentation corpus for a fictional SDK plus graded relevance
316
+ judgments, built adversarially around seven failure modes: exact-token lookups
317
+ (error codes, parameter names), paraphrase queries with no lexical overlap,
318
+ keyword-stuffed distractor pages, near-sibling semantic distractors, version-
319
+ pinned questions, acronym/synonym gaps, and "X without Y" constraint queries.
320
+ Each query records the trap it sets in a `design_note`, and a manifest pins the
321
+ dataset by hash — editing it is a deliberate act that invalidates baselines.
322
+
323
+ ```bash
324
+ # Score every arm (vector-only, BM25-only, LanceDB-native hybrid, RRF, weighted)
325
+ # against a temporary store built from the committed corpus:
326
+ docugraph bench run --json report.json
327
+
328
+ # Hold a report against a committed baseline; exits 1 when nDCG@10 drops by
329
+ # more than the tolerance, overall or in any single category:
330
+ docugraph bench compare benchmarks/retrieval/baselines/bge-small-en-v1.5.json report.json
331
+ ```
332
+
333
+ Reported metrics are recall@1/5/10, MRR@10, and nDCG@10 (graded, linear gain),
334
+ macro-averaged overall and per category. The per-category table is the point:
335
+ a fusion change that wins on average while losing exact-code lookup shows up
336
+ as the regression it is. Baselines are per embedding model and live in
337
+ `benchmarks/retrieval/baselines/`; the `Retrieval benchmark` workflow re-runs
338
+ the suite weekly against the committed baseline.
339
+
340
+ Honest scope: the corpus is small and purpose-built, so scores are comparable
341
+ across docugraph changes — not with BEIR or any external leaderboard. The
342
+ first thing this benchmark caught predates it: before v0.2.0 no FTS index was
343
+ ever created, so "keyword" search silently returned vector results and
344
+ RRF fused the vector ranking with itself. The benchmark's `fts` arm and the
345
+ hybrid arm's refuse-to-score-on-fallback check exist so that class of silent
346
+ degradation cannot come back unnoticed.
347
+
311
348
  ### KuzuDB version
312
349
 
313
350
  `kuzu` is pinned to `==0.11.3` in `pyproject.toml` (reached through
@@ -16,6 +16,7 @@ Code and other MCP clients. The CLI is for out-of-band index bootstrapping
16
16
  - **Agent memory**: persistent, repository/branch-scoped key-value store
17
17
  - **Optional reranking**: local multilingual cross-encoder (BAAI/bge-reranker-v2-m3), opt-in
18
18
  - **Torch-free**: default embeddings and reranking run on ONNX — no PyTorch in the dependency tree
19
+ - **Measured retrieval**: a committed adversarial benchmark scores every search mode per failure category, with a regression gate (`docugraph bench`)
19
20
 
20
21
  ## Quick start
21
22
 
@@ -246,6 +247,42 @@ required**. However, Graphiti performs entity extraction with an **LLM**, so
246
247
  OpenAI/Anthropic key). Without one, the graph tools degrade gracefully with a
247
248
  clear message; vector and keyword search are unaffected.
248
249
 
250
+ ## Retrieval benchmark
251
+
252
+ Retrieval quality is measured, not asserted. `benchmarks/retrieval/` commits a
253
+ hand-authored documentation corpus for a fictional SDK plus graded relevance
254
+ judgments, built adversarially around seven failure modes: exact-token lookups
255
+ (error codes, parameter names), paraphrase queries with no lexical overlap,
256
+ keyword-stuffed distractor pages, near-sibling semantic distractors, version-
257
+ pinned questions, acronym/synonym gaps, and "X without Y" constraint queries.
258
+ Each query records the trap it sets in a `design_note`, and a manifest pins the
259
+ dataset by hash — editing it is a deliberate act that invalidates baselines.
260
+
261
+ ```bash
262
+ # Score every arm (vector-only, BM25-only, LanceDB-native hybrid, RRF, weighted)
263
+ # against a temporary store built from the committed corpus:
264
+ docugraph bench run --json report.json
265
+
266
+ # Hold a report against a committed baseline; exits 1 when nDCG@10 drops by
267
+ # more than the tolerance, overall or in any single category:
268
+ docugraph bench compare benchmarks/retrieval/baselines/bge-small-en-v1.5.json report.json
269
+ ```
270
+
271
+ Reported metrics are recall@1/5/10, MRR@10, and nDCG@10 (graded, linear gain),
272
+ macro-averaged overall and per category. The per-category table is the point:
273
+ a fusion change that wins on average while losing exact-code lookup shows up
274
+ as the regression it is. Baselines are per embedding model and live in
275
+ `benchmarks/retrieval/baselines/`; the `Retrieval benchmark` workflow re-runs
276
+ the suite weekly against the committed baseline.
277
+
278
+ Honest scope: the corpus is small and purpose-built, so scores are comparable
279
+ across docugraph changes — not with BEIR or any external leaderboard. The
280
+ first thing this benchmark caught predates it: before v0.2.0 no FTS index was
281
+ ever created, so "keyword" search silently returned vector results and
282
+ RRF fused the vector ranking with itself. The benchmark's `fts` arm and the
283
+ hybrid arm's refuse-to-score-on-fallback check exist so that class of silent
284
+ degradation cannot come back unnoticed.
285
+
249
286
  ### KuzuDB version
250
287
 
251
288
  `kuzu` is pinned to `==0.11.3` in `pyproject.toml` (reached through
@@ -0,0 +1,13 @@
1
+ {
2
+ "dataset": "docugraph-retrieval-bench",
3
+ "version": "0.1.0-pilot",
4
+ "description": "Hand-authored adversarial documentation corpus (fictional 'Quorva' SDK) with graded relevance judgments across seven retrieval failure modes. Pilot scale; see the benchmark README section for authoring rules.",
5
+ "counts": {
6
+ "corpus": 36,
7
+ "queries": 21
8
+ },
9
+ "sha256": {
10
+ "corpus.jsonl": "6318b464c188eccd62a4f6eea8c457e7c63780d1d3b7d3e6a0728795955ea5ca",
11
+ "queries.jsonl": "afcc08a10c73d350244fd13c2006a047aa82d18a4be4fbfc2323cb3741979344"
12
+ }
13
+ }