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.
- {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/workflows/ci.yml +47 -1
- {groundworkers-0.7.0 → groundworkers-0.7.1}/PKG-INFO +1 -1
- groundworkers-0.7.1/docs/usage/testing.md +45 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/pyproject.toml +3 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/plugins.py +1 -1
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/assisted.py +5 -1
- groundworkers-0.7.1/tests/conftest.py +114 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/integration/test_omop_emb_adapter.py +6 -4
- groundworkers-0.7.1/tests/integration/test_omop_emb_contract.py +147 -0
- groundworkers-0.7.1/tests/integration/test_omop_graph_adapter.py +356 -0
- groundworkers-0.7.1/tests/integration/test_omop_postgres_runtime.py +209 -0
- groundworkers-0.7.1/tests/support/embedding_contract_fixture.py +55 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/support/seed_postgres_fixture.py +27 -2
- groundworkers-0.7.1/tests/support/wheel_smoke.py +93 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/test_rest_api.py +33 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_plugins.py +49 -2
- groundworkers-0.7.1/tests/unit/test_required_integration_preflight.py +80 -0
- groundworkers-0.7.1/tests/unit/test_required_test_gate.py +16 -0
- groundworkers-0.7.1/tests/unit/test_source_planning_assisted_classifier.py +200 -0
- groundworkers-0.7.1/tests/unit/test_source_planning_document_formats.py +200 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_tools.py +36 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_tui_app.py +14 -2
- groundworkers-0.7.0/docs/usage/testing.md +0 -34
- groundworkers-0.7.0/tests/conftest.py +0 -33
- groundworkers-0.7.0/tests/integration/test_omop_graph_adapter.py +0 -430
- {groundworkers-0.7.0 → groundworkers-0.7.1}/.githooks/pre-commit +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/CONTRIBUTING.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/ISSUE_TEMPLATE/not_working.yml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/release-drafter.yml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/workflows/docs.yml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/workflows/merge.yml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/.github/workflows/publish.yml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/.gitignore +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/.python-version +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/CHANGELOG.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/Makefile +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/README.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/config/docker-compose.yaml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/config/groundworkers.example.toml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/config/groundworkers_db.env +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/adapters/cdm.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/adapters/omop_emb.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/adapters/omop_graph.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/architecture.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/concepts.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/development/extending.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/development/integration.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/development/plugins.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/from-scratch.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/index.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/js/mermaid-init.js +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/reference/ref_adapters.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/reference/ref_base.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/reference/ref_config.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/reference/ref_services.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/reference/ref_tools.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/domain.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/graph.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/grounding.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/mapping.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/semantic_projection.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/source_planning.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/text.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/services/vocab.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/01_create_config.png +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/02_empty_config.png +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/03_config_wizard.png +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/04_test_cnx.png +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/04_warnings.png +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/05_graph_setup.png +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/06_load_predicates.png +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/07_vector_store.png +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/08_register_embedding.png +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/static/10_setup_complete.png +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/concept.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/domain.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/embedding.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/knowledge.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/mapping.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/overview.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/resolver.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/search.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/semantic_projection.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/source_planning.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/system.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/tools/text.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/usage/configuration.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/usage/installation.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/usage/integrations.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/usage/setup_journey.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/docs/usage/upgrading-from-0.6.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/mkdocs.yaml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/_env.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/adapters/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/adapters/cdm.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/adapters/llm.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/adapters/omop_emb.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/adapters/omop_graph.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/app.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/configuration.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/configuration_provider.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/databases.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/embedding_artifacts.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/embedding_capability.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/embedding_coverage.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/embedding_population.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/embedding_reconciliation.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/embedding_registry.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/embedding_setup.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/graph_maintenance.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/integration.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/maintenance_runs.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/model_inventory.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/models.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/performance_maintenance.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/plugin_configuration.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/application/setup/runtime_setup.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/base/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/base/concept_payload.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/base/domain_names.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/base/errors.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/base/results.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/base/server.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/base/sql.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/bootstrap.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/config/README +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/config/predicate_classification.csv +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/config/predicate_mapping.csv +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/config.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/criteria-gate-yesno/guidance.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/criteria-gate-yesno/manifest.yaml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/diagnosis-role-modifier/guidance.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/diagnosis-role-modifier/manifest.yaml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/domain-routing/guidance.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/domain-routing/manifest.yaml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/domain-routing/rules.yaml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/drug-concept-granularity/guidance.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/drug-concept-granularity/manifest.yaml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/drug-concept-granularity/rules.yaml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/history-of-event/guidance.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/history-of-event/manifest.yaml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/standard-concept-preference/guidance.md +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/standard-concept-preference/manifest.yaml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/knowledge/packs/core/standard-concept-preference/rules.yaml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/server.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/domain.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/graph.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/grounding.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/knowledge/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/knowledge/catalogue.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/knowledge/models.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/mapping.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/semantic_projection/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/semantic_projection/definitions.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/semantic_projection/models.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/semantic_projection/service.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/canonical_headers.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/classifier.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposer.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/csv_.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/ddl_.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/docx_.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/json_.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/pdf_.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/xlsx_.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/decomposers/xml_.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/detector.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/models.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/normalisation.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/provenance.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/router.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/serialisation.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/service.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/source_profiles/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/source_profiles/base.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/source_profiles/redcap.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/source_profiles/registry.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/warnings.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/text/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/text/models.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/text/prompts.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/text/service.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/vocab.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/concept_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/domain_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/embedding_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/knowledge_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/mapping_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/resolver_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/search_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/semantic_projection_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/source_planning_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/system_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tools/text_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/transports/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/transports/rest/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/transports/rest/api.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/transports/rest/models.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/app.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/pages/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/pages/base.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/pages/plugin_config.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/pages/plugin_setup.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/pages/setup.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/base.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/chat.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/configuration.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/database.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/embeddings.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/graph.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/llm_provider.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/overview.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/performance.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/presenters/runs.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/routes.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/state.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/wizards/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/wizards/config_location.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/wizards/embedding_store.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/wizards/embeddings.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/wizards/graph_maintenance.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/wizards/performance_maintenance.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/tui/wizards/plugin_setup.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/integration/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/support/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/support/omop.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/support/postgres_ci_config.toml +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/support/stack_config.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/test_bootstrap_config.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/test_server_registry.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/__init__.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_concept_payload.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_concept_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_config_location_wizard.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_configuration_provider.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_domain_names.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_domain_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_embedding_population_scope.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_embedding_reconciliation.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_embedding_registry_step.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_embedding_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_env_bootstrap.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_error_boundary.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_graph_maintenance.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_graph_maintenance_wizard.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_graph_service.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_graph_status.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_grounding_constraints.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_grounding_service.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_knowledge_catalogue.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_llm_adapter.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_maintenance_runs_phase2.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_mapping_context.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_mapping_service.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_mapping_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_migration_boundaries.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_normalized_index.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_omop_emb_adapter.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_omop_emb_setup_config.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_omop_graph_adapter.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_performance.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_performance_maintenance_wizard.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_phase3_overview.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_plugin_configuration.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_provider_fields.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_schema_resolution.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_semantic_projection_service.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_semantic_projection_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_artifacts_and_coverage.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_configuration.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_configuration_section.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_databases.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_embeddings.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_provider_conformance.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_runtime.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_truth.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_setup_write_flows.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_classifier.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_decomposer.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_detector.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_models.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_normalisation.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_router.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_source_planning_service.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_split_classified_edges.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_stack_config_fixtures.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_system_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_text_service.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_text_tools.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_tui_state.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_vector_store_setup.py +0 -0
- {groundworkers-0.7.0 → groundworkers-0.7.1}/tests/unit/test_vocab_service.py +0 -0
- {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
|
|
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
|
|
@@ -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
|
{groundworkers-0.7.0 → groundworkers-0.7.1}/src/groundworkers/services/source_planning/assisted.py
RENAMED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|