groundworkers 0.7.0__tar.gz → 0.7.1__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 (307) hide show
  1. {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/workflows/ci.yml +47 -1
  2. {groundworkers-0.7.0 → groundworkers-0.7.1}/PKG-INFO +1 -1
  3. groundworkers-0.7.1/docs/usage/testing.md +45 -0
  4. {groundworkers-0.7.0 → groundworkers-0.7.1}/pyproject.toml +3 -0
  5. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/plugins.py +1 -1
  6. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/assisted.py +5 -1
  7. groundworkers-0.7.1/tests/conftest.py +114 -0
  8. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/integration/test_omop_emb_adapter.py +6 -4
  9. groundworkers-0.7.1/tests/integration/test_omop_emb_contract.py +147 -0
  10. groundworkers-0.7.1/tests/integration/test_omop_graph_adapter.py +356 -0
  11. groundworkers-0.7.1/tests/integration/test_omop_postgres_runtime.py +209 -0
  12. groundworkers-0.7.1/tests/support/embedding_contract_fixture.py +55 -0
  13. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/support/seed_postgres_fixture.py +27 -2
  14. groundworkers-0.7.1/tests/support/wheel_smoke.py +93 -0
  15. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/test_rest_api.py +33 -0
  16. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_plugins.py +49 -2
  17. groundworkers-0.7.1/tests/unit/test_required_integration_preflight.py +80 -0
  18. groundworkers-0.7.1/tests/unit/test_required_test_gate.py +16 -0
  19. groundworkers-0.7.1/tests/unit/test_source_planning_assisted_classifier.py +200 -0
  20. groundworkers-0.7.1/tests/unit/test_source_planning_document_formats.py +200 -0
  21. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_tools.py +36 -0
  22. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_tui_app.py +14 -2
  23. groundworkers-0.7.0/docs/usage/testing.md +0 -34
  24. groundworkers-0.7.0/tests/conftest.py +0 -33
  25. groundworkers-0.7.0/tests/integration/test_omop_graph_adapter.py +0 -430
  26. {groundworkers-0.7.0 → groundworkers-0.7.1}/.githooks/pre-commit +0 -0
  27. {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/CONTRIBUTING.md +0 -0
  28. {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  29. {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  30. {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  31. {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/ISSUE_TEMPLATE/not_working.yml +0 -0
  32. {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  33. {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/release-drafter.yml +0 -0
  34. {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/workflows/docs.yml +0 -0
  35. {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/workflows/merge.yml +0 -0
  36. {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/workflows/publish.yml +0 -0
  37. {groundworkers-0.7.0 → groundworkers-0.7.1}/.gitignore +0 -0
  38. {groundworkers-0.7.0 → groundworkers-0.7.1}/.python-version +0 -0
  39. {groundworkers-0.7.0 → groundworkers-0.7.1}/CHANGELOG.md +0 -0
  40. {groundworkers-0.7.0 → groundworkers-0.7.1}/Makefile +0 -0
  41. {groundworkers-0.7.0 → groundworkers-0.7.1}/README.md +0 -0
  42. {groundworkers-0.7.0 → groundworkers-0.7.1}/config/docker-compose.yaml +0 -0
  43. {groundworkers-0.7.0 → groundworkers-0.7.1}/config/groundworkers.example.toml +0 -0
  44. {groundworkers-0.7.0 → groundworkers-0.7.1}/config/groundworkers_db.env +0 -0
  45. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/adapters/cdm.md +0 -0
  46. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/adapters/omop_emb.md +0 -0
  47. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/adapters/omop_graph.md +0 -0
  48. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/architecture.md +0 -0
  49. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/concepts.md +0 -0
  50. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/development/extending.md +0 -0
  51. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/development/integration.md +0 -0
  52. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/development/plugins.md +0 -0
  53. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/from-scratch.md +0 -0
  54. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/index.md +0 -0
  55. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/js/mermaid-init.js +0 -0
  56. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/reference/ref_adapters.md +0 -0
  57. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/reference/ref_base.md +0 -0
  58. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/reference/ref_config.md +0 -0
  59. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/reference/ref_services.md +0 -0
  60. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/reference/ref_tools.md +0 -0
  61. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/domain.md +0 -0
  62. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/graph.md +0 -0
  63. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/grounding.md +0 -0
  64. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/mapping.md +0 -0
  65. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/semantic_projection.md +0 -0
  66. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/source_planning.md +0 -0
  67. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/text.md +0 -0
  68. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/vocab.md +0 -0
  69. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/01_create_config.png +0 -0
  70. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/02_empty_config.png +0 -0
  71. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/03_config_wizard.png +0 -0
  72. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/04_test_cnx.png +0 -0
  73. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/04_warnings.png +0 -0
  74. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/05_graph_setup.png +0 -0
  75. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/06_load_predicates.png +0 -0
  76. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/07_vector_store.png +0 -0
  77. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/08_register_embedding.png +0 -0
  78. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/10_setup_complete.png +0 -0
  79. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/concept.md +0 -0
  80. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/domain.md +0 -0
  81. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/embedding.md +0 -0
  82. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/knowledge.md +0 -0
  83. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/mapping.md +0 -0
  84. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/overview.md +0 -0
  85. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/resolver.md +0 -0
  86. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/search.md +0 -0
  87. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/semantic_projection.md +0 -0
  88. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/source_planning.md +0 -0
  89. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/system.md +0 -0
  90. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/text.md +0 -0
  91. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/usage/configuration.md +0 -0
  92. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/usage/installation.md +0 -0
  93. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/usage/integrations.md +0 -0
  94. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/usage/setup_journey.md +0 -0
  95. {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/usage/upgrading-from-0.6.md +0 -0
  96. {groundworkers-0.7.0 → groundworkers-0.7.1}/mkdocs.yaml +0 -0
  97. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/__init__.py +0 -0
  98. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/_env.py +0 -0
  99. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/adapters/__init__.py +0 -0
  100. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/adapters/cdm.py +0 -0
  101. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/adapters/llm.py +0 -0
  102. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/adapters/omop_emb.py +0 -0
  103. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/adapters/omop_graph.py +0 -0
  104. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/app.py +0 -0
  105. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/__init__.py +0 -0
  106. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/__init__.py +0 -0
  107. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/configuration.py +0 -0
  108. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/configuration_provider.py +0 -0
  109. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/databases.py +0 -0
  110. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/embedding_artifacts.py +0 -0
  111. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/embedding_capability.py +0 -0
  112. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/embedding_coverage.py +0 -0
  113. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/embedding_population.py +0 -0
  114. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/embedding_reconciliation.py +0 -0
  115. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/embedding_registry.py +0 -0
  116. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/embedding_setup.py +0 -0
  117. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/graph_maintenance.py +0 -0
  118. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/integration.py +0 -0
  119. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/maintenance_runs.py +0 -0
  120. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/model_inventory.py +0 -0
  121. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/models.py +0 -0
  122. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/performance_maintenance.py +0 -0
  123. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/plugin_configuration.py +0 -0
  124. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/runtime_setup.py +0 -0
  125. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/base/__init__.py +0 -0
  126. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/base/concept_payload.py +0 -0
  127. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/base/domain_names.py +0 -0
  128. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/base/errors.py +0 -0
  129. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/base/results.py +0 -0
  130. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/base/server.py +0 -0
  131. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/base/sql.py +0 -0
  132. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/bootstrap.py +0 -0
  133. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/config/README +0 -0
  134. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/config/predicate_classification.csv +0 -0
  135. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/config/predicate_mapping.csv +0 -0
  136. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/config.py +0 -0
  137. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/criteria-gate-yesno/guidance.md +0 -0
  138. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/criteria-gate-yesno/manifest.yaml +0 -0
  139. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/diagnosis-role-modifier/guidance.md +0 -0
  140. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/diagnosis-role-modifier/manifest.yaml +0 -0
  141. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/domain-routing/guidance.md +0 -0
  142. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/domain-routing/manifest.yaml +0 -0
  143. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/domain-routing/rules.yaml +0 -0
  144. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/drug-concept-granularity/guidance.md +0 -0
  145. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/drug-concept-granularity/manifest.yaml +0 -0
  146. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/drug-concept-granularity/rules.yaml +0 -0
  147. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/history-of-event/guidance.md +0 -0
  148. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/history-of-event/manifest.yaml +0 -0
  149. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/standard-concept-preference/guidance.md +0 -0
  150. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/standard-concept-preference/manifest.yaml +0 -0
  151. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/standard-concept-preference/rules.yaml +0 -0
  152. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/server.py +0 -0
  153. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/__init__.py +0 -0
  154. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/domain.py +0 -0
  155. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/graph.py +0 -0
  156. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/grounding.py +0 -0
  157. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/knowledge/__init__.py +0 -0
  158. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/knowledge/catalogue.py +0 -0
  159. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/knowledge/models.py +0 -0
  160. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/mapping.py +0 -0
  161. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/semantic_projection/__init__.py +0 -0
  162. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/semantic_projection/definitions.py +0 -0
  163. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/semantic_projection/models.py +0 -0
  164. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/semantic_projection/service.py +0 -0
  165. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/__init__.py +0 -0
  166. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/canonical_headers.py +0 -0
  167. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/classifier.py +0 -0
  168. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposer.py +0 -0
  169. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/__init__.py +0 -0
  170. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/csv_.py +0 -0
  171. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/ddl_.py +0 -0
  172. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/docx_.py +0 -0
  173. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/json_.py +0 -0
  174. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/pdf_.py +0 -0
  175. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/xlsx_.py +0 -0
  176. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/xml_.py +0 -0
  177. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/detector.py +0 -0
  178. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/models.py +0 -0
  179. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/normalisation.py +0 -0
  180. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/provenance.py +0 -0
  181. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/router.py +0 -0
  182. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/serialisation.py +0 -0
  183. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/service.py +0 -0
  184. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/source_profiles/__init__.py +0 -0
  185. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/source_profiles/base.py +0 -0
  186. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/source_profiles/redcap.py +0 -0
  187. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/source_profiles/registry.py +0 -0
  188. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/warnings.py +0 -0
  189. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/text/__init__.py +0 -0
  190. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/text/models.py +0 -0
  191. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/text/prompts.py +0 -0
  192. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/text/service.py +0 -0
  193. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/vocab.py +0 -0
  194. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/__init__.py +0 -0
  195. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/concept_tools.py +0 -0
  196. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/domain_tools.py +0 -0
  197. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/embedding_tools.py +0 -0
  198. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/knowledge_tools.py +0 -0
  199. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/mapping_tools.py +0 -0
  200. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/resolver_tools.py +0 -0
  201. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/search_tools.py +0 -0
  202. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/semantic_projection_tools.py +0 -0
  203. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/source_planning_tools.py +0 -0
  204. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/system_tools.py +0 -0
  205. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/text_tools.py +0 -0
  206. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/transports/__init__.py +0 -0
  207. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/transports/rest/__init__.py +0 -0
  208. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/transports/rest/api.py +0 -0
  209. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/transports/rest/models.py +0 -0
  210. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/__init__.py +0 -0
  211. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/app.py +0 -0
  212. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/pages/__init__.py +0 -0
  213. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/pages/base.py +0 -0
  214. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/pages/plugin_config.py +0 -0
  215. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/pages/plugin_setup.py +0 -0
  216. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/pages/setup.py +0 -0
  217. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/__init__.py +0 -0
  218. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/base.py +0 -0
  219. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/chat.py +0 -0
  220. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/configuration.py +0 -0
  221. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/database.py +0 -0
  222. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/embeddings.py +0 -0
  223. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/graph.py +0 -0
  224. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/llm_provider.py +0 -0
  225. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/overview.py +0 -0
  226. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/performance.py +0 -0
  227. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/runs.py +0 -0
  228. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/routes.py +0 -0
  229. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/state.py +0 -0
  230. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/wizards/__init__.py +0 -0
  231. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/wizards/config_location.py +0 -0
  232. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/wizards/embedding_store.py +0 -0
  233. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/wizards/embeddings.py +0 -0
  234. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/wizards/graph_maintenance.py +0 -0
  235. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/wizards/performance_maintenance.py +0 -0
  236. {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/wizards/plugin_setup.py +0 -0
  237. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/__init__.py +0 -0
  238. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/integration/__init__.py +0 -0
  239. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/support/__init__.py +0 -0
  240. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/support/omop.py +0 -0
  241. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/support/postgres_ci_config.toml +0 -0
  242. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/support/stack_config.py +0 -0
  243. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/test_bootstrap_config.py +0 -0
  244. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/test_server_registry.py +0 -0
  245. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/__init__.py +0 -0
  246. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_concept_payload.py +0 -0
  247. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_concept_tools.py +0 -0
  248. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_config_location_wizard.py +0 -0
  249. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_configuration_provider.py +0 -0
  250. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_domain_names.py +0 -0
  251. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_domain_tools.py +0 -0
  252. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_embedding_population_scope.py +0 -0
  253. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_embedding_reconciliation.py +0 -0
  254. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_embedding_registry_step.py +0 -0
  255. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_embedding_tools.py +0 -0
  256. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_env_bootstrap.py +0 -0
  257. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_error_boundary.py +0 -0
  258. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_graph_maintenance.py +0 -0
  259. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_graph_maintenance_wizard.py +0 -0
  260. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_graph_service.py +0 -0
  261. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_graph_status.py +0 -0
  262. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_grounding_constraints.py +0 -0
  263. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_grounding_service.py +0 -0
  264. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_knowledge_catalogue.py +0 -0
  265. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_llm_adapter.py +0 -0
  266. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_maintenance_runs_phase2.py +0 -0
  267. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_mapping_context.py +0 -0
  268. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_mapping_service.py +0 -0
  269. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_mapping_tools.py +0 -0
  270. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_migration_boundaries.py +0 -0
  271. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_normalized_index.py +0 -0
  272. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_omop_emb_adapter.py +0 -0
  273. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_omop_emb_setup_config.py +0 -0
  274. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_omop_graph_adapter.py +0 -0
  275. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_performance.py +0 -0
  276. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_performance_maintenance_wizard.py +0 -0
  277. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_phase3_overview.py +0 -0
  278. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_plugin_configuration.py +0 -0
  279. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_provider_fields.py +0 -0
  280. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_schema_resolution.py +0 -0
  281. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_semantic_projection_service.py +0 -0
  282. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_semantic_projection_tools.py +0 -0
  283. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_artifacts_and_coverage.py +0 -0
  284. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_configuration.py +0 -0
  285. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_configuration_section.py +0 -0
  286. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_databases.py +0 -0
  287. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_embeddings.py +0 -0
  288. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_provider_conformance.py +0 -0
  289. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_runtime.py +0 -0
  290. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_truth.py +0 -0
  291. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_write_flows.py +0 -0
  292. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_classifier.py +0 -0
  293. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_decomposer.py +0 -0
  294. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_detector.py +0 -0
  295. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_models.py +0 -0
  296. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_normalisation.py +0 -0
  297. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_router.py +0 -0
  298. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_service.py +0 -0
  299. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_split_classified_edges.py +0 -0
  300. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_stack_config_fixtures.py +0 -0
  301. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_system_tools.py +0 -0
  302. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_text_service.py +0 -0
  303. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_text_tools.py +0 -0
  304. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_tui_state.py +0 -0
  305. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_vector_store_setup.py +0 -0
  306. {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_vocab_service.py +0 -0
  307. {groundworkers-0.7.0 → groundworkers-0.7.1}/uv.lock +0 -0
@@ -35,7 +35,7 @@ jobs:
35
35
  --password postgres \
36
36
  --database-name test_db \
37
37
  --test-only false
38
- uv run omop-config databases add cdm test_cdm_db --connection pg_test --cdm-schema public
38
+ uv run omop-config databases add cdm test_cdm_db --connection pg_test --cdm-schema gw_primary --vocab-schema gw_vocab
39
39
  uv run omop-config databases add generic test_vector_db --connection pg_test --schema-name embeddings
40
40
  uv run omop-config vector-stores add test_embeddings \
41
41
  --backend-type pgvector \
@@ -44,3 +44,49 @@ jobs:
44
44
  --cdm-db test_cdm_db \
45
45
  --vector-store-name test_embeddings
46
46
  uv run python -m tests.support.seed_postgres_fixture
47
+ test-evidence:
48
+ runs-on: ubuntu-latest
49
+ steps:
50
+ - uses: actions/checkout@v4
51
+ - uses: actions/setup-python@v5
52
+ with:
53
+ python-version: "3.12"
54
+ - uses: astral-sh/setup-uv@v6
55
+ - name: Run hermetic suite with coverage and skip reasons
56
+ run: |
57
+ uv sync --frozen --all-extras --dev
58
+ export GROUNDWORKERS_REQUIRE_FORMAT_TESTS=1
59
+ echo "GROUNDWORKERS_REQUIRE_FORMAT_TESTS=1" >> "$GITHUB_ENV"
60
+ COVERAGE_FILE="$RUNNER_TEMP/groundworkers.coverage" uv run pytest -q -ra \
61
+ --cov=groundworkers --cov-branch \
62
+ --cov-report=xml:"$RUNNER_TEMP/coverage.xml" \
63
+ --junitxml="$RUNNER_TEMP/pytest.xml"
64
+ - uses: actions/upload-artifact@v4
65
+ if: always()
66
+ with:
67
+ name: groundworkers-test-evidence
68
+ path: |
69
+ ${{ runner.temp }}/coverage.xml
70
+ ${{ runner.temp }}/pytest.xml
71
+ if-no-files-found: warn
72
+ wheel-smoke:
73
+ runs-on: ubuntu-latest
74
+ steps:
75
+ - uses: actions/checkout@v4
76
+ - uses: actions/setup-python@v5
77
+ with:
78
+ python-version: "3.12"
79
+ - uses: astral-sh/setup-uv@v6
80
+ - name: Build and smoke-test base and optional wheel installs
81
+ run: |
82
+ uv build --wheel
83
+ wheel="$(find dist -maxdepth 1 -name '*.whl' -print -quit)"
84
+ wheel="$(pwd)/$wheel"
85
+ uv venv "$RUNNER_TEMP/gw-wheel-base" --python 3.12
86
+ uv pip install --python "$RUNNER_TEMP/gw-wheel-base/bin/python" "$wheel"
87
+ mkdir -p "$RUNNER_TEMP/gw-wheel-run"
88
+ cd "$RUNNER_TEMP/gw-wheel-run"
89
+ "$RUNNER_TEMP/gw-wheel-base/bin/python" "$GITHUB_WORKSPACE/tests/support/wheel_smoke.py" base
90
+ uv venv "$RUNNER_TEMP/gw-wheel-extras" --python 3.12
91
+ uv pip install --python "$RUNNER_TEMP/gw-wheel-extras/bin/python" "$wheel[all_source,tui]" httpx
92
+ "$RUNNER_TEMP/gw-wheel-extras/bin/python" "$GITHUB_WORKSPACE/tests/support/wheel_smoke.py" extras
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: groundworkers
3
- Version: 0.7.0
3
+ Version: 0.7.1
4
4
  Summary: Groundworkers MCP server — read-only agentive access to OMOP vocabularies, concept graphs, and embeddings.
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: fastapi<1.0,>=0.110
@@ -0,0 +1,45 @@
1
+ # Testing
2
+
3
+ ## Test lanes
4
+
5
+ Groundworkers uses distinct lanes because an isolated unit run cannot prove the database, optional parser, plugin-entry-point, or built-wheel contracts.
6
+
7
+ | Lane | What it proves | CI behavior |
8
+ |---|---|---|
9
+ | Unit and service | Deterministic policy, adapter boundaries, source planning, transport behavior, and controlled provider responses | Runs without a live database or model service |
10
+ | Optional dependency | Real XLSX, DOCX, and PDF parsing plus missing-extra guidance | Required format tests must pass in the all-extras CI lane; missing-extra behavior is simulated |
11
+ | PostgreSQL integration | Seeded graph retrieval, real pgvector storage and reader behavior, model selection, filters, and embedding-tier grounding | Required: every required_integration case must be collected and pass |
12
+ | Wheel smoke | Base and optional-extra installations, package entry-point discovery, CLI startup, and successful MCP/REST planning operations | Builds once and runs outside the checkout |
13
+ | Optional live provider | Real configured model-provider connectivity | Opt-in developer smoke only; never substitutes for seeded embedding tests |
14
+
15
+ ## Running tests
16
+
17
+ ~~~bash
18
+ # Hermetic suite, including real optional-format parsers
19
+ uv run pytest -q -ra
20
+
21
+ # Unit tests only
22
+ uv run pytest tests/unit/
23
+
24
+ # Required PostgreSQL cases; provide a seeded test stack first
25
+ GROUNDWORKERS_CONFIG_PATH=/path/to/stack.toml \
26
+ GROUNDWORKERS_REQUIRE_INTEGRATION=1 \
27
+ GROUNDWORKERS_TEST_DATABASE_URL=postgresql+psycopg://... \
28
+ uv run pytest -m required_integration -q -ra
29
+
30
+ # Coverage for local review
31
+ COVERAGE_FILE=/tmp/groundworkers.coverage uv run pytest -q -ra \
32
+ --cov=groundworkers --cov-branch --cov-report=term-missing
33
+ ~~~
34
+
35
+ The required integration gate derives its cases from the `required_integration` marker, so every marked case is checked for collection and a passing result. Missing configuration, unavailable adapters, and absent seeded rows fail in that lane. The all-extras CI job applies the same execution check to `required_format` cases, so a missing parser cannot silently turn the format contract into a skip. Without the corresponding `GROUNDWORKERS_REQUIRE_*` flag, PostgreSQL and optional-format fixtures remain opt-in and skip with a reason.
36
+
37
+ ## Configuration and optional provider smoke
38
+
39
+ Set GROUNDWORKERS_CONFIG_PATH (or GROUNDWORKERS_CONFIG) to the explicit Groundworkers stack file for integration tests. OA_CONFIG_PATH selects the oa-configurator default stack, but does not by itself opt the graph integration tests into a required run. PostgreSQL CI sets both paths and seeds a compact fixture before pytest starts.
40
+
41
+ The external-provider smoke tests are separate from seeded deterministic embedding tests. To run them locally, set GROUNDWORKERS_RUN_EMBEDDING_INTEGRATION=1, GROUNDWORKERS_CONFIG_PATH, and GROUNDWORKERS_TEST_CONCEPT_ID; they require a configured provider and a concept with stored vectors.
42
+
43
+ ## Coverage and skip evidence
44
+
45
+ CI publishes coverage.xml and pytest.xml from the hermetic all-extras run. The JUnit report carries named skip reasons. PostgreSQL CI separately enforces the required test list, so optional local skips cannot satisfy that gate. Coverage percentages are review evidence, not a global pass threshold. Prioritize unexecuted behavior at database, provider, transport, and packaging boundaries.
@@ -112,5 +112,8 @@ include = ["src"]
112
112
  testpaths = ["tests"]
113
113
  markers = [
114
114
  "integration: requires a live backend or database",
115
+ "required_integration: deterministic integration contract required by CI",
116
+ "required_format: optional document-format contract required in all-extras CI",
117
+ "optional_integration: environment-dependent integration smoke test",
115
118
  "requires_client: requires an embedding API client",
116
119
  ]
@@ -20,7 +20,6 @@ from enum import StrEnum
20
20
  from importlib.metadata import entry_points
21
21
  from typing import TYPE_CHECKING, Any, ClassVar, Protocol, runtime_checkable
22
22
 
23
- from groundskeeping.contracts import CommandPlan
24
23
  from oa_configurator import ( # type: ignore[import-untyped]
25
24
  PackageConfigBase,
26
25
  ResolvedDatabase,
@@ -36,6 +35,7 @@ if TYPE_CHECKING:
36
35
  ConfigWorkflowSpec,
37
36
  MutationOperation,
38
37
  )
38
+ from groundskeeping.contracts import CommandPlan
39
39
 
40
40
  from groundworkers.services.grounding import ConceptGroundingService
41
41
  from groundworkers.services.mapping import MappingService
@@ -9,7 +9,7 @@ from __future__ import annotations
9
9
 
10
10
  from dataclasses import replace
11
11
 
12
- from pydantic import BaseModel, Field, ValidationError
12
+ from pydantic import BaseModel, ConfigDict, Field, ValidationError
13
13
 
14
14
  from groundworkers.adapters.llm import LLMAdapter
15
15
  from groundworkers.base.errors import GroundworkersError
@@ -31,6 +31,8 @@ Respond with raw JSON only.
31
31
 
32
32
 
33
33
  class AssistedColumnDecision(BaseModel):
34
+ model_config = ConfigDict(extra="ignore")
35
+
34
36
  header: str
35
37
  role: str | None = None
36
38
  confidence: float = Field(ge=0.0, le=1.0)
@@ -40,6 +42,8 @@ class AssistedColumnDecision(BaseModel):
40
42
 
41
43
 
42
44
  class AssistedClassificationResult(BaseModel):
45
+ model_config = ConfigDict(extra="ignore")
46
+
43
47
  decisions: list[AssistedColumnDecision] = Field(default_factory=list)
44
48
 
45
49
 
@@ -0,0 +1,114 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ import pytest
8
+
9
+ ROOT = Path(__file__).resolve().parents[1]
10
+ SRC = ROOT / "src"
11
+
12
+ _REQUIRED_TEST_MARKERS = {
13
+ "GROUNDWORKERS_REQUIRE_INTEGRATION": "required_integration",
14
+ "GROUNDWORKERS_REQUIRE_FORMAT_TESTS": "required_format",
15
+ }
16
+ _required_test_ids: dict[str, set[str]] = {}
17
+ _required_test_outcomes: dict[str, dict[str, str]] = {}
18
+
19
+ if str(SRC) not in sys.path:
20
+ sys.path.insert(0, str(SRC))
21
+
22
+
23
+ @pytest.fixture(autouse=True)
24
+ def _isolated_env_file(tmp_path_factory, monkeypatch):
25
+ """Keep the recorded config location out of the developer's home directory.
26
+
27
+ `groundworkers._env.ENV_FILE` is a real path under ~/.config/omop, and the
28
+ location wizard writes it. Autouse rather than opt-in: a test that records a
29
+ location and forgets to redirect it repoints the machine Groundworkers reads
30
+ from, which is not a failure any assertion would catch.
31
+ """
32
+ from groundworkers import _env
33
+
34
+ monkeypatch.setattr(
35
+ _env, "ENV_FILE", tmp_path_factory.mktemp("env") / "groundworkers.env"
36
+ )
37
+ monkeypatch.delenv(_env.ENV_CONFIG_PATH, raising=False)
38
+ # `load_environment` writes this module global directly, so a test that calls
39
+ # it leaves every later test looking like it was launched with a broken
40
+ # OA_CONFIG_PATH -- which makes the setup page open the location wizard.
41
+ monkeypatch.setattr(_env, "_rejected", None)
42
+
43
+
44
+ def _missing_required_tests(expected: set[str], selected: set[str]) -> list[str]:
45
+ return sorted(expected - selected)
46
+
47
+
48
+ def _required_test_failures(
49
+ expected: set[str], outcomes: dict[str, str]
50
+ ) -> list[str]:
51
+ return sorted(nodeid for nodeid in expected if outcomes.get(nodeid) != "passed")
52
+
53
+
54
+ @pytest.hookimpl(hookwrapper=True, tryfirst=True)
55
+ def pytest_collection_modifyitems(session: pytest.Session, items: list[pytest.Item]):
56
+ enabled = {
57
+ marker
58
+ for variable, marker in _REQUIRED_TEST_MARKERS.items()
59
+ if os.getenv(variable) == "1"
60
+ }
61
+ expected = {
62
+ marker: {item.nodeid for item in items if item.get_closest_marker(marker)}
63
+ for marker in enabled
64
+ }
65
+ yield
66
+ selected_items = {item.nodeid for item in session.items}
67
+ _required_test_ids.clear()
68
+ _required_test_ids.update(expected)
69
+ for marker in enabled:
70
+ _required_test_outcomes[marker] = {}
71
+ session.config._required_test_ids = expected
72
+ session.config._required_test_collection_errors = {
73
+ marker: _missing_required_tests(ids, selected_items)
74
+ for marker, ids in expected.items()
75
+ }
76
+ session.config._required_test_outcomes = {
77
+ marker: _required_test_outcomes.setdefault(marker, {}) for marker in enabled
78
+ }
79
+
80
+
81
+ def pytest_runtest_logreport(report: pytest.TestReport) -> None:
82
+ if report.when not in {"setup", "call", "teardown"}:
83
+ return
84
+ for marker, outcomes in _required_test_outcomes.items():
85
+ if report.when == "call" or report.outcome != "passed":
86
+ if report.nodeid in _required_test_ids.get(marker, set()):
87
+ outcomes[report.nodeid] = report.outcome
88
+
89
+
90
+ def pytest_sessionfinish(session: pytest.Session, exitstatus: int) -> None:
91
+ required = getattr(session.config, "_required_test_ids", {})
92
+ collection_errors = getattr(session.config, "_required_test_collection_errors", {})
93
+ outcomes = getattr(session.config, "_required_test_outcomes", {})
94
+ problems = {
95
+ marker: sorted(
96
+ set(collection_errors.get(marker, []))
97
+ | set(_required_test_failures(ids, outcomes.get(marker, {})))
98
+ )
99
+ for marker, ids in required.items()
100
+ }
101
+ for marker, ids in required.items():
102
+ if not ids:
103
+ problems[marker].append(f"no tests collected with {marker} marker")
104
+ failures = {marker: entries for marker, entries in problems.items() if entries}
105
+ if not failures:
106
+ return
107
+ reporter = session.config.pluginmanager.get_plugin("terminalreporter")
108
+ if reporter is not None:
109
+ for marker, entries in failures.items():
110
+ reporter.write_sep("!", f"required {marker} execution gate failed")
111
+ for nodeid in entries:
112
+ outcome = outcomes.get(marker, {}).get(nodeid, "not executed or deselected")
113
+ reporter.write_line(f"{nodeid}: {outcome}")
114
+ session.exitstatus = pytest.ExitCode.TESTS_FAILED
@@ -42,7 +42,7 @@ def _load_integration_adapter():
42
42
  return adapter
43
43
 
44
44
 
45
- @pytest.mark.integration
45
+ @pytest.mark.optional_integration
46
46
  def test_index_status_returns_at_least_one_model():
47
47
  adapter = _load_integration_adapter()
48
48
 
@@ -59,7 +59,7 @@ def _get_test_concept_id() -> int:
59
59
  return int(value)
60
60
 
61
61
 
62
- @pytest.mark.integration
62
+ @pytest.mark.optional_integration
63
63
  def test_get_neighbours_returns_sorted_results():
64
64
  adapter = _load_integration_adapter()
65
65
  status = adapter.index_status()
@@ -71,11 +71,12 @@ def test_get_neighbours_returns_sorted_results():
71
71
  model_name=status["models"][0]["model_name"],
72
72
  )
73
73
 
74
+ assert result["results"], "configured embedding smoke test returned no neighbours"
74
75
  similarities = [item["similarity"] for item in result["results"]]
75
76
  assert similarities == sorted(similarities, reverse=True)
76
77
 
77
78
 
78
- @pytest.mark.integration
79
+ @pytest.mark.optional_integration
79
80
  def test_get_neighbours_excludes_query_concept():
80
81
  adapter = _load_integration_adapter()
81
82
  status = adapter.index_status()
@@ -87,10 +88,11 @@ def test_get_neighbours_excludes_query_concept():
87
88
  model_name=status["models"][0]["model_name"],
88
89
  )
89
90
 
91
+ assert result["results"], "configured embedding smoke test returned no neighbours"
90
92
  assert all(item["concept_id"] != concept_id for item in result["results"])
91
93
 
92
94
 
93
- @pytest.mark.integration
95
+ @pytest.mark.optional_integration
94
96
  def test_get_neighbours_unknown_concept_raises_not_found():
95
97
  adapter = _load_integration_adapter()
96
98
  status = adapter.index_status()
@@ -0,0 +1,147 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+
5
+ import pytest
6
+ from oa_configurator import ResolvedModel, ResolvedProvider
7
+ from omop_emb.backends import open_vector_store_reader
8
+ from omop_emb.config import MetricType
9
+ from omop_emb.interface import EmbeddingReaderInterface
10
+
11
+ from groundworkers.adapters.omop_emb import OmopEmbAdapter
12
+ from groundworkers.adapters.omop_graph import OmopGraphAdapter
13
+ from groundworkers.base.errors import GroundworkersError
14
+ from groundworkers.bootstrap import build_app_config
15
+ from groundworkers.services.graph import GraphService
16
+ from groundworkers.services.grounding import ConceptGroundingService
17
+ from tests.support.embedding_contract_fixture import (
18
+ MODEL_NAME,
19
+ seed_embedding_contract,
20
+ )
21
+
22
+
23
+ class FixedEncoder:
24
+ provider = "ollama"
25
+ model = MODEL_NAME
26
+
27
+ def embed_texts(self, texts, *, role, batch_size=None):
28
+ return [[1.0, 0.0, 0.0] if text != "orthogonal query" else [0.0, 1.0, 0.0] for text in texts]
29
+
30
+ async def async_embed_texts(self, texts, *, role, batch_size=None):
31
+ return self.embed_texts(texts, role=role, batch_size=batch_size)
32
+
33
+
34
+ @pytest.fixture(scope="module")
35
+ def embedding_contract():
36
+ if os.getenv("GROUNDWORKERS_REQUIRE_INTEGRATION") != "1":
37
+ pytest.skip("deterministic pgvector embedding contract runs in required PostgreSQL CI")
38
+ config_path = os.getenv("GROUNDWORKERS_CONFIG_PATH") or os.getenv("GROUNDWORKERS_CONFIG")
39
+ if not config_path:
40
+ raise RuntimeError("required embedding contract needs GROUNDWORKERS_CONFIG_PATH")
41
+ config = build_app_config(config_path=config_path)
42
+ if config.vector_store is None:
43
+ raise AssertionError("required embedding contract has no configured vector store")
44
+ seed_embedding_contract(config)
45
+ adapter = OmopEmbAdapter(
46
+ backend_factory=lambda: open_vector_store_reader(config.vector_store),
47
+ backend_type=config.vector_store.backend_type,
48
+ cdm_session_factory=config.cdm_session_factory,
49
+ )
50
+ adapter._model_backend_factory = lambda: FixedEncoder()
51
+ try:
52
+ yield config, adapter
53
+ finally:
54
+ adapter.close()
55
+
56
+
57
+ @pytest.mark.required_integration
58
+ def test_real_reader_returns_seeded_neighbours_and_keeps_batch_alignment(embedding_contract):
59
+ _config, adapter = embedding_contract
60
+ neighbours = adapter.get_neighbours(201826, limit=5, model_name=MODEL_NAME)
61
+ assert [item["concept_id"] for item in neighbours["results"]][:2] == [201827, 201830]
62
+ assert 201826 not in {item["concept_id"] for item in neighbours["results"]}
63
+
64
+ record = adapter._resolve_model_record(MODEL_NAME)
65
+ reader = EmbeddingReaderInterface(
66
+ model=record.model_name,
67
+ backend=adapter._get_backend(),
68
+ metric_type=record.metric_type or MetricType.COSINE,
69
+ provider_type=record.provider_type,
70
+ )
71
+ batch = reader.get_nearest_concepts_from_query_texts(
72
+ ["nearest to diabetes", "orthogonal query"],
73
+ model_backend=FixedEncoder(),
74
+ k=1,
75
+ faiss_index_config=record.index_config,
76
+ )
77
+ assert [[match.concept_id for match in row] for row in batch] == [[201826], [201828]]
78
+
79
+
80
+ @pytest.mark.required_integration
81
+ def test_real_model_registry_handles_missing_and_ambiguous_selection(embedding_contract):
82
+ _, adapter = embedding_contract
83
+ assert adapter.resolve_model_name(MODEL_NAME) == MODEL_NAME
84
+ with pytest.raises(GroundworkersError) as missing:
85
+ adapter.resolve_model_name("not-registered:v1")
86
+ assert missing.value.code == "NOT_FOUND"
87
+ with pytest.raises(GroundworkersError) as ambiguous:
88
+ adapter.resolve_model_name(None)
89
+ assert ambiguous.value.code == "BACKEND_UNAVAIL"
90
+
91
+
92
+ @pytest.mark.required_integration
93
+ def test_real_text_search_applies_domain_vocabulary_standard_and_active_filters(embedding_contract):
94
+ _, adapter = embedding_contract
95
+ result = adapter.search(
96
+ "a deterministic unrelated phrase",
97
+ limit=10,
98
+ domain="Condition",
99
+ vocabulary="SNOMED",
100
+ standard_only=True,
101
+ active_only=True,
102
+ model_name=MODEL_NAME,
103
+ )
104
+ assert {item["concept_id"] for item in result["results"]} == {201826, 201827, 201828}
105
+
106
+
107
+ @pytest.mark.required_integration
108
+ def test_graph_grounding_executes_real_embedding_tier_with_seeded_reader(embedding_contract):
109
+ config, _adapter = embedding_contract
110
+ assert config.vector_store is not None
111
+ resolved_model = ResolvedModel(
112
+ name="groundworkers_fixture",
113
+ provider=ResolvedProvider(
114
+ name="fixture_provider", provider="ollama", base_url=None, api_key=None
115
+ ),
116
+ model=MODEL_NAME,
117
+ embedding_dim=3,
118
+ document_prefix=None,
119
+ query_prefix=None,
120
+ embeddings=True,
121
+ tool_use=False,
122
+ structured_output=False,
123
+ extended_thinking=False,
124
+ configuration={},
125
+ )
126
+ graph_adapter = OmopGraphAdapter(
127
+ engine=config.cdm_engine,
128
+ cdm_session_factory=config.cdm_session_factory,
129
+ embedding_backend_factory=lambda: open_vector_store_reader(config.vector_store),
130
+ resolved_embedding_model=resolved_model,
131
+ model_backend_factory=FixedEncoder,
132
+ )
133
+ assert graph_adapter.is_available() is True
134
+ graph = GraphService(graph_adapter)
135
+ assert graph.embedding_resolver_active is True
136
+
137
+ result = ConceptGroundingService(graph).ground(
138
+ "a deterministic unrelated phrase",
139
+ limit=5,
140
+ domain="Condition",
141
+ vocabulary_id=None,
142
+ )
143
+
144
+ assert result["grounding_explanation"]["matched_tier"] == "EMBEDDING_NEAREST"
145
+ assert result["grounding_explanation"]["used_embedding"] is True
146
+ assert [hit["concept_id"] for hit in result["results"]]
147
+ assert result["results"][0]["concept_id"] == 201826