mineproductivity 2.0.0__py3-none-any.whl
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.
- mineproductivity/README.md +31 -0
- mineproductivity/__init__.py +50 -0
- mineproductivity/agents/README.md +27 -0
- mineproductivity/agents/__init__.py +85 -0
- mineproductivity/agents/_registry.py +84 -0
- mineproductivity/agents/abstractions.py +113 -0
- mineproductivity/agents/approval.py +56 -0
- mineproductivity/agents/audit.py +102 -0
- mineproductivity/agents/capability.py +91 -0
- mineproductivity/agents/communication.py +82 -0
- mineproductivity/agents/conversation.py +61 -0
- mineproductivity/agents/discovery.py +78 -0
- mineproductivity/agents/exceptions.py +60 -0
- mineproductivity/agents/executor.py +273 -0
- mineproductivity/agents/goal.py +41 -0
- mineproductivity/agents/memory.py +43 -0
- mineproductivity/agents/metadata.py +78 -0
- mineproductivity/agents/persistence.py +23 -0
- mineproductivity/agents/policy.py +228 -0
- mineproductivity/agents/result.py +57 -0
- mineproductivity/agents/state.py +50 -0
- mineproductivity/agents/task.py +87 -0
- mineproductivity/agents/tool.py +107 -0
- mineproductivity/agents/workflow.py +114 -0
- mineproductivity/analytics/README.md +90 -0
- mineproductivity/analytics/__init__.py +206 -0
- mineproductivity/analytics/_registry.py +75 -0
- mineproductivity/analytics/abstractions.py +99 -0
- mineproductivity/analytics/aggregation.py +214 -0
- mineproductivity/analytics/anomaly.py +62 -0
- mineproductivity/analytics/baseline.py +130 -0
- mineproductivity/analytics/batch.py +67 -0
- mineproductivity/analytics/benchmarking.py +258 -0
- mineproductivity/analytics/exceptions.py +40 -0
- mineproductivity/analytics/forecasting.py +56 -0
- mineproductivity/analytics/incremental.py +127 -0
- mineproductivity/analytics/metadata.py +74 -0
- mineproductivity/analytics/outliers.py +72 -0
- mineproductivity/analytics/pipeline.py +136 -0
- mineproductivity/analytics/quality.py +312 -0
- mineproductivity/analytics/result.py +275 -0
- mineproductivity/analytics/rolling.py +138 -0
- mineproductivity/analytics/statistics.py +348 -0
- mineproductivity/analytics/streaming.py +144 -0
- mineproductivity/analytics/timeseries.py +126 -0
- mineproductivity/analytics/trend.py +124 -0
- mineproductivity/analytics/windowing.py +48 -0
- mineproductivity/benchmark/README.md +38 -0
- mineproductivity/benchmark/__init__.py +7 -0
- mineproductivity/certification/README.md +38 -0
- mineproductivity/certification/__init__.py +7 -0
- mineproductivity/cli/README.md +38 -0
- mineproductivity/cli/__init__.py +7 -0
- mineproductivity/config/README.md +39 -0
- mineproductivity/config/__init__.py +7 -0
- mineproductivity/connectors/README.md +224 -0
- mineproductivity/connectors/__init__.py +77 -0
- mineproductivity/connectors/_registry.py +30 -0
- mineproductivity/connectors/auth.py +74 -0
- mineproductivity/connectors/base.py +109 -0
- mineproductivity/connectors/contract_tests.py +89 -0
- mineproductivity/connectors/exceptions.py +39 -0
- mineproductivity/connectors/file/__init__.py +8 -0
- mineproductivity/connectors/file/_common.py +125 -0
- mineproductivity/connectors/file/csv_connector.py +122 -0
- mineproductivity/connectors/file/excel_connector.py +131 -0
- mineproductivity/connectors/health.py +33 -0
- mineproductivity/connectors/network/__init__.py +8 -0
- mineproductivity/connectors/network/graphql_connector.py +143 -0
- mineproductivity/connectors/network/rest_connector.py +152 -0
- mineproductivity/connectors/normalization.py +109 -0
- mineproductivity/connectors/oem/__init__.py +33 -0
- mineproductivity/connectors/oem/dispatch_shape.py +61 -0
- mineproductivity/connectors/oem/hexagon_shape.py +61 -0
- mineproductivity/connectors/oem/minestar_shape.py +64 -0
- mineproductivity/connectors/oem/modular_shape.py +61 -0
- mineproductivity/connectors/oem/wenco_shape.py +63 -0
- mineproductivity/connectors/retry.py +108 -0
- mineproductivity/connectors/streaming/__init__.py +8 -0
- mineproductivity/connectors/streaming/_common.py +64 -0
- mineproductivity/connectors/streaming/kafka_connector.py +90 -0
- mineproductivity/connectors/streaming/mqtt_connector.py +87 -0
- mineproductivity/core/README.md +350 -0
- mineproductivity/core/__init__.py +99 -0
- mineproductivity/core/builder.py +56 -0
- mineproductivity/core/configuration.py +49 -0
- mineproductivity/core/entity.py +70 -0
- mineproductivity/core/exceptions.py +74 -0
- mineproductivity/core/factory.py +47 -0
- mineproductivity/core/identifier.py +68 -0
- mineproductivity/core/maybe.py +117 -0
- mineproductivity/core/metadata.py +62 -0
- mineproductivity/core/repository.py +123 -0
- mineproductivity/core/result.py +142 -0
- mineproductivity/core/serialization.py +140 -0
- mineproductivity/core/service.py +30 -0
- mineproductivity/core/specification.py +116 -0
- mineproductivity/core/typing.py +72 -0
- mineproductivity/core/validator.py +118 -0
- mineproductivity/core/value_object.py +85 -0
- mineproductivity/core/versioning.py +47 -0
- mineproductivity/datasets/README.md +40 -0
- mineproductivity/datasets/__init__.py +7 -0
- mineproductivity/decision/README.md +65 -0
- mineproductivity/decision/__init__.py +181 -0
- mineproductivity/decision/_registry.py +66 -0
- mineproductivity/decision/abstractions.py +146 -0
- mineproductivity/decision/alerting.py +103 -0
- mineproductivity/decision/audit.py +130 -0
- mineproductivity/decision/batch.py +99 -0
- mineproductivity/decision/exceptions.py +50 -0
- mineproductivity/decision/explanation.py +117 -0
- mineproductivity/decision/metadata.py +73 -0
- mineproductivity/decision/pipeline.py +131 -0
- mineproductivity/decision/planning.py +166 -0
- mineproductivity/decision/policy.py +197 -0
- mineproductivity/decision/prioritization.py +85 -0
- mineproductivity/decision/ranking.py +111 -0
- mineproductivity/decision/realtime.py +236 -0
- mineproductivity/decision/recommendation.py +80 -0
- mineproductivity/decision/result.py +321 -0
- mineproductivity/decision/root_cause.py +55 -0
- mineproductivity/decision/rules.py +117 -0
- mineproductivity/decision/scoring.py +139 -0
- mineproductivity/decision/strategy.py +212 -0
- mineproductivity/decision/thresholds.py +61 -0
- mineproductivity/decision/what_if.py +57 -0
- mineproductivity/digital_twin/README.md +57 -0
- mineproductivity/digital_twin/__init__.py +115 -0
- mineproductivity/digital_twin/_registry.py +71 -0
- mineproductivity/digital_twin/abstractions.py +140 -0
- mineproductivity/digital_twin/caching.py +82 -0
- mineproductivity/digital_twin/categories.py +164 -0
- mineproductivity/digital_twin/discovery.py +64 -0
- mineproductivity/digital_twin/exceptions.py +50 -0
- mineproductivity/digital_twin/lifecycle.py +31 -0
- mineproductivity/digital_twin/metadata.py +100 -0
- mineproductivity/digital_twin/persistence.py +32 -0
- mineproductivity/digital_twin/result.py +112 -0
- mineproductivity/digital_twin/simulation.py +64 -0
- mineproductivity/digital_twin/snapshot.py +58 -0
- mineproductivity/digital_twin/state.py +77 -0
- mineproductivity/digital_twin/synchronization.py +230 -0
- mineproductivity/digital_twin/telemetry.py +50 -0
- mineproductivity/events/README.md +203 -0
- mineproductivity/events/__init__.py +83 -0
- mineproductivity/events/base_event.py +44 -0
- mineproductivity/events/bus.py +98 -0
- mineproductivity/events/canonical/__init__.py +60 -0
- mineproductivity/events/canonical/consumption_event.py +53 -0
- mineproductivity/events/canonical/cycle_event.py +74 -0
- mineproductivity/events/canonical/delay_event.py +46 -0
- mineproductivity/events/canonical/maintenance_event.py +51 -0
- mineproductivity/events/canonical/production_event.py +46 -0
- mineproductivity/events/canonical/safety_event.py +54 -0
- mineproductivity/events/envelope.py +88 -0
- mineproductivity/events/exceptions.py +48 -0
- mineproductivity/events/identifier.py +69 -0
- mineproductivity/events/replay.py +63 -0
- mineproductivity/events/schema.py +73 -0
- mineproductivity/events/serialization/__init__.py +16 -0
- mineproductivity/events/serialization/arrow_codec.py +91 -0
- mineproductivity/events/serialization/json_codec.py +126 -0
- mineproductivity/events/serialization/parquet_codec.py +100 -0
- mineproductivity/events/snapshot.py +38 -0
- mineproductivity/events/store.py +219 -0
- mineproductivity/events/validation.py +118 -0
- mineproductivity/events/versioning.py +31 -0
- mineproductivity/exceptions/README.md +39 -0
- mineproductivity/exceptions/__init__.py +7 -0
- mineproductivity/io/README.md +39 -0
- mineproductivity/io/__init__.py +7 -0
- mineproductivity/kpis/README.md +245 -0
- mineproductivity/kpis/__init__.py +96 -0
- mineproductivity/kpis/_registry.py +61 -0
- mineproductivity/kpis/aggregation.py +63 -0
- mineproductivity/kpis/backends/__init__.py +44 -0
- mineproductivity/kpis/backends/base_backend.py +78 -0
- mineproductivity/kpis/backends/duckdb_backend.py +59 -0
- mineproductivity/kpis/backends/numpy_backend.py +83 -0
- mineproductivity/kpis/backends/pandas_backend.py +48 -0
- mineproductivity/kpis/backends/polars_backend.py +55 -0
- mineproductivity/kpis/base_kpi.py +72 -0
- mineproductivity/kpis/caching.py +79 -0
- mineproductivity/kpis/categories/__init__.py +30 -0
- mineproductivity/kpis/categories/_common.py +38 -0
- mineproductivity/kpis/categories/cost_kpi.py +18 -0
- mineproductivity/kpis/categories/delay_kpi.py +31 -0
- mineproductivity/kpis/categories/energy_kpi.py +19 -0
- mineproductivity/kpis/categories/haulage_kpi.py +18 -0
- mineproductivity/kpis/categories/maintenance_kpi.py +18 -0
- mineproductivity/kpis/categories/production_kpi.py +20 -0
- mineproductivity/kpis/categories/quality_kpi.py +18 -0
- mineproductivity/kpis/categories/safety_kpi.py +19 -0
- mineproductivity/kpis/categories/utilization_kpi.py +20 -0
- mineproductivity/kpis/certification.py +57 -0
- mineproductivity/kpis/composite.py +61 -0
- mineproductivity/kpis/dependency_graph.py +93 -0
- mineproductivity/kpis/engine.py +223 -0
- mineproductivity/kpis/exceptions.py +42 -0
- mineproductivity/kpis/inheritance.py +54 -0
- mineproductivity/kpis/lifecycle.py +20 -0
- mineproductivity/kpis/metadata.py +151 -0
- mineproductivity/kpis/naming.py +97 -0
- mineproductivity/kpis/result.py +61 -0
- mineproductivity/kpis/standard_library/__init__.py +46 -0
- mineproductivity/kpis/standard_library/cost.py +70 -0
- mineproductivity/kpis/standard_library/delay.py +48 -0
- mineproductivity/kpis/standard_library/energy.py +50 -0
- mineproductivity/kpis/standard_library/haulage.py +51 -0
- mineproductivity/kpis/standard_library/maintenance.py +46 -0
- mineproductivity/kpis/standard_library/production.py +53 -0
- mineproductivity/kpis/standard_library/quality.py +49 -0
- mineproductivity/kpis/standard_library/safety.py +54 -0
- mineproductivity/kpis/standard_library/utilization.py +211 -0
- mineproductivity/kpis/validation.py +67 -0
- mineproductivity/kpis/windowing.py +68 -0
- mineproductivity/ontology/README.md +242 -0
- mineproductivity/ontology/__init__.py +119 -0
- mineproductivity/ontology/cost/__init__.py +7 -0
- mineproductivity/ontology/cost/cost_center.py +56 -0
- mineproductivity/ontology/entity_type.py +241 -0
- mineproductivity/ontology/environmental/__init__.py +7 -0
- mineproductivity/ontology/environmental/emissions.py +65 -0
- mineproductivity/ontology/equipment/__init__.py +35 -0
- mineproductivity/ontology/equipment/ancillary.py +57 -0
- mineproductivity/ontology/equipment/drill.py +27 -0
- mineproductivity/ontology/equipment/equipment_type.py +53 -0
- mineproductivity/ontology/equipment/fixed_plant.py +56 -0
- mineproductivity/ontology/equipment/haul_truck.py +53 -0
- mineproductivity/ontology/equipment/loading_unit.py +61 -0
- mineproductivity/ontology/exceptions.py +23 -0
- mineproductivity/ontology/graph_projection.py +53 -0
- mineproductivity/ontology/location/__init__.py +10 -0
- mineproductivity/ontology/location/mine.py +45 -0
- mineproductivity/ontology/location/pit.py +68 -0
- mineproductivity/ontology/location/route.py +72 -0
- mineproductivity/ontology/location/underground.py +77 -0
- mineproductivity/ontology/maintenance/__init__.py +7 -0
- mineproductivity/ontology/maintenance/failure_mode.py +73 -0
- mineproductivity/ontology/material/__init__.py +8 -0
- mineproductivity/ontology/material/commodity.py +44 -0
- mineproductivity/ontology/material/material_type.py +15 -0
- mineproductivity/ontology/organization/__init__.py +9 -0
- mineproductivity/ontology/organization/business_unit.py +45 -0
- mineproductivity/ontology/organization/crew.py +61 -0
- mineproductivity/ontology/organization/fleet.py +43 -0
- mineproductivity/ontology/production/__init__.py +7 -0
- mineproductivity/ontology/production/shift.py +95 -0
- mineproductivity/ontology/quality/__init__.py +7 -0
- mineproductivity/ontology/quality/grade.py +74 -0
- mineproductivity/ontology/reference/__init__.py +12 -0
- mineproductivity/ontology/reference/delay_taxonomy.py +47 -0
- mineproductivity/ontology/relationship.py +50 -0
- mineproductivity/ontology/safety/__init__.py +7 -0
- mineproductivity/ontology/safety/hazard.py +86 -0
- mineproductivity/ontology/validation.py +75 -0
- mineproductivity/optimization/README.md +34 -0
- mineproductivity/optimization/__init__.py +112 -0
- mineproductivity/optimization/_registry.py +47 -0
- mineproductivity/optimization/abstractions.py +109 -0
- mineproductivity/optimization/comparison.py +64 -0
- mineproductivity/optimization/constraint_programming.py +38 -0
- mineproductivity/optimization/discovery.py +80 -0
- mineproductivity/optimization/evolutionary.py +49 -0
- mineproductivity/optimization/exceptions.py +49 -0
- mineproductivity/optimization/executor.py +234 -0
- mineproductivity/optimization/linear_programming.py +44 -0
- mineproductivity/optimization/metadata.py +72 -0
- mineproductivity/optimization/mixed_integer_programming.py +43 -0
- mineproductivity/optimization/multi_objective.py +43 -0
- mineproductivity/optimization/network_optimization.py +43 -0
- mineproductivity/optimization/persistence.py +23 -0
- mineproductivity/optimization/problem.py +238 -0
- mineproductivity/optimization/result.py +61 -0
- mineproductivity/optimization/run.py +71 -0
- mineproductivity/optimization/sensitivity.py +130 -0
- mineproductivity/optimization/state.py +48 -0
- mineproductivity/plugins/README.md +148 -0
- mineproductivity/plugins/__init__.py +33 -0
- mineproductivity/plugins/dependency.py +70 -0
- mineproductivity/plugins/exceptions.py +18 -0
- mineproductivity/plugins/lifecycle.py +140 -0
- mineproductivity/plugins/loader.py +53 -0
- mineproductivity/plugins/manifest.py +57 -0
- mineproductivity/py.typed +0 -0
- mineproductivity/registry/README.md +172 -0
- mineproductivity/registry/__init__.py +43 -0
- mineproductivity/registry/caching.py +55 -0
- mineproductivity/registry/decorators.py +63 -0
- mineproductivity/registry/entry_point.py +80 -0
- mineproductivity/registry/exceptions.py +34 -0
- mineproductivity/registry/registry.py +141 -0
- mineproductivity/registry/version_compat.py +75 -0
- mineproductivity/simulation/README.md +62 -0
- mineproductivity/simulation/__init__.py +115 -0
- mineproductivity/simulation/_registry.py +63 -0
- mineproductivity/simulation/abstractions.py +127 -0
- mineproductivity/simulation/caching.py +80 -0
- mineproductivity/simulation/calibration.py +55 -0
- mineproductivity/simulation/clock.py +82 -0
- mineproductivity/simulation/comparison.py +95 -0
- mineproductivity/simulation/discovery.py +104 -0
- mineproductivity/simulation/discrete_event.py +50 -0
- mineproductivity/simulation/exceptions.py +52 -0
- mineproductivity/simulation/executor.py +275 -0
- mineproductivity/simulation/experiment.py +117 -0
- mineproductivity/simulation/metadata.py +92 -0
- mineproductivity/simulation/montecarlo.py +58 -0
- mineproductivity/simulation/persistence.py +29 -0
- mineproductivity/simulation/replay.py +73 -0
- mineproductivity/simulation/result.py +66 -0
- mineproductivity/simulation/run.py +93 -0
- mineproductivity/simulation/scenario.py +156 -0
- mineproductivity/simulation/sensitivity.py +92 -0
- mineproductivity/simulation/state.py +70 -0
- mineproductivity/simulation/system_dynamics.py +49 -0
- mineproductivity/typing/why.txt +15 -0
- mineproductivity/utils/README.md +39 -0
- mineproductivity/utils/__init__.py +7 -0
- mineproductivity/validation/README.md +39 -0
- mineproductivity/validation/__init__.py +7 -0
- mineproductivity/visualization/README.md +27 -0
- mineproductivity/visualization/__init__.py +84 -0
- mineproductivity/visualization/_registry.py +85 -0
- mineproductivity/visualization/abstractions.py +179 -0
- mineproductivity/visualization/dashboard.py +64 -0
- mineproductivity/visualization/dashboard_builder.py +107 -0
- mineproductivity/visualization/discovery.py +46 -0
- mineproductivity/visualization/exceptions.py +42 -0
- mineproductivity/visualization/export.py +62 -0
- mineproductivity/visualization/layout.py +44 -0
- mineproductivity/visualization/persistence.py +26 -0
- mineproductivity/visualization/pipeline.py +88 -0
- mineproductivity/visualization/presentation.py +56 -0
- mineproductivity/visualization/renderer.py +90 -0
- mineproductivity/visualization/report.py +47 -0
- mineproductivity/visualization/report_builder.py +63 -0
- mineproductivity/visualization/theme.py +44 -0
- mineproductivity/visualization/widget.py +49 -0
- mineproductivity-2.0.0.dist-info/METADATA +445 -0
- mineproductivity-2.0.0.dist-info/RECORD +344 -0
- mineproductivity-2.0.0.dist-info/WHEEL +4 -0
- mineproductivity-2.0.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# mineproductivity (package root)
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
The single installable Python package containing every MineProductivity subsystem, organized as independently documented sub-packages with an enforced, inward-pointing dependency direction.
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
|
|
9
|
+
The `src/` layout root for the `mineproductivity` distribution. Contains 24 subsystem packages plus the package-level `__init__.py` exposing `__version__`. Does not contain tests (see `tests/`), datasets (see `datasets/`), or documentation (see `docs/`).
|
|
10
|
+
|
|
11
|
+
## Responsibilities
|
|
12
|
+
|
|
13
|
+
- Expose the package version and top-level public API surface (currently empty).
|
|
14
|
+
- House all subsystem sub-packages listed below, each independently documented.
|
|
15
|
+
|
|
16
|
+
## Contents
|
|
17
|
+
|
|
18
|
+
- `core/`, `events/`, `ontology/`, `kpis/`, `datasets/`, `connectors/`, `analytics/`, `optimization/`, `simulation/`, `digital_twin/`, `decision/`, `agents/`, `visualization/`, `benchmark/`, `certification/`, `config/`, `io/`, `utils/`, `typing/`, `cli/`, `exceptions/`, `registry/`, `plugins/`, `validation/`
|
|
19
|
+
|
|
20
|
+
## Dependencies
|
|
21
|
+
|
|
22
|
+
No third-party runtime dependencies are declared yet (see pyproject.toml). Internal dependency direction between sub-packages is documented in the root README.md.
|
|
23
|
+
|
|
24
|
+
## Future Work
|
|
25
|
+
|
|
26
|
+
Implement each sub-package per the phasing in ROADMAP.md, always starting with tests and metadata before behavior.
|
|
27
|
+
|
|
28
|
+
## References
|
|
29
|
+
|
|
30
|
+
- Master Architecture Handbook v1.0
|
|
31
|
+
- Reference Implementation Blueprint v1.0
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""MineProductivity — a metadata-first, ontology-driven, event-sourced
|
|
2
|
+
platform for mining productivity intelligence.
|
|
3
|
+
|
|
4
|
+
The ``mineproductivity.core`` package contains the platform's foundational
|
|
5
|
+
framework primitives (entities, value objects, identifiers, specifications,
|
|
6
|
+
repositories, and friends). The ``mineproductivity.events`` package
|
|
7
|
+
implements the immutable, append-only event model (Event Sourcing) built on
|
|
8
|
+
it, per the locked Event Framework Design Specification. The
|
|
9
|
+
``mineproductivity.ontology`` package implements the full typed,
|
|
10
|
+
machine-readable domain vocabulary (ten sub-ontology families,
|
|
11
|
+
relationships, contextual validation, and the Knowledge Graph projection
|
|
12
|
+
contract) per the locked Ontology Framework Design Specification. The
|
|
13
|
+
``mineproductivity.registry`` and ``mineproductivity.plugins`` packages
|
|
14
|
+
implement the plugin-first discovery-and-lifecycle backbone (the generic
|
|
15
|
+
``Registry`` mechanism, entry-point discovery, and version-gated,
|
|
16
|
+
isolation-guaranteed plugin activation) per the locked Registry Framework
|
|
17
|
+
Design Specification. The ``mineproductivity.connectors`` package
|
|
18
|
+
implements the vendor-neutral ingestion boundary (the ``FMSConnector``
|
|
19
|
+
contract, reference file/network/streaming connectors, and
|
|
20
|
+
documentation-only OEM adapter shapes) per the locked Connector Framework
|
|
21
|
+
Design Specification. The ``mineproductivity.kpis`` package now implements
|
|
22
|
+
the metric backbone -- the metadata-first, self-describing KPI Engine (the
|
|
23
|
+
``BaseKPI``/``CompositeKPI`` object model, dependency-graph orchestration,
|
|
24
|
+
pluggable execution backends, and the 12-KPI Standard Library reference
|
|
25
|
+
implementation) per the locked KPI Engine Design Specification. The
|
|
26
|
+
``mineproductivity.analytics`` package implements the statistical and
|
|
27
|
+
analytical computation layer built directly on ``kpis`` -- trend, baseline,
|
|
28
|
+
and benchmark analysis, rolling and aggregate statistics, data-quality
|
|
29
|
+
scoring, batch/streaming/incremental execution modes, and the plugin
|
|
30
|
+
registry -- per the locked Analytics Engine Design Specification. The
|
|
31
|
+
``mineproductivity.decision`` package implements the platform's
|
|
32
|
+
prescriptive layer built directly on ``analytics`` -- rule/policy-driven
|
|
33
|
+
decision strategies, ranking, explanation, prioritization, action
|
|
34
|
+
planning, alerting, real-time and batch decision execution, and an
|
|
35
|
+
append-only decision audit trail -- per the locked Decision Intelligence
|
|
36
|
+
Design Specification. The ``mineproductivity.digital_twin``,
|
|
37
|
+
``mineproductivity.simulation``, and ``mineproductivity.optimization``
|
|
38
|
+
packages implement the stateful representation, projection, and
|
|
39
|
+
prescriptive-search layers respectively, ``mineproductivity.agents``
|
|
40
|
+
implements the model-independent agent-orchestration layer, and
|
|
41
|
+
``mineproductivity.visualization`` implements the presentation layer --
|
|
42
|
+
the final package in the architecture -- each per its own locked design
|
|
43
|
+
specification. Every package in the locked dependency chain is now
|
|
44
|
+
implemented. See the root README.md and docs/architecture/README.md for
|
|
45
|
+
the governing architecture, and ROADMAP.md for the implementation
|
|
46
|
+
phasing.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
__version__ = "2.0.0"
|
|
50
|
+
__all__ = ["__version__"]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# mineproductivity.agents
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
The AI-agent orchestration layer (design spec 11) — a model-independent orchestration layer for autonomous and semi-autonomous work, built directly above `optimization`. It defines *how* agent work is governed (`AgentPolicy`/`PolicyEngine`/`AgentCapabilitySet`), gated (`ApprovalRequest`), executed (`Task`/`TaskExecutor`), decomposed and delegated (`Goal`/`WorkflowEngine`/`AgentMessage`/`DelegationRequest`), and audited (`AgentAuditTrail`) — while `Agent`, `Tool`, and `AgentMemory` remain interface-only extension points: choosing a reasoning backend is exactly the implementation decision this package excludes.
|
|
6
|
+
|
|
7
|
+
## Governing documents
|
|
8
|
+
|
|
9
|
+
- [`docs/architecture/11_AI_Agents_Design_Specification.md`](../../../docs/architecture/11_AI_Agents_Design_Specification.md)
|
|
10
|
+
- [`docs/design/11_AI_Agents_Implementation_Checklist.md`](../../../docs/design/11_AI_Agents_Implementation_Checklist.md)
|
|
11
|
+
- [`docs/adr/ADR-0011-AI-Agents.md`](../../../docs/adr/ADR-0011-AI-Agents.md)
|
|
12
|
+
|
|
13
|
+
## Scope
|
|
14
|
+
|
|
15
|
+
**What belongs here:** agent/task/policy/approval/workflow/audit orchestration contracts and their reference execution path.
|
|
16
|
+
|
|
17
|
+
**What must never belong here:** a concrete `Agent`, `Tool`, or `AgentMemory` implementation; any LLM-provider SDK coupling; any recomputation of a KPI, statistical, decision, twin-state, simulation, or optimization fact a lower package already owns.
|
|
18
|
+
|
|
19
|
+
## Dependencies
|
|
20
|
+
|
|
21
|
+
**Depends on:** `core`, `events` (transport for `AgentMessage`, composed by callers), `registry`, `connectors` (`RetryPolicy` as the retry-configuration shape, per spec 11 §12), `kpis`, `analytics`, `decision` (`Explanation` reused directly), `digital_twin`, `simulation`, `optimization`.
|
|
22
|
+
|
|
23
|
+
**Depended on by:** `visualization` (future). No lower package imports `agents`.
|
|
24
|
+
|
|
25
|
+
## Extension points
|
|
26
|
+
|
|
27
|
+
Register a concrete `Agent` with `@register` (entry-point group `mineproductivity.agents`) and a concrete `Tool` with `@register_tool` (group `mineproductivity.agents.tools`); implement a `TaskRepository` backend as a `core.BaseRepository[Task, str]`; subclass `AgentMemory` for a memory backend (wired per-agent, not globally discovered).
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""``mineproductivity.agents`` -- the AI-agent orchestration layer
|
|
2
|
+
(design spec 11): a model-independent orchestration layer for
|
|
3
|
+
autonomous and semi-autonomous work, built directly above
|
|
4
|
+
``optimization``. Defines *how* agent work is governed, gated,
|
|
5
|
+
executed, delegated, and audited -- ``Agent``/``Tool``/``AgentMemory``
|
|
6
|
+
are interface-only extension points; choosing a reasoning backend is
|
|
7
|
+
exactly the implementation decision this package excludes (§3.1, §4).
|
|
8
|
+
|
|
9
|
+
Public API (design spec §7) -- every name stable once implementation
|
|
10
|
+
begins.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from mineproductivity.agents._registry import REGISTRY, TOOLS, register, register_tool
|
|
16
|
+
from mineproductivity.agents.abstractions import Agent, AgentContext
|
|
17
|
+
from mineproductivity.agents.approval import ApprovalRequest, ApprovalStatus
|
|
18
|
+
from mineproductivity.agents.audit import AgentAuditEntry, AgentAuditTrail
|
|
19
|
+
from mineproductivity.agents.capability import AgentCapabilitySet, Permission
|
|
20
|
+
from mineproductivity.agents.communication import AgentMessage, DelegationRequest
|
|
21
|
+
from mineproductivity.agents.conversation import ConversationContext, ConversationTurn
|
|
22
|
+
from mineproductivity.agents.discovery import by_category, by_scope
|
|
23
|
+
from mineproductivity.agents.exceptions import (
|
|
24
|
+
AgentExecutionError,
|
|
25
|
+
AgentValidationError,
|
|
26
|
+
AgentVersionConflictError,
|
|
27
|
+
PermissionDeniedError,
|
|
28
|
+
PolicyConflictError,
|
|
29
|
+
TaskNotFoundError,
|
|
30
|
+
)
|
|
31
|
+
from mineproductivity.agents.executor import TaskExecutor
|
|
32
|
+
from mineproductivity.agents.goal import Goal
|
|
33
|
+
from mineproductivity.agents.memory import AgentMemory
|
|
34
|
+
from mineproductivity.agents.metadata import AgentCategory, AgentMetadata
|
|
35
|
+
from mineproductivity.agents.persistence import TaskRepository
|
|
36
|
+
from mineproductivity.agents.policy import AgentPolicy, PolicyEngine, PolicyStatus
|
|
37
|
+
from mineproductivity.agents.result import AgentResult
|
|
38
|
+
from mineproductivity.agents.state import TaskState
|
|
39
|
+
from mineproductivity.agents.task import Task, TaskStatus
|
|
40
|
+
from mineproductivity.agents.tool import Tool, ToolInvocation, ToolMetadata
|
|
41
|
+
from mineproductivity.agents.workflow import WorkflowEngine
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
"Agent",
|
|
45
|
+
"AgentAuditEntry",
|
|
46
|
+
"AgentAuditTrail",
|
|
47
|
+
"AgentCapabilitySet",
|
|
48
|
+
"AgentCategory",
|
|
49
|
+
"AgentContext",
|
|
50
|
+
"AgentExecutionError",
|
|
51
|
+
"AgentMemory",
|
|
52
|
+
"AgentMessage",
|
|
53
|
+
"AgentMetadata",
|
|
54
|
+
"AgentPolicy",
|
|
55
|
+
"AgentResult",
|
|
56
|
+
"AgentValidationError",
|
|
57
|
+
"AgentVersionConflictError",
|
|
58
|
+
"ApprovalRequest",
|
|
59
|
+
"ApprovalStatus",
|
|
60
|
+
"ConversationContext",
|
|
61
|
+
"ConversationTurn",
|
|
62
|
+
"DelegationRequest",
|
|
63
|
+
"Goal",
|
|
64
|
+
"Permission",
|
|
65
|
+
"PermissionDeniedError",
|
|
66
|
+
"PolicyConflictError",
|
|
67
|
+
"PolicyEngine",
|
|
68
|
+
"PolicyStatus",
|
|
69
|
+
"REGISTRY",
|
|
70
|
+
"TOOLS",
|
|
71
|
+
"Task",
|
|
72
|
+
"TaskExecutor",
|
|
73
|
+
"TaskNotFoundError",
|
|
74
|
+
"TaskRepository",
|
|
75
|
+
"TaskState",
|
|
76
|
+
"TaskStatus",
|
|
77
|
+
"Tool",
|
|
78
|
+
"ToolInvocation",
|
|
79
|
+
"ToolMetadata",
|
|
80
|
+
"WorkflowEngine",
|
|
81
|
+
"by_category",
|
|
82
|
+
"by_scope",
|
|
83
|
+
"register",
|
|
84
|
+
"register_tool",
|
|
85
|
+
]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""The Agent Registry and the Tool Registry (design spec §22) -- two
|
|
2
|
+
typed specializations of ``registry.Registry``, the first package in
|
|
3
|
+
this series to hold two distinct ``Registry`` instances rather than
|
|
4
|
+
one, since an ``Agent`` type and a ``Tool`` type are orthogonal
|
|
5
|
+
registrable concepts (§22): an ``Agent`` decides; a ``Tool`` acts at
|
|
6
|
+
an ``Agent``'s direction. The two are never merged. Both answer the
|
|
7
|
+
type-level question ("which types are known"), never conflated with
|
|
8
|
+
``TaskRepository`` (instance-level) or ``discovery.py`` (query
|
|
9
|
+
facade). Entry-point discovery uses ``registry.EntryPointDiscovery``
|
|
10
|
+
with ``EntryPointSpec(group="mineproductivity.agents",
|
|
11
|
+
target_registry="agents")`` and
|
|
12
|
+
``EntryPointSpec(group="mineproductivity.agents.tools",
|
|
13
|
+
target_registry="agents.tools")`` (design spec §31), exactly as every
|
|
14
|
+
prior domain package already wires its own group.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from mineproductivity.registry import Registry
|
|
20
|
+
|
|
21
|
+
from mineproductivity.agents.abstractions import Agent
|
|
22
|
+
from mineproductivity.agents.exceptions import (
|
|
23
|
+
AgentValidationError,
|
|
24
|
+
AgentVersionConflictError,
|
|
25
|
+
)
|
|
26
|
+
from mineproductivity.agents.tool import Tool
|
|
27
|
+
|
|
28
|
+
__all__ = ["REGISTRY", "TOOLS", "register", "register_tool"]
|
|
29
|
+
|
|
30
|
+
REGISTRY: Registry[str, type[Agent]] = Registry(name="agents")
|
|
31
|
+
TOOLS: Registry[str, type[Tool]] = Registry(name="agents.tools")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def register(cls: type[Agent]) -> type[Agent]:
|
|
35
|
+
"""Register ``cls`` into :data:`REGISTRY`, keyed by
|
|
36
|
+
``cls.meta.code``.
|
|
37
|
+
|
|
38
|
+
Raises
|
|
39
|
+
------
|
|
40
|
+
AgentValidationError
|
|
41
|
+
If ``cls.meta.code`` is empty (defensive, redundant guard --
|
|
42
|
+
``AgentMetadata.validate()`` already rejects it).
|
|
43
|
+
AgentVersionConflictError
|
|
44
|
+
If ``cls.meta.code`` is already registered -- add-only, raised
|
|
45
|
+
at registration time, never deferred (design spec §26).
|
|
46
|
+
"""
|
|
47
|
+
if not cls.meta.code:
|
|
48
|
+
raise AgentValidationError(f"{cls.__name__}.meta.code must not be empty")
|
|
49
|
+
|
|
50
|
+
result = REGISTRY.register(cls.meta.code, cls, metadata=cls.meta)
|
|
51
|
+
if result.is_err:
|
|
52
|
+
raise AgentVersionConflictError(
|
|
53
|
+
f"Agent code {cls.meta.code!r} is already registered; changing what it "
|
|
54
|
+
f"means requires a new code or a reviewed version bump, not "
|
|
55
|
+
f"re-registration"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
return cls
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def register_tool(cls: type[Tool]) -> type[Tool]:
|
|
62
|
+
"""Register ``cls`` into :data:`TOOLS`, keyed by ``cls.meta.code``
|
|
63
|
+
-- identical shape and identical error semantics as
|
|
64
|
+
:func:`register`, specialized for ``Tool`` (design spec §22).
|
|
65
|
+
|
|
66
|
+
Raises
|
|
67
|
+
------
|
|
68
|
+
AgentValidationError
|
|
69
|
+
If ``cls.meta.code`` is empty.
|
|
70
|
+
AgentVersionConflictError
|
|
71
|
+
If ``cls.meta.code`` is already registered in :data:`TOOLS`.
|
|
72
|
+
"""
|
|
73
|
+
if not cls.meta.code:
|
|
74
|
+
raise AgentValidationError(f"{cls.__name__}.meta.code must not be empty")
|
|
75
|
+
|
|
76
|
+
result = TOOLS.register(cls.meta.code, cls, metadata=cls.meta)
|
|
77
|
+
if result.is_err:
|
|
78
|
+
raise AgentVersionConflictError(
|
|
79
|
+
f"Tool code {cls.meta.code!r} is already registered; changing what it "
|
|
80
|
+
f"means requires a new code or a reviewed version bump, not "
|
|
81
|
+
f"re-registration"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
return cls
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""``Agent``: the "Agent-as-object" root, and ``AgentContext``, the
|
|
2
|
+
collaborator/evidence bundle a concrete agent reasons over (design
|
|
3
|
+
spec §8).
|
|
4
|
+
|
|
5
|
+
Reuse audit: ``AgentContext`` mirrors
|
|
6
|
+
``optimization.OptimizationContext`` (spec 10 §8) one layer up,
|
|
7
|
+
extended with ``optimization_results`` -- an agent may read an
|
|
8
|
+
already-solved plan a not-yet-solved ``OptimizationProblem`` could
|
|
9
|
+
never reference. Evidence is read, never re-derived (§3.2), and
|
|
10
|
+
assembly happens once, at construction -- never re-fetched per retry
|
|
11
|
+
or per delegation (§27, §36). Per ``decision.DecisionContext``'s own
|
|
12
|
+
established convention (spec 07 §8), the caller-assembles pattern is
|
|
13
|
+
used exclusively. ``Agent`` shares one abstract method (``_act``)
|
|
14
|
+
across all ten categories, mirroring ``decision.DecisionModel``'s
|
|
15
|
+
shared-``_decide`` posture (spec 07 §8) rather than ``simulation``'s/
|
|
16
|
+
``optimization``'s no-shared-method posture: every category answers
|
|
17
|
+
the same underlying question -- given a task and context, decide what
|
|
18
|
+
should happen next. THIS MODULE SHIPS NO CONCRETE SUBCLASS --
|
|
19
|
+
choosing a specific reasoning backend (a hosted large-language-model
|
|
20
|
+
service, a local model, a rule engine) is exactly the kind of
|
|
21
|
+
implementation decision this package's charter (§3.1, §3.5, §4)
|
|
22
|
+
excludes.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from abc import ABC, abstractmethod
|
|
28
|
+
from collections.abc import Sequence
|
|
29
|
+
from typing import TYPE_CHECKING, ClassVar
|
|
30
|
+
|
|
31
|
+
from mineproductivity.analytics import AnalyticsResult
|
|
32
|
+
from mineproductivity.decision import DecisionResult
|
|
33
|
+
from mineproductivity.digital_twin import TwinSnapshot
|
|
34
|
+
from mineproductivity.kpis import KPIResult
|
|
35
|
+
from mineproductivity.optimization import OptimizationResult
|
|
36
|
+
from mineproductivity.simulation import SimulationResult
|
|
37
|
+
|
|
38
|
+
from mineproductivity.agents.metadata import AgentMetadata
|
|
39
|
+
|
|
40
|
+
if TYPE_CHECKING:
|
|
41
|
+
from mineproductivity.agents.result import AgentResult
|
|
42
|
+
from mineproductivity.agents.task import Task
|
|
43
|
+
|
|
44
|
+
__all__ = ["Agent", "AgentContext"]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AgentContext:
|
|
48
|
+
"""Bundles the evidence an ``Agent`` may need (design spec §8) --
|
|
49
|
+
carried once, at construction; every fact an agent reasons over
|
|
50
|
+
has a stable, structured home in a lower package, never a
|
|
51
|
+
re-derivation of its own.
|
|
52
|
+
|
|
53
|
+
Examples
|
|
54
|
+
--------
|
|
55
|
+
>>> context = AgentContext()
|
|
56
|
+
>>> context.kpi_results
|
|
57
|
+
()
|
|
58
|
+
>>> context.twin_snapshot is None
|
|
59
|
+
True
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
*,
|
|
65
|
+
kpi_results: Sequence[KPIResult] = (),
|
|
66
|
+
analytics_results: Sequence[AnalyticsResult] = (),
|
|
67
|
+
decision_results: Sequence[DecisionResult] = (),
|
|
68
|
+
twin_snapshot: TwinSnapshot | None = None,
|
|
69
|
+
simulation_results: Sequence[SimulationResult] = (),
|
|
70
|
+
optimization_results: Sequence[OptimizationResult] = (),
|
|
71
|
+
) -> None:
|
|
72
|
+
self.kpi_results = tuple(kpi_results)
|
|
73
|
+
self.analytics_results = tuple(analytics_results)
|
|
74
|
+
self.decision_results = tuple(decision_results)
|
|
75
|
+
self.twin_snapshot = twin_snapshot
|
|
76
|
+
self.simulation_results = tuple(simulation_results)
|
|
77
|
+
self.optimization_results = tuple(optimization_results)
|
|
78
|
+
|
|
79
|
+
def __repr__(self) -> str:
|
|
80
|
+
return (
|
|
81
|
+
f"{type(self).__name__}(kpi_results={self.kpi_results!r}, "
|
|
82
|
+
f"analytics_results={self.analytics_results!r}, "
|
|
83
|
+
f"decision_results={self.decision_results!r}, "
|
|
84
|
+
f"twin_snapshot={self.twin_snapshot!r}, "
|
|
85
|
+
f"simulation_results={self.simulation_results!r}, "
|
|
86
|
+
f"optimization_results={self.optimization_results!r})"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class Agent(ABC):
|
|
91
|
+
"""The root of every registrable agent type (design spec §8) --
|
|
92
|
+
the direct counterpart of ``optimization.OptimizationModel`` one
|
|
93
|
+
layer down, whose category members (§29) are domain roles rather
|
|
94
|
+
than algorithmic paradigms.
|
|
95
|
+
|
|
96
|
+
**Statelessness.** Every subclass, of every category, is stateless
|
|
97
|
+
-- no instance attribute is mutated by any ``_act`` implementation
|
|
98
|
+
(§29, §32); statefulness lives entirely in ``Task`` (§11), so the
|
|
99
|
+
same registered ``Agent`` type can execute many concurrent
|
|
100
|
+
``Task``\\ s safely.
|
|
101
|
+
|
|
102
|
+
**Qualify, don't coerce** (§30): no ``_act`` implementation raises
|
|
103
|
+
for a legitimately incomplete or ambiguous task -- it returns an
|
|
104
|
+
``AgentResult`` carrying a warning instead.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
meta: ClassVar[AgentMetadata]
|
|
108
|
+
|
|
109
|
+
@abstractmethod
|
|
110
|
+
def _act(self, task: Task, *, context: AgentContext) -> AgentResult:
|
|
111
|
+
"""Given ``task`` and ``context``, decide what should happen
|
|
112
|
+
next -- the one shared reasoning call every category answers
|
|
113
|
+
(design spec §8)."""
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""``ApprovalRequest``/``ApprovalStatus``: the human-in-the-loop gate
|
|
2
|
+
(design spec §16).
|
|
3
|
+
|
|
4
|
+
Reuse audit: ``core.BaseValueObject`` reused verbatim. Genuinely new
|
|
5
|
+
to this series: no package below ``agents`` pauses mid-execution for a
|
|
6
|
+
human decision. An ``ApprovalRequest`` resolving to ``APPROVED``
|
|
7
|
+
transitions its ``Task`` from ``AWAITING_APPROVAL`` back to
|
|
8
|
+
``RUNNING``; a resolution to ``REJECTED`` transitions it directly to
|
|
9
|
+
``FAILED``, carrying the rejection as a warning on the eventual
|
|
10
|
+
``AgentResult`` (§16, §11). ``TaskExecutor`` never resolves an
|
|
11
|
+
``ApprovalRequest`` itself -- resolution is exclusively a caller
|
|
12
|
+
(human-supervisor-facing) action.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import dataclasses
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from enum import Enum
|
|
20
|
+
|
|
21
|
+
from mineproductivity.core import BaseValueObject
|
|
22
|
+
|
|
23
|
+
__all__ = ["ApprovalRequest", "ApprovalStatus"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ApprovalStatus(Enum):
|
|
27
|
+
"""The three-state approval lifecycle a ``Task`` pauses on when
|
|
28
|
+
``PolicyEngine`` (design spec §10) requires it -- closed; a new
|
|
29
|
+
approval *policy* (who must approve what) is an ``AgentPolicy``
|
|
30
|
+
concern, not a change here."""
|
|
31
|
+
|
|
32
|
+
PENDING = "pending"
|
|
33
|
+
APPROVED = "approved"
|
|
34
|
+
REJECTED = "rejected"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
38
|
+
class ApprovalRequest(BaseValueObject):
|
|
39
|
+
"""One pending, approved, or rejected request for a human to
|
|
40
|
+
authorize a ``Task`` before ``TaskExecutor`` (design spec §12)
|
|
41
|
+
resumes it.
|
|
42
|
+
|
|
43
|
+
Examples
|
|
44
|
+
--------
|
|
45
|
+
>>> request = ApprovalRequest(task_id="TASK-1", requested_action="approve_shutdown")
|
|
46
|
+
>>> request.status
|
|
47
|
+
<ApprovalStatus.PENDING: 'pending'>
|
|
48
|
+
>>> request.approver is None
|
|
49
|
+
True
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
task_id: str
|
|
53
|
+
requested_action: str
|
|
54
|
+
status: ApprovalStatus = dataclasses.field(default=ApprovalStatus.PENDING, kw_only=True)
|
|
55
|
+
approver: str | None = dataclasses.field(default=None, kw_only=True)
|
|
56
|
+
resolved_at: datetime | None = dataclasses.field(default=None, kw_only=True)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""``AgentAuditTrail``/``AgentAuditEntry``: accountability for
|
|
2
|
+
autonomous action (design spec §21), mirroring
|
|
3
|
+
``decision.DecisionAuditTrail``'s pattern and rationale (spec 07 §27)
|
|
4
|
+
-- the accountability mechanism an autonomous-action system requires
|
|
5
|
+
even more urgently than a purely recommending one does.
|
|
6
|
+
|
|
7
|
+
Thread safety (design spec §32): :meth:`AgentAuditTrail.record`
|
|
8
|
+
serializes concurrent appends internally via one ``threading.Lock``,
|
|
9
|
+
so many concurrently-executing ``Task``\\ s can share one trail
|
|
10
|
+
instance safely; :meth:`AgentAuditTrail.query` reads a snapshot taken
|
|
11
|
+
under the same lock, then filters outside it, so it never blocks on a
|
|
12
|
+
concurrent ``record()`` beyond the snapshot itself. Every
|
|
13
|
+
``AgentResult``'s ``explanation`` and every ``ToolInvocation`` it
|
|
14
|
+
carries are preserved verbatim in the recorded entry -- an agent's
|
|
15
|
+
audit record is never a summary, always the full, structured outcome.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import dataclasses
|
|
21
|
+
import threading
|
|
22
|
+
from collections.abc import Mapping, Sequence
|
|
23
|
+
from datetime import datetime
|
|
24
|
+
from types import MappingProxyType
|
|
25
|
+
|
|
26
|
+
from mineproductivity.core import BaseValueObject
|
|
27
|
+
|
|
28
|
+
from mineproductivity.agents.result import AgentResult
|
|
29
|
+
|
|
30
|
+
__all__ = ["AgentAuditEntry", "AgentAuditTrail"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
34
|
+
class AgentAuditEntry(BaseValueObject):
|
|
35
|
+
"""One append-only record: which agent produced which result, for
|
|
36
|
+
what scope, when (design spec §21).
|
|
37
|
+
|
|
38
|
+
Examples
|
|
39
|
+
--------
|
|
40
|
+
>>> from datetime import timezone
|
|
41
|
+
>>> entry = AgentAuditEntry(
|
|
42
|
+
... recorded_at=datetime(2026, 7, 1, tzinfo=timezone.utc),
|
|
43
|
+
... result=AgentResult(task_id="TASK-1"),
|
|
44
|
+
... agent_code="FLEET.ReassignmentAdvisor", scope={"pit": "north"},
|
|
45
|
+
... )
|
|
46
|
+
>>> dict(entry.scope)
|
|
47
|
+
{'pit': 'north'}
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
recorded_at: datetime
|
|
51
|
+
result: AgentResult
|
|
52
|
+
agent_code: str
|
|
53
|
+
scope: Mapping[str, str]
|
|
54
|
+
|
|
55
|
+
def _normalize(self) -> None:
|
|
56
|
+
super(AgentAuditEntry, self)._normalize()
|
|
57
|
+
object.__setattr__(self, "scope", MappingProxyType(dict(self.scope)))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class AgentAuditTrail:
|
|
61
|
+
"""Append-only record of every terminal ``AgentResult`` produced
|
|
62
|
+
by any ``TaskExecutor`` run in this process (design spec §21).
|
|
63
|
+
Serializes via ``core.serialization`` exactly as every other value
|
|
64
|
+
object in this platform does.
|
|
65
|
+
|
|
66
|
+
Examples
|
|
67
|
+
--------
|
|
68
|
+
>>> from datetime import timezone
|
|
69
|
+
>>> trail = AgentAuditTrail()
|
|
70
|
+
>>> trail.record(AgentAuditEntry(
|
|
71
|
+
... recorded_at=datetime(2026, 7, 1, tzinfo=timezone.utc),
|
|
72
|
+
... result=AgentResult(task_id="TASK-1"),
|
|
73
|
+
... agent_code="FLEET.ReassignmentAdvisor", scope={"pit": "north"},
|
|
74
|
+
... ))
|
|
75
|
+
>>> len(trail.query())
|
|
76
|
+
1
|
|
77
|
+
>>> len(trail.query(scope={"pit": "south"}))
|
|
78
|
+
0
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def __init__(self) -> None:
|
|
82
|
+
self._entries: list[AgentAuditEntry] = []
|
|
83
|
+
self._lock = threading.Lock()
|
|
84
|
+
|
|
85
|
+
def record(self, entry: AgentAuditEntry) -> None:
|
|
86
|
+
with self._lock:
|
|
87
|
+
self._entries.append(entry)
|
|
88
|
+
|
|
89
|
+
def query(self, *, scope: Mapping[str, str] | None = None) -> Sequence[AgentAuditEntry]:
|
|
90
|
+
with self._lock:
|
|
91
|
+
snapshot = tuple(self._entries)
|
|
92
|
+
if scope is None:
|
|
93
|
+
return snapshot
|
|
94
|
+
return tuple(
|
|
95
|
+
entry
|
|
96
|
+
for entry in snapshot
|
|
97
|
+
if all(entry.scope.get(key) == value for key, value in scope.items())
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def __repr__(self) -> str:
|
|
101
|
+
with self._lock:
|
|
102
|
+
return f"{type(self).__name__}(entries={len(self._entries)})"
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""``Permission``/``AgentCapabilitySet``: authorization for autonomous
|
|
2
|
+
action (design spec §9).
|
|
3
|
+
|
|
4
|
+
Reuse audit: ``core.BaseValueObject`` and the
|
|
5
|
+
``MappingProxyType``-freezing convention reused verbatim; ``scope``
|
|
6
|
+
carries the same ontology-expressed vocabulary (equipment, pit, shift)
|
|
7
|
+
``digital_twin.Twin.scope`` already carries (spec 08 §9), held as
|
|
8
|
+
plain strings per Documentation Governance Rule #005. Genuinely new to
|
|
9
|
+
this series: no package below ``agents`` models authorization, since
|
|
10
|
+
none of them acts autonomously in the first place.
|
|
11
|
+
|
|
12
|
+
Governance: an ``AgentCapabilitySet`` is always an explicit, authored,
|
|
13
|
+
and governed artifact -- never inferred from an ``Agent`` subclass's
|
|
14
|
+
own code at runtime (design spec §9). :func:`publish_capabilities`/
|
|
15
|
+
:func:`published_capabilities` mirror ``optimization.publish_problem``'s
|
|
16
|
+
process-wide governed store, keyed by ``agent_code``; neither is
|
|
17
|
+
re-exported from the package's top-level ``__all__`` (design spec §7
|
|
18
|
+
names ``Permission``/``AgentCapabilitySet`` only).
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import dataclasses
|
|
24
|
+
import threading
|
|
25
|
+
from collections.abc import Mapping
|
|
26
|
+
from types import MappingProxyType
|
|
27
|
+
|
|
28
|
+
from mineproductivity.core import BaseValueObject
|
|
29
|
+
|
|
30
|
+
__all__ = ["AgentCapabilitySet", "Permission"]
|
|
31
|
+
|
|
32
|
+
_capabilities: dict[str, AgentCapabilitySet] = {}
|
|
33
|
+
_lock = threading.Lock()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
37
|
+
class Permission(BaseValueObject):
|
|
38
|
+
"""One capability a registered ``Agent`` is authorized to
|
|
39
|
+
exercise, scoped the same way ``digital_twin.Twin.scope`` already
|
|
40
|
+
is (design spec §9).
|
|
41
|
+
|
|
42
|
+
Examples
|
|
43
|
+
--------
|
|
44
|
+
>>> Permission(capability="approve_shutdown", scope={"pit": "north"}).capability
|
|
45
|
+
'approve_shutdown'
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
capability: str
|
|
49
|
+
scope: Mapping[str, str] = dataclasses.field(default_factory=dict, kw_only=True)
|
|
50
|
+
|
|
51
|
+
def _normalize(self) -> None:
|
|
52
|
+
super(Permission, self)._normalize()
|
|
53
|
+
object.__setattr__(self, "scope", MappingProxyType(dict(self.scope)))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
57
|
+
class AgentCapabilitySet(BaseValueObject):
|
|
58
|
+
"""The full set of ``Permission``\\ s a registered ``Agent`` type
|
|
59
|
+
carries -- authored and governed, never inferred from an agent's
|
|
60
|
+
own code (design spec §9).
|
|
61
|
+
|
|
62
|
+
Examples
|
|
63
|
+
--------
|
|
64
|
+
>>> caps = AgentCapabilitySet(
|
|
65
|
+
... agent_code="FLEET.ReassignmentAdvisor",
|
|
66
|
+
... permissions=(Permission(capability="reassign_truck"),),
|
|
67
|
+
... )
|
|
68
|
+
>>> caps.permissions[0].capability
|
|
69
|
+
'reassign_truck'
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
agent_code: str
|
|
73
|
+
permissions: tuple[Permission, ...]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def publish_capabilities(capabilities: AgentCapabilitySet) -> AgentCapabilitySet:
|
|
77
|
+
"""Publish ``capabilities`` into the process-wide governed store,
|
|
78
|
+
keyed by ``capabilities.agent_code`` (design spec §9, §31) -- the
|
|
79
|
+
explicit authoring step that makes a capability grant a
|
|
80
|
+
reviewable artifact."""
|
|
81
|
+
with _lock:
|
|
82
|
+
_capabilities[capabilities.agent_code] = capabilities
|
|
83
|
+
return capabilities
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def published_capabilities(agent_code: str) -> AgentCapabilitySet | None:
|
|
87
|
+
"""Non-raising lookup of the currently-published capability set
|
|
88
|
+
for ``agent_code``, or ``None`` -- an agent with no published set
|
|
89
|
+
holds no permissions at all."""
|
|
90
|
+
with _lock:
|
|
91
|
+
return _capabilities.get(agent_code)
|