deriva-cli 0.8.3__tar.gz → 0.8.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/PKG-INFO +3 -3
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/grafeo/__init__.py +8 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/grafeo/manager.py +51 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/graph/manager.py +3 -3
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/graph/metamodel.py +43 -1
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/manager.py +81 -7
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/model_registry.py +20 -3
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/models.py +7 -3
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/retry.py +5 -1
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/translate.py +1 -2
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/repository/manager.py +155 -12
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/repository/models.py +2 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/csharp.py +2 -2
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/javascript.py +25 -14
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/python.py +5 -3
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/manager.py +1 -1
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/cli.py +83 -7
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/commands/config.py +41 -1
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/commands/graph.py +1 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/commands/repo.py +51 -4
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/commands/run.py +3 -2
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/file_utils.py +28 -1
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/settings.py +8 -1
- deriva_cli-0.8.4/deriva/common/urls.py +15 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/component_services.py +12 -2
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/element_base.py +1 -1
- deriva_cli-0.8.4/deriva/modules/derivation/quality.py +107 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/joint_consistency.py +28 -16
- deriva_cli-0.8.4/deriva/modules/extraction/coverage.py +126 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/data_domains.py +5 -1
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/directory_classification.py +4 -1
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/edges.py +2 -1
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/__init__.py +5 -2
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/config.py +56 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/extraction.py +115 -16
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/model_configs.py +70 -2
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/ontology.py +30 -16
- deriva_cli-0.8.4/deriva/services/quality.py +47 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/session.py +204 -14
- deriva_cli-0.8.4/deriva/services/storage.py +92 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/pyproject.toml +3 -3
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/pyproject.toml.orig +3 -3
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/LICENSE +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/README.md +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/documentation/Diagram.html +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/documentation/View.html +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/documentation/model.html +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/examples/archimate3_Diagram.xsd +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/examples/archimate3_Model.xsd +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/examples/archimate3_View.xsd +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/schemas/archimate3_Diagram.xsd +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/schemas/archimate3_Model.xsd +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/schemas/archimate3_View.xsd +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-1.xml +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-10.xml +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-2.xml +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-3.xml +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-4.xml +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-5.xml +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-7.xml +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-8.xml +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-9.xml +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/manager.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/models.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/validation.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/views.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/xml_export.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/README.md +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/data/derivation_config.json +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/data/derivation_patterns.json +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/data/extraction_config.json +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/data/file_types.json +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/data/function_models/pcf-8.0-cross-industry.csv +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/data/system_settings.json +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/db_tool.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/manager.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/scripts/7_extraction_params.sql +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/scripts/schema.sql +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/graph/README.md +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/graph/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/graph/cache.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/graph/models.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/README.md +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/cache.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/rate_limiter.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/schemas.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/extract.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/language.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/manager.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/models.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/phrases.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/settings.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/text.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/repository/README.md +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/repository/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/cdk.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/base.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/bms.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/cobol.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/java.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/jcl.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/sql.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/structural.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/models.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/README.md +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/commands/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/progress.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/cache_utils.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/chunking.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/document_reader.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/exceptions.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/json_utils.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/llm_utils.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/logging.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/naming.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/ocel.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/paths.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/schema_utils.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/time_utils.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/timing.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/types.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/consistency.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/conventions.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/cross_repo_analysis.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/deviation.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/fit_analysis.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/model_quality.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/run_consistency.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/semantic_matching.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/stability_analysis.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/types.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/application_component.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/application_interface.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/application_service.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/base.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/business_actor.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/business_event.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/business_function.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/business_object.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/business_process.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/data_object.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/detail_level.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/device.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/function_model.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/levels.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/node.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/prep.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/abstraction_levels.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/base.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/cross_layer.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/duplicate_elements.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/duplicate_relationships.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/graph_relationships.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/joint.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/normalization.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/orphan_elements.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/structural_consistency.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/system_software.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/technology_service.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/__init__.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/base.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/build_modules.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/business_concept.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/classification.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/concept_candidates.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/cooperation.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/data_entities.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/deployable_units.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/deployments.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/directory.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/external_dependency.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/file.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/lifecycles.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/method.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/repository.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/roles.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/step_tags.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/technologies.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/technology_candidates.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/test.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/type_definition.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/about.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/config_models.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/derivation.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/export_bundle.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/export_levels.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/llm_log.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/overfit.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/pipeline.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/trace.py +0 -0
- {deriva_cli-0.8.3 → deriva_cli-0.8.4}/packages/README-deriva-cli.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deriva-cli
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
4
4
|
Summary: Deriva is a research project aimed at enabling digital architects by automating the derivation of enterprise architecture models. Deriva uses dual knowledge graphs, heuristics and LLM's.
|
|
5
5
|
Keywords: archimate,enterprise-architecture,code-analysis,llm,grafeo,fastapi
|
|
6
6
|
Author: S. T. Grond
|
|
@@ -38,8 +38,8 @@ Requires-Dist: spacy==3.8.16
|
|
|
38
38
|
Requires-Dist: ctranslate2==4.8.3
|
|
39
39
|
Requires-Dist: sentencepiece==0.2.2
|
|
40
40
|
Requires-Dist: solvor>=0.6.3
|
|
41
|
-
Requires-Dist: pydantic-ai-slim[openai,anthropic]>=2.
|
|
42
|
-
Requires-Dist: httpx2>=2.
|
|
41
|
+
Requires-Dist: pydantic-ai-slim[openai,anthropic]>=2.57
|
|
42
|
+
Requires-Dist: httpx2>=2.14
|
|
43
43
|
Requires-Dist: lmdb>=1.6
|
|
44
44
|
Requires-Dist: typer>=0.27
|
|
45
45
|
Requires-Dist: tree-sitter-typescript==0.23.2
|
|
@@ -21,9 +21,13 @@ from .manager import (
|
|
|
21
21
|
DEFAULT_DATABASE,
|
|
22
22
|
GrafeoConnection,
|
|
23
23
|
close_database,
|
|
24
|
+
database_bytes,
|
|
25
|
+
database_exists,
|
|
24
26
|
database_file,
|
|
27
|
+
delete_database,
|
|
25
28
|
engine_info,
|
|
26
29
|
get_database,
|
|
30
|
+
list_databases,
|
|
27
31
|
use_database,
|
|
28
32
|
)
|
|
29
33
|
|
|
@@ -31,8 +35,12 @@ __all__ = [
|
|
|
31
35
|
"DEFAULT_DATABASE",
|
|
32
36
|
"GrafeoConnection",
|
|
33
37
|
"close_database",
|
|
38
|
+
"database_bytes",
|
|
39
|
+
"database_exists",
|
|
34
40
|
"database_file",
|
|
41
|
+
"delete_database",
|
|
35
42
|
"engine_info",
|
|
36
43
|
"get_database",
|
|
44
|
+
"list_databases",
|
|
37
45
|
"use_database",
|
|
38
46
|
]
|
|
@@ -27,8 +27,10 @@ Usage:
|
|
|
27
27
|
from __future__ import annotations
|
|
28
28
|
|
|
29
29
|
import logging
|
|
30
|
+
import shutil
|
|
30
31
|
import time
|
|
31
32
|
import weakref
|
|
33
|
+
from pathlib import Path
|
|
32
34
|
from typing import Any
|
|
33
35
|
|
|
34
36
|
from deriva.common.paths import resolve
|
|
@@ -77,6 +79,55 @@ def database_file(key: str) -> str | None:
|
|
|
77
79
|
return str(folder / f"{key}.grafeo")
|
|
78
80
|
|
|
79
81
|
|
|
82
|
+
def _stored_paths(key: str) -> tuple[Path, Path] | None:
|
|
83
|
+
"""The key's database file and its write-ahead log folder, never created (None in memory or for an invalid key)."""
|
|
84
|
+
directory = GrafeoSettings().db_dir
|
|
85
|
+
if not directory or not key or key in (".", "..") or any(sep in key for sep in ("/", "\\")):
|
|
86
|
+
return None
|
|
87
|
+
folder = resolve(directory)
|
|
88
|
+
return folder / f"{key}.grafeo", folder / f"{key}.grafeo.wal"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def database_exists(key: str) -> bool:
|
|
92
|
+
"""Whether the key's database file exists; never creates it or its folder (False in memory or for an invalid key)."""
|
|
93
|
+
paths = _stored_paths(key)
|
|
94
|
+
return paths is not None and paths[0].exists()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def list_databases() -> list[str]:
|
|
98
|
+
"""The keys with a database file in GRAFEO_DB_DIR, sorted (none in memory)."""
|
|
99
|
+
directory = GrafeoSettings().db_dir
|
|
100
|
+
folder = resolve(directory) if directory else None
|
|
101
|
+
if folder is None or not folder.is_dir():
|
|
102
|
+
return []
|
|
103
|
+
return sorted(path.name.removesuffix(".grafeo") for path in folder.glob("*.grafeo") if path.is_file())
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def database_bytes(key: str) -> int:
|
|
107
|
+
"""Bytes the key's database takes on disk: its file and its write-ahead log (0 when it has none)."""
|
|
108
|
+
paths = _stored_paths(key)
|
|
109
|
+
if paths is None:
|
|
110
|
+
return 0
|
|
111
|
+
database, log = paths
|
|
112
|
+
size = database.stat().st_size if database.is_file() else 0
|
|
113
|
+
return size + (sum(f.stat().st_size for f in log.rglob("*") if f.is_file()) if log.is_dir() else 0)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def delete_database(key: str) -> bool:
|
|
117
|
+
"""Delete the key's database file and log; False when it had none. The active database is never deleted (switch
|
|
118
|
+
away first): a query after the delete would create a new, empty file."""
|
|
119
|
+
if key == _db_key:
|
|
120
|
+
raise ValueError(f"The database {key!r} is open; switch to another one before deleting it")
|
|
121
|
+
paths = _stored_paths(key)
|
|
122
|
+
if paths is None or not (paths[0].exists() or paths[1].exists()):
|
|
123
|
+
return False
|
|
124
|
+
database, log = paths
|
|
125
|
+
database.unlink(missing_ok=True)
|
|
126
|
+
if log.is_dir():
|
|
127
|
+
shutil.rmtree(log)
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
|
|
80
131
|
def get_database() -> Any:
|
|
81
132
|
"""Get or open the active GrafeoDB instance (see ``use_database``).
|
|
82
133
|
|
|
@@ -26,7 +26,7 @@ from __future__ import annotations
|
|
|
26
26
|
|
|
27
27
|
import logging
|
|
28
28
|
import re
|
|
29
|
-
from collections.abc import
|
|
29
|
+
from collections.abc import Generator
|
|
30
30
|
from contextlib import contextmanager
|
|
31
31
|
from dataclasses import dataclass, field
|
|
32
32
|
from typing import Any
|
|
@@ -280,7 +280,7 @@ class GraphManager:
|
|
|
280
280
|
raise
|
|
281
281
|
|
|
282
282
|
@contextmanager
|
|
283
|
-
def step_tags(self, step: str, version: int | None = None) ->
|
|
283
|
+
def step_tags(self, step: str, version: int | None = None) -> Generator[None]:
|
|
284
284
|
"""Nodes and edges written inside the block carry the extraction step (``extractStep``) and its version."""
|
|
285
285
|
previous = self._step_tags
|
|
286
286
|
self._step_tags = {"extractStep": step, **({"extractVersion": version} if version is not None else {})}
|
|
@@ -290,7 +290,7 @@ class GraphManager:
|
|
|
290
290
|
self._step_tags = previous
|
|
291
291
|
|
|
292
292
|
@contextmanager
|
|
293
|
-
def recording(self) ->
|
|
293
|
+
def recording(self) -> Generator[GraphWrites]:
|
|
294
294
|
"""The nodes and edges written inside the block (for the run log); failed writes are left out."""
|
|
295
295
|
recorder = GraphWrites()
|
|
296
296
|
self._recorders.append(recorder)
|
|
@@ -13,6 +13,10 @@ from typing import Any
|
|
|
13
13
|
from . import models
|
|
14
14
|
|
|
15
15
|
__all__ = [
|
|
16
|
+
"GROUP_ORDER",
|
|
17
|
+
"NODE_GROUPS",
|
|
18
|
+
"node_description",
|
|
19
|
+
"node_group",
|
|
16
20
|
"get_all_node_classes",
|
|
17
21
|
"get_node_properties",
|
|
18
22
|
"get_relationship_types",
|
|
@@ -36,7 +40,45 @@ def get_all_node_classes() -> dict[str, type]:
|
|
|
36
40
|
return node_classes
|
|
37
41
|
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
# The intermediate ontology's groups (as ArchiMate has its layers): where a node type's evidence comes from
|
|
44
|
+
GROUP_ORDER = ("structure", "code", "business", "deployment")
|
|
45
|
+
NODE_GROUPS = {
|
|
46
|
+
"Repository": "structure",
|
|
47
|
+
"Directory": "structure",
|
|
48
|
+
"File": "structure",
|
|
49
|
+
"Module": "structure",
|
|
50
|
+
"BuildModule": "structure",
|
|
51
|
+
"TypeDefinition": "code",
|
|
52
|
+
"Method": "code",
|
|
53
|
+
"Test": "code",
|
|
54
|
+
"ExternalDependency": "code",
|
|
55
|
+
"Service": "code",
|
|
56
|
+
"BusinessConcept": "business",
|
|
57
|
+
"DataEntity": "business",
|
|
58
|
+
"DataDomain": "business",
|
|
59
|
+
"Lifecycle": "business",
|
|
60
|
+
"LifecycleEvent": "business",
|
|
61
|
+
"Role": "business",
|
|
62
|
+
"DeployableUnit": "deployment",
|
|
63
|
+
"Deployment": "deployment",
|
|
64
|
+
"Environment": "deployment",
|
|
65
|
+
"Software": "deployment",
|
|
66
|
+
"Technology": "deployment",
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def node_group(node_type: str) -> str:
|
|
71
|
+
"""The group of a node type (``other`` for a label the metamodel does not name)."""
|
|
72
|
+
return NODE_GROUPS.get(node_type, "other")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def node_description(node_class: type) -> str:
|
|
76
|
+
"""The first line of a node class's docstring."""
|
|
77
|
+
lines = (node_class.__doc__ or "").strip().splitlines()
|
|
78
|
+
return lines[0].strip() if lines else ""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def get_node_properties(node_class: type[Any]) -> list[str]:
|
|
40
82
|
"""Get all property names for a node class.
|
|
41
83
|
|
|
42
84
|
Args:
|
|
@@ -43,8 +43,9 @@ from typing import Any, TypeVar, cast, overload
|
|
|
43
43
|
|
|
44
44
|
import httpx2
|
|
45
45
|
from pydantic import BaseModel
|
|
46
|
-
from pydantic_ai import Agent
|
|
47
|
-
from pydantic_ai.exceptions import ModelHTTPError
|
|
46
|
+
from pydantic_ai import Agent, capture_run_messages
|
|
47
|
+
from pydantic_ai.exceptions import IncompleteToolCall, ModelHTTPError
|
|
48
|
+
from pydantic_ai.messages import ModelResponse
|
|
48
49
|
from pydantic_ai.settings import ModelSettings
|
|
49
50
|
|
|
50
51
|
from deriva.common import settings
|
|
@@ -53,7 +54,7 @@ from deriva.common.paths import resolve
|
|
|
53
54
|
from deriva.common.settings import LlmSettings
|
|
54
55
|
|
|
55
56
|
from .cache import CacheManager
|
|
56
|
-
from .model_registry import VALID_PROVIDERS, get_pydantic_ai_model, parse_sampling, reasoning_settings
|
|
57
|
+
from .model_registry import VALID_PROVIDERS, default_sampling, get_pydantic_ai_model, parse_sampling, reasoning_settings
|
|
57
58
|
from .models import (
|
|
58
59
|
BenchmarkModelConfig,
|
|
59
60
|
CachedResponse,
|
|
@@ -214,9 +215,10 @@ def load_benchmark_models() -> dict[str, BenchmarkModelConfig]:
|
|
|
214
215
|
api_url=fields["url"],
|
|
215
216
|
api_key=fields["key"],
|
|
216
217
|
api_key_env=fields["key_env"],
|
|
217
|
-
sampling=parse_sampling(fields.get("sampling")),
|
|
218
|
+
sampling=parse_sampling(fields.get("sampling"), provider.lower(), model),
|
|
218
219
|
reasoning=(fields.get("reasoning") or "").strip().lower() or None,
|
|
219
220
|
temperature=float(own) if (own := (fields.get("temperature") or "").strip()) else None,
|
|
221
|
+
context=int(size) if (size := (fields.get("context") or "").strip()) else None,
|
|
220
222
|
)
|
|
221
223
|
except ValueError:
|
|
222
224
|
continue
|
|
@@ -224,6 +226,22 @@ def load_benchmark_models() -> dict[str, BenchmarkModelConfig]:
|
|
|
224
226
|
return configs
|
|
225
227
|
|
|
226
228
|
|
|
229
|
+
def _key_variable(config: BenchmarkModelConfig) -> str:
|
|
230
|
+
"""The variable a model config's key is read from: its KEY_ENV reference, or its own LLM_{NAME}_KEY."""
|
|
231
|
+
return config.api_key_env or f"LLM_{config.name.upper().replace('-', '_')}_KEY"
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _has_cut_response(history: Any) -> bool:
|
|
235
|
+
"""Whether a model response in a run's messages stopped at the output limit (finish reason ``length``)."""
|
|
236
|
+
return any(isinstance(m, ModelResponse) and m.finish_reason == "length" for m in history or [])
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _was_cut(result: Any) -> bool:
|
|
240
|
+
"""Whether a model response in the run's result stopped at the output limit."""
|
|
241
|
+
messages = getattr(result, "all_messages", None)
|
|
242
|
+
return _has_cut_response(messages() if callable(messages) else [])
|
|
243
|
+
|
|
244
|
+
|
|
227
245
|
def _serialize_output(output: object) -> str:
|
|
228
246
|
"""Serialize a pydantic model or arbitrary output to a JSON string."""
|
|
229
247
|
dump = getattr(output, "model_dump_json", None)
|
|
@@ -314,6 +332,7 @@ class LLMManager:
|
|
|
314
332
|
self.max_tokens = self.config.get("max_tokens")
|
|
315
333
|
self.sampling = self.config.get("sampling", True)
|
|
316
334
|
self.reasoning = self.config.get("reasoning")
|
|
335
|
+
self.context = self.config.get("context")
|
|
317
336
|
|
|
318
337
|
@classmethod
|
|
319
338
|
def from_config(
|
|
@@ -349,6 +368,8 @@ class LLMManager:
|
|
|
349
368
|
"provider": config.provider,
|
|
350
369
|
"api_url": config.get_api_url(),
|
|
351
370
|
"api_key": config.get_api_key(),
|
|
371
|
+
"api_key_variable": _key_variable(config),
|
|
372
|
+
"label": config.name,
|
|
352
373
|
"model": config.model,
|
|
353
374
|
"cache_dir": cache_dir,
|
|
354
375
|
"max_retries": max_retries,
|
|
@@ -357,6 +378,7 @@ class LLMManager:
|
|
|
357
378
|
"nocache": nocache,
|
|
358
379
|
"sampling": config.sampling,
|
|
359
380
|
"reasoning": config.reasoning,
|
|
381
|
+
"context": config.context,
|
|
360
382
|
}
|
|
361
383
|
|
|
362
384
|
instance._validate_config()
|
|
@@ -389,6 +411,7 @@ class LLMManager:
|
|
|
389
411
|
instance.max_tokens = None
|
|
390
412
|
instance.sampling = config.sampling
|
|
391
413
|
instance.reasoning = config.reasoning
|
|
414
|
+
instance.context = config.context
|
|
392
415
|
|
|
393
416
|
return instance
|
|
394
417
|
|
|
@@ -397,6 +420,22 @@ class LLMManager:
|
|
|
397
420
|
"""Get the provider name."""
|
|
398
421
|
return self.config.get("provider", "unknown")
|
|
399
422
|
|
|
423
|
+
def _warn_on_a_full_context(self, result: Any) -> None:
|
|
424
|
+
"""A call whose last request used the model's whole context (when the config names it) lost part of its prompt
|
|
425
|
+
or answer: a local server such as Ollama cuts a prompt longer than its window without an error."""
|
|
426
|
+
if not self.context:
|
|
427
|
+
return
|
|
428
|
+
usage = getattr(getattr(result, "response", None), "usage", None)
|
|
429
|
+
used = (getattr(usage, "input_tokens", 0) or 0) + (getattr(usage, "output_tokens", 0) or 0)
|
|
430
|
+
if used >= self.context:
|
|
431
|
+
logger.warning(
|
|
432
|
+
"An LLM call used %d tokens of %s's %d-token context: part of the prompt or the answer was cut. "
|
|
433
|
+
"Raise the server's context (Ollama: OLLAMA_CONTEXT_LENGTH) and the model's context setting",
|
|
434
|
+
used,
|
|
435
|
+
self.model,
|
|
436
|
+
self.context,
|
|
437
|
+
)
|
|
438
|
+
|
|
400
439
|
def _redact(self, text: str) -> str:
|
|
401
440
|
"""Keep the configured API key out of exception text: h11 quotes header values in its errors."""
|
|
402
441
|
key = self.config.get("api_key")
|
|
@@ -417,11 +456,13 @@ class LLMManager:
|
|
|
417
456
|
provider = config.provider
|
|
418
457
|
api_url = config.get_api_url()
|
|
419
458
|
api_key = config.get_api_key()
|
|
459
|
+
key_variable, label = _key_variable(config), config.name
|
|
420
460
|
model = config.model
|
|
421
|
-
sampling, reasoning = config.sampling, config.reasoning
|
|
461
|
+
sampling, reasoning, context = config.sampling, config.reasoning, config.context
|
|
422
462
|
else:
|
|
423
463
|
provider = llm.provider
|
|
424
|
-
|
|
464
|
+
reasoning, context = None, None
|
|
465
|
+
key_variable, label = f"LLM_{(provider or '').upper()}_API_KEY", provider
|
|
425
466
|
|
|
426
467
|
if provider == "azure":
|
|
427
468
|
api_url = llm.azure_api_url
|
|
@@ -449,6 +490,7 @@ class LLMManager:
|
|
|
449
490
|
model = llm.lmstudio_model
|
|
450
491
|
else:
|
|
451
492
|
raise ConfigurationError(f"Unknown LLM provider: {provider}")
|
|
493
|
+
sampling = default_sampling(provider, model)
|
|
452
494
|
|
|
453
495
|
max_tokens = llm.max_tokens
|
|
454
496
|
|
|
@@ -456,6 +498,8 @@ class LLMManager:
|
|
|
456
498
|
"provider": provider,
|
|
457
499
|
"api_url": api_url,
|
|
458
500
|
"api_key": api_key,
|
|
501
|
+
"api_key_variable": key_variable,
|
|
502
|
+
"label": label,
|
|
459
503
|
"model": model,
|
|
460
504
|
"cache_dir": llm.cache_dir,
|
|
461
505
|
"cache_ttl": llm.cache_ttl,
|
|
@@ -466,6 +510,7 @@ class LLMManager:
|
|
|
466
510
|
"nocache": llm.nocache,
|
|
467
511
|
"sampling": sampling,
|
|
468
512
|
"reasoning": reasoning,
|
|
513
|
+
"context": context,
|
|
469
514
|
"requests_per_minute": llm.rate_limit_rpm,
|
|
470
515
|
"min_request_delay": llm.rate_limit_delay,
|
|
471
516
|
# Retry configuration
|
|
@@ -494,6 +539,12 @@ class LLMManager:
|
|
|
494
539
|
required_fields = ["provider", "api_key", "model"]
|
|
495
540
|
|
|
496
541
|
missing = [f for f in required_fields if not self.config.get(f)]
|
|
542
|
+
# The variable to set and the .env this install reads, never a value
|
|
543
|
+
if "api_key" in missing and self.config.get("api_key_variable"):
|
|
544
|
+
raise ConfigurationError(
|
|
545
|
+
f"The model config {self.config.get('label') or provider} has no API key: set {self.config['api_key_variable']} "
|
|
546
|
+
f"in {settings.env_file()} or in the environment (General in the studio saves a key there)"
|
|
547
|
+
)
|
|
497
548
|
if missing:
|
|
498
549
|
raise ConfigurationError(f"Missing required config fields: {', '.join(missing)}")
|
|
499
550
|
|
|
@@ -555,7 +606,7 @@ class LLMManager:
|
|
|
555
606
|
max_delay = self.config.get("retry_max_delay", 60.0)
|
|
556
607
|
for attempt in range(self.max_retries + 1):
|
|
557
608
|
try:
|
|
558
|
-
return
|
|
609
|
+
return self._run_once(agent, prompt, settings), attempt
|
|
559
610
|
except Exception as e:
|
|
560
611
|
if attempt == self.max_retries or not _is_retriable(e):
|
|
561
612
|
raise
|
|
@@ -579,6 +630,19 @@ class LLMManager:
|
|
|
579
630
|
time.sleep(delay)
|
|
580
631
|
raise AssertionError("unreachable")
|
|
581
632
|
|
|
633
|
+
@staticmethod
|
|
634
|
+
def _run_once(agent: Agent[None, Any], prompt: str, settings: ModelSettings) -> Any:
|
|
635
|
+
"""One agent run. pydantic-ai asks again after an answer cut at the output limit; when those answers fail
|
|
636
|
+
too it raises "Exceeded maximum output retries", which is the cut: raised as IncompleteToolCall."""
|
|
637
|
+
with capture_run_messages() as messages:
|
|
638
|
+
try:
|
|
639
|
+
return agent.run_sync(prompt, model_settings=settings)
|
|
640
|
+
except Exception as e:
|
|
641
|
+
if not isinstance(e, IncompleteToolCall) and _has_cut_response(messages):
|
|
642
|
+
limit = settings.get("max_tokens") or "provider default"
|
|
643
|
+
raise IncompleteToolCall(f"Model token limit ({limit}) reached: the answer was cut, and the answers after it failed ({e})") from e
|
|
644
|
+
raise
|
|
645
|
+
|
|
582
646
|
def _agent(self, output_type: Any, system_prompt: str) -> Agent[None, Any]:
|
|
583
647
|
"""Reusable agent for this output type and system prompt, per thread (created lazily).
|
|
584
648
|
|
|
@@ -756,6 +820,16 @@ class LLMManager:
|
|
|
756
820
|
"total_tokens": run_usage.input_tokens + run_usage.output_tokens,
|
|
757
821
|
}
|
|
758
822
|
|
|
823
|
+
self._warn_on_a_full_context(result)
|
|
824
|
+
|
|
825
|
+
# A structured answer cut at the output limit is asked again by pydantic-ai, and a shorter answer that
|
|
826
|
+
# validates can leave part of the question out: a failure (never cached), which a caller can split
|
|
827
|
+
if output_type is not str and _was_cut(result):
|
|
828
|
+
call["error_type"] = "IncompleteToolCall"
|
|
829
|
+
message = f"Model token limit ({effective_max_tokens or 'provider default'}) reached: the answer was cut, and the shorter answer after it may leave part of it out"
|
|
830
|
+
logger.warning("LLM answer cut at the output limit (%s)", self.model)
|
|
831
|
+
return FailedResponse(prompt=prompt, model=self.model, error=message, error_type="IncompleteToolCall")
|
|
832
|
+
|
|
759
833
|
# Handle response
|
|
760
834
|
output: Any = result.output
|
|
761
835
|
|
|
@@ -29,10 +29,21 @@ PROVIDER_REASONING_LEVELS = {
|
|
|
29
29
|
_SAMPLING_VALUES = {"true": True, "1": True, "yes": True, "on": True, "false": False, "0": False, "no": False, "off": False}
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def
|
|
33
|
-
"""
|
|
32
|
+
def default_sampling(provider: str, model: str) -> bool:
|
|
33
|
+
"""Whether a model takes a temperature when its config does not say. pydantic-ai's model profile knows the Anthropic
|
|
34
|
+
models that refuse sampling settings (it drops them, with a warning on every call); any other model takes one."""
|
|
35
|
+
if provider == "anthropic":
|
|
36
|
+
from pydantic_ai.profiles.anthropic import anthropic_model_profile
|
|
37
|
+
|
|
38
|
+
profile = anthropic_model_profile(model) or {}
|
|
39
|
+
return not profile.get("anthropic_disallows_sampling_settings", False)
|
|
40
|
+
return True
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def parse_sampling(value: str | None, provider: str = "", model: str = "") -> bool:
|
|
44
|
+
"""LLM_{NAME}_SAMPLING: off for models that take no temperature (unset: what the model's profile says)."""
|
|
34
45
|
if value is None or not value.strip():
|
|
35
|
-
return
|
|
46
|
+
return default_sampling(provider, model)
|
|
36
47
|
if value.strip().lower() not in _SAMPLING_VALUES:
|
|
37
48
|
raise ValueError(f"sampling must be true or false, got {value!r}")
|
|
38
49
|
return _SAMPLING_VALUES[value.strip().lower()]
|
|
@@ -126,6 +137,12 @@ def get_pydantic_ai_model(config: dict[str, Any]) -> Model | str:
|
|
|
126
137
|
from pydantic_ai.models.openai import OpenAIChatModel
|
|
127
138
|
from pydantic_ai.providers.azure import AzureProvider
|
|
128
139
|
|
|
140
|
+
# Azure's v1 API (<resource>/openai/v1/) is OpenAI's and takes no api_version: the OpenAI client goes there
|
|
141
|
+
if api_url and "/openai/v1" in api_url:
|
|
142
|
+
from pydantic_ai.providers.openai import OpenAIProvider
|
|
143
|
+
|
|
144
|
+
base_url = api_url[: api_url.index("/openai/v1") + len("/openai/v1")]
|
|
145
|
+
return OpenAIChatModel(model, provider=OpenAIProvider(base_url=base_url, api_key=api_key))
|
|
129
146
|
azure_endpoint = _normalize_azure_url(api_url) if api_url else None
|
|
130
147
|
azure_provider = AzureProvider(
|
|
131
148
|
azure_endpoint=azure_endpoint,
|
|
@@ -69,6 +69,7 @@ class BenchmarkModelConfig:
|
|
|
69
69
|
sampling: False for models that take no temperature (Claude Haiku 5.5); the call leaves it out
|
|
70
70
|
reasoning: Reasoning level ("none" turns it off; see ``model_registry.reasoning_settings``)
|
|
71
71
|
temperature: The model's own temperature (0 to 2), over LLM_TEMPERATURE; unused when sampling is off
|
|
72
|
+
context: The model's context size in tokens, when known (a local server's window); a call that fills it is warned
|
|
72
73
|
"""
|
|
73
74
|
|
|
74
75
|
name: str
|
|
@@ -80,6 +81,7 @@ class BenchmarkModelConfig:
|
|
|
80
81
|
sampling: bool = True
|
|
81
82
|
reasoning: str | None = None
|
|
82
83
|
temperature: float | None = None
|
|
84
|
+
context: int | None = None
|
|
83
85
|
|
|
84
86
|
def __post_init__(self):
|
|
85
87
|
"""Validate provider and reasoning level."""
|
|
@@ -97,6 +99,8 @@ class BenchmarkModelConfig:
|
|
|
97
99
|
reasoning_settings(self.provider, self.reasoning)
|
|
98
100
|
if self.temperature is not None and not 0.0 <= self.temperature <= 2.0:
|
|
99
101
|
raise ValueError(f"temperature must be between 0 and 2, got {self.temperature}")
|
|
102
|
+
if self.context is not None and self.context < 1:
|
|
103
|
+
raise ValueError(f"context must be a positive number of tokens, got {self.context}")
|
|
100
104
|
|
|
101
105
|
def get_api_key(self) -> str | None:
|
|
102
106
|
"""Get API key from direct value or environment variable."""
|
|
@@ -143,7 +147,7 @@ class BaseResponse(BaseModel):
|
|
|
143
147
|
class LiveResponse(BaseResponse):
|
|
144
148
|
"""Response from a live API call."""
|
|
145
149
|
|
|
146
|
-
response_type: Literal[ResponseType.LIVE] = ResponseType.LIVE
|
|
150
|
+
response_type: Literal[ResponseType.LIVE] = ResponseType.LIVE # ty: ignore[invalid-mutable-override]: the discriminator of a pydantic union
|
|
147
151
|
content: str
|
|
148
152
|
usage: dict[str, Any] | None = None
|
|
149
153
|
finish_reason: str | None = None
|
|
@@ -152,7 +156,7 @@ class LiveResponse(BaseResponse):
|
|
|
152
156
|
class CachedResponse(BaseResponse):
|
|
153
157
|
"""Response retrieved from cache."""
|
|
154
158
|
|
|
155
|
-
response_type: Literal[ResponseType.CACHED] = ResponseType.CACHED
|
|
159
|
+
response_type: Literal[ResponseType.CACHED] = ResponseType.CACHED # ty: ignore[invalid-mutable-override]: the discriminator of a pydantic union
|
|
156
160
|
content: str
|
|
157
161
|
cache_key: str
|
|
158
162
|
cached_at: str
|
|
@@ -161,7 +165,7 @@ class CachedResponse(BaseResponse):
|
|
|
161
165
|
class FailedResponse(BaseResponse):
|
|
162
166
|
"""Response when API call fails."""
|
|
163
167
|
|
|
164
|
-
response_type: Literal[ResponseType.FAILED] = ResponseType.FAILED
|
|
168
|
+
response_type: Literal[ResponseType.FAILED] = ResponseType.FAILED # ty: ignore[invalid-mutable-override]: the discriminator of a pydantic union
|
|
165
169
|
error: str
|
|
166
170
|
error_type: str
|
|
167
171
|
|
|
@@ -9,7 +9,7 @@ wrapper exceptions.
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
import httpx2
|
|
12
|
-
from pydantic_ai.exceptions import ModelHTTPError
|
|
12
|
+
from pydantic_ai.exceptions import IncompleteToolCall, ModelHTTPError
|
|
13
13
|
|
|
14
14
|
from deriva.common.exceptions import (
|
|
15
15
|
CircuitOpenError,
|
|
@@ -72,6 +72,10 @@ def classify_exception(
|
|
|
72
72
|
if isinstance(exception, CircuitOpenError):
|
|
73
73
|
return ("permanent", None)
|
|
74
74
|
|
|
75
|
+
# An answer cut at the output limit is cut again for the same prompt; the caller can split it instead
|
|
76
|
+
if isinstance(exception, IncompleteToolCall):
|
|
77
|
+
return ("permanent", None)
|
|
78
|
+
|
|
75
79
|
# An HTTP status error carries its own status code
|
|
76
80
|
if isinstance(exception, ModelHTTPError):
|
|
77
81
|
status_code = getattr(exception, "status_code", None)
|
|
@@ -20,8 +20,7 @@ class Translator:
|
|
|
20
20
|
|
|
21
21
|
def __init__(self, package_dir: Path):
|
|
22
22
|
self.pieces = sentencepiece.SentencePieceProcessor(model_file=str(package_dir / "sentencepiece.model"))
|
|
23
|
-
|
|
24
|
-
self.model = ctranslate2.Translator(str(package_dir / "model"), device="cpu", inter_threads=4, intra_threads=1) # ty: ignore[possibly-missing-attribute]
|
|
23
|
+
self.model = ctranslate2.Translator(str(package_dir / "model"), device="cpu", inter_threads=4, intra_threads=1)
|
|
25
24
|
|
|
26
25
|
def translate(self, texts: list[str], settings: Settings) -> dict[str, str]:
|
|
27
26
|
"""Each text on its own (batch size 1), so a translation never depends on the other texts."""
|