generic-ml-cache-core 0.2.0__tar.gz → 0.3.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.
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/PKG-INFO +1 -1
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/pyproject.toml +1 -1
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/persistence/in_memory_execution_repository.py +13 -1
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/persistence/sqlite_execution_repository.py +47 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/execution/ml_execution.py +12 -1
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/inbound/run_managed_local_execution_command.py +1 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/out/execution_repository_port.py +12 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/usecase/cached_ml_execution_service.py +15 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/usecase/run_managed_local_execution_service.py +5 -1
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_execution_repository.py +25 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_ml_execution.py +12 -1
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_run_managed_local_execution_service.py +21 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_sqlite_execution_repository.py +25 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/.gitignore +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/LICENSE +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/NOTICE +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/README.md +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/inbound/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/inbound/composition.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/api/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/api/stub_api_client_adapter.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/client/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/client/claude.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/client/codex.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/client/cursor.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/client/discover.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/client/isolation.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/client/local_client_runner.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/client/passthrough_client_runner.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/client/prime_directive.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/client/registry.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/clock/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/clock/system_clock.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/fingerprint/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/fingerprint/filesystem_file_fingerprint.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/metrics/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/metrics/access_registry.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/metrics/journal_metrics.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/persistence/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/persistence/call_identity_serialization.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/storage/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/adapter/out/storage/filesystem_blob_store.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/client_status.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/execution/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/execution/artifact.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/execution/execution_failure.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/execution/execution_kind.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/execution/execution_state.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/identity/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/identity/api_call_identity.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/identity/call_identity.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/identity/managed_call_identity.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/identity/passthrough_call_identity.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/model_info.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/model_listing.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/parsed_output.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/probe/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/probe/probe_report.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/probe/probe_status.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/run/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/run/cache_mode.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/run/client_run_request.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/run/client_run_result.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/run/message.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/usage/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/usage/token_usage.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/model/usage/usage.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/service/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/service/cacheability.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/domain/service/message_fingerprinting.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/inbound/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/inbound/probe_command.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/inbound/probe_use_case.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/inbound/run_api_execution_command.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/inbound/run_api_execution_use_case.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/inbound/run_managed_local_execution_use_case.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/inbound/run_passthrough_execution_command.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/inbound/run_passthrough_execution_use_case.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/out/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/out/api_client_port.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/out/base.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/out/blob_store_port.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/out/client_runner_port.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/out/clock_port.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/out/file_fingerprint_port.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/out/metrics_port.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/port/out/passthrough_runner_port.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/usecase/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/usecase/call_identity_building.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/usecase/journal_events.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/usecase/probe_service.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/usecase/run_api_execution_service.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/application/usecase/run_passthrough_execution_service.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/common/__init__.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/common/checksum.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/common/errors.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/stream.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/conftest.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/fake_client.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_adapters.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_api_call_identity.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_api_client_port.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_artifact.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_blob_store_port.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_cache_mode.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_cacheability.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_call_identity_building.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_call_identity_serialization.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_checksum.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_client_run_request.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_client_run_result.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_client_runner_port.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_clock_port.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_composition.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_execution_failure.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_execution_kind.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_execution_state.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_file_content_fingerprint.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_file_fingerprint_port.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_filesystem_blob_store.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_journal_metrics.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_local_client_runner.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_managed_call_identity.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_message.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_message_fingerprinting.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_metrics_port.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_passthrough_call_identity.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_passthrough_client_runner.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_passthrough_runner_port.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_probe_command.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_probe_report.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_probe_service.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_run_api_execution_command.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_run_api_execution_service.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_run_managed_local_execution_command.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_run_passthrough_execution_command.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_run_passthrough_execution_service.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_token_usage.py +0 -0
- {generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_usage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: generic-ml-cache-core
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Hexagonal core library for generic-ml-cache: domain, use cases, ports, and the default outbound adapters (SQLite repo, blob store, local clients, API). Stateless; inject the data source. Zero runtime deps.
|
|
5
5
|
Project-URL: Homepage, https://github.com/danielslobozian/generic-ml-cache
|
|
6
6
|
Project-URL: Repository, https://github.com/danielslobozian/generic-ml-cache
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "generic-ml-cache-core"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "Hexagonal core library for generic-ml-cache: domain, use cases, ports, and the default outbound adapters (SQLite repo, blob store, local clients, API). Stateless; inject the data source. Zero runtime deps."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
from dataclasses import replace
|
|
8
|
-
from typing import Dict, List, Optional
|
|
8
|
+
from typing import Dict, List, Optional, Set
|
|
9
9
|
|
|
10
10
|
from generic_ml_cache_core.application.domain.model.execution.execution_state import ExecutionState
|
|
11
11
|
from generic_ml_cache_core.application.domain.model.execution.ml_execution import MlExecution
|
|
@@ -30,6 +30,7 @@ class InMemoryExecutionRepository(ExecutionRepositoryPort):
|
|
|
30
30
|
def __init__(self, clock: ClockPort) -> None:
|
|
31
31
|
self._clock = clock
|
|
32
32
|
self._by_key: Dict[str, List[MlExecution]] = {}
|
|
33
|
+
self._tags_by_key: Dict[str, Set[str]] = {}
|
|
33
34
|
|
|
34
35
|
def find_current(self, execution_key: str) -> Optional[MlExecution]:
|
|
35
36
|
for execution in self._by_key.get(execution_key, []):
|
|
@@ -51,6 +52,17 @@ class InMemoryExecutionRepository(ExecutionRepositoryPort):
|
|
|
51
52
|
prior.superseded_at = superseded_at
|
|
52
53
|
history.append(stored)
|
|
53
54
|
|
|
55
|
+
def add_tags(self, execution_key: str, tags: List[str]) -> None:
|
|
56
|
+
# Tags the key's current execution; a no-op when there is none.
|
|
57
|
+
if not tags or self.find_current(execution_key) is None:
|
|
58
|
+
return
|
|
59
|
+
self._tags_by_key.setdefault(execution_key, set()).update(tags)
|
|
60
|
+
|
|
61
|
+
def tags_for(self, execution_key: str) -> List[str]:
|
|
62
|
+
if self.find_current(execution_key) is None:
|
|
63
|
+
return []
|
|
64
|
+
return sorted(self._tags_by_key.get(execution_key, set()))
|
|
65
|
+
|
|
54
66
|
@staticmethod
|
|
55
67
|
def _is_servable(execution: MlExecution) -> bool:
|
|
56
68
|
"""A servable execution is the current cached answer: a persisted success
|
|
@@ -86,6 +86,11 @@ CREATE TABLE IF NOT EXISTS token_usage (
|
|
|
86
86
|
cost_usd REAL,
|
|
87
87
|
raw_json TEXT NOT NULL
|
|
88
88
|
);
|
|
89
|
+
CREATE TABLE IF NOT EXISTS execution_tags (
|
|
90
|
+
execution_id INTEGER NOT NULL,
|
|
91
|
+
tag TEXT NOT NULL,
|
|
92
|
+
UNIQUE(execution_id, tag)
|
|
93
|
+
);
|
|
89
94
|
"""
|
|
90
95
|
|
|
91
96
|
|
|
@@ -288,6 +293,48 @@ class SqliteExecutionRepository(ExecutionRepositoryPort):
|
|
|
288
293
|
),
|
|
289
294
|
)
|
|
290
295
|
|
|
296
|
+
# -- tags (a separate annotation; never rewrites an execution) --------
|
|
297
|
+
|
|
298
|
+
@staticmethod
|
|
299
|
+
def _current_execution_id(connection: sqlite3.Connection, execution_key: str) -> Optional[int]:
|
|
300
|
+
row = connection.execute(
|
|
301
|
+
"SELECT id FROM executions WHERE execution_key = ? AND state = ? "
|
|
302
|
+
"AND output_persisted = 1 AND superseded_at IS NULL ORDER BY id DESC LIMIT 1",
|
|
303
|
+
(execution_key, ExecutionState.SUCCESS.value),
|
|
304
|
+
).fetchone()
|
|
305
|
+
return int(row[0]) if row is not None else None
|
|
306
|
+
|
|
307
|
+
def add_tags(self, execution_key: str, tags: List[str]) -> None:
|
|
308
|
+
if not tags:
|
|
309
|
+
return
|
|
310
|
+
connection = self._connect()
|
|
311
|
+
try:
|
|
312
|
+
execution_id = self._current_execution_id(connection, execution_key)
|
|
313
|
+
if execution_id is None:
|
|
314
|
+
return
|
|
315
|
+
for tag in tags:
|
|
316
|
+
connection.execute(
|
|
317
|
+
"INSERT OR IGNORE INTO execution_tags (execution_id, tag) VALUES (?, ?)",
|
|
318
|
+
(execution_id, tag),
|
|
319
|
+
)
|
|
320
|
+
connection.commit()
|
|
321
|
+
finally:
|
|
322
|
+
connection.close()
|
|
323
|
+
|
|
324
|
+
def tags_for(self, execution_key: str) -> List[str]:
|
|
325
|
+
connection = self._connect()
|
|
326
|
+
try:
|
|
327
|
+
execution_id = self._current_execution_id(connection, execution_key)
|
|
328
|
+
if execution_id is None:
|
|
329
|
+
return []
|
|
330
|
+
rows = connection.execute(
|
|
331
|
+
"SELECT tag FROM execution_tags WHERE execution_id = ? ORDER BY tag",
|
|
332
|
+
(execution_id,),
|
|
333
|
+
).fetchall()
|
|
334
|
+
return [tag for (tag,) in rows]
|
|
335
|
+
finally:
|
|
336
|
+
connection.close()
|
|
337
|
+
|
|
291
338
|
# -- reconstruction ---------------------------------------------------
|
|
292
339
|
|
|
293
340
|
def _load_execution(self, connection: sqlite3.Connection, row: tuple) -> MlExecution:
|
|
@@ -6,7 +6,7 @@ from __future__ import annotations
|
|
|
6
6
|
|
|
7
7
|
from dataclasses import dataclass, field
|
|
8
8
|
from datetime import datetime
|
|
9
|
-
from typing import List, Optional
|
|
9
|
+
from typing import Iterable, List, Optional
|
|
10
10
|
|
|
11
11
|
from generic_ml_cache_core.application.domain.model.execution.artifact import Artifact
|
|
12
12
|
from generic_ml_cache_core.application.domain.model.identity.call_identity import CallIdentity
|
|
@@ -39,3 +39,14 @@ class MlExecution:
|
|
|
39
39
|
token_usage: Optional[TokenUsage] = None
|
|
40
40
|
failure: Optional[ExecutionFailure] = None
|
|
41
41
|
superseded_at: Optional[datetime] = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def normalize_tags(raw_tags: Iterable[str]) -> List[str]:
|
|
45
|
+
"""Normalise user-supplied tags: trim, drop blanks, de-duplicate, sort.
|
|
46
|
+
|
|
47
|
+
Tags are metadata, never part of the cache key. Normalising at the boundary
|
|
48
|
+
keeps stored tags deterministic (the same set in any input order compares
|
|
49
|
+
equal) without interpreting their meaning — they are stored verbatim
|
|
50
|
+
otherwise.
|
|
51
|
+
"""
|
|
52
|
+
return sorted({tag.strip() for tag in raw_tags if tag and tag.strip()})
|
|
@@ -38,3 +38,15 @@ class ExecutionRepositoryPort(ABC):
|
|
|
38
38
|
"""Append a new execution. If it is a servable success, atomically
|
|
39
39
|
supersede the prior current execution for the same key — the supersession
|
|
40
40
|
happens here, where atomicity belongs, never in the caller."""
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def add_tags(self, execution_key: str, tags: List[str]) -> None:
|
|
44
|
+
"""Attach ``tags`` to the current execution for ``execution_key``,
|
|
45
|
+
idempotently — already-present tags are left untouched, new ones added.
|
|
46
|
+
A separate annotation layer: this never rewrites the execution record,
|
|
47
|
+
and is a no-op if there is no current execution for the key."""
|
|
48
|
+
|
|
49
|
+
@abstractmethod
|
|
50
|
+
def tags_for(self, execution_key: str) -> List[str]:
|
|
51
|
+
"""Return the tags on the current execution for ``execution_key``, sorted;
|
|
52
|
+
empty if none (or no current execution)."""
|
|
@@ -93,6 +93,19 @@ class CachedMlExecutionService(ABC):
|
|
|
93
93
|
"""Whether this command cannot be cached. Default: always cacheable."""
|
|
94
94
|
return False
|
|
95
95
|
|
|
96
|
+
def _execution_tags(self, command: CacheableExecutionCommand) -> List[str]:
|
|
97
|
+
"""User-supplied tags to attach to executions this service records.
|
|
98
|
+
Metadata only — never part of the key. Default: none."""
|
|
99
|
+
return []
|
|
100
|
+
|
|
101
|
+
def _apply_tags(self, execution_key: str, command: CacheableExecutionCommand) -> None:
|
|
102
|
+
"""Attach the command's tags to the current execution for this key,
|
|
103
|
+
idempotently (a no-op when there are none). Tags are a separate
|
|
104
|
+
annotation: adding one never rewrites the execution record."""
|
|
105
|
+
tags = self._execution_tags(command)
|
|
106
|
+
if tags:
|
|
107
|
+
self._repository.add_tags(execution_key, tags)
|
|
108
|
+
|
|
96
109
|
# -- resolution paths -------------------------------------------------
|
|
97
110
|
|
|
98
111
|
def _serve_offline(self, command: CacheableExecutionCommand, execution_key: str) -> MlExecution:
|
|
@@ -107,6 +120,7 @@ class CachedMlExecutionService(ABC):
|
|
|
107
120
|
) -> MlExecution:
|
|
108
121
|
hydrated_execution = self._hydrate(current_execution)
|
|
109
122
|
self._record_event(journal_events.HIT, execution_key, command)
|
|
123
|
+
self._apply_tags(execution_key, command)
|
|
110
124
|
return hydrated_execution
|
|
111
125
|
|
|
112
126
|
def _run_uncacheable(
|
|
@@ -139,6 +153,7 @@ class CachedMlExecutionService(ABC):
|
|
|
139
153
|
if should_store:
|
|
140
154
|
self._repository.save(execution)
|
|
141
155
|
self._record_event(journal_events.RECORD, execution_key, command)
|
|
156
|
+
self._apply_tags(execution_key, command)
|
|
142
157
|
else:
|
|
143
158
|
self._record_event(journal_events.RUN, execution_key, command)
|
|
144
159
|
return execution
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
-
from typing import Tuple
|
|
7
|
+
from typing import List, Tuple
|
|
8
8
|
|
|
9
|
+
from generic_ml_cache_core.application.domain.model.execution.ml_execution import normalize_tags
|
|
9
10
|
from generic_ml_cache_core.application.domain.model.identity.call_identity import CallIdentity
|
|
10
11
|
from generic_ml_cache_core.application.domain.model.run.client_run_request import ClientRunRequest
|
|
11
12
|
from generic_ml_cache_core.application.domain.model.run.client_run_result import ClientRunResult
|
|
@@ -66,6 +67,9 @@ class RunManagedLocalExecutionService(CachedMlExecutionService, RunManagedLocalE
|
|
|
66
67
|
def _is_uncacheable(self, command: RunManagedLocalExecutionCommand) -> bool:
|
|
67
68
|
return command.is_uncacheable
|
|
68
69
|
|
|
70
|
+
def _execution_tags(self, command: RunManagedLocalExecutionCommand) -> List[str]:
|
|
71
|
+
return normalize_tags(command.tags)
|
|
72
|
+
|
|
69
73
|
@staticmethod
|
|
70
74
|
def _build_client_run_request(command: RunManagedLocalExecutionCommand) -> ClientRunRequest:
|
|
71
75
|
# The one allowed self-less method (AGENTS §6): the inbound-command ->
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_execution_repository.py
RENAMED
|
@@ -107,6 +107,31 @@ def test_save_then_find_current_returns_the_execution():
|
|
|
107
107
|
assert found.execution_state is ExecutionState.SUCCESS
|
|
108
108
|
|
|
109
109
|
|
|
110
|
+
def test_add_tags_then_tags_for_returns_them_sorted():
|
|
111
|
+
repository = _repository()
|
|
112
|
+
identity = _identity()
|
|
113
|
+
repository.save(_execution(identity))
|
|
114
|
+
repository.add_tags(identity.generate_key(), ["ticket", "id-scan"])
|
|
115
|
+
assert repository.tags_for(identity.generate_key()) == ["id-scan", "ticket"]
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_add_tags_is_idempotent_and_accumulates():
|
|
119
|
+
repository = _repository()
|
|
120
|
+
identity = _identity()
|
|
121
|
+
repository.save(_execution(identity))
|
|
122
|
+
key = identity.generate_key()
|
|
123
|
+
repository.add_tags(key, ["ticket"])
|
|
124
|
+
repository.add_tags(key, ["ticket", "id-scan"]) # 'ticket' already present
|
|
125
|
+
assert repository.tags_for(key) == ["id-scan", "ticket"]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_add_tags_is_a_no_op_without_a_current_execution():
|
|
129
|
+
repository = _repository()
|
|
130
|
+
identity = _identity()
|
|
131
|
+
repository.add_tags(identity.generate_key(), ["x"]) # nothing stored
|
|
132
|
+
assert repository.tags_for(identity.generate_key()) == []
|
|
133
|
+
|
|
134
|
+
|
|
110
135
|
def test_find_current_returns_dehydrated_artifacts():
|
|
111
136
|
repository = _repository()
|
|
112
137
|
identity = _identity()
|
|
@@ -16,7 +16,10 @@ from generic_ml_cache_core.application.domain.model.execution.execution_failure
|
|
|
16
16
|
)
|
|
17
17
|
from generic_ml_cache_core.application.domain.model.execution.execution_kind import ExecutionKind
|
|
18
18
|
from generic_ml_cache_core.application.domain.model.execution.execution_state import ExecutionState
|
|
19
|
-
from generic_ml_cache_core.application.domain.model.execution.ml_execution import
|
|
19
|
+
from generic_ml_cache_core.application.domain.model.execution.ml_execution import (
|
|
20
|
+
MlExecution,
|
|
21
|
+
normalize_tags,
|
|
22
|
+
)
|
|
20
23
|
from generic_ml_cache_core.application.domain.model.usage.token_usage import TokenUsage
|
|
21
24
|
|
|
22
25
|
|
|
@@ -152,3 +155,11 @@ def test_dehydrated_execution_has_artifacts_without_content():
|
|
|
152
155
|
)
|
|
153
156
|
assert execution.artifacts[0].is_hydrated is False
|
|
154
157
|
assert execution.artifacts[0].blob_key == "k"
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_normalize_tags_trims_drops_blanks_dedupes_and_sorts():
|
|
161
|
+
assert normalize_tags([" beta ", "alpha", "beta", "", " ", "alpha"]) == ["alpha", "beta"]
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def test_normalize_tags_of_nothing_is_empty():
|
|
165
|
+
assert normalize_tags([]) == []
|
|
@@ -382,3 +382,24 @@ def test_grants_change_the_identity():
|
|
|
382
382
|
harness.use_case.execute(_command())
|
|
383
383
|
harness.use_case.execute(_command(grants=["net"]))
|
|
384
384
|
assert len(harness.runner.calls) == 2
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
# --- tags (non-identity metadata) --------------------------------------------
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def test_tags_are_normalized_and_applied_to_the_recorded_execution():
|
|
391
|
+
harness = _Harness(ClientRunResult(exit_code=0, stdout="answer\n"))
|
|
392
|
+
execution = harness.use_case.execute(_command(tags=[" ticket ", "scan", "ticket", "", "scan"]))
|
|
393
|
+
key = execution.call_identity.generate_key()
|
|
394
|
+
assert harness.repository.tags_for(key) == ["scan", "ticket"]
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def test_a_hit_accumulates_new_tags_onto_the_entry():
|
|
398
|
+
harness = _Harness()
|
|
399
|
+
first = harness.use_case.execute(_command(tags=["scan"]))
|
|
400
|
+
key = first.call_identity.generate_key()
|
|
401
|
+
harness.use_case.execute(_command(tags=["ticket"])) # same input -> a hit
|
|
402
|
+
# Tags are out of the key (still one run) and accumulate on the entry.
|
|
403
|
+
assert len(harness.runner.calls) == 1
|
|
404
|
+
assert harness.metrics.events == ["record", "hit"]
|
|
405
|
+
assert harness.repository.tags_for(key) == ["scan", "ticket"]
|
|
@@ -173,6 +173,31 @@ def test_token_usage_round_trips(tmp_path):
|
|
|
173
173
|
assert restored == usage
|
|
174
174
|
|
|
175
175
|
|
|
176
|
+
def test_add_tags_then_tags_for_returns_them_sorted(tmp_path):
|
|
177
|
+
repository = _repository(tmp_path)
|
|
178
|
+
identity = _managed_identity()
|
|
179
|
+
repository.save(_execution(identity))
|
|
180
|
+
repository.add_tags(identity.generate_key(), ["ticket", "id-scan"])
|
|
181
|
+
assert repository.tags_for(identity.generate_key()) == ["id-scan", "ticket"]
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def test_add_tags_is_idempotent_and_accumulates(tmp_path):
|
|
185
|
+
repository = _repository(tmp_path)
|
|
186
|
+
identity = _managed_identity()
|
|
187
|
+
repository.save(_execution(identity))
|
|
188
|
+
key = identity.generate_key()
|
|
189
|
+
repository.add_tags(key, ["ticket"])
|
|
190
|
+
repository.add_tags(key, ["ticket", "id-scan"]) # 'ticket' already present
|
|
191
|
+
assert repository.tags_for(key) == ["id-scan", "ticket"]
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def test_add_tags_is_a_no_op_without_a_current_execution(tmp_path):
|
|
195
|
+
repository = _repository(tmp_path)
|
|
196
|
+
identity = _managed_identity()
|
|
197
|
+
repository.add_tags(identity.generate_key(), ["x"]) # nothing stored
|
|
198
|
+
assert repository.tags_for(identity.generate_key()) == []
|
|
199
|
+
|
|
200
|
+
|
|
176
201
|
def test_failure_round_trips_in_history(tmp_path):
|
|
177
202
|
repository = _repository(tmp_path)
|
|
178
203
|
identity = _managed_identity()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/src/generic_ml_cache_core/stream.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_call_identity_building.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_client_run_request.py
RENAMED
|
File without changes
|
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_client_runner_port.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_file_content_fingerprint.py
RENAMED
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_file_fingerprint_port.py
RENAMED
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_filesystem_blob_store.py
RENAMED
|
File without changes
|
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_local_client_runner.py
RENAMED
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_managed_call_identity.py
RENAMED
|
File without changes
|
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_message_fingerprinting.py
RENAMED
|
File without changes
|
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_passthrough_call_identity.py
RENAMED
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_passthrough_client_runner.py
RENAMED
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_passthrough_runner_port.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_run_api_execution_command.py
RENAMED
|
File without changes
|
{generic_ml_cache_core-0.2.0 → generic_ml_cache_core-0.3.0}/tests/test_run_api_execution_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|