Visitar URL original
[Sync] merge to main for 3.0.2 release - bug fixes; knowledge indexing; future compatibility & dependencies by deruyter92 · Pull Request #3495 · DeepLabCut/DeepLabCut · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
246 commits
Select commit Hold shift + click to select a range
9cc1d18
Add Matplotlib colormap compatibility shim
C-Achard Jul 13, 2026
9518b9d
Allow custom warning stacklevel in deprecated
C-Achard Jul 13, 2026
bd7fdbb
Use shared get_colormap helper everywhere
C-Achard Jul 13, 2026
4bf4921
Merge branch 'main' into cy/init-mpl-version-change
C-Achard Jul 16, 2026
aafaa2e
Guard CoW read-only .to_numpy()/.values mutations (#3362)
AxelNoun Jul 19, 2026
8529146
linting
deruyter92 Jul 22, 2026
18bc897
Guard remaining CoW read-only .to_numpy()/.values mutations
AxelNoun Jul 25, 2026
b953416
fix pandas future mode tool: enable instead of "warn"
deruyter92 Jul 27, 2026
7101f3c
add tests for pd future mode tool
deruyter92 Jul 27, 2026
db296a8
Merge branch 'main' into fix/pandas3-cow-readonly-tonumpy
AxelNoun Jul 27, 2026
22ffb1b
fix test parametrization in test_pandas_cow
deruyter92 Jul 28, 2026
7c99777
Add copy=True to to_numpy in evaluate_multianimal peaks_gt
deruyter92 Jul 28, 2026
a3977ff
fix test_pandas_cow leaking global `infer_string` and `copy_on_write`…
deruyter92 Jul 28, 2026
fcdfc44
Add detector_eval_interval to train_from_coco to enable validation ou…
juan-cobos Aug 5, 2026
a5d5221
Merge remote-tracking branch 'upstream/main' into cy/init-mpl-version…
deruyter92 Aug 9, 2026
d5945cc
Fix get_colormap_names() legacy fallback to call _legacy_get_colormap…
deruyter92 Aug 9, 2026
d77d4f8
update version for "deprecated since .." warning
deruyter92 Aug 9, 2026
5e739e2
Add name override to deprecated() for legacy shim warnings
deruyter92 Aug 9, 2026
e6cfac6
Rename matplotlib_future_mode.py → matplotlib_compat.py
deruyter92 Aug 9, 2026
d9288fc
add tests for matplotlib compat layer
deruyter92 Aug 9, 2026
d359d83
add top-level None-type normalization in ProjectConfig
deruyter92 Aug 9, 2026
5471c9f
remove `ignore_empty` parameter in DLCBaseConfig.from_yaml
deruyter92 Aug 9, 2026
d4d73b3
add tests for None-type/empty ProjectConfig fields
deruyter92 Aug 9, 2026
eb63bda
fix docstring clarity and deprecation warning message
deruyter92 Aug 9, 2026
432cd25
fix config editor crash: QLabel does not accept a Path
deruyter92 Aug 12, 2026
75b317b
move config editor opening into DefaultTab
deruyter92 Aug 12, 2026
a1c901e
add tests for the DefaultTab config editor helper
deruyter92 Aug 12, 2026
f34a8c6
Fix incorrect scorer filtering in viz
C-Achard Aug 13, 2026
2f309c2
Add visualization evaluation regression tests
C-Achard Aug 13, 2026
13521a6
Validate plotting mode and clarify warnings
C-Achard Aug 13, 2026
3d15036
Fix unique bodypart plotting per row
C-Achard Aug 13, 2026
bed40e0
Harden evaluation plotting and typing
C-Achard Aug 13, 2026
fb4b16d
Normalize visualization file headers
C-Achard Aug 13, 2026
32fd7e7
Fix unique bodypart handling in plotting
C-Achard Aug 13, 2026
c1dfdc4
Clean up visualization shape and type naming
C-Achard Aug 13, 2026
947f28b
Don’t skip plots on individual label mismatch
C-Achard Aug 13, 2026
8eb9998
Guard unique-row plotting when data is missing
C-Achard Aug 13, 2026
c88f69c
Fix invalid fixture arg
C-Achard Aug 13, 2026
6bce484
Fix color indexing in eval visualization
C-Achard Aug 13, 2026
c422601
Fix evaluation plot color offset handling
C-Achard Aug 13, 2026
5bc3260
Add tests for unique bodypart evaluation plotting
C-Achard Aug 13, 2026
ba536eb
Normalize video path matching in extraction
C-Achard Aug 14, 2026
819fecc
Add frame extraction path handling tests
C-Achard Aug 14, 2026
012bf98
Fix missing closing parenthesis
C-Achard Aug 14, 2026
23b601e
Fix io import in frame extraction tests
C-Achard Aug 14, 2026
06dba4a
Merge branch 'cy/fix-frame-extraction-path-matching' of https://githu…
C-Achard Aug 14, 2026
74d25a3
Fix snapshot download rename mapping arg
C-Achard Aug 14, 2026
d59eb24
Warn when model zoo inference returns no poses
C-Achard Aug 14, 2026
d8dd415
Fix model download filename remapping
C-Achard Aug 17, 2026
b629813
Raise error when video pose predictions are empty
C-Achard Aug 17, 2026
39b883e
Use logging for eval warnings and update tests
C-Achard Aug 18, 2026
9be111c
Refactor labeled image rendering utilities
C-Achard Aug 18, 2026
974cb52
Simplify frame size handling in visualization
C-Achard Aug 18, 2026
2e01e34
Robust png replacement
C-Achard Aug 18, 2026
9fc4de5
Fix eval plot y-axis orientation
C-Achard Aug 18, 2026
1008670
Fix snapshot download rename mapping
C-Achard Aug 18, 2026
d0d4a9e
Fix super animal snapshot rename check
C-Achard Aug 19, 2026
fdfd856
Merge branch 'main' into cy/viz-fix-and-tests
C-Achard Aug 25, 2026
635bd3a
Empty - Trigger CI
C-Achard Aug 26, 2026
d63ba79
Tighten video path typing in frame extraction
C-Achard Aug 26, 2026
cf79a7d
Merge branch 'main' into cy/fix-modelzoo-humanbody-download
C-Achard Aug 26, 2026
6fc0ca3
Fix missing func usage
C-Achard Aug 28, 2026
c202539
fix WandbLoggerConfig include additional kwargs in pydantic schema
deruyter92 Aug 28, 2026
fcfb6eb
Treat a null box_score_thresh as the default detector threshold
rysk-t Aug 30, 2026
4746150
fix misaligned error log for GUI create videos tab
deruyter92 Aug 28, 2026
d7dec0a
Silence misleading video-extension warning for non-video collections
deruyter92 Aug 28, 2026
a18ede6
Fix h5->csv/nwb conversion silently skipping non-mp4 projects
deruyter92 Aug 28, 2026
6c33529
fix missing str conversion in failed_videos report for GUI CreateVide…
deruyter92 Aug 28, 2026
400454c
fix str missing conversion in plot_trajectories
deruyter92 Aug 28, 2026
ec58481
Fix outdated str function call to correct Path suffix swap for CSV to…
C-Achard Aug 28, 2026
f95cf13
fix `export_model` dumping objects in pickle: add `normalize=True` in…
deruyter92 Aug 28, 2026
b447933
add test to ensure export model writes only primitive types
deruyter92 Aug 31, 2026
6db842d
Normalize detector box score threshold
C-Achard Aug 31, 2026
21a0eb9
Refactor detector threshold tests to config level
C-Achard Aug 31, 2026
c61ddcc
Fix incorrect import
C-Achard Aug 31, 2026
c19bc07
Skip TensorFlow-backed tests cleanly when the tf extra is absent
natsukium Jul 20, 2026
1a729f6
Raise numpy lower bound to 1.22.4 for np.percentile's method= keyword
natsukium Jul 20, 2026
1107360
Replace NumPy 2.0-removed array APIs with supported equivalents
natsukium Jul 20, 2026
252183c
Estimate tracklet rank with a dense SVD instead of interpolative SVD
natsukium Jul 20, 2026
73f20e8
Compare montblanc assemblies order-independently in the assembler test
natsukium Jul 20, 2026
1f034ae
`Tracklet`: restore divsion-by-zero guard
deruyter92 Jul 27, 2026
1734b8e
add basic test for `Tracklet.estimate_rank`
deruyter92 Jul 27, 2026
4e0983d
deduplicate crossvalutils._calc_separability into a shared utility
deruyter92 Jul 27, 2026
b070e52
Hoist `np.trapz` compat check to module-level constants
deruyter92 Jul 27, 2026
9e611d5
Update uv.lock
C-Achard Aug 26, 2026
f304fba
fix COCOLoader max_individuals: make PoseConfig authorative, not json
deruyter92 Aug 5, 2026
488af3f
add basic tests for COCOLoader
deruyter92 Aug 5, 2026
4b733ba
refactor COCOLoader get_project_parameters: non-authorative bootstrap…
deruyter92 Aug 5, 2026
85fe2c0
Merge pull request #3436 from juan-cobos/feat/detector-eval-interval
deruyter92 Aug 31, 2026
f0e1c21
Merge pull request #3439 from deruyter92/jaap/project-config-null-nor…
deruyter92 Aug 31, 2026
43a47d9
Merge pull request #3441 from DeepLabCut/jaap/gui-config-editor-helper
deruyter92 Aug 31, 2026
c6ad634
Merge pull request #3445 from AxelNoun/fix/pandas3-cow-guards
deruyter92 Aug 31, 2026
085efe5
Merge pull request #3447 from DeepLabCut/cy/viz-fix-and-tests
deruyter92 Aug 31, 2026
b4ae22f
Merge pull request #3452 from DeepLabCut/cy/fix-modelzoo-humanbody-do…
deruyter92 Aug 31, 2026
73fca7b
update `test_apis_export`: strict type checking in asserting primitiv…
deruyter92 Aug 31, 2026
1c8da4e
Broaden video selection types and error message
C-Achard Aug 31, 2026
92f9413
Pass selected videos as Path objects in GUI
C-Achard Aug 31, 2026
8556ccf
Clarify extract_frames docstring and examples
C-Achard Aug 31, 2026
2df5f30
Raise on unmatched selected videos
C-Achard Aug 31, 2026
df59a9f
Update test_frame_extraction.py
C-Achard Aug 31, 2026
1150d78
Merge pull request #3473 from DeepLabCut/jaap/fix-export-model
deruyter92 Sep 1, 2026
aac861c
Fix model config test defaults for TD
C-Achard Sep 1, 2026
85072d9
Use config cutoff for detector boxes
C-Achard Sep 1, 2026
7893a35
Fix detector box_score_thresh handling
C-Achard Sep 1, 2026
5e58815
Clarify detector threshold docs
C-Achard Sep 1, 2026
3e2611e
Fix `create_video_with_all_detections` -> read default `bbox_pcutoff`…
deruyter92 Sep 1, 2026
baff692
revert shared box score threshold default -> inline config default
deruyter92 Sep 1, 2026
0f5330a
update docstring for `DetectorModelConfig`
deruyter92 Sep 1, 2026
256dfb9
add test for building config with null threshold
deruyter92 Sep 1, 2026
6e0d695
add `DefaultIfNone` normalizer to validation.py
deruyter92 Sep 1, 2026
aa4bedc
add tests for `DefaultIfNone` validator
deruyter92 Sep 1, 2026
95068f1
add tests for other config validators
deruyter92 Sep 1, 2026
83ab775
apply `DefaultIfNone` to `DetectorModelConfig.box_score_thresh`
deruyter92 Sep 1, 2026
2a00c40
Make `ProjectConfig.bboxes_pcutoff`` a property rather than a field
deruyter92 Sep 1, 2026
2689d30
add xfail for `ProjectConfig.bboxes_pcutoff`
deruyter92 Sep 1, 2026
27eb40d
Bump tornado from 6.5.7 to 6.5.8
dependabot[bot] Sep 1, 2026
b1dd957
Merge pull request #3470 from rysk-t/detector-bugfixes
deruyter92 Sep 3, 2026
685bb08
Merge pull request #3477 from DeepLabCut/main
deruyter92 Sep 3, 2026
390c5da
fix typo in docstring
deruyter92 Sep 3, 2026
61ea91a
partly revert c202539956f6f1da90ca2f32a93485643cabdc87: remove top-le…
deruyter92 Sep 3, 2026
55dd210
WandbLoggerConfig: add before validator that accepts top-level WandB …
deruyter92 Sep 3, 2026
dea2622
WandbLoggerConfig add field validator for wandb_kwargs (do not allow …
deruyter92 Sep 3, 2026
7f9a86c
add tests for WandbLoggerConfig
deruyter92 Sep 3, 2026
1af2081
Bump mkdocs-material from 9.7.6 to 9.7.7
dependabot[bot] Sep 3, 2026
e20e03a
fix gui tests: use `config_path` in `MainWindow` config tests
deruyter92 Sep 4, 2026
aaa86db
Merge pull request #3480 from DeepLabCut/jaap/fix-gui-test-config-path
deruyter92 Sep 4, 2026
b698519
Improve WandB logger error handling
C-Achard Sep 4, 2026
13ebd15
Add error handling tests for wandb logger
C-Achard Sep 4, 2026
0473dcd
Potential fix for pull request finding
deruyter92 Sep 4, 2026
e3d0bb8
Potential fix for pull request finding
deruyter92 Sep 4, 2026
11fe5c7
Update deeplabcut/pose_estimation_pytorch/config/logger.py
deruyter92 Sep 4, 2026
17080e8
copy dict `data` in `WandbLoggerConfig`
deruyter92 Sep 4, 2026
12c7be9
Merge branch 'dev' into cy/fix-frame-extraction-path-matching
C-Achard Sep 4, 2026
6bce3a2
fix WandbLoggerConfig missing return value
deruyter92 Sep 4, 2026
350e209
Import matplotlib.cm explicitly in the compat layer
C-Achard Sep 4, 2026
54ac2ac
Prevent duplicate legacy colormap registration
C-Achard Sep 4, 2026
b1c97fb
Route all plt.get_cmap calls throught the compat layer
C-Achard Sep 4, 2026
c1cf140
Use compat helper to clear matplotlib artists
C-Achard Sep 4, 2026
0e4a691
Patch matplotlib's Axes logger by name
C-Achard Sep 4, 2026
d3472c3
Test the name and stacklevel options of @deprecated
C-Achard Sep 4, 2026
0be8837
Expand matplotlib compat test coverage
C-Achard Sep 4, 2026
fe9d488
Remove unused compat helpers
C-Achard Sep 4, 2026
304bcc6
Remove unecessary tests
C-Achard Sep 4, 2026
4a82207
Merge pull request #3449 from DeepLabCut/cy/fix-frame-extraction-path…
deruyter92 Sep 4, 2026
aa007f6
Rename matplotlib compat module to utils
C-Achard Sep 4, 2026
0584d53
Replace matplotlib_compat colormap helpers
C-Achard Sep 4, 2026
1c15225
Simplify Matplotlib axes logger helper
C-Achard Sep 4, 2026
8a0ea71
Fix Matplotlib artist iteration
C-Achard Sep 4, 2026
1535b51
Merge pull request #3467 from DeepLabCut/jaap/pathlib-fixes
deruyter92 Sep 8, 2026
cc37cd0
Fix `tools` imports under a plain `pytest` invocation
deruyter92 Sep 7, 2026
44b47ca
Merge pull request #3475 from DeepLabCut/jaap/add-config-null-default…
deruyter92 Sep 8, 2026
3e8ce1a
Merge pull request #3469 from DeepLabCut/jaap/fix-wandb-logger-config
deruyter92 Sep 8, 2026
6646f58
Merge pull request #3481 from DeepLabCut/cy/harden-wandb-logger-errors
deruyter92 Sep 8, 2026
0e87bb2
docs: fix dead Model Zoo link in examples/README
wilmund Sep 7, 2026
91bff49
Merge pull request #3486 from wilmund/fix-dead-modelzoo-link
deruyter92 Sep 8, 2026
4cf9f35
Merge branch 'dev' into jaap/fix-create-video
deruyter92 Sep 8, 2026
fe1a73f
Merge pull request #3468 from DeepLabCut/jaap/fix-create-video
deruyter92 Sep 8, 2026
426943f
Merge branch 'dev' into cy/init-mpl-version-change
deruyter92 Sep 8, 2026
4c0907a
Merge pull request #3492 from DeepLabCut/main
deruyter92 Sep 8, 2026
7e8dcd2
Merge pull request #3484 from DeepLabCut/jaap/fix-tool-imports-pytest
deruyter92 Sep 8, 2026
2b7bce5
Tools: add knowledge indexing generator for an LLM-friendly docs index
deruyter92 Aug 12, 2026
c37bd0d
fix default output dir: `_build`
deruyter92 Aug 26, 2026
a2a5e0b
use full commit hash of repo
deruyter92 Aug 26, 2026
fe0fbfb
add duplicate check
deruyter92 Aug 26, 2026
65cc178
move knowledge index to JSONL + llms.txt
deruyter92 Aug 26, 2026
75ef519
add content hash and distinct `package_version` (in addition to docs …
deruyter92 Aug 26, 2026
9baf364
update pyproject.toml: fix missing `griffelib` dependency for knowled…
deruyter92 Aug 26, 2026
3ca5c90
allow separate adjustment of doc index and dev-docs api index
deruyter92 Aug 26, 2026
e309fcd
add CI workflows for knowlede-index deployment
deruyter92 Aug 26, 2026
cc46137
update docstrings and readme
deruyter92 Aug 26, 2026
6dc52fb
separate dependencies for leaner CI build&deployment of knowledge-index
deruyter92 Aug 26, 2026
6237e28
simplify llms.txt no per-page summary of doc sections. Prominent ref…
deruyter92 Aug 26, 2026
72f5494
update uv.lock
deruyter92 Aug 26, 2026
21d68b9
validate version labels + tighten manifest reading
deruyter92 Aug 27, 2026
c2db3dd
CI: harden reusable docs workflows against script injection
deruyter92 Aug 27, 2026
cf1f87d
fix delete version + refuse delete "main".
deruyter92 Aug 27, 2026
bf00732
Fix bug in checkout from tag merging into pre-existing checkout
deruyter92 Aug 27, 2026
06967d3
knowledge-index Fix bug in checkout from tag merging into pre-existin…
deruyter92 Aug 27, 2026
8b23cf9
check if dev-docs version is deployed before attempting a delete
deruyter92 Aug 27, 2026
908c6dc
mark dev-docs and knowledge-index dependencies as dependecy-group (ra…
deruyter92 Aug 27, 2026
67111b8
update uv.lock
deruyter92 Sep 4, 2026
e7ab837
add tests for tools/knowledge-indexing
deruyter92 Sep 4, 2026
ff047dd
set `ignore_errors=False` on `delete_version` -> raise if directory i…
deruyter92 Sep 7, 2026
0630f83
fix docs_index frontmatter matching via regex
deruyter92 Sep 7, 2026
6b618fb
update test_write: use real API dataclasses instead of `NameSpace` + …
deruyter92 Sep 7, 2026
e10d279
add tests for frontmatter matching
deruyter92 Sep 7, 2026
76cb4ae
conditional guard dependency install knowledge-indexing: deployment-o…
deruyter92 Sep 7, 2026
58461ed
Move intermediate nodes to schemas (importable without dependencies)
deruyter92 Sep 7, 2026
8ce5334
remove tests/tools/knowledge_indexing/conftest.py
deruyter92 Sep 7, 2026
fcad299
update pyproject.toml and uv lock - add markdow-it-py as dev dependency
deruyter92 Sep 7, 2026
e5bcc48
lazy import docutils (so it is not required at test-time)
deruyter92 Sep 7, 2026
57a9f93
Only the first H1 titles a docs page
deruyter92 Sep 7, 2026
2c884cb
Harden docs frontmatter and record ids
deruyter92 Sep 7, 2026
7864a65
Read docs section anchors from the built site instead of markdown
deruyter92 Sep 7, 2026
b5fb2a0
Resolve api.latest through the latest-release alias
deruyter92 Sep 7, 2026
4453c30
Separate symbol ids from submodule ids with `#`
deruyter92 Sep 7, 2026
02b4f95
Index class methods as their own api records
deruyter92 Sep 7, 2026
3eaa5e1
install knowlegde-index dependency group for testing
deruyter92 Sep 8, 2026
c96b3ff
add agents.md
deruyter92 Sep 7, 2026
a993fac
Update AGENTS.md
deruyter92 Sep 8, 2026
5a6061a
Update AGENTS.md
deruyter92 Sep 8, 2026
242174c
Update AGENTS.md
deruyter92 Sep 8, 2026
7a27dcc
Update AGENTS.md (incorporate review feedback)
deruyter92 Sep 8, 2026
1ff346f
Merge pull request #3453 from DeepLabCut/jaap/add-llm-knowledge-index
deruyter92 Sep 8, 2026
8af305a
Merge pull request #3483 from DeepLabCut/jaap/add-agents-md
deruyter92 Sep 8, 2026
31380eb
Merge branch 'dev' into cy/init-mpl-version-change
deruyter92 Sep 8, 2026
42d1b62
Merge pull request #3478 from DeepLabCut/dependabot/uv/mkdocs-materia…
deruyter92 Sep 8, 2026
c1f7aaa
Merge pull request #3476 from DeepLabCut/dependabot/uv/tornado-6.5.8
deruyter92 Sep 8, 2026
db6639e
bound dependencies for dependencies for knowledge-index dependency group
deruyter92 Sep 8, 2026
bbaa99f
discriminate between stable release and rolling main reference in llm…
deruyter92 Sep 8, 2026
de29151
Merge branch 'dev' into cy/init-mpl-version-change
deruyter92 Sep 9, 2026
8ebe974
Reject --skip-api on non-main labels as a no-op
deruyter92 Sep 9, 2026
0b594d9
Write empty JSONL as an empty file
deruyter92 Sep 9, 2026
c47f846
Avoid publishing empty knowledge version directories
deruyter92 Sep 9, 2026
9f87d15
Keep published-anchor occurrence counts aligned
deruyter92 Sep 9, 2026
c1664eb
update tests: docs link resolution and root API re-exports
deruyter92 Sep 9, 2026
29b1594
update uv.lock
deruyter92 Sep 9, 2026
795d2b0
Merge pull request #3394 from DeepLabCut/cy/init-mpl-version-change
deruyter92 Sep 9, 2026
7f504c0
Fix llms.txt API reference links 404s
C-Achard Sep 9, 2026
569b7fa
Fix API docs URLs for aliased symbols
C-Achard Sep 9, 2026
f8d9bb4
Clarify empty knowledge indexing error
C-Achard Sep 9, 2026
8480fc4
Fix docs URLs for re-exported class methods
C-Achard Sep 9, 2026
2a6e3a1
Clarify llms.txt API link test
C-Achard Sep 9, 2026
8c6db5c
fix numpy compat in crossvalutils and predict_multianimal
deruyter92 Sep 9, 2026
2b0ac75
Add test: cannot swap main/stable api links
C-Achard Sep 9, 2026
676f0f2
Update README.md
C-Achard Sep 9, 2026
8b0536b
Assert the api-autonav constants match dev-docs/mkdocs.yml
C-Achard Sep 9, 2026
a325912
Bump gitpython from 3.1.50 to 3.1.59
dependabot[bot] Sep 9, 2026
f2f16c2
Merge pull request #3501 from DeepLabCut/dependabot/uv/gitpython-3.1.59
deruyter92 Sep 9, 2026
053d105
Merge pull request #3498 from DeepLabCut/jaap/numpy-2-compatibility
deruyter92 Sep 9, 2026
1df0fc1
fix(trackingutils): take the real part of eig eigenvectors in Ellipse…
ender10 Sep 9, 2026
fce1e4f
Merge pull request #3502 from michael-denyer/fix/ellipse-fit-complex-eig
deruyter92 Sep 9, 2026
3ea6e61
Merge branch 'dev' into jaap/knowledge-index-improvements
C-Achard Sep 11, 2026
175e10f
Remove hard-coded overwrite
C-Achard Sep 14, 2026
3461180
Add proper overwrite kwarg to analyze_videos
C-Achard Sep 14, 2026
da969d4
Pass overwrite to tracklets generation as well
C-Achard Sep 14, 2026
c0a6b11
Avoid shuffling existing positional calls
C-Achard Sep 14, 2026
7be7ca3
Fix overwrite forwarding in analyze_videos
C-Achard Sep 14, 2026
96f0c44
Follow-up fixes for numpy2 compatibility (#3508)
C-Achard Sep 14, 2026
5599af5
Tighten strict handling for canary CI cells (#3509)
C-Achard Sep 14, 2026
0417ae2
remove dependency upper bounds for numpy and matplotlib (#3499)
deruyter92 Sep 14, 2026
1c38232
Merge pull request #3494 from DeepLabCut/jaap/knowledge-index-improve…
deruyter92 Sep 14, 2026
a897636
Harden regression test for issue #3513
deruyter92 Sep 14, 2026
5ea382c
improve docstring in analyze_videos
deruyter92 Sep 14, 2026
fd4a89f
Add regression check for overwrite in auto-track path
deruyter92 Sep 14, 2026
60998ef
Merge pull request #3514 from DeepLabCut/cy/fix-overwrite-torch-kwarg
deruyter92 Sep 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,11 @@ gui-dev = [
"pytest-qt",
]
knowledge-index = [
"docutils",
"griffelib",
"markdown-it-py",
# Keep these aligned with the majors the docs toolchain resolves to:
# ids, anchors, signatures and symbol kinds are derived through them.
"docutils>=0.20,<0.22",
"griffelib>=2,<3",
"markdown-it-py>=3,<4",
"pyyaml",
]

Expand Down
11 changes: 11 additions & 0 deletions tests/tools/knowledge_indexing/test_api_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,14 @@ def test_the_class_itself_is_still_recorded(nodes):
symbols = _symbols(nodes, "demopkg.thing")

assert symbols["Thing"].kind == "class"


def test_root_reexports_are_indexed_on_the_package(nodes):
# Public API is re-exported from the package root; URLs must stay on that page.
symbols = _symbols(nodes, "demopkg")

assert symbols["Thing"].kind == "class"
assert symbols["Thing"].docs_url == ("https://example.test/reference/demopkg/thing/#demopkg.thing.Thing")
# A re-exported class's methods follow the class, not the re-exporting page,
# while keeping the name the caller reaches them under.
assert symbols["Thing.run"].docs_url == ("https://example.test/reference/demopkg/thing/#demopkg.thing.Thing.run")
59 changes: 59 additions & 0 deletions tests/tools/knowledge_indexing/test_docs_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
from tools.knowledge_indexing.docs_index import (
_page_anchors,
_read_structure,
_resolve_target,
_split_frontmatter,
build_docs_nodes,
read_published_anchors,
)
from tools.knowledge_indexing.toc import TocEntry
Expand Down Expand Up @@ -99,6 +101,20 @@ def test_page_title_h1_does_not_consume_a_later_heading_s_occurrence():
assert section.anchor == "overview"


def test_heading_without_section_id_still_advances_occurrence():
# A titled heading with no usable section id must still count, or the next
# repeat of that title would resolve to the wrong published anchor.
html = """
<section id=""><h2>Overview</h2></section>
<section id="id1"><h2>Overview</h2></section>
"""
markdown = "# Title\n\n## Overview\n\nA.\n\n## Overview\n\nB.\n"
first, second = _sections(markdown, read_published_anchors(html))

assert first.anchor == ""
assert second.anchor == "id1"


def test_only_the_first_h1_is_the_page_title():
# Some pages use h1 throughout, so only the first may be taken as the title.
sections = _sections("# Title\n\nLead.\n\n# Second\n\nA.\n\n# Third\n\nB.\n")
Expand Down Expand Up @@ -179,3 +195,46 @@ def test_malformed_yaml_keeps_the_page_whole():
def test_page_without_frontmatter_is_unchanged():
text = "# Title\n\nProse.\n"
assert _split_frontmatter(text) == ({}, text)


@pytest.mark.parametrize(
("target", "directory", "labels", "expected"),
[
("file:install", "docs", {"file:install": "installation", "install": "installation"}, "installation"),
("install", "docs", {"file:install": "installation", "install": "installation"}, "installation"),
("../sibling.md", "docs/guide", {}, "sibling"),
("https://example.test/x", "docs", {}, ""),
("#only-anchor", "docs", {}, ""),
("../../escape.md", "docs", {}, ""),
("image.png", "docs", {}, ""),
("unknown-label", "docs", {}, ""),
],
)
def test_resolve_target(target, directory, labels, expected):
assert _resolve_target(target, directory, labels) == expected


def test_build_docs_nodes_skips_hidden_and_resolves_related(tmp_path: Path):
(tmp_path / "_toc.yml").write_text(
"format: jb-book\nroot: docs/a\nchapters:\n - file: docs/b\n - file: docs/hidden\n - file: docs/orphan\n",
encoding="utf-8",
)
docs = tmp_path / "docs"
docs.mkdir()
(docs / "a.md").write_text(
"# A\n\nSee {ref}`file:b-page` and [B](b.md).\n\n(file:a-page)=\n",
encoding="utf-8",
)
(docs / "b.md").write_text("# B\n\nProse.\n\n(file:b-page)=\n", encoding="utf-8")
(docs / "hidden.md").write_text(
"---\ndeeplabcut:\n ignore: true\n---\n# Hidden\n",
encoding="utf-8",
)
(docs / "orphan.md").write_text(
"---\ndeeplabcut:\n visibility: orphaned\n---\n# Orphan\n",
encoding="utf-8",
)

nodes = {node.id: node for node in build_docs_nodes(tmp_path)}
assert set(nodes) == {"docs:a", "docs:b"}
assert nodes["docs:a"].related_pages == ("docs:b",)
63 changes: 63 additions & 0 deletions tests/tools/knowledge_indexing/test_llms_txt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from __future__ import annotations

import re
from pathlib import Path

from tools.knowledge_indexing.__main__ import API_BASE_URL, DOCS_VERSION_LABEL, LLMS_TXT, PACKAGE, main
from tools.knowledge_indexing.llms_txt import build_llms_txt
from tools.knowledge_indexing.schemas import LATEST_RELEASE_ALIAS


def test_llms_txt_lists_stable_and_rolling_api_links():
# The urls are landing pages, carrying the package segment: api-autonav
# publishes no index at `reference/`, so a link to it 404s.
text = build_llms_txt(
docs_base_url="https://example.test/",
stable_api_reference_url="https://example.test/dev/latest-release/reference/deeplabcut/",
rolling_api_reference_url="https://example.test/dev/main/reference/deeplabcut/",
knowledge_base_url="https://example.test/knowledge/",
version_label="main",
)

assert "- [API reference (stable release)](https://example.test/dev/latest-release/reference/deeplabcut/)" in text
assert "- [API reference (main / unreleased)](https://example.test/dev/main/reference/deeplabcut/)" in text


def test_llms_txt_keeps_machine_readable_links_version_scoped():
text = build_llms_txt(
docs_base_url="https://example.test/",
stable_api_reference_url="https://example.test/dev/latest-release/reference/deeplabcut/",
rolling_api_reference_url="https://example.test/dev/main/reference/deeplabcut/",
knowledge_base_url="https://example.test/knowledge/",
version_label="3.0",
)

assert "https://example.test/knowledge/3.0/docs.jsonl" in text
assert "https://example.test/knowledge/3.0/api.jsonl" in text


def test_cli_points_each_api_link_at_the_right_deploy(tmp_path: Path):
# The unit tests above are handed both urls, so they cannot catch the two
# being swapped or built from the wrong label in `__main__.py`.
(tmp_path / "_toc.yml").write_text("format: jb-book\nroot: README\n", encoding="utf-8")
(tmp_path / "README.md").write_text("# DeepLabCut\n\nLead.\n", encoding="utf-8")
package = tmp_path / PACKAGE
package.mkdir()
(package / "__init__.py").write_text('"""Package."""\n', encoding="utf-8")

output = tmp_path / "out"
assert main(["--output", str(output), "--repo", str(tmp_path), "--revision", "abc123"]) == 0

link = re.compile(r"^- \[(?P<label>[^\]]+)\]\((?P<url>[^)]+)\)")
lines = {
m["label"]: m["url"]
for m in (link.match(line) for line in (output / LLMS_TXT).read_text(encoding="utf-8").splitlines())
if m and m["label"].startswith("API reference")
}
reference = f"reference/{PACKAGE}/"
assert lines["API reference (stable release)"] == (
f"{API_BASE_URL.format(version=LATEST_RELEASE_ALIAS)}{reference}"
)
assert lines["API reference (main / unreleased)"] == (
f"{API_BASE_URL.format(version=DOCS_VERSION_LABEL)}{reference}"
)
17 changes: 17 additions & 0 deletions tests/tools/knowledge_indexing/test_mkdocs_sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from __future__ import annotations

from pathlib import Path

import yaml

from tools.knowledge_indexing.api_index import API_ROOT_URI, EXCLUDED_MODULES


def test_constants_match_the_dev_docs_config():
# These mirror plugins.api-autonav in dev-docs/mkdocs.yml. Drift there
# silently repoints every generated api url at a page that does not exist.
config = yaml.safe_load((Path(__file__).resolve().parents[3] / "dev-docs/mkdocs.yml").read_text(encoding="utf-8"))
autonav = next(p["api-autonav"] for p in config["plugins"] if isinstance(p, dict) and "api-autonav" in p)

assert autonav["api_root_uri"] == API_ROOT_URI
assert tuple(autonav["exclude"]) == EXCLUDED_MODULES
26 changes: 26 additions & 0 deletions tests/tools/knowledge_indexing/test_write.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from tools.knowledge_indexing.write import (
_check_unique_ids,
_read_json,
_write_jsonl,
delete_version,
write_top_manifest,
write_version,
Expand Down Expand Up @@ -85,6 +86,14 @@ def test_check_unique_ids_raises_on_duplicate():
_check_unique_ids("api.jsonl", ["a", "b", "a"])


def test_empty_jsonl_is_an_empty_file(tmp_path: Path):
path = tmp_path / "empty.jsonl"
_write_jsonl(path, ())
# A trailing blank line would break `json.loads(line)` without filtering.
assert path.read_text(encoding="utf-8") == ""
assert [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines()] == []


def test_read_json_missing_returns_none(tmp_path: Path):
assert _read_json(tmp_path / "missing.json") is None

Expand Down Expand Up @@ -150,6 +159,13 @@ def test_write_version_skip_keeps_existing_half(tmp_path: Path):
assert second["docs"]["revision"] == "second"


def test_skipping_api_without_existing_provenance_leaves_no_directory(tmp_path: Path):
knowledge_dir = tmp_path / KNOWLEDGE_DIR
with pytest.raises(ValueError, match="No api provenance"):
write_version(knowledge_dir, "3.0", None, _sample_docs(), revision="r1")
assert not (knowledge_dir / "3.0").exists()


def test_write_top_manifest_and_delete_version(tmp_path: Path):
knowledge_dir = tmp_path / KNOWLEDGE_DIR
write_version(knowledge_dir, "main", _sample_api(), _sample_docs(), revision="r1")
Expand Down Expand Up @@ -303,3 +319,13 @@ def test_delete_main_via_cli_refused(tmp_path: Path, capsys):
assert code == 1
assert "cannot be deleted" in capsys.readouterr().err
assert (knowledge_dir / "main").is_dir()


def test_skip_api_on_non_main_is_rejected(tmp_path: Path, capsys):
# Docs are only indexed for main, so --skip-api on another label leaves
# nothing to do and must not report success.
from tools.knowledge_indexing.__main__ import main

code = main(["--version-label", "3.0", "--skip-api", "--output", str(tmp_path)])
assert code == 1
assert "nothing to do" in capsys.readouterr().err
14 changes: 11 additions & 3 deletions tools/knowledge_indexing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ methods, attributes and properties are not recorded.

Two constants in `api_index.py` mirror `dev-docs/mkdocs.yml` and must be kept in
sync with it: `EXCLUDED_MODULES` and `API_ROOT_URI`.
The `knowledge-index` dependency-group bounds in `pyproject.toml` are the same
kind of coupling: griffe, docutils and markdown-it-py must stay on majors that
match the docs toolchain, or published signatures, symbol kinds and section ids
can drift without any source change.

### Docs pages

Expand Down Expand Up @@ -166,9 +170,13 @@ explicit `ignore: true`.
Written only for `--version-label main`, alongside `docs.jsonl`. Follows the
[llmstxt.org](https://llmstxt.org) convention: an H1 title, a one-line
description, then `##` sections of links.
Links to the docs site, the API reference, and prominently to
`knowledge/manifest.json`, `docs.jsonl` and `api.jsonl` for agents that want
the structured index.
Links to the docs site, both the stable-release and rolling-main API
reference, and prominently to `knowledge/manifest.json`, `docs.jsonl` and
`api.jsonl` for agents that want the structured index.

The stable link points at mike's `latest-release` alias rather than at
`--version-label`: only the `main` build writes `llms.txt`, so the label would
aim it at the rolling unreleased API (see "Versioning").

## Deployment

Expand Down
17 changes: 12 additions & 5 deletions tools/knowledge_indexing/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,14 @@ def main(argv: list[str] | None = None) -> int:
args = _parse_args(argv)
repo: Path = args.repo

if args.skip_api and args.skip_docs:
print("Error: --skip-api and --skip-docs together leave nothing to do.", file=sys.stderr)
include_api = not args.skip_api
include_docs = args.version_label == DOCS_VERSION_LABEL and not args.skip_docs
if not args.delete and not include_api and not include_docs:
docs_reason = "--skip-docs" if args.skip_docs else f"docs are only indexed for '{DOCS_VERSION_LABEL}'"
print(
f"Error: nothing to do for version {args.version_label!r}: --skip-api, and {docs_reason}.",
file=sys.stderr,
)
return 1

if args.delete and (args.skip_api or args.skip_docs):
Expand Down Expand Up @@ -218,8 +224,8 @@ def main(argv: list[str] | None = None) -> int:
return 1

api_base_url = API_BASE_URL.format(version=args.version_label)
include_api = not args.skip_api
include_docs = args.version_label == DOCS_VERSION_LABEL and not args.skip_docs
stable_api_reference_url = f"{API_BASE_URL.format(version=LATEST_RELEASE_ALIAS)}reference/{PACKAGE}/"
rolling_api_reference_url = f"{API_BASE_URL.format(version=DOCS_VERSION_LABEL)}reference/{PACKAGE}/"

apis = None
if include_api:
Expand Down Expand Up @@ -282,7 +288,8 @@ def main(argv: list[str] | None = None) -> int:
if include_docs:
llms_txt = build_llms_txt(
docs_base_url=DOCS_BASE_URL,
api_base_url=api_base_url,
stable_api_reference_url=stable_api_reference_url,
rolling_api_reference_url=rolling_api_reference_url,
knowledge_base_url=f"{DOCS_BASE_URL}{KNOWLEDGE_DIR}/",
version_label=args.version_label,
)
Expand Down
14 changes: 10 additions & 4 deletions tools/knowledge_indexing/api_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,28 +150,34 @@ def _symbols(module: griffe.Module, package: str, base_url: str = "") -> tuple[S
if not target.docstring:
continue

canonical = target.canonical_path
symbols.append(
Symbol(
name=name,
kind=kind,
summary=_summary(target),
signature=_signature(target),
source=_source(target),
docs_url=_docs_url(module.path, f"{module.path}.{name}", base_url),
docs_url=_docs_url(canonical.rsplit(".", 1)[0], canonical, base_url),
)
)

if kind == "class":
symbols.extend(_methods(target, module.path, name, base_url))
symbols.extend(_methods(target, name, base_url))

return tuple(sorted(symbols, key=lambda symbol: symbol.name))


def _methods(cls: griffe.Class, module_path: str, class_name: str, base_url: str = "") -> Iterator[Symbol]:
def _methods(cls: griffe.Class, class_name: str, base_url: str = "") -> Iterator[Symbol]:
"""Documented public methods a class declares itself.

`cls.members` excludes inherited members, which the base class documents.

`class_name` names each method as the caller reaches it, while the url comes
from the class's canonical path. The two differ for a re-exported class,
which mkdocstrings documents on its defining module's page.
"""
owner = cls.canonical_path.rsplit(".", 1)[0]
for name, member in sorted(cls.members.items()):
if name.startswith("_") or member.is_alias or not member.is_function:
continue
Expand All @@ -185,7 +191,7 @@ def _methods(cls: griffe.Class, module_path: str, class_name: str, base_url: str
summary=_summary(member),
signature=_signature(member),
source=_source(member),
docs_url=_docs_url(module_path, f"{module_path}.{qualified}", base_url),
docs_url=_docs_url(owner, f"{cls.canonical_path}.{name}", base_url),
)


Expand Down
7 changes: 5 additions & 2 deletions tools/knowledge_indexing/docs_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,12 @@ def handle_endtag(self, tag: str) -> None:
elif tag in _HEADING_TAGS and self._heading is not None:
title = " ".join("".join(self._heading).split())
anchor = self._sections[-1] if self._sections else ""
if title and anchor:
# Count every titled heading, even without a usable section id, so
# occurrence numbers stay aligned with `_read_structure`.
if title:
self._seen[title] += 1
self.anchors.setdefault((title, self._seen[title]), anchor)
if anchor:
self.anchors.setdefault((title, self._seen[title]), anchor)
self._heading = None
elif tag == "a" and self._skip:
self._skip -= 1
Expand Down
Loading
Loading