descriptron-core 2.0.2__tar.gz → 2.0.4__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 (117) hide show
  1. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/PKG-INFO +1 -1
  2. descriptron_core-2.0.4/pyproject.toml +82 -0
  3. descriptron_core-2.0.4/pyproject.toml.bak_pre_204_20260924_165335 +82 -0
  4. descriptron_core-2.0.4/pyproject.toml.bak_pre_keys_20260924_165335 +81 -0
  5. descriptron_core-2.0.4/src/descriptron_core/cli.py +99 -0
  6. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_llm_backend.py +6 -0
  7. descriptron_core-2.0.4/src/descriptron_core/tools/descriptron_credentials.py +178 -0
  8. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/run_full_pipeline_v2.py +13 -0
  9. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/.gitignore +0 -0
  10. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/LICENSE +0 -0
  11. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/NOTICE +0 -0
  12. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/README.md +0 -0
  13. /descriptron_core-2.0.2/pyproject.toml → /descriptron_core-2.0.4/pyproject.toml.bak_pre_203_20260924_145320 +0 -0
  14. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/__init__.py +0 -0
  15. /descriptron_core-2.0.2/src/descriptron_core/cli.py → /descriptron_core-2.0.4/src/descriptron_core/cli.py.bak_pre_keys_20260924_165335 +0 -0
  16. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/biorag_system_prompts_v1.txt +0 -0
  17. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/biorag_system_prompts_v2.txt +0 -0
  18. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/diaphorina_user_prompts.txt +0 -0
  19. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/ontologies/aism.obo +0 -0
  20. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/ontologies/pato.obo +0 -0
  21. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/SDD.xsd +0 -0
  22. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/SDD_EnumLib.xsd +0 -0
  23. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/SDD_TypeLib.xsd +0 -0
  24. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/UBIF_BasePattern.xsd +0 -0
  25. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/UBIF_CoreExtensions.xsd +0 -0
  26. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/UBIF_CoreOntology.xsd +0 -0
  27. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/UBIF_EnumLib.xsd +0 -0
  28. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/UBIF_LinneanCore.xsd +0 -0
  29. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/UBIF_TypeLib.xsd +0 -0
  30. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/w3c-schemata/xml.xsd +0 -0
  31. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/schemas/taxpub/sample_treatment_deposit_example.xml +0 -0
  32. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/schemas/taxpub/sample_zookeys_26056_tp.xml +0 -0
  33. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/schemas/taxpub/tax-treatment-NS0-v1_flat.dtd +0 -0
  34. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/taxon_profiles/TEMPLATE_taxon_profile.yaml +0 -0
  35. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/data/biorag_prompts/taxon_profiles/diaphorina_taxon_profile.yaml +0 -0
  36. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/FHS_and_CLAHE_V21.py +0 -0
  37. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_add_discrete_characters_v1.py +0 -0
  38. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_annotation_reorient_v1.py +0 -0
  39. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_annotation_screen_v1.py +0 -0
  40. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_autapomorphy_v1.py +0 -0
  41. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_calibrate_v1.py +0 -0
  42. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_calibration_figures_v1.py +0 -0
  43. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_character_gate_v1.py +0 -0
  44. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_character_reliability_v1.py +0 -0
  45. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_character_robustness_v1.py +0 -0
  46. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_collaborator_forms_v1.py +0 -0
  47. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_computed_sets_v1.py +0 -0
  48. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_confabulation_checker_v2.py +0 -0
  49. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_congruence_compare_v1.py +0 -0
  50. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_description_refiner_v1.py +0 -0
  51. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_descriptive_scoring_v1.py +0 -0
  52. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_feature_policy.py +0 -0
  53. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_frame_orientation_calibrate_v1.py +0 -0
  54. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_frame_orientation_v1.py +0 -0
  55. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_gate_compare_v1.py +0 -0
  56. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_graph_identify_v1.py +0 -0
  57. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_graph_support_figure_v1.py +0 -0
  58. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_homology_frame_v1.py +0 -0
  59. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_instrument_compare_v3.py +0 -0
  60. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_key_builder_v1.py +0 -0
  61. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_key_feature_filter_v2.py +0 -0
  62. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_key_fuzzy_figures_v1.py +0 -0
  63. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_key_fuzzy_v1.py +0 -0
  64. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_key_holdout_v1.py +0 -0
  65. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_key_jackknife_support_v1.py +0 -0
  66. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_label_conflict_check_v1.py +0 -0
  67. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_make_taxon_profile_v1.py +0 -0
  68. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_methods_summary_figure_v1.py +0 -0
  69. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_novelty_score_v1.py +0 -0
  70. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_ontology_annotator_v2.py +0 -0
  71. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_outgroup_batch_check_v1.py +0 -0
  72. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_outline_shape_v1.py +0 -0
  73. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/biorag_system_prompts_v1.txt +0 -0
  74. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/biorag_system_prompts_v2.txt +0 -0
  75. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/diaphorina_user_prompts.txt +0 -0
  76. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/ontologies/aism.obo +0 -0
  77. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/ontologies/pato.obo +0 -0
  78. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/SDD.xsd +0 -0
  79. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/SDD_EnumLib.xsd +0 -0
  80. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/SDD_TypeLib.xsd +0 -0
  81. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/UBIF_BasePattern.xsd +0 -0
  82. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/UBIF_CoreExtensions.xsd +0 -0
  83. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/UBIF_CoreOntology.xsd +0 -0
  84. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/UBIF_EnumLib.xsd +0 -0
  85. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/UBIF_LinneanCore.xsd +0 -0
  86. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/UBIF_TypeLib.xsd +0 -0
  87. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/w3c-schemata/xml.xsd +0 -0
  88. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/schemas/taxpub/sample_treatment_deposit_example.xml +0 -0
  89. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/schemas/taxpub/sample_zookeys_26056_tp.xml +0 -0
  90. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/schemas/taxpub/tax-treatment-NS0-v1_flat.dtd +0 -0
  91. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/taxon_profiles/TEMPLATE_taxon_profile.yaml +0 -0
  92. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_prompts/taxon_profiles/diaphorina_taxon_profile.yaml +0 -0
  93. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_rebuild_downstream_v1.sh +0 -0
  94. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_reliability_figures_v1.py +0 -0
  95. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_rename_species_v1.py +0 -0
  96. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_rerun_descriptive_tables_v1.sh +0 -0
  97. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_retest_compare_v1.py +0 -0
  98. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_specimen_id.py +0 -0
  99. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_subjective_checks_v1.py +0 -0
  100. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_type_material_v1.py +0 -0
  101. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_vlm_characters_v1.py +0 -0
  102. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_vlm_heatmap_figures_v1.py +0 -0
  103. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_vlm_reliability_figure_v1.py +0 -0
  104. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biorag_vlm_roi_figures_v1.py +0 -0
  105. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biosyslit_rag_retrieval.py +0 -0
  106. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/biosyslit_rag_retrieval_v2.py +0 -0
  107. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/build_monograph_exports_v1.py +0 -0
  108. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/build_species_treatment_docx_v2.py +0 -0
  109. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/color_extraction_with_names-v8.py +0 -0
  110. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/color_phenomics_homology_v2_1.py +0 -0
  111. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/compile_specimen_data.py +0 -0
  112. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/descriptron_rosetta.py +0 -0
  113. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/landmark_gpa_V1.py +0 -0
  114. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/make_reproducibility_bundle.py +0 -0
  115. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/semi_landmark_and_kpts_procrustesV34_GPA.py +0 -0
  116. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/texture_phenomics_from_coco.py +0 -0
  117. {descriptron_core-2.0.2 → descriptron_core-2.0.4}/src/descriptron_core/tools/texture_phenomics_homology.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: descriptron-core
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Summary: Evidence-tiered species descriptions, keys and audits from phenomic matrices
5
5
  Project-URL: Homepage, https://github.com/alexrvandam/Descriptron
6
6
  Project-URL: Repository, https://github.com/alexrvandam/Descriptron
@@ -0,0 +1,82 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "descriptron-core"
7
+ version = "2.0.4"
8
+ description = "Evidence-tiered species descriptions, keys and audits from phenomic matrices"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "Apache-2.0" }
12
+ authors = [{ name = "Alex R. Van Dam" }]
13
+ keywords = ["taxonomy", "morphometrics", "phenomics", "species description", "COCO"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Topic :: Scientific/Engineering :: Bio-Informatics",
17
+ "Topic :: Scientific/Engineering :: Image Processing",
18
+ "Intended Audience :: Science/Research",
19
+ "License :: OSI Approved :: Apache Software License",
20
+ "Operating System :: OS Independent",
21
+ ]
22
+
23
+ # Derived from the imports of the 62 programs in this package, not from memory:
24
+ # `python -m descriptron_core.cli --list` names them. No torch, no CUDA, no
25
+ # compiler — this installs in about a minute on Linux, macOS and Windows, and
26
+ # with the deposited COCO files it reproduces every analysis in the methods paper.
27
+ dependencies = [
28
+ "numpy>=1.23",
29
+ "pandas>=1.5",
30
+ "scipy>=1.9",
31
+ "matplotlib>=3.6",
32
+ "scikit-learn>=1.2",
33
+ "scikit-image>=0.20",
34
+ "opencv-python-headless>=4.7", # headless: the analysis half never opens a window
35
+ "pillow>=9",
36
+ "pycocotools>=2.0.6",
37
+ "seaborn>=0.12",
38
+ "statsmodels>=0.14",
39
+ "scikit-posthocs>=0.7",
40
+ "umap-learn>=0.5",
41
+ "networkx>=3",
42
+ "pyefd>=1.6",
43
+ "biopython>=1.81",
44
+ "PyYAML>=6",
45
+ "lxml>=4.9",
46
+ "openpyxl>=3.1",
47
+ "python-docx>=1.0", # treatment and monograph .docx export
48
+ ]
49
+
50
+ [project.optional-dependencies]
51
+ # Only needed to *write* treatments. The audit, key, matrix, novelty scoring and
52
+ # delimitation are all deterministic and need no model at all.
53
+ llm = ["anthropic>=0.40"]
54
+ # literature retrieval (the RAG in BioRAG): BioSysLit treatments from Zenodo and your
55
+ # own PDFs. Keyword retrieval needs only the first two; sentence-embedding retrieval
56
+ # (`index --embeddings`) also needs sentence-transformers + faiss-cpu, which pull in torch.
57
+ rag = ["requests>=2.28", "pdfplumber>=0.10"]
58
+ rag-embeddings = ["requests>=2.28", "pdfplumber>=0.10", "sentence-transformers>=2.2", "faiss-cpu>=1.7"]
59
+ dev = ["pytest>=7", "build>=1", "twine>=5"]
60
+
61
+ [project.scripts]
62
+ descriptron = "descriptron_core.cli:main"
63
+ biorag-pipeline = "descriptron_core.cli:run_pipeline"
64
+ biorag-key = "descriptron_core.cli:run_key_builder"
65
+ biorag-audit = "descriptron_core.cli:run_confabulation_checker"
66
+ biorag-novelty = "descriptron_core.cli:run_novelty_score"
67
+ biorag-calibrate = "descriptron_core.cli:run_calibrate"
68
+ descriptron-keys = "descriptron_core.cli:run_keys"
69
+
70
+ [project.urls]
71
+ Homepage = "https://github.com/alexrvandam/Descriptron"
72
+ Repository = "https://github.com/alexrvandam/Descriptron"
73
+ Issues = "https://github.com/alexrvandam/Descriptron/issues"
74
+
75
+ [tool.hatch.build.targets.wheel]
76
+ packages = ["src/descriptron_core"]
77
+
78
+ # tools/ and data/ sit inside the package directory, so `packages` already ships
79
+ # them; a force-include here would add every file a second time and the build
80
+ # fails with "a second file is being added to the wheel archive at the same path".
81
+ [tool.hatch.build]
82
+ artifacts = ["src/descriptron_core/tools/**", "src/descriptron_core/data/**"]
@@ -0,0 +1,82 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "descriptron-core"
7
+ version = "2.0.3"
8
+ description = "Evidence-tiered species descriptions, keys and audits from phenomic matrices"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "Apache-2.0" }
12
+ authors = [{ name = "Alex R. Van Dam" }]
13
+ keywords = ["taxonomy", "morphometrics", "phenomics", "species description", "COCO"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Topic :: Scientific/Engineering :: Bio-Informatics",
17
+ "Topic :: Scientific/Engineering :: Image Processing",
18
+ "Intended Audience :: Science/Research",
19
+ "License :: OSI Approved :: Apache Software License",
20
+ "Operating System :: OS Independent",
21
+ ]
22
+
23
+ # Derived from the imports of the 62 programs in this package, not from memory:
24
+ # `python -m descriptron_core.cli --list` names them. No torch, no CUDA, no
25
+ # compiler — this installs in about a minute on Linux, macOS and Windows, and
26
+ # with the deposited COCO files it reproduces every analysis in the methods paper.
27
+ dependencies = [
28
+ "numpy>=1.23",
29
+ "pandas>=1.5",
30
+ "scipy>=1.9",
31
+ "matplotlib>=3.6",
32
+ "scikit-learn>=1.2",
33
+ "scikit-image>=0.20",
34
+ "opencv-python-headless>=4.7", # headless: the analysis half never opens a window
35
+ "pillow>=9",
36
+ "pycocotools>=2.0.6",
37
+ "seaborn>=0.12",
38
+ "statsmodels>=0.14",
39
+ "scikit-posthocs>=0.7",
40
+ "umap-learn>=0.5",
41
+ "networkx>=3",
42
+ "pyefd>=1.6",
43
+ "biopython>=1.81",
44
+ "PyYAML>=6",
45
+ "lxml>=4.9",
46
+ "openpyxl>=3.1",
47
+ "python-docx>=1.0", # treatment and monograph .docx export
48
+ ]
49
+
50
+ [project.optional-dependencies]
51
+ # Only needed to *write* treatments. The audit, key, matrix, novelty scoring and
52
+ # delimitation are all deterministic and need no model at all.
53
+ llm = ["anthropic>=0.40"]
54
+ # literature retrieval (the RAG in BioRAG): BioSysLit treatments from Zenodo and your
55
+ # own PDFs. Keyword retrieval needs only the first two; sentence-embedding retrieval
56
+ # (`index --embeddings`) also needs sentence-transformers + faiss-cpu, which pull in torch.
57
+ rag = ["requests>=2.28", "pdfplumber>=0.10"]
58
+ rag-embeddings = ["requests>=2.28", "pdfplumber>=0.10", "sentence-transformers>=2.2", "faiss-cpu>=1.7"]
59
+ dev = ["pytest>=7", "build>=1", "twine>=5"]
60
+
61
+ [project.scripts]
62
+ descriptron = "descriptron_core.cli:main"
63
+ biorag-pipeline = "descriptron_core.cli:run_pipeline"
64
+ biorag-key = "descriptron_core.cli:run_key_builder"
65
+ biorag-audit = "descriptron_core.cli:run_confabulation_checker"
66
+ biorag-novelty = "descriptron_core.cli:run_novelty_score"
67
+ biorag-calibrate = "descriptron_core.cli:run_calibrate"
68
+ descriptron-keys = "descriptron_core.cli:run_keys"
69
+
70
+ [project.urls]
71
+ Homepage = "https://github.com/alexrvandam/Descriptron"
72
+ Repository = "https://github.com/alexrvandam/Descriptron"
73
+ Issues = "https://github.com/alexrvandam/Descriptron/issues"
74
+
75
+ [tool.hatch.build.targets.wheel]
76
+ packages = ["src/descriptron_core"]
77
+
78
+ # tools/ and data/ sit inside the package directory, so `packages` already ships
79
+ # them; a force-include here would add every file a second time and the build
80
+ # fails with "a second file is being added to the wheel archive at the same path".
81
+ [tool.hatch.build]
82
+ artifacts = ["src/descriptron_core/tools/**", "src/descriptron_core/data/**"]
@@ -0,0 +1,81 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "descriptron-core"
7
+ version = "2.0.3"
8
+ description = "Evidence-tiered species descriptions, keys and audits from phenomic matrices"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "Apache-2.0" }
12
+ authors = [{ name = "Alex R. Van Dam" }]
13
+ keywords = ["taxonomy", "morphometrics", "phenomics", "species description", "COCO"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Topic :: Scientific/Engineering :: Bio-Informatics",
17
+ "Topic :: Scientific/Engineering :: Image Processing",
18
+ "Intended Audience :: Science/Research",
19
+ "License :: OSI Approved :: Apache Software License",
20
+ "Operating System :: OS Independent",
21
+ ]
22
+
23
+ # Derived from the imports of the 62 programs in this package, not from memory:
24
+ # `python -m descriptron_core.cli --list` names them. No torch, no CUDA, no
25
+ # compiler — this installs in about a minute on Linux, macOS and Windows, and
26
+ # with the deposited COCO files it reproduces every analysis in the methods paper.
27
+ dependencies = [
28
+ "numpy>=1.23",
29
+ "pandas>=1.5",
30
+ "scipy>=1.9",
31
+ "matplotlib>=3.6",
32
+ "scikit-learn>=1.2",
33
+ "scikit-image>=0.20",
34
+ "opencv-python-headless>=4.7", # headless: the analysis half never opens a window
35
+ "pillow>=9",
36
+ "pycocotools>=2.0.6",
37
+ "seaborn>=0.12",
38
+ "statsmodels>=0.14",
39
+ "scikit-posthocs>=0.7",
40
+ "umap-learn>=0.5",
41
+ "networkx>=3",
42
+ "pyefd>=1.6",
43
+ "biopython>=1.81",
44
+ "PyYAML>=6",
45
+ "lxml>=4.9",
46
+ "openpyxl>=3.1",
47
+ "python-docx>=1.0", # treatment and monograph .docx export
48
+ ]
49
+
50
+ [project.optional-dependencies]
51
+ # Only needed to *write* treatments. The audit, key, matrix, novelty scoring and
52
+ # delimitation are all deterministic and need no model at all.
53
+ llm = ["anthropic>=0.40"]
54
+ # literature retrieval (the RAG in BioRAG): BioSysLit treatments from Zenodo and your
55
+ # own PDFs. Keyword retrieval needs only the first two; sentence-embedding retrieval
56
+ # (`index --embeddings`) also needs sentence-transformers + faiss-cpu, which pull in torch.
57
+ rag = ["requests>=2.28", "pdfplumber>=0.10"]
58
+ rag-embeddings = ["requests>=2.28", "pdfplumber>=0.10", "sentence-transformers>=2.2", "faiss-cpu>=1.7"]
59
+ dev = ["pytest>=7", "build>=1", "twine>=5"]
60
+
61
+ [project.scripts]
62
+ descriptron = "descriptron_core.cli:main"
63
+ biorag-pipeline = "descriptron_core.cli:run_pipeline"
64
+ biorag-key = "descriptron_core.cli:run_key_builder"
65
+ biorag-audit = "descriptron_core.cli:run_confabulation_checker"
66
+ biorag-novelty = "descriptron_core.cli:run_novelty_score"
67
+ biorag-calibrate = "descriptron_core.cli:run_calibrate"
68
+
69
+ [project.urls]
70
+ Homepage = "https://github.com/alexrvandam/Descriptron"
71
+ Repository = "https://github.com/alexrvandam/Descriptron"
72
+ Issues = "https://github.com/alexrvandam/Descriptron/issues"
73
+
74
+ [tool.hatch.build.targets.wheel]
75
+ packages = ["src/descriptron_core"]
76
+
77
+ # tools/ and data/ sit inside the package directory, so `packages` already ships
78
+ # them; a force-include here would add every file a second time and the build
79
+ # fails with "a second file is being added to the wheel archive at the same path".
80
+ [tool.hatch.build]
81
+ artifacts = ["src/descriptron_core/tools/**", "src/descriptron_core/data/**"]
@@ -0,0 +1,99 @@
1
+ """
2
+ descriptron_core.cli — one entry point for the analysis programs
3
+ ================================================================
4
+
5
+ The 62 programs in this package are run with `runpy` exactly as they are run
6
+ from a shell, rather than being imported as modules. That is deliberate: they
7
+ were written as scripts, they use `if __name__ == "__main__"`, and several
8
+ import their siblings by plain name. Executing them as `__main__` with their own
9
+ directory on `sys.path` preserves that behaviour exactly, so packaging cannot
10
+ quietly change what a program does — which matters when the output is a
11
+ published species description.
12
+
13
+ descriptron --list
14
+ descriptron biorag_key_builder_v1 --help
15
+ biorag-audit --help
16
+ """
17
+ from __future__ import annotations
18
+
19
+ import os
20
+ import runpy
21
+ import sys
22
+ from importlib.resources import files
23
+ from pathlib import Path
24
+
25
+ __all__ = ["main", "tools_dir", "data_path", "available"]
26
+
27
+
28
+ def tools_dir() -> Path:
29
+ return Path(str(files("descriptron_core") / "tools"))
30
+
31
+
32
+ def data_path(*parts: str) -> Path:
33
+ """Prompts, taxon profiles, ontology releases and schemas that ship with the package."""
34
+ return Path(str(files("descriptron_core") / "data")).joinpath(*parts)
35
+
36
+
37
+ def available() -> list[str]:
38
+ d = tools_dir()
39
+ if not d.exists():
40
+ return []
41
+ return sorted(p.stem for p in d.iterdir()
42
+ if p.suffix in (".py", ".sh") and not p.name.startswith("_"))
43
+
44
+
45
+ def _resolve(name: str) -> Path:
46
+ d = tools_dir()
47
+ for candidate in (d / name, d / f"{name}.py", d / f"{name}.sh"):
48
+ if candidate.exists():
49
+ return candidate
50
+ matches = [t for t in available() if t.startswith(name)]
51
+ if len(matches) == 1:
52
+ return _resolve(matches[0])
53
+ if matches:
54
+ raise SystemExit(f"'{name}' is ambiguous: {', '.join(matches)}")
55
+ raise SystemExit(f"unknown program '{name}'. `descriptron --list` shows them all.")
56
+
57
+
58
+ def run(name: str, argv: list[str] | None = None) -> None:
59
+ path = _resolve(name)
60
+ argv = list(sys.argv[1:] if argv is None else argv)
61
+ # the programs' own directory goes on sys.path so a script that imports a
62
+ # sibling by plain name keeps working, and the packaged prompts and ontology
63
+ # releases are findable without anyone passing a path
64
+ sys.path.insert(0, str(path.parent))
65
+ os.environ.setdefault("DESCRIPTRON_DATA", str(data_path()))
66
+ if path.suffix == ".sh":
67
+ os.execvp("bash", ["bash", str(path), *argv])
68
+ sys.argv = [str(path), *argv]
69
+ runpy.run_path(str(path), run_name="__main__")
70
+
71
+
72
+ def main(argv: list[str] | None = None) -> None:
73
+ args = list(sys.argv[1:] if argv is None else argv)
74
+ if not args or args[0] in ("-h", "--help", "help"):
75
+ print(__doc__.strip())
76
+ print(f"\n{len(available())} programs available; --list shows them.")
77
+ return
78
+ if args[0] in ("--list", "list"):
79
+ for name in available():
80
+ print(name)
81
+ return
82
+ if args[0] in ("--where", "where"):
83
+ print(f"tools: {tools_dir()}\ndata: {data_path()}")
84
+ return
85
+ run(args[0], args[1:])
86
+
87
+
88
+ def _fixed(script: str):
89
+ def entry() -> None:
90
+ run(script, sys.argv[1:])
91
+ return entry
92
+
93
+
94
+ run_pipeline = _fixed("run_full_pipeline_v2")
95
+ run_key_builder = _fixed("biorag_key_builder_v1")
96
+ run_confabulation_checker = _fixed("biorag_confabulation_checker_v2")
97
+ run_novelty_score = _fixed("biorag_novelty_score_v1")
98
+ run_calibrate = _fixed("biorag_calibrate_v1")
99
+ run_keys = _fixed("descriptron_credentials")
@@ -288,6 +288,12 @@ def client_from_args(args, log_path: Optional[str] = None):
288
288
  --api-base-url, or no model at all."""
289
289
  import os
290
290
  key_env = getattr(args, "api_key_env", None) or "ANTHROPIC_API_KEY"
291
+ if (getattr(args, "llm_backend", "") or "").lower() == "api" and not os.environ.get(key_env):
292
+ try: # v2.0.4: credentials file / first-use prompt
293
+ import descriptron_credentials as _creds
294
+ _creds.ensure_key(key_env)
295
+ except ImportError:
296
+ pass
291
297
  return make_llm_client(getattr(args, "llm_backend", "claude-code"),
292
298
  api_key=os.environ.get(key_env),
293
299
  claude_bin=getattr(args, "claude_bin", None),
@@ -0,0 +1,178 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ descriptron_credentials.py — where Descriptron finds your API keys and tokens
4
+ =============================================================================
5
+
6
+ Two keys are ever needed, each by one optional part of Descriptron:
7
+
8
+ ANTHROPIC_API_KEY species descriptions with --llm_backend api
9
+ HF_TOKEN DINOLand, to download Meta's gated DINOv3 weights once
10
+
11
+ Where a key is looked up, in this order:
12
+
13
+ 1. the environment (`export ANTHROPIC_API_KEY=...`) — always wins;
14
+ 2. the credentials file, one `NAME=value` per line:
15
+ $DESCRIPTRON_CREDENTIALS, or ~/.config/descriptron/credentials
16
+ You can write this file yourself, or let Descriptron write it:
17
+ 3. a prompt, the first time the key is actually needed — only in an interactive
18
+ terminal (or the GUI), never in batch jobs, and never when
19
+ DESCRIPTRON_NO_KEY_PROMPT=1. Pressing Enter skips it; the step that needed the
20
+ key then reports what is missing instead of failing obscurely.
21
+
22
+ The file is created readable by you only (0600). Keys are never written to logs,
23
+ project folders or the Docker image.
24
+
25
+ python descriptron_credentials.py --list # which keys are set (values hidden)
26
+ python descriptron_credentials.py --set HF_TOKEN # enter or replace one
27
+ python descriptron_credentials.py --remove HF_TOKEN
28
+ """
29
+ from __future__ import annotations
30
+
31
+ import argparse
32
+ import getpass
33
+ import os
34
+ import sys
35
+ from pathlib import Path
36
+ from typing import Dict, Optional
37
+
38
+ KNOWN = {
39
+ "ANTHROPIC_API_KEY": ("species descriptions (--llm_backend api)",
40
+ "https://console.anthropic.com/settings/keys"),
41
+ "HF_TOKEN": ("DINOLand: downloading Meta's DINOv3 weights (accept the licence first at "
42
+ "https://huggingface.co/facebook/dinov3-vitb16-pretrain-lvd1689m)",
43
+ "https://huggingface.co/settings/tokens"),
44
+ }
45
+
46
+
47
+ def credentials_path() -> Path:
48
+ p = os.environ.get("DESCRIPTRON_CREDENTIALS")
49
+ if p:
50
+ return Path(p).expanduser()
51
+ base = os.environ.get("XDG_CONFIG_HOME") or os.path.join(os.path.expanduser("~"), ".config")
52
+ return Path(base) / "descriptron" / "credentials"
53
+
54
+
55
+ def read_file(path: Optional[Path] = None) -> Dict[str, str]:
56
+ path = path or credentials_path()
57
+ out: Dict[str, str] = {}
58
+ try:
59
+ for line in path.read_text().splitlines():
60
+ line = line.strip()
61
+ if not line or line.startswith("#") or "=" not in line:
62
+ continue
63
+ k, v = line.split("=", 1)
64
+ k, v = k.strip(), v.strip().strip('"').strip("'")
65
+ if k and v:
66
+ out[k] = v
67
+ except OSError:
68
+ pass
69
+ return out
70
+
71
+
72
+ def write_file(values: Dict[str, str], path: Optional[Path] = None) -> Path:
73
+ path = path or credentials_path()
74
+ path.parent.mkdir(parents=True, exist_ok=True)
75
+ try:
76
+ os.chmod(path.parent, 0o700)
77
+ except OSError:
78
+ pass
79
+ body = "# Descriptron keys — one NAME=value per line; keep this file private\n" + \
80
+ "".join(f"{k}={v}\n" for k, v in sorted(values.items()))
81
+ fd = os.open(str(path), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
82
+ with os.fdopen(fd, "w") as f:
83
+ f.write(body)
84
+ os.chmod(path, 0o600)
85
+ return path
86
+
87
+
88
+ def load_into_environment() -> Dict[str, str]:
89
+ """Copy keys from the file into os.environ where not already set (the environment wins).
90
+ Returns the names that were loaded. Child processes then inherit them."""
91
+ loaded = {}
92
+ for k, v in read_file().items():
93
+ if not os.environ.get(k):
94
+ os.environ[k] = v
95
+ loaded[k] = v
96
+ return loaded
97
+
98
+
99
+ def prompting_allowed() -> bool:
100
+ if os.environ.get("DESCRIPTRON_NO_KEY_PROMPT", "").strip() not in ("", "0", "false", "no"):
101
+ return False
102
+ try:
103
+ return sys.stdin.isatty() and sys.stderr.isatty()
104
+ except (AttributeError, ValueError):
105
+ return False
106
+
107
+
108
+ def save_key(name: str, value: str) -> Path:
109
+ vals = read_file()
110
+ vals[name] = value
111
+ os.environ[name] = value
112
+ return write_file(vals)
113
+
114
+
115
+ def ensure_key(name: str, purpose: Optional[str] = None, where: Optional[str] = None,
116
+ ask=None) -> Optional[str]:
117
+ """Return the key: environment, then the credentials file, then (first use only) a prompt.
118
+
119
+ `ask` replaces the terminal prompt (the GUI passes a dialog); it receives
120
+ (name, purpose, where) and returns the value or None. Nothing is asked when prompting
121
+ is switched off or no one is there to answer; then None is returned."""
122
+ if os.environ.get(name):
123
+ return os.environ[name]
124
+ stored = read_file().get(name)
125
+ if stored:
126
+ os.environ[name] = stored
127
+ return stored
128
+ purpose = purpose or KNOWN.get(name, ("", ""))[0]
129
+ where = where or KNOWN.get(name, ("", ""))[1]
130
+ if os.environ.get("DESCRIPTRON_NO_KEY_PROMPT", "").strip() not in ("", "0", "false", "no"):
131
+ return None
132
+ if ask is not None:
133
+ value = ask(name, purpose, where)
134
+ elif prompting_allowed():
135
+ sys.stderr.write(f"\nDescriptron needs {name} for {purpose}.\n")
136
+ if where:
137
+ sys.stderr.write(f" Get one at: {where}\n")
138
+ sys.stderr.write(f" It will be saved (readable by you only) in {credentials_path()}\n"
139
+ f" so you are asked only once. Press Enter to skip.\n")
140
+ value = getpass.getpass(f" {name}: ")
141
+ else:
142
+ return None
143
+ value = (value or "").strip()
144
+ if not value:
145
+ return None
146
+ path = save_key(name, value)
147
+ sys.stderr.write(f" saved {name} to {path}\n")
148
+ return value
149
+
150
+
151
+ def main(argv=None):
152
+ ap = argparse.ArgumentParser(description="Show, set or remove the keys Descriptron uses.")
153
+ g = ap.add_mutually_exclusive_group()
154
+ g.add_argument("--list", action="store_true", help="which keys are set (values hidden) — the default")
155
+ g.add_argument("--set", metavar="NAME", help="enter or replace a key (asked for without echo)")
156
+ g.add_argument("--remove", metavar="NAME", help="delete a key from the file")
157
+ a = ap.parse_args(argv)
158
+ path = credentials_path()
159
+ if a.set:
160
+ v = getpass.getpass(f"{a.set}: ").strip()
161
+ if not v:
162
+ print("nothing entered; unchanged"); return 1
163
+ print(f"saved {a.set} to {save_key(a.set, v)}"); return 0
164
+ if a.remove:
165
+ vals = read_file()
166
+ if vals.pop(a.remove, None) is None:
167
+ print(f"{a.remove} is not in {path}"); return 1
168
+ write_file(vals); print(f"removed {a.remove} from {path}"); return 0
169
+ stored = read_file()
170
+ print(f"credentials file: {path}{'' if path.exists() else ' (not created yet)'}")
171
+ for k in sorted(set(KNOWN) | set(stored)):
172
+ src = "environment" if os.environ.get(k) else ("file" if k in stored else "not set")
173
+ print(f" {k:18s} {src:12s} {KNOWN.get(k, ('',))[0]}")
174
+ return 0
175
+
176
+
177
+ if __name__ == "__main__":
178
+ sys.exit(main())
@@ -1871,6 +1871,19 @@ def main():
1871
1871
  ],
1872
1872
  )
1873
1873
 
1874
+ # v2.0.4: the API key comes from the environment, the credentials file, or (first use, interactive
1875
+ # only) a prompt; asked here once so every step inherits it. Never logged.
1876
+ if args.llm_backend == "api":
1877
+ try:
1878
+ import descriptron_credentials as _creds
1879
+ _creds.load_into_environment()
1880
+ if not _creds.ensure_key("ANTHROPIC_API_KEY"):
1881
+ logger.warning(" --llm_backend api but no ANTHROPIC_API_KEY (environment, %s, or prompt): "
1882
+ "the description steps will fail; use --llm_backend none for the data-only "
1883
+ "outputs", _creds.credentials_path())
1884
+ except ImportError:
1885
+ pass
1886
+
1874
1887
  logger.info("=" * 70)
1875
1888
  logger.info("Descriptron → BioRAG Full Pipeline")
1876
1889
  logger.info("=" * 70)