groundworkers 0.5.0__tar.gz → 0.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {groundworkers-0.5.0 → groundworkers-0.6.0}/PKG-INFO +2 -2
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/development/plugins.md +3 -3
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/services/vocab.md +34 -16
- {groundworkers-0.5.0 → groundworkers-0.6.0}/pyproject.toml +7 -2
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/adapters/omop_emb.py +12 -2
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/app.py +48 -9
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/databases.py +129 -21
- groundworkers-0.6.0/src/groundworkers/application/setup/performance_maintenance.py +239 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/runtime_setup.py +0 -1
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/base/errors.py +1 -0
- groundworkers-0.6.0/src/groundworkers/base/sql.py +165 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/plugins.py +77 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/mapping.py +305 -58
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/vocab.py +325 -100
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tools/embedding_tools.py +14 -2
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tools/mapping_tools.py +14 -2
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/pages/setup.py +31 -13
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/presenters/database.py +6 -5
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/presenters/performance.py +71 -27
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/routes.py +1 -1
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/wizards/performance_maintenance.py +44 -11
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_embedding_tools.py +52 -4
- groundworkers-0.6.0/tests/unit/test_mapping_context.py +103 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_mapping_service.py +142 -6
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_mapping_tools.py +11 -0
- groundworkers-0.6.0/tests/unit/test_normalized_index.py +201 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_performance.py +16 -6
- groundworkers-0.6.0/tests/unit/test_performance_maintenance_wizard.py +25 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_setup_databases.py +1 -3
- groundworkers-0.6.0/tests/unit/test_vocab_service.py +328 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/uv.lock +30 -25
- groundworkers-0.5.0/src/groundworkers/application/setup/performance_maintenance.py +0 -151
- groundworkers-0.5.0/src/groundworkers/base/sql.py +0 -30
- groundworkers-0.5.0/tests/unit/test_vocab_service.py +0 -148
- {groundworkers-0.5.0 → groundworkers-0.6.0}/.githooks/pre-commit +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/.github/CONTRIBUTING.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/.github/ISSUE_TEMPLATE/not_working.yml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/.github/release-drafter.yml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/.github/workflows/ci.yml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/.github/workflows/docs.yml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/.github/workflows/merge.yml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/.github/workflows/publish.yml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/.gitignore +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/.python-version +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/CHANGELOG.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/Makefile +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/README.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/config/docker-compose.yaml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/config/groundworkers.example.toml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/config/groundworkers_db.env +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/adapters/cdm.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/adapters/omop_emb.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/adapters/omop_graph.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/architecture.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/concepts.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/development/extending.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/development/integration.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/from-scratch.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/index.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/js/mermaid-init.js +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/reference/ref_adapters.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/reference/ref_base.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/reference/ref_config.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/reference/ref_services.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/reference/ref_tools.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/services/domain.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/services/graph.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/services/grounding.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/services/mapping.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/services/semantic_projection.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/services/source_planning.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/services/text.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/static/01_create_config.png +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/static/02_empty_config.png +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/static/03_config_wizard.png +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/static/04_test_cnx.png +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/static/04_warnings.png +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/static/05_graph_setup.png +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/static/06_load_predicates.png +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/static/07_vector_store.png +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/static/08_register_embedding.png +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/static/10_setup_complete.png +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/tools/concept.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/tools/domain.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/tools/embedding.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/tools/knowledge.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/tools/mapping.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/tools/overview.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/tools/resolver.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/tools/search.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/tools/semantic_projection.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/tools/source_planning.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/tools/system.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/tools/text.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/usage/configuration.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/usage/installation.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/usage/integrations.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/usage/setup_journey.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/docs/usage/testing.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/mkdocs.yaml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/_env.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/adapters/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/adapters/cdm.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/adapters/llm.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/adapters/omop_graph.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/configuration.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/configuration_provider.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/embedding_artifacts.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/embedding_capability.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/embedding_coverage.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/embedding_population.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/embedding_reconciliation.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/embedding_registry.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/embedding_setup.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/graph_maintenance.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/integration.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/maintenance_runs.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/model_inventory.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/models.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/plugin_configuration.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/base/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/base/concept_payload.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/base/domain_names.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/base/results.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/base/server.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/bootstrap.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/config/README +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/config/predicate_classification.csv +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/config/predicate_mapping.csv +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/config.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/knowledge/packs/core/criteria-gate-yesno/guidance.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/knowledge/packs/core/criteria-gate-yesno/manifest.yaml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/knowledge/packs/core/diagnosis-role-modifier/guidance.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/knowledge/packs/core/diagnosis-role-modifier/manifest.yaml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/knowledge/packs/core/domain-routing/guidance.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/knowledge/packs/core/domain-routing/manifest.yaml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/knowledge/packs/core/domain-routing/rules.yaml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/knowledge/packs/core/drug-concept-granularity/guidance.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/knowledge/packs/core/drug-concept-granularity/manifest.yaml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/knowledge/packs/core/drug-concept-granularity/rules.yaml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/knowledge/packs/core/history-of-event/guidance.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/knowledge/packs/core/history-of-event/manifest.yaml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/knowledge/packs/core/standard-concept-preference/guidance.md +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/knowledge/packs/core/standard-concept-preference/manifest.yaml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/knowledge/packs/core/standard-concept-preference/rules.yaml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/server.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/domain.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/graph.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/grounding.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/knowledge/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/knowledge/catalogue.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/knowledge/models.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/semantic_projection/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/semantic_projection/definitions.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/semantic_projection/models.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/semantic_projection/service.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/assisted.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/canonical_headers.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/classifier.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/decomposer.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/decomposers/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/decomposers/csv_.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/decomposers/ddl_.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/decomposers/docx_.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/decomposers/json_.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/decomposers/pdf_.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/decomposers/xlsx_.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/decomposers/xml_.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/detector.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/models.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/normalisation.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/provenance.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/router.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/serialisation.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/service.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/source_profiles/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/source_profiles/base.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/source_profiles/redcap.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/source_profiles/registry.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/source_planning/warnings.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/text/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/text/models.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/text/prompts.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/services/text/service.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tools/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tools/concept_tools.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tools/domain_tools.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tools/knowledge_tools.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tools/resolver_tools.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tools/search_tools.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tools/semantic_projection_tools.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tools/source_planning_tools.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tools/system_tools.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tools/text_tools.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/transports/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/transports/rest/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/transports/rest/api.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/transports/rest/models.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/app.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/pages/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/pages/base.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/pages/plugin_config.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/pages/plugin_setup.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/presenters/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/presenters/base.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/presenters/chat.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/presenters/configuration.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/presenters/embeddings.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/presenters/graph.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/presenters/llm_provider.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/presenters/overview.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/presenters/runs.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/state.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/wizards/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/wizards/config_location.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/wizards/embedding_store.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/wizards/embeddings.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/wizards/graph_maintenance.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/tui/wizards/plugin_setup.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/conftest.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/integration/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/integration/test_omop_emb_adapter.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/integration/test_omop_graph_adapter.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/support/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/support/postgres_ci_config.toml +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/support/seed_postgres_fixture.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/support/stack_config.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/test_bootstrap_config.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/test_rest_api.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/test_server_registry.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/__init__.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_concept_payload.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_concept_tools.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_config_location_wizard.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_configuration_provider.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_domain_names.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_domain_tools.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_embedding_population_scope.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_embedding_reconciliation.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_embedding_registry_step.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_env_bootstrap.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_error_boundary.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_graph_maintenance.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_graph_maintenance_wizard.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_graph_service.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_graph_status.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_grounding_constraints.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_grounding_service.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_knowledge_catalogue.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_llm_adapter.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_maintenance_runs_phase2.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_migration_boundaries.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_omop_emb_adapter.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_omop_emb_setup_config.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_omop_graph_adapter.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_phase3_overview.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_plugin_configuration.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_plugins.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_provider_fields.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_schema_resolution.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_semantic_projection_service.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_semantic_projection_tools.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_setup_artifacts_and_coverage.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_setup_configuration.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_setup_configuration_section.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_setup_embeddings.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_setup_provider_conformance.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_setup_runtime.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_setup_truth.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_setup_write_flows.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_source_planning_classifier.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_source_planning_decomposer.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_source_planning_detector.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_source_planning_models.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_source_planning_normalisation.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_source_planning_router.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_source_planning_service.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_source_planning_tools.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_stack_config_fixtures.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_system_tools.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_text_service.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_text_tools.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_tui_app.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_tui_state.py +0 -0
- {groundworkers-0.5.0 → groundworkers-0.6.0}/tests/unit/test_vector_store_setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: groundworkers
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
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
|
|
@@ -36,7 +36,7 @@ Requires-Dist: pytest>=9.0.3; extra == 'dev'
|
|
|
36
36
|
Requires-Dist: requests>=2.33.0; extra == 'dev'
|
|
37
37
|
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
38
38
|
Requires-Dist: textual>=0.60; extra == 'dev'
|
|
39
|
-
Requires-Dist: tornado>=6.5.
|
|
39
|
+
Requires-Dist: tornado>=6.5.9; extra == 'dev'
|
|
40
40
|
Requires-Dist: ty>=0.0.59; extra == 'dev'
|
|
41
41
|
Provides-Extra: docx
|
|
42
42
|
Requires-Dist: python-docx>=1.0; extra == 'docx'
|
|
@@ -76,12 +76,12 @@ The concept-embedding row and the free-text-embedding row look similar but are n
|
|
|
76
76
|
|
|
77
77
|
### Assumptions this table is making
|
|
78
78
|
|
|
79
|
-
- **Vocabulary only, so far.** Every example above reads OMOP *vocabulary* i.e. operations over concepts, concept relationships, concept embeddings. Nothing about `context.cdm_engine` restricts a plugin to vocabulary tables specifically, however. These tables are available over the same CDM connection groundworkers itself uses, and a plugin *could* query clinical/person-level tables (`condition_occurrence`, `drug_exposure`, etc.) through it just as easily, subject to whatever environmental and procedural access controls actually govern that data.
|
|
80
|
-
- **
|
|
79
|
+
- **Vocabulary only, so far.** Every example above reads OMOP *vocabulary* i.e. operations over concepts, concept relationships, concept embeddings, or the host's shared grounding cascade. Nothing about `context.cdm_engine` restricts a plugin to vocabulary tables specifically, however. These tables are available over the same CDM connection groundworkers itself uses, and a plugin *could* query clinical/person-level tables (`condition_occurrence`, `drug_exposure`, etc.) through it just as easily, subject to whatever environmental and procedural access controls actually govern that data.
|
|
80
|
+
- **Shared mapping access.** `PluginContext.mapping_context` is an optional,read-only capability façade over the host's mapping and grounding policy. Use its operations when a plugin needs candidate bundles, text grounding, or concept context. `concept_shared_standard_ancestor(concept_ids, max_depth=5)` returns the unique nearest shared standard ancestor when one exists; raise `max_depth` to widen the hierarchy search. The underlying vocabulary, graph, and service objects are deliberately not exposed. Do not construct a second `omop-graph` or vocabulary adapter from `context.cdm_engine`. The field is `None` only when the host did not compose services for the plugin context.
|
|
81
81
|
|
|
82
82
|
## Build and register a new plugin
|
|
83
83
|
|
|
84
|
-
`build()` receives `PluginContext`. The context provides the resolved CDM database and engine, vector store, shared lazy model backend factories, and a deliberately narrow resolver for independent named OA resources. Return `None` when a prerequisite is unavailable. Groundworkers then keeps serving its core capabilities and reports why the plugin was not activated.
|
|
84
|
+
`build()` receives `PluginContext`. The context provides the resolved CDM database and engine, vector store, shared lazy model backend factories, the optional read-only `mapping_context`, and a deliberately narrow resolver for independent named OA resources. Return `None` when a prerequisite is unavailable. Groundworkers then keeps serving its core capabilities and reports why the plugin was not activated.
|
|
85
85
|
|
|
86
86
|
`register()` adds MCP tools, prompts, or resources against the state returned by `build()`. The shared MCP registration wrapper supplies the same safe error translation used by core tools. Code-defined prompts can continue to derive their arguments from the callable signature. A plugin publishing data-defined prompts can pass explicit string metadata with `PromptArgument`; this keeps dynamic pack fields out of Python closure signatures:
|
|
87
87
|
|
|
@@ -48,11 +48,29 @@ vocab.search_normalized(
|
|
|
48
48
|
vocabulary_id: str | None = None,
|
|
49
49
|
standard_only: bool = False,
|
|
50
50
|
limit: int = 20,
|
|
51
|
+
allow_unindexed_scan: bool = False,
|
|
51
52
|
) -> list[ConceptMatch]
|
|
52
53
|
```
|
|
53
54
|
|
|
54
55
|
Normalized search: lowercases and strips punctuation from both the query and concept names before matching. Catches common surface-form differences (abbreviations, spacing, case) that exact search misses without the false-positive risk of full-text search.
|
|
55
56
|
|
|
57
|
+
#### Why this one needs a selective filter
|
|
58
|
+
|
|
59
|
+
Comparison is on a *computed* expression, so an ordinary index on `concept_name` cannot serve it. Without a selective filter the planner scans every concept. Measured against a 1.7M-concept CDM:
|
|
60
|
+
|
|
61
|
+
| Call | Time |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| unconstrained | 40s |
|
|
64
|
+
| `domain='Drug'` | 25s |
|
|
65
|
+
| `vocabulary_id='MedDRA'` | 0.4s |
|
|
66
|
+
|
|
67
|
+
So the call is **refused** unless one of the following holds:
|
|
68
|
+
|
|
69
|
+
- `vocabulary_id` or `parent_ids` is given. Note that `domain` does **not** count — it looks like a narrowing filter but the largest domains hold millions of concepts, which is why `domain='Drug'` still took 25s.
|
|
70
|
+
- `allow_unindexed_scan=True`, which accepts the scan deliberately.
|
|
71
|
+
|
|
72
|
+
It raises rather than warning because the unindexed query does not fail — it succeeds slowly, so a warning gets absorbed into a caller that merely looks sluggish.
|
|
73
|
+
|
|
56
74
|
### `search_fulltext`
|
|
57
75
|
|
|
58
76
|
```python
|
|
@@ -79,32 +97,32 @@ Synonym FTS is included when the `concept_synonym_name_tsvector` sidecar is also
|
|
|
79
97
|
```python
|
|
80
98
|
vocab.navigate_to_standard(
|
|
81
99
|
concept_ids: list[int],
|
|
82
|
-
) -> list[
|
|
100
|
+
) -> list[ConceptMappingResult]
|
|
83
101
|
```
|
|
84
102
|
|
|
85
103
|
Batch navigation: given a list of concept IDs (which may be non-standard), returns their standard OMOP equivalents by following `"Maps to"` relationship edges. Two queries total regardless of input list size.
|
|
86
104
|
|
|
87
|
-
Concepts that are already standard are returned as self-mappings (`relationship_id="self"`). Concepts with no `"Maps to"` edge
|
|
105
|
+
Concepts that are already standard are returned as self-mappings (`relationship_id="self"`). Concepts with no `"Maps to"` edge have an empty `targets` list. Concept IDs not found in the vocabulary are silently omitted.
|
|
88
106
|
|
|
89
107
|
### `navigate_to_value`
|
|
90
108
|
|
|
91
109
|
```python
|
|
92
110
|
vocab.navigate_to_value(
|
|
93
111
|
concept_ids: list[int],
|
|
94
|
-
) -> list[
|
|
112
|
+
) -> list[ConceptMappingResult]
|
|
95
113
|
```
|
|
96
114
|
|
|
97
|
-
Same batch navigation pattern as `navigate_to_standard`, but follows `"Maps to value"` relationship edges. Used for value-domain mapping in OMOP measurement and observation workflows.
|
|
115
|
+
Same batch navigation pattern as `navigate_to_standard`, but follows `"Maps to value"` relationship edges. Used for value-domain mapping in OMOP measurement and observation workflows. Results use the same `ConceptMappingResult` shape, with targets in `targets`.
|
|
98
116
|
|
|
99
117
|
### `navigate_to_unit`
|
|
100
118
|
|
|
101
119
|
```python
|
|
102
120
|
vocab.navigate_to_unit(
|
|
103
121
|
concept_ids: list[int],
|
|
104
|
-
) -> list[
|
|
122
|
+
) -> list[ConceptMappingResult]
|
|
105
123
|
```
|
|
106
124
|
|
|
107
|
-
Batch navigation following `"Maps to unit"` relationship edges.
|
|
125
|
+
Batch navigation following `"Maps to unit"` relationship edges. Results use the same `ConceptMappingResult` shape, with targets in `targets`.
|
|
108
126
|
|
|
109
127
|
## Return types
|
|
110
128
|
|
|
@@ -126,15 +144,16 @@ class ConceptMatch:
|
|
|
126
144
|
ts_rank: float | None # only set by search_fulltext
|
|
127
145
|
```
|
|
128
146
|
|
|
129
|
-
### `
|
|
147
|
+
### `ConceptMappingResult`
|
|
130
148
|
|
|
131
149
|
```python
|
|
132
150
|
@dataclass
|
|
133
|
-
class
|
|
151
|
+
class ConceptMappingResult:
|
|
134
152
|
source_concept_id: int
|
|
135
153
|
source_concept_name: str
|
|
136
154
|
source_standard_concept: bool
|
|
137
|
-
|
|
155
|
+
source_classification_concept: bool
|
|
156
|
+
targets: list[MappedConcept]
|
|
138
157
|
```
|
|
139
158
|
|
|
140
159
|
### `MappedConcept`
|
|
@@ -142,20 +161,19 @@ class StandardMapping:
|
|
|
142
161
|
```python
|
|
143
162
|
@dataclass
|
|
144
163
|
class MappedConcept:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
vocabulary_id: str
|
|
148
|
-
domain_id: str
|
|
149
|
-
concept_class_id: str
|
|
150
|
-
relationship_id: str # "Maps to", "Maps to value", "Maps to unit", or "self"
|
|
164
|
+
concept: dict[str, Any] # concept payload, including identity and quality fields
|
|
165
|
+
relationship_id: str # "Maps to", "Maps to value", "Maps to unit", or "self"
|
|
151
166
|
```
|
|
152
167
|
|
|
168
|
+
The direct Python API uses `targets` for all three navigation methods. MCP serialization names that list `standard_concepts` for `navigate_to_standard` and `related_concepts` for value and unit navigation.
|
|
169
|
+
|
|
153
170
|
## Serialization helpers
|
|
154
171
|
|
|
155
172
|
`VocabService` provides `serialise_*` helpers that convert the typed return objects to plain dicts suitable for MCP tool responses:
|
|
156
173
|
|
|
157
174
|
- `serialise_concept_match(match: ConceptMatch) -> dict`
|
|
158
|
-
- `serialise_standard_mapping(mapping:
|
|
175
|
+
- `serialise_standard_mapping(mapping: ConceptMappingResult) -> dict`
|
|
176
|
+
- `serialise_related_concept_mapping(mapping: ConceptMappingResult) -> dict`
|
|
159
177
|
|
|
160
178
|
The search MCP tools in `search_tools.py` use these helpers to convert service results into the wire format. Direct Python consumers can use the dataclasses directly without serializing.
|
|
161
179
|
|
|
@@ -56,7 +56,7 @@ embedding-faiss = [
|
|
|
56
56
|
dev = [
|
|
57
57
|
"httpx>=0.27,<1",
|
|
58
58
|
"ipython>=8.0",
|
|
59
|
-
"tornado>=6.5.
|
|
59
|
+
"tornado>=6.5.9",
|
|
60
60
|
"pytest>=9.0.3",
|
|
61
61
|
"pytest-cov>=4.0",
|
|
62
62
|
"mypy>=1.8",
|
|
@@ -89,7 +89,12 @@ packages = ["src/groundworkers"]
|
|
|
89
89
|
|
|
90
90
|
[tool.uv]
|
|
91
91
|
cache-keys = [{ file = "pyproject.toml" }, { git = { commit = true, tags = true } }]
|
|
92
|
-
constraint-dependencies = [
|
|
92
|
+
constraint-dependencies = [
|
|
93
|
+
"setuptools>=83.0.0",
|
|
94
|
+
"httpx2>=2.12.0",
|
|
95
|
+
"urllib3>=2.8.0",
|
|
96
|
+
"PyJWT>=2.15.0",
|
|
97
|
+
]
|
|
93
98
|
|
|
94
99
|
[tool.ruff]
|
|
95
100
|
src = ["src", "tests"]
|
|
@@ -162,8 +162,15 @@ class OmopEmbAdapter:
|
|
|
162
162
|
concept_id: int,
|
|
163
163
|
limit: int,
|
|
164
164
|
model_name: str | None,
|
|
165
|
+
standard_only: bool = True,
|
|
166
|
+
active_only: bool = True,
|
|
165
167
|
) -> dict[str, Any]:
|
|
166
|
-
"""Return stored-vector neighbours without calling the model provider.
|
|
168
|
+
"""Return stored-vector neighbours without calling the model provider.
|
|
169
|
+
|
|
170
|
+
The pgvector HNSW index is partial on ``is_standard AND is_valid``. This
|
|
171
|
+
method defaults to that indexed set. Pass ``standard_only=False`` to include
|
|
172
|
+
non-standard neighbours, at the cost of a full scan.
|
|
173
|
+
"""
|
|
167
174
|
|
|
168
175
|
record = self._resolve_model_record(model_name)
|
|
169
176
|
reader = self._build_reader(record)
|
|
@@ -183,7 +190,10 @@ class OmopEmbAdapter:
|
|
|
183
190
|
vector = np.asarray(vectors[concept_id], dtype=float).reshape(1, -1)
|
|
184
191
|
raw = reader.get_nearest_concepts(
|
|
185
192
|
query_embedding=vector,
|
|
186
|
-
concept_filter=self._build_concept_filter(
|
|
193
|
+
concept_filter=self._build_concept_filter(
|
|
194
|
+
standard_only=standard_only,
|
|
195
|
+
active_only=active_only,
|
|
196
|
+
),
|
|
187
197
|
k=limit + 1,
|
|
188
198
|
faiss_index_config=record.index_config,
|
|
189
199
|
)
|
|
@@ -15,6 +15,7 @@ from groundworkers.config import AppConfig
|
|
|
15
15
|
from groundworkers.plugins import (
|
|
16
16
|
GroundworkersPlugin,
|
|
17
17
|
GroundworkersPluginReadiness,
|
|
18
|
+
MappingContext,
|
|
18
19
|
PluginConfigResolver,
|
|
19
20
|
PluginContext,
|
|
20
21
|
PluginReadinessResult,
|
|
@@ -52,8 +53,12 @@ class Adapters:
|
|
|
52
53
|
# _build_plugin_context) so a plugin reuses the one backend connection
|
|
53
54
|
# instead of opening a second one. None when the corresponding backend
|
|
54
55
|
# is unconfigured, same gating as the adapters above.
|
|
55
|
-
embedding_backend_factory: Callable[[], Any] | None = field(
|
|
56
|
-
|
|
56
|
+
embedding_backend_factory: Callable[[], Any] | None = field(
|
|
57
|
+
default=None, repr=False
|
|
58
|
+
)
|
|
59
|
+
embedding_model_backend_factory: Callable[[], Any] | None = field(
|
|
60
|
+
default=None, repr=False
|
|
61
|
+
)
|
|
57
62
|
chat_backend_factory: Callable[[], Any] | None = field(default=None, repr=False)
|
|
58
63
|
|
|
59
64
|
|
|
@@ -104,7 +109,9 @@ def build_adapters(config: AppConfig) -> Adapters:
|
|
|
104
109
|
nonlocal shared_model_backend
|
|
105
110
|
if shared_model_backend is None:
|
|
106
111
|
if resolved_model is None:
|
|
107
|
-
raise RuntimeError(
|
|
112
|
+
raise RuntimeError(
|
|
113
|
+
"No embedding model is configured for Groundworkers."
|
|
114
|
+
)
|
|
108
115
|
from omop_llm import build_model_backend_from_resolved
|
|
109
116
|
|
|
110
117
|
shared_model_backend = build_model_backend_from_resolved(resolved_model)
|
|
@@ -180,9 +187,14 @@ def build_adapters(config: AppConfig) -> Adapters:
|
|
|
180
187
|
def build_services(config: AppConfig, adapters: Adapters) -> Services:
|
|
181
188
|
services = Services()
|
|
182
189
|
assisted_classifier = None
|
|
183
|
-
if
|
|
190
|
+
if (
|
|
191
|
+
adapters.llm is not None
|
|
192
|
+
and config.groundworkers.source_planning_llm_assisted_enabled
|
|
193
|
+
):
|
|
184
194
|
assisted_classifier = AssistedColumnRoleClassifier(adapters.llm)
|
|
185
|
-
services.source_planning = SourcePlanningService(
|
|
195
|
+
services.source_planning = SourcePlanningService(
|
|
196
|
+
assisted_classifier=assisted_classifier
|
|
197
|
+
)
|
|
186
198
|
if adapters.omop_graph is not None:
|
|
187
199
|
# cdm is optional; only the classified-edge traversals
|
|
188
200
|
# (concept_associations / concept_extended_inheritance) require it.
|
|
@@ -206,7 +218,27 @@ def build_services(config: AppConfig, adapters: Adapters) -> Services:
|
|
|
206
218
|
return services
|
|
207
219
|
|
|
208
220
|
|
|
209
|
-
def
|
|
221
|
+
def build_mapping_context(
|
|
222
|
+
config: AppConfig,
|
|
223
|
+
*,
|
|
224
|
+
adapters: Adapters | None = None,
|
|
225
|
+
services: Services | None = None,
|
|
226
|
+
) -> MappingContext:
|
|
227
|
+
"""Build the mapping capabilities shared by hosted and standalone plugins."""
|
|
228
|
+
|
|
229
|
+
shared_adapters = adapters or build_adapters(config)
|
|
230
|
+
shared_services = services or build_services(config, shared_adapters)
|
|
231
|
+
return MappingContext(
|
|
232
|
+
mapping_service=shared_services.mapping,
|
|
233
|
+
grounding_service=shared_services.grounding,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _build_plugin_context(
|
|
238
|
+
config: AppConfig,
|
|
239
|
+
adapters: Adapters,
|
|
240
|
+
services: Services | None = None,
|
|
241
|
+
) -> PluginContext:
|
|
210
242
|
"""Assemble the resolved handles every plugin is given.
|
|
211
243
|
|
|
212
244
|
`cdm_database`/`vector_store` come straight off `AppConfig`, already
|
|
@@ -223,6 +255,11 @@ def _build_plugin_context(config: AppConfig, adapters: Adapters) -> PluginContex
|
|
|
223
255
|
embedding_backend_factory=adapters.embedding_backend_factory,
|
|
224
256
|
embedding_model_backend_factory=adapters.embedding_model_backend_factory,
|
|
225
257
|
chat_backend_factory=adapters.chat_backend_factory,
|
|
258
|
+
mapping_context=(
|
|
259
|
+
build_mapping_context(config, adapters=adapters, services=services)
|
|
260
|
+
if services is not None
|
|
261
|
+
else None
|
|
262
|
+
),
|
|
226
263
|
)
|
|
227
264
|
|
|
228
265
|
|
|
@@ -292,7 +329,8 @@ def verify_plugin_readiness(
|
|
|
292
329
|
summary="This plugin does not expose readiness verification.",
|
|
293
330
|
)
|
|
294
331
|
adapters = build_adapters(config)
|
|
295
|
-
|
|
332
|
+
services = build_services(config, adapters)
|
|
333
|
+
context = _build_plugin_context(config, adapters, services)
|
|
296
334
|
state, issue = _build_plugin(config, context, plugin)
|
|
297
335
|
if state is None:
|
|
298
336
|
return PluginReadinessResult(
|
|
@@ -342,13 +380,14 @@ def load_plugin_readiness(
|
|
|
342
380
|
|
|
343
381
|
def build_application(config: AppConfig) -> GroundworkersApp:
|
|
344
382
|
adapters = build_adapters(config)
|
|
345
|
-
|
|
383
|
+
services = build_services(config, adapters)
|
|
384
|
+
context = _build_plugin_context(config, adapters, services)
|
|
346
385
|
plugin_definitions = tuple(discover_plugins())
|
|
347
386
|
plugins, plugin_issues = _build_plugins(config, context, plugin_definitions)
|
|
348
387
|
return GroundworkersApp(
|
|
349
388
|
config=config,
|
|
350
389
|
adapters=adapters,
|
|
351
|
-
services=
|
|
390
|
+
services=services,
|
|
352
391
|
plugins=plugins,
|
|
353
392
|
plugin_definitions=plugin_definitions,
|
|
354
393
|
plugin_issues=plugin_issues,
|
{groundworkers-0.5.0 → groundworkers-0.6.0}/src/groundworkers/application/setup/databases.py
RENAMED
|
@@ -35,7 +35,10 @@ from groundworkers.application.setup.models import (
|
|
|
35
35
|
DiagnosticSeverity,
|
|
36
36
|
ResourceDiagnostic,
|
|
37
37
|
)
|
|
38
|
-
from groundworkers.base.sql import
|
|
38
|
+
from groundworkers.base.sql import (
|
|
39
|
+
normalization_key_expression_sql,
|
|
40
|
+
quote_identifier,
|
|
41
|
+
)
|
|
39
42
|
from groundworkers.config import GroundworkersConfig
|
|
40
43
|
|
|
41
44
|
CDM_TABLES = (
|
|
@@ -88,6 +91,23 @@ GROUNDWORKERS_TRIGRAM_INDEX_TARGETS = (
|
|
|
88
91
|
("idx_concept_synonym_lower_name_trgm",),
|
|
89
92
|
),
|
|
90
93
|
)
|
|
94
|
+
# (table, column, label, index name). The indexed K expression is not restated
|
|
95
|
+
# here: it is rendered from base.sql.normalization_key_expression, the same
|
|
96
|
+
# builder search_normalized queries with, so it cannot drift.
|
|
97
|
+
GROUNDWORKERS_NORMALIZED_INDEX_TARGETS = (
|
|
98
|
+
(
|
|
99
|
+
"concept",
|
|
100
|
+
"concept_name",
|
|
101
|
+
"concept normalized name",
|
|
102
|
+
"idx_concept_name_normkey",
|
|
103
|
+
),
|
|
104
|
+
(
|
|
105
|
+
"concept_synonym",
|
|
106
|
+
"concept_synonym_name",
|
|
107
|
+
"concept_synonym normalized name",
|
|
108
|
+
"idx_concept_synonym_name_normkey",
|
|
109
|
+
),
|
|
110
|
+
)
|
|
91
111
|
# Taken from omop-emb rather than restated: these describe tables omop-emb owns
|
|
92
112
|
# and creates, so a column added there must not need a matching edit here to stay
|
|
93
113
|
# checkable.
|
|
@@ -111,6 +131,9 @@ def resolve_database_targets(
|
|
|
111
131
|
cdm = resolver.resolve_database(groundworkers.cdm_db)
|
|
112
132
|
if not isinstance(cdm, ResolvedCDMDatabase):
|
|
113
133
|
return ()
|
|
134
|
+
cdm_schema = cdm.schema_name or (
|
|
135
|
+
"main" if cdm.connection.url.startswith("sqlite") else "public"
|
|
136
|
+
)
|
|
114
137
|
embedding_target: DatabaseTarget | None = None
|
|
115
138
|
expected_embedding_model_name: str | None = None
|
|
116
139
|
embedding_safe_url: str | None = None
|
|
@@ -146,7 +169,7 @@ def resolve_database_targets(
|
|
|
146
169
|
database_entry_name=cdm.name,
|
|
147
170
|
connection_name=cdm.connection.name,
|
|
148
171
|
safe_url=cdm.connection.safe_url,
|
|
149
|
-
cdm_schema=
|
|
172
|
+
cdm_schema=cdm_schema,
|
|
150
173
|
vocabulary_schema=cdm.vocab_schema,
|
|
151
174
|
connection_url=cdm.connection.url,
|
|
152
175
|
role="cdm",
|
|
@@ -157,7 +180,7 @@ def resolve_database_targets(
|
|
|
157
180
|
database_entry_name=cdm.name,
|
|
158
181
|
connection_name=cdm.connection.name,
|
|
159
182
|
safe_url=cdm.connection.safe_url,
|
|
160
|
-
cdm_schema=
|
|
183
|
+
cdm_schema=cdm_schema,
|
|
161
184
|
vocabulary_schema=cdm.vocab_schema,
|
|
162
185
|
connection_url=cdm.connection.url,
|
|
163
186
|
role="graph",
|
|
@@ -168,7 +191,7 @@ def resolve_database_targets(
|
|
|
168
191
|
database_entry_name=cdm.name,
|
|
169
192
|
connection_name=cdm.connection.name,
|
|
170
193
|
safe_url=cdm.connection.safe_url,
|
|
171
|
-
cdm_schema=
|
|
194
|
+
cdm_schema=cdm_schema,
|
|
172
195
|
vocabulary_schema=cdm.vocab_schema,
|
|
173
196
|
connection_url=cdm.connection.url,
|
|
174
197
|
role="groundworkers",
|
|
@@ -437,6 +460,11 @@ def _groundworkers_diagnostics(
|
|
|
437
460
|
)
|
|
438
461
|
)
|
|
439
462
|
|
|
463
|
+
if connection.dialect.name == "postgresql":
|
|
464
|
+
diagnostics.extend(
|
|
465
|
+
_normalized_index_diagnostics(connection, schema, existing_tables)
|
|
466
|
+
)
|
|
467
|
+
|
|
440
468
|
diagnostics.extend(
|
|
441
469
|
_groundworkers_embedding_model_diagnostics(
|
|
442
470
|
target,
|
|
@@ -478,9 +506,7 @@ def _groundworkers_embedding_model_diagnostics(
|
|
|
478
506
|
"Groundworkers grounding model could not be checked because the omop-emb model registry table is missing.",
|
|
479
507
|
),
|
|
480
508
|
)
|
|
481
|
-
rows = _embedding_registry_rows(
|
|
482
|
-
connection, schema=target.embedding_schema
|
|
483
|
-
)
|
|
509
|
+
rows = _embedding_registry_rows(connection, schema=target.embedding_schema)
|
|
484
510
|
except Exception as exc:
|
|
485
511
|
# Broad except: reported as a redacted warning.
|
|
486
512
|
failure = classify_connection_error(exc)
|
|
@@ -779,6 +805,83 @@ def _functional_lower_index_exists(
|
|
|
779
805
|
return any(index.get("name") in aliases for index in indexes)
|
|
780
806
|
|
|
781
807
|
|
|
808
|
+
def _normalized_index_diagnostics(
|
|
809
|
+
connection: Connection,
|
|
810
|
+
schema: str | None,
|
|
811
|
+
existing_tables: set[str],
|
|
812
|
+
) -> tuple[ResourceDiagnostic, ...]:
|
|
813
|
+
targets = tuple(
|
|
814
|
+
target
|
|
815
|
+
for target in GROUNDWORKERS_NORMALIZED_INDEX_TARGETS
|
|
816
|
+
if target[0] in existing_tables
|
|
817
|
+
)
|
|
818
|
+
if not targets:
|
|
819
|
+
return ()
|
|
820
|
+
statuses = tuple(
|
|
821
|
+
(label, normalized_index_status(connection, schema, table, column))
|
|
822
|
+
for table, column, label, _name in targets
|
|
823
|
+
)
|
|
824
|
+
unchecked = tuple(label for label, present in statuses if present is None)
|
|
825
|
+
if unchecked:
|
|
826
|
+
return (
|
|
827
|
+
_info(
|
|
828
|
+
"normalized_indexes_unchecked",
|
|
829
|
+
"Normalized-name indexes unchecked because PostgreSQL index listings "
|
|
830
|
+
f"could not be read: {', '.join(unchecked)}.",
|
|
831
|
+
),
|
|
832
|
+
)
|
|
833
|
+
missing = tuple(label for label, present in statuses if not present)
|
|
834
|
+
if missing:
|
|
835
|
+
return (
|
|
836
|
+
_warning(
|
|
837
|
+
"normalized_indexes_missing",
|
|
838
|
+
"Optional normalized-name indexes are missing or out of date: "
|
|
839
|
+
f"{', '.join(missing)}. Groundworkers can still run, but unbounded "
|
|
840
|
+
"normalized searches scan the whole vocabulary table.",
|
|
841
|
+
),
|
|
842
|
+
)
|
|
843
|
+
return (
|
|
844
|
+
_info(
|
|
845
|
+
"normalized_indexes_present",
|
|
846
|
+
"Optional normalized-name indexes are present: "
|
|
847
|
+
f"{', '.join(label for _table, _column, label, _name in targets)}.",
|
|
848
|
+
),
|
|
849
|
+
)
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
def normalized_index_matches(
|
|
853
|
+
connection: Connection,
|
|
854
|
+
schema: str | None,
|
|
855
|
+
table_name: str,
|
|
856
|
+
column_name: str,
|
|
857
|
+
) -> bool:
|
|
858
|
+
"""True when an index on *table_name* covers the shared K expression."""
|
|
859
|
+
return normalized_index_status(connection, schema, table_name, column_name) is True
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
def normalized_index_status(
|
|
863
|
+
connection: Connection,
|
|
864
|
+
schema: str | None,
|
|
865
|
+
table_name: str,
|
|
866
|
+
column_name: str,
|
|
867
|
+
) -> bool | None:
|
|
868
|
+
"""Return True/False for the K index, or None when listing failed."""
|
|
869
|
+
definitions = _postgres_index_definitions(connection, schema, table_name)
|
|
870
|
+
if definitions is None:
|
|
871
|
+
return None
|
|
872
|
+
expected = _squash_index_sql(
|
|
873
|
+
normalization_key_expression_sql(connection, column_name)
|
|
874
|
+
)
|
|
875
|
+
return any(expected in _squash_index_sql(indexdef) for indexdef in definitions)
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
def _squash_index_sql(sql: str) -> str:
|
|
879
|
+
# pg_get_indexdef adds ::text casts and parentheses the compiled expression
|
|
880
|
+
# lacks; drop both, plus whitespace and identifier quotes, on both sides.
|
|
881
|
+
squashed = sql.lower().replace("::text", "")
|
|
882
|
+
return "".join(ch for ch in squashed if ch not in ' \t\n"()')
|
|
883
|
+
|
|
884
|
+
|
|
782
885
|
def _trigram_lower_index_exists(
|
|
783
886
|
connection: Connection,
|
|
784
887
|
inspector: Any,
|
|
@@ -811,6 +914,21 @@ def _postgres_lower_index_exists(
|
|
|
811
914
|
*,
|
|
812
915
|
required_fragment: str | None = None,
|
|
813
916
|
) -> bool:
|
|
917
|
+
return any(
|
|
918
|
+
_index_defines_lower_expression(
|
|
919
|
+
indexdef,
|
|
920
|
+
column_name,
|
|
921
|
+
required_fragment=required_fragment,
|
|
922
|
+
)
|
|
923
|
+
for indexdef in (_postgres_index_definitions(connection, schema, table_name) or ())
|
|
924
|
+
)
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
def _postgres_index_definitions(
|
|
928
|
+
connection: Connection,
|
|
929
|
+
schema: str | None,
|
|
930
|
+
table_name: str,
|
|
931
|
+
) -> tuple[str, ...] | None:
|
|
814
932
|
try:
|
|
815
933
|
rows = connection.execute(
|
|
816
934
|
text(
|
|
@@ -824,16 +942,9 @@ def _postgres_lower_index_exists(
|
|
|
824
942
|
{"schema": schema or "public", "table_name": table_name},
|
|
825
943
|
)
|
|
826
944
|
except Exception:
|
|
827
|
-
# Broad except: fall back to reflected
|
|
828
|
-
return
|
|
829
|
-
return
|
|
830
|
-
_index_defines_lower_expression(
|
|
831
|
-
str(row[0]),
|
|
832
|
-
column_name,
|
|
833
|
-
required_fragment=required_fragment,
|
|
834
|
-
)
|
|
835
|
-
for row in rows
|
|
836
|
-
)
|
|
945
|
+
# Broad except: callers fall back to reflected names or report absence.
|
|
946
|
+
return None
|
|
947
|
+
return tuple(str(row[0]) for row in rows)
|
|
837
948
|
|
|
838
949
|
|
|
839
950
|
def _index_defines_lower_expression(
|
|
@@ -903,10 +1014,7 @@ def _column_has_null_values(
|
|
|
903
1014
|
column = quote_identifier(connection, column_name)
|
|
904
1015
|
return bool(
|
|
905
1016
|
connection.execute(
|
|
906
|
-
text(
|
|
907
|
-
f"SELECT EXISTS (SELECT 1 FROM {qualified} "
|
|
908
|
-
f"WHERE {column} IS NULL)"
|
|
909
|
-
)
|
|
1017
|
+
text(f"SELECT EXISTS (SELECT 1 FROM {qualified} WHERE {column} IS NULL)")
|
|
910
1018
|
).scalar()
|
|
911
1019
|
)
|
|
912
1020
|
|