clm-kernel 0.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.
- clm/__init__.py +230 -0
- clm/__main__.py +25 -0
- clm/cross_cutting/__init__.py +50 -0
- clm/cross_cutting/_test_runner.py +36 -0
- clm/cross_cutting/async_utils.py +25 -0
- clm/cross_cutting/cel_builder.py +123 -0
- clm/cross_cutting/cel_converter.py +697 -0
- clm/cross_cutting/cel_engine.py +252 -0
- clm/cross_cutting/cel_ops.py +72 -0
- clm/cross_cutting/cel_ops_db.py +157 -0
- clm/cross_cutting/cel_ops_files.py +99 -0
- clm/cross_cutting/cel_ops_state.py +71 -0
- clm/cross_cutting/cel_ops_strings.py +64 -0
- clm/cross_cutting/cel_resolver.py +63 -0
- clm/cross_cutting/cel_slices.py +56 -0
- clm/cross_cutting/cel_store.py +330 -0
- clm/cross_cutting/clm_logger.py +140 -0
- clm/cross_cutting/config/__init__.py +155 -0
- clm/cross_cutting/config/config_constants.py +392 -0
- clm/cross_cutting/config/env_parameters.py +90 -0
- clm/cross_cutting/config/logging.py +378 -0
- clm/cross_cutting/config/settings.py +504 -0
- clm/cross_cutting/context.py +5 -0
- clm/cross_cutting/domain_models.py +55 -0
- clm/cross_cutting/domain_types.py +61 -0
- clm/cross_cutting/effects.py +67 -0
- clm/cross_cutting/errors.py +153 -0
- clm/cross_cutting/governance.py +331 -0
- clm/cross_cutting/guards.py +41 -0
- clm/cross_cutting/mcard.py +321 -0
- clm/cross_cutting/native.py +185 -0
- clm/cross_cutting/observability.py +289 -0
- clm/cross_cutting/pcard.py +434 -0
- clm/cross_cutting/registry.py +71 -0
- clm/cross_cutting/resources.py +84 -0
- clm/cross_cutting/secrets.py +44 -0
- clm/cross_cutting/subprocess_utils.py +118 -0
- clm/cross_cutting/telemetry.py +164 -0
- clm/cross_cutting/testing/__init__.py +8 -0
- clm/cross_cutting/testing/comparator.py +187 -0
- clm/cross_cutting/testing/datasets.py +87 -0
- clm/cross_cutting/timeutil.py +20 -0
- clm/cross_cutting/types/new_type_check.py +0 -0
- clm/cross_cutting/utils/__init__.py +1 -0
- clm/cross_cutting/utils/url_safety.py +50 -0
- clm/cross_cutting/vcard.py +560 -0
- clm/cross_cutting/writer.py +118 -0
- clm/layer0/README.md +50 -0
- clm/layer0/__init__.py +223 -0
- clm/layer0/algebra.py +60 -0
- clm/layer0/algebra_baldwin.py +133 -0
- clm/layer0/algebra_baldwin_generators.py +36 -0
- clm/layer0/algebra_equivalence.py +262 -0
- clm/layer0/algebra_ingest.py +306 -0
- clm/layer0/algebra_manifest_io.py +156 -0
- clm/layer0/assembler.py +481 -0
- clm/layer0/cel/__init__.py +23 -0
- clm/layer0/cel_eval.py +491 -0
- clm/layer0/codec.py +106 -0
- clm/layer0/context.py +122 -0
- clm/layer0/db.py +160 -0
- clm/layer0/exceptions.py +73 -0
- clm/layer0/fibration.py +106 -0
- clm/layer0/gates.py +50 -0
- clm/layer0/hash.py +80 -0
- clm/layer0/loader.py +826 -0
- clm/layer0/math_utils.py +163 -0
- clm/layer0/mcard.py +96 -0
- clm/layer0/model/__init__.py +0 -0
- clm/layer0/model/action.py +223 -0
- clm/layer0/model/card.py +280 -0
- clm/layer0/model/card_triad.py +427 -0
- clm/layer0/model/compatibility.py +60 -0
- clm/layer0/model/dictionary.py +46 -0
- clm/layer0/model/dots.py +331 -0
- clm/layer0/model/event_producer.py +88 -0
- clm/layer0/model/g_time.py +89 -0
- clm/layer0/model/handle.py +163 -0
- clm/layer0/model/hash/__init__.py +6 -0
- clm/layer0/model/hash/algorithms/__init__.py +3 -0
- clm/layer0/model/hash/algorithms/custom_hash.py +7 -0
- clm/layer0/model/hash/algorithms/local_sha256.py +178 -0
- clm/layer0/model/hash/constants.py +53 -0
- clm/layer0/model/hash/enums.py +60 -0
- clm/layer0/model/hash/validator.py +307 -0
- clm/layer0/model/interpreter.py +275 -0
- clm/layer0/model/pagination.py +45 -0
- clm/layer0/model/pcard.py +716 -0
- clm/layer0/model/schema.py +44 -0
- clm/layer0/model/utils/__init__.py +1 -0
- clm/layer0/model/utils/content_analyzer.py +152 -0
- clm/layer0/model/validators/__init__.py +1 -0
- clm/layer0/model/validators/base_validator.py +44 -0
- clm/layer0/model/validators/binary_validator.py +66 -0
- clm/layer0/model/validators/text_validator.py +82 -0
- clm/layer0/model/validators/validation_registry.py +49 -0
- clm/layer0/model/vcard.py +823 -0
- clm/layer0/model/vcard_ext/__init__.py +39 -0
- clm/layer0/model/vcard_ext/core.py +60 -0
- clm/layer0/model/vcard_ext/network.py +23 -0
- clm/layer0/model/vcard_ext/observability.py +63 -0
- clm/layer0/model/vcard_ext/storage.py +60 -0
- clm/layer0/model/vcard_ext/vendors.py +211 -0
- clm/layer0/model/vcard_sandwich.py +119 -0
- clm/layer0/model/vcard_vocabulary.py +463 -0
- clm/layer0/model/workflow.py +274 -0
- clm/layer0/narrative.py +431 -0
- clm/layer0/ontology/collection.py +125 -0
- clm/layer0/parser.py +429 -0
- clm/layer0/relations.py +73 -0
- clm/layer0/rule_evaluator.py +251 -0
- clm/layer0/schema/__init__.py +38 -0
- clm/layer0/schema/ddl.py +97 -0
- clm/layer0/templates/abstract.yaml +99 -0
- clm/layer0/templates/balanced.yaml +253 -0
- clm/layer0/templates/concrete.yaml +172 -0
- clm/layer0/type_registry.py +223 -0
- clm/layer0/types.py +204 -0
- clm/layer0/utils.py +886 -0
- clm/layer0/verifier_core.py +262 -0
- clm/layer1/__init__.py +31 -0
- clm/layer1/action_dispatcher.py +275 -0
- clm/layer1/adapter_registry.py +233 -0
- clm/layer1/algebra_baldwin_generators.py +192 -0
- clm/layer1/arrow_compiler.py +525 -0
- clm/layer1/batch_processor.py +152 -0
- clm/layer1/cel/__init__.py +27 -0
- clm/layer1/cel/builder.py +173 -0
- clm/layer1/engine.py +759 -0
- clm/layer1/engine_async.py +89 -0
- clm/layer1/eval_callback.py +49 -0
- clm/layer1/gate_composer.py +229 -0
- clm/layer1/hoare_sandwich.py +298 -0
- clm/layer1/identity_guard.py +348 -0
- clm/layer1/matrix.py +138 -0
- clm/layer1/neural_runtime.py +221 -0
- clm/layer1/operations/__init__.py +123 -0
- clm/layer1/operations/augment.py +156 -0
- clm/layer1/operations/base.py +109 -0
- clm/layer1/operations/builtins.py +338 -0
- clm/layer1/operations/dispatch.py +144 -0
- clm/layer1/operations/exclude.py +107 -0
- clm/layer1/operations/handle.py +197 -0
- clm/layer1/operations/identity.py +139 -0
- clm/layer1/operations/invert.py +66 -0
- clm/layer1/operations/loader.py +116 -0
- clm/layer1/operations/manipulators/__init__.py +23 -0
- clm/layer1/operations/manipulators/document.py +211 -0
- clm/layer1/operations/manipulators/media.py +134 -0
- clm/layer1/operations/manipulators/structured.py +159 -0
- clm/layer1/operations/manipulators/tabular.py +334 -0
- clm/layer1/operations/port.py +65 -0
- clm/layer1/operations/services.py +529 -0
- clm/layer1/operations/split.py +117 -0
- clm/layer1/operations/substitute.py +75 -0
- clm/layer1/petri.py +1052 -0
- clm/layer1/pocketflow_orchestrator.py +408 -0
- clm/layer1/pocketflow_scheduler.py +519 -0
- clm/layer1/protocols.py +197 -0
- clm/layer1/runtime_adapter.py +718 -0
- clm/layer1/runtimes/__init__.py +71 -0
- clm/layer1/runtimes/_generated_assertions.py +125 -0
- clm/layer1/runtimes/_generated_types.py +42 -0
- clm/layer1/runtimes/base.py +265 -0
- clm/layer1/runtimes/binary.py +101 -0
- clm/layer1/runtimes/factory.py +203 -0
- clm/layer1/runtimes/javascript.py +321 -0
- clm/layer1/runtimes/javascript_runtime.js +33 -0
- clm/layer1/runtimes/js_scripts/isomorphism.mjs +22 -0
- clm/layer1/runtimes/js_scripts/loader.mjs +52 -0
- clm/layer1/runtimes/lambda_calc.py +126 -0
- clm/layer1/runtimes/python.py +288 -0
- clm/layer1/runtimes/python_runtime.py +41 -0
- clm/layer1/runtimes/script.py +114 -0
- clm/layer1/sandbox.py +334 -0
- clm/layer1/savepoint_guard.py +136 -0
- clm/layer1/sparse_net.py +122 -0
- clm/layer1/spec_tester.py +165 -0
- clm/layer1/store.py +295 -0
- clm/layer1/test_runner.py +498 -0
- clm/layer1/type_inspector.py +166 -0
- clm/layer1/verifier.py +772 -0
- clm/layer1/vm.py +91 -0
- clm/layer2/__init__.py +78 -0
- clm/layer2/card_collection.py +678 -0
- clm/layer2/classifier.py +479 -0
- clm/layer2/engine/__init__.py +1 -0
- clm/layer2/engine/abstract_sql_engine.py +320 -0
- clm/layer2/engine/base.py +193 -0
- clm/layer2/engine/duckdb_engine.py +576 -0
- clm/layer2/engine/sqlite_engine.py +518 -0
- clm/layer2/fiber/__init__.py +31 -0
- clm/layer2/fiber/context.py +89 -0
- clm/layer2/fiber/disposable.py +163 -0
- clm/layer2/fiber/fiber.py +125 -0
- clm/layer2/fiber/hoare.py +191 -0
- clm/layer2/file_io.py +376 -0
- clm/layer2/file_registrar.py +77 -0
- clm/layer2/ingest.py +202 -0
- clm/layer2/lineage.py +322 -0
- clm/layer2/mcard_fs.py +262 -0
- clm/layer2/merkle.py +11 -0
- clm/layer2/mime.py +244 -0
- clm/layer2/mime_detector.py +245 -0
- clm/layer2/private_collection.py +118 -0
- clm/layer2/shared_store.py +11 -0
- clm/layer2/storage/__init__.py +190 -0
- clm/layer2/storage/db_bitemporal.py +141 -0
- clm/layer2/storage/db_driver.py +331 -0
- clm/layer2/storage/db_hyperlinks.py +214 -0
- clm/layer2/storage/db_pool.py +139 -0
- clm/layer2/storage/db_repository.py +229 -0
- clm/layer2/storage/savepoint.py +78 -0
- clm/layer2/storage/sqlite_wal.py +74 -0
- clm/layer2/tridb.py +245 -0
- clm/layer2/vcard_receipt.py +71 -0
- clm/layer2/vfs.py +17 -0
- clm/layer3/__init__.py +141 -0
- clm/layer3/agency/__init__.py +19 -0
- clm/layer3/agency/config.py +220 -0
- clm/layer3/agency/providers/__init__.py +15 -0
- clm/layer3/agency/providers/base.py +91 -0
- clm/layer3/agency/providers/mlc_llm.py +169 -0
- clm/layer3/agency/providers/ollama.py +203 -0
- clm/layer3/agency/router.py +71 -0
- clm/layer3/agency/runtime.py +524 -0
- clm/layer3/gateway.py +353 -0
- clm/layer3/open_interpreter_adapter.py +159 -0
- clm/layer3/rag/__init__.py +70 -0
- clm/layer3/rag/cli.py +228 -0
- clm/layer3/rag/config.py +153 -0
- clm/layer3/rag/embeddings/__init__.py +16 -0
- clm/layer3/rag/embeddings/base.py +79 -0
- clm/layer3/rag/embeddings/ollama.py +211 -0
- clm/layer3/rag/embeddings/vision.py +290 -0
- clm/layer3/rag/engine.py +276 -0
- clm/layer3/rag/graph/__init__.py +22 -0
- clm/layer3/rag/graph/community.py +198 -0
- clm/layer3/rag/graph/engine.py +433 -0
- clm/layer3/rag/graph/extractor.py +255 -0
- clm/layer3/rag/graph/schema.py +115 -0
- clm/layer3/rag/graph/store.py +641 -0
- clm/layer3/rag/indexer.py +333 -0
- clm/layer3/rag/llm_providers.json +52 -0
- clm/layer3/rag/semantic_versioning.py +313 -0
- clm/layer3/rag/vector/__init__.py +28 -0
- clm/layer3/rag/vector/handle_vector_store.py +749 -0
- clm/layer3/rag/vector/schema.py +151 -0
- clm/layer3/rag/vector/store.py +632 -0
- clm/layer3/reticulum/__init__.py +55 -0
- clm/layer3/reticulum/bridge.py +74 -0
- clm/layer3/reticulum/daemon.py +88 -0
- clm/layer3/reticulum/discovery.py +164 -0
- clm/layer3/reticulum/identity.py +107 -0
- clm/layer3/reticulum/media.py +148 -0
- clm/layer3/reticulum/transport.py +133 -0
- clm/layer3/satori/__init__.py +209 -0
- clm/layer3/satori/alpha_conversion.py +168 -0
- clm/layer3/satori/beta_reduction.py +359 -0
- clm/layer3/satori/continuation.py +38 -0
- clm/layer3/satori/eta_conversion.py +205 -0
- clm/layer3/satori/free_variables.py +163 -0
- clm/layer3/satori/io_effects.py +370 -0
- clm/layer3/satori/lambda_runtime.py +726 -0
- clm/layer3/satori/lambda_term.py +230 -0
- clm/layer3/satori/sheaf_audit.py +261 -0
- clm/layer3/satori/speech_act.py +279 -0
- clm/layer4/README.md +9 -0
- clm/layer4/__init__.py +67 -0
- clm/layer4/_sovereign_executor.py +580 -0
- clm/layer4/action_commands.py +719 -0
- clm/layer4/api.py +236 -0
- clm/layer4/bootstrap/__init__.py +13 -0
- clm/layer4/bootstrap/bootstrapper.py +118 -0
- clm/layer4/bootstrap/cleanup.py +44 -0
- clm/layer4/bootstrap/clm_hooks.py +49 -0
- clm/layer4/bootstrap/genesis.py +77 -0
- clm/layer4/bootstrap/injection.py +125 -0
- clm/layer4/cli.py +549 -0
- clm/layer4/collection_manager.py +553 -0
- clm/layer4/commands/__init__.py +81 -0
- clm/layer4/commands/action_cmd.py +42 -0
- clm/layer4/commands/check_cmd.py +82 -0
- clm/layer4/commands/envelope.py +73 -0
- clm/layer4/commands/evaluate_cmd.py +224 -0
- clm/layer4/commands/mcard_cmd.py +24 -0
- clm/layer4/commands/petri_cmd.py +92 -0
- clm/layer4/commands/tridb_cmd.py +319 -0
- clm/layer4/commands/version_cmd.py +27 -0
- clm/layer4/diff_commands.py +167 -0
- clm/layer4/eoa_sponsor.py +232 -0
- clm/layer4/events.py +69 -0
- clm/layer4/flux_gateway.py +133 -0
- clm/layer4/gateway.py +91 -0
- clm/layer4/loader.py +307 -0
- clm/layer4/mcard_commands.py +166 -0
- clm/layer4/membrane.py +273 -0
- clm/layer4/navigation.py +50 -0
- clm/layer4/protocol/protocol_entry_python.py +108 -0
- clm/layer4/protocol/python_sync.py +126 -0
- clm/layer4/runner.py +150 -0
- clm/layer4/satori.py +262 -0
- clm/layer4/server/python_sync_client.py +150 -0
- clm/layer4/server/python_sync_server.py +139 -0
- clm/layer4/server/service_api.py +315 -0
- clm/layer4/server/start_servers.py +107 -0
- clm/layer4/server/stop_servers.py +24 -0
- clm/layer4/simulated_optimization_fix.py +0 -0
- clm/layer4/storage.py +463 -0
- clm/layer4/tridb_admin.py +268 -0
- clm/layer4/websocket/test_ws_client.py +104 -0
- clm/layer4/websocket/test_ws_server.py +138 -0
- clm/layer4/websocket/ws_server_local.py +160 -0
- clm/layer5/__init__.py +51 -0
- clm/layer5/async_batch_writer.py +133 -0
- clm/layer5/learning.py +276 -0
- clm/layer5/telemetry_hook.py +107 -0
- clm/layer5/type_lattice.py +238 -0
- clm/py.typed +0 -0
- clm/schemas/__init__.py +652 -0
- clm/schemas/abstract_face_schema.yaml +77 -0
- clm/schemas/cel_schema.yaml +416 -0
- clm/schemas/lifecycle/__init__.py +45 -0
- clm/schemas/lifecycle/models.py +123 -0
- clm/schemas/lifecycle/service.py +364 -0
- clm/schemas/mcard_schema.sql +192 -0
- clm/schemas/mcard_vector_schema.sql +283 -0
- clm/schemas/mlp-context-v1.json +91 -0
- clm_kernel-0.0.0.dist-info/METADATA +121 -0
- clm_kernel-0.0.0.dist-info/RECORD +333 -0
- clm_kernel-0.0.0.dist-info/WHEEL +4 -0
- clm_kernel-0.0.0.dist-info/entry_points.txt +3 -0
- clm_kernel-0.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Layer 1: Arrow to Colored Petri Net (CPN) Compiler (DV-373-01).
|
|
3
|
+
|
|
4
|
+
Compiles Arrow combinators (arr, >>>, first, ***, &&&) into bipartite Colored
|
|
5
|
+
Petri Net topologies operating under the 4-phase PTR lifecycle:
|
|
6
|
+
prep (P_in -> P_pre) -> exec (P_pre -> P_exec) -> post (P_exec -> P_post) -> seal (P_post -> P_out)
|
|
7
|
+
|
|
8
|
+
Satisfies:
|
|
9
|
+
- INV-REF-01: Headless Kernel Purity (no browser/DOM imports)
|
|
10
|
+
- INV-REF-02: Single Implementation Home (lives in clm/layer1)
|
|
11
|
+
- Noetherian conservation invariants (x^T · D = 0)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import uuid
|
|
18
|
+
from collections.abc import Callable
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class VCardGuard:
|
|
25
|
+
"""Represents a pre- or post-condition guard for an Arrow."""
|
|
26
|
+
|
|
27
|
+
name: str
|
|
28
|
+
predicate: Callable[[Any], bool]
|
|
29
|
+
error_message: str = "VCard guard condition not satisfied"
|
|
30
|
+
|
|
31
|
+
def check(self, val: Any) -> bool:
|
|
32
|
+
try:
|
|
33
|
+
return bool(self.predicate(val))
|
|
34
|
+
except Exception:
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Arrow:
|
|
39
|
+
"""Arrow abstraction for composable computation with VCard sandwich gates."""
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
func: Callable[[Any], Any],
|
|
44
|
+
name: str = "arrow",
|
|
45
|
+
v_pre: VCardGuard | list[VCardGuard] | None = None,
|
|
46
|
+
v_post: VCardGuard | list[VCardGuard] | None = None,
|
|
47
|
+
agent_did: str | None = None,
|
|
48
|
+
node_id: str | None = None,
|
|
49
|
+
) -> None:
|
|
50
|
+
self.func = func
|
|
51
|
+
self.name = name
|
|
52
|
+
self.node_id = node_id or f"arrow_{uuid.uuid4().hex[:8]}"
|
|
53
|
+
self.v_pre: list[VCardGuard] = (
|
|
54
|
+
[v_pre] if isinstance(v_pre, VCardGuard) else (v_pre or [])
|
|
55
|
+
)
|
|
56
|
+
self.v_post: list[VCardGuard] = (
|
|
57
|
+
[v_post] if isinstance(v_post, VCardGuard) else (v_post or [])
|
|
58
|
+
)
|
|
59
|
+
self.agent_did = agent_did or "did:key:z6MkhaXgBZDvotDkL5257faiz48Z8x28xhr554x4tp4MaEBx"
|
|
60
|
+
|
|
61
|
+
def __call__(self, input_val: Any) -> Any:
|
|
62
|
+
return self.func(input_val)
|
|
63
|
+
|
|
64
|
+
def __rshift__(self, other: Arrow) -> Arrow:
|
|
65
|
+
"""Sequential composition combinator: a1 >>> a2."""
|
|
66
|
+
return self.compose(self, other)
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def arr(
|
|
70
|
+
cls,
|
|
71
|
+
func: Callable[[Any], Any],
|
|
72
|
+
name: str = "arr",
|
|
73
|
+
v_pre: VCardGuard | list[VCardGuard] | None = None,
|
|
74
|
+
v_post: VCardGuard | list[VCardGuard] | None = None,
|
|
75
|
+
agent_did: str | None = None,
|
|
76
|
+
) -> Arrow:
|
|
77
|
+
"""Lifts a pure or effectful function into an Arrow."""
|
|
78
|
+
return cls(func, name=name, v_pre=v_pre, v_post=v_post, agent_did=agent_did)
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def compose(cls, first: Arrow, second: Arrow) -> Arrow:
|
|
82
|
+
"""Sequential composition: first >>> second."""
|
|
83
|
+
composed_name = f"({first.name} >>> {second.name})"
|
|
84
|
+
|
|
85
|
+
def composed_fn(x: Any) -> Any:
|
|
86
|
+
res1 = first(x)
|
|
87
|
+
return second(res1)
|
|
88
|
+
|
|
89
|
+
arrow = cls(
|
|
90
|
+
composed_fn,
|
|
91
|
+
name=composed_name,
|
|
92
|
+
v_pre=first.v_pre,
|
|
93
|
+
v_post=second.v_post,
|
|
94
|
+
agent_did=first.agent_did,
|
|
95
|
+
)
|
|
96
|
+
arrow._is_composite = True
|
|
97
|
+
arrow._first = first
|
|
98
|
+
arrow._second = second
|
|
99
|
+
return arrow
|
|
100
|
+
|
|
101
|
+
def first(self) -> Arrow:
|
|
102
|
+
"""Lifts this Arrow (A -> B) to (A x C -> B x C)."""
|
|
103
|
+
orig_fn = self.func
|
|
104
|
+
|
|
105
|
+
def first_fn(pair: tuple[Any, Any]) -> tuple[Any, Any]:
|
|
106
|
+
if not isinstance(pair, (tuple, list)) or len(pair) != 2:
|
|
107
|
+
raise TypeError(f"Arrow.first expects a 2-tuple, got {type(pair)}")
|
|
108
|
+
a, c = pair[0], pair[1]
|
|
109
|
+
return (orig_fn(a), c)
|
|
110
|
+
|
|
111
|
+
return Arrow(
|
|
112
|
+
first_fn,
|
|
113
|
+
name=f"first({self.name})",
|
|
114
|
+
v_pre=self.v_pre,
|
|
115
|
+
v_post=self.v_post,
|
|
116
|
+
agent_did=self.agent_did,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def second(self) -> Arrow:
|
|
120
|
+
"""Lifts this Arrow (A -> B) to (C x A -> C x B)."""
|
|
121
|
+
orig_fn = self.func
|
|
122
|
+
|
|
123
|
+
def second_fn(pair: tuple[Any, Any]) -> tuple[Any, Any]:
|
|
124
|
+
if not isinstance(pair, (tuple, list)) or len(pair) != 2:
|
|
125
|
+
raise TypeError(f"Arrow.second expects a 2-tuple, got {type(pair)}")
|
|
126
|
+
c, a = pair[0], pair[1]
|
|
127
|
+
return (c, orig_fn(a))
|
|
128
|
+
|
|
129
|
+
return Arrow(
|
|
130
|
+
second_fn,
|
|
131
|
+
name=f"second({self.name})",
|
|
132
|
+
v_pre=self.v_pre,
|
|
133
|
+
v_post=self.v_post,
|
|
134
|
+
agent_did=self.agent_did,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
def split(self, other: Arrow) -> Arrow:
|
|
138
|
+
"""Parallel product combinator (***): (A -> B) x (C -> D) -> (A x C -> B x D)."""
|
|
139
|
+
f = self.func
|
|
140
|
+
g = other.func
|
|
141
|
+
|
|
142
|
+
def split_fn(pair: tuple[Any, Any]) -> tuple[Any, Any]:
|
|
143
|
+
if not isinstance(pair, (tuple, list)) or len(pair) != 2:
|
|
144
|
+
raise TypeError(f"Arrow.split expects a 2-tuple, got {type(pair)}")
|
|
145
|
+
return (f(pair[0]), g(pair[1]))
|
|
146
|
+
|
|
147
|
+
return Arrow(
|
|
148
|
+
split_fn,
|
|
149
|
+
name=f"({self.name} *** {other.name})",
|
|
150
|
+
agent_did=self.agent_did,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def __mul__(self, other: Arrow) -> Arrow:
|
|
154
|
+
"""Operator alias for split (***)."""
|
|
155
|
+
return self.split(other)
|
|
156
|
+
|
|
157
|
+
def fanout(self, other: Arrow) -> Arrow:
|
|
158
|
+
"""Fanout combinator (&&&): A -> (B x C)."""
|
|
159
|
+
f = self.func
|
|
160
|
+
g = other.func
|
|
161
|
+
|
|
162
|
+
def fanout_fn(x: Any) -> tuple[Any, Any]:
|
|
163
|
+
return (f(x), g(x))
|
|
164
|
+
|
|
165
|
+
return Arrow(
|
|
166
|
+
fanout_fn,
|
|
167
|
+
name=f"({self.name} &&& {other.name})",
|
|
168
|
+
agent_did=self.agent_did,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def __and__(self, other: Arrow) -> Arrow:
|
|
172
|
+
"""Operator alias for fanout (&&&)."""
|
|
173
|
+
return self.fanout(other)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@dataclass
|
|
177
|
+
class CPNCompiledNet:
|
|
178
|
+
"""Compiled Colored Petri Net representation of an Arrow or Arrow pipeline."""
|
|
179
|
+
|
|
180
|
+
net_id: str
|
|
181
|
+
places: list[dict[str, Any]]
|
|
182
|
+
transitions: list[dict[str, Any]]
|
|
183
|
+
incidence_matrix: list[list[int]]
|
|
184
|
+
place_index: dict[str, int]
|
|
185
|
+
transition_index: dict[str, int]
|
|
186
|
+
entry_place: str
|
|
187
|
+
exit_place: str
|
|
188
|
+
arrow: Arrow | None = None
|
|
189
|
+
steps: list[Arrow] = field(default_factory=list)
|
|
190
|
+
|
|
191
|
+
def __call__(self, val: Any) -> Any:
|
|
192
|
+
"""Executes the underlying Arrow computation."""
|
|
193
|
+
if self.arrow is not None:
|
|
194
|
+
return self.arrow(val)
|
|
195
|
+
raise RuntimeError("No executable Arrow attached to CPNCompiledNet")
|
|
196
|
+
|
|
197
|
+
def to_dict(self) -> dict[str, Any]:
|
|
198
|
+
serializable_transitions = []
|
|
199
|
+
for t in self.transitions:
|
|
200
|
+
t_copy = dict(t)
|
|
201
|
+
if "arrow_ref" in t_copy and hasattr(t_copy["arrow_ref"], "name"):
|
|
202
|
+
t_copy["arrow_ref"] = t_copy["arrow_ref"].name
|
|
203
|
+
serializable_transitions.append(t_copy)
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
"net_id": self.net_id,
|
|
207
|
+
"places": self.places,
|
|
208
|
+
"transitions": serializable_transitions,
|
|
209
|
+
"incidence_matrix": self.incidence_matrix,
|
|
210
|
+
"entry_place": self.entry_place,
|
|
211
|
+
"exit_place": self.exit_place,
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
def to_json(self, indent: int = 2) -> str:
|
|
215
|
+
return json.dumps(self.to_dict(), indent=indent)
|
|
216
|
+
|
|
217
|
+
def to_graphviz(self) -> str:
|
|
218
|
+
"""Exports CPN net topology as Graphviz DOT notation."""
|
|
219
|
+
lines = [f'digraph "{self.net_id}" {{', ' rankdir=LR;', ' node [fontname="Helvetica"];']
|
|
220
|
+
for p in self.places:
|
|
221
|
+
pid = p["id"]
|
|
222
|
+
label = f"{p.get('label', pid)}\\n(tokens={p.get('initial_tokens', 0)})"
|
|
223
|
+
lines.append(f' "{pid}" [shape=circle, label="{label}"];')
|
|
224
|
+
for t in self.transitions:
|
|
225
|
+
tid = t["id"]
|
|
226
|
+
label = t.get("label", tid)
|
|
227
|
+
lines.append(f' "{tid}" [shape=box, style=filled, fillcolor="#e0e7ff", label="{label}"];')
|
|
228
|
+
for t in self.transitions:
|
|
229
|
+
tid = t["id"]
|
|
230
|
+
for inp in t.get("inputs", []):
|
|
231
|
+
lines.append(f' "{inp}" -> "{tid}";')
|
|
232
|
+
for out in t.get("outputs", []):
|
|
233
|
+
lines.append(f' "{tid}" -> "{out}";')
|
|
234
|
+
lines.append("}")
|
|
235
|
+
return "\n".join(lines)
|
|
236
|
+
|
|
237
|
+
def to_mermaid(self) -> str:
|
|
238
|
+
"""Exports CPN net topology as Mermaid flowchart diagram."""
|
|
239
|
+
lines = ["graph LR"]
|
|
240
|
+
for p in self.places:
|
|
241
|
+
pid = p["id"]
|
|
242
|
+
label = p.get("label", pid)
|
|
243
|
+
lines.append(f' {pid}(("{label}"))')
|
|
244
|
+
for t in self.transitions:
|
|
245
|
+
tid = t["id"]
|
|
246
|
+
label = t.get("label", tid)
|
|
247
|
+
lines.append(f' {tid}["{label}"]')
|
|
248
|
+
for t in self.transitions:
|
|
249
|
+
tid = t["id"]
|
|
250
|
+
for inp in t.get("inputs", []):
|
|
251
|
+
lines.append(f" {inp} --> {tid}")
|
|
252
|
+
for out in t.get("outputs", []):
|
|
253
|
+
lines.append(f" {tid} --> {out}")
|
|
254
|
+
return "\n".join(lines)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _flatten_arrow_sequence(arrow: Arrow) -> list[Arrow]:
|
|
258
|
+
"""Flattens a tree of sequentially composed arrows into an ordered list."""
|
|
259
|
+
if getattr(arrow, "_is_composite", False):
|
|
260
|
+
return _flatten_arrow_sequence(arrow._first) + _flatten_arrow_sequence(arrow._second)
|
|
261
|
+
return [arrow]
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def compile_to_cpn(arrow: Arrow, net_id: str | None = None) -> CPNCompiledNet:
|
|
265
|
+
"""
|
|
266
|
+
Compiles an Arrow (or sequentially composed Arrow chain) into a Colored Petri Net.
|
|
267
|
+
|
|
268
|
+
For each atomic Arrow i in the sequence:
|
|
269
|
+
Places:
|
|
270
|
+
P_in_{i}: Input token place
|
|
271
|
+
P_pre_{i}: Pre-condition satisfied place
|
|
272
|
+
P_exec_{i}: PCard execution completed place
|
|
273
|
+
P_post_{i}: Post-condition verified place
|
|
274
|
+
P_out_{i}: Sealed output place
|
|
275
|
+
Transitions:
|
|
276
|
+
T_prep_{i}: Consumes P_in_{i}, verifies identity and V_pre, produces P_pre_{i}
|
|
277
|
+
T_exec_{i}: Consumes P_pre_{i}, executes PCard, produces P_exec_{i}
|
|
278
|
+
T_post_{i}: Consumes P_exec_{i}, verifies V_post and Noetherian P-invariant, produces P_post_{i}
|
|
279
|
+
T_seal_{i}: Consumes P_post_{i}, commits token, produces P_out_{i}
|
|
280
|
+
Sequential composition joins P_out_{i} with P_in_{i+1}.
|
|
281
|
+
"""
|
|
282
|
+
net_id = net_id or f"cpn_{uuid.uuid4().hex[:8]}"
|
|
283
|
+
arrows = _flatten_arrow_sequence(arrow)
|
|
284
|
+
|
|
285
|
+
places: list[dict[str, Any]] = []
|
|
286
|
+
transitions: list[dict[str, Any]] = []
|
|
287
|
+
|
|
288
|
+
prev_out_place: str | None = None
|
|
289
|
+
entry_place: str = ""
|
|
290
|
+
exit_place: str = ""
|
|
291
|
+
|
|
292
|
+
for idx, sub_arr in enumerate(arrows):
|
|
293
|
+
prefix = f"{net_id}_step_{idx}"
|
|
294
|
+
|
|
295
|
+
if idx == 0:
|
|
296
|
+
in_place = f"{prefix}_p_in"
|
|
297
|
+
places.append({"id": in_place, "label": f"P_in_{idx}", "initial_tokens": 1})
|
|
298
|
+
entry_place = in_place
|
|
299
|
+
else:
|
|
300
|
+
in_place = prev_out_place
|
|
301
|
+
|
|
302
|
+
p_pre = f"{prefix}_p_pre"
|
|
303
|
+
p_exec = f"{prefix}_p_exec"
|
|
304
|
+
p_post = f"{prefix}_p_post"
|
|
305
|
+
p_out = f"{prefix}_p_out"
|
|
306
|
+
|
|
307
|
+
places.extend([
|
|
308
|
+
{"id": p_pre, "label": f"P_pre_{idx}", "initial_tokens": 0},
|
|
309
|
+
{"id": p_exec, "label": f"P_exec_{idx}", "initial_tokens": 0},
|
|
310
|
+
{"id": p_post, "label": f"P_post_{idx}", "initial_tokens": 0},
|
|
311
|
+
{"id": p_out, "label": f"P_out_{idx}", "initial_tokens": 0},
|
|
312
|
+
])
|
|
313
|
+
|
|
314
|
+
# 4-Phase Transitions
|
|
315
|
+
t_prep = {
|
|
316
|
+
"id": f"{prefix}_t_prep",
|
|
317
|
+
"label": f"Prep ({sub_arr.name})",
|
|
318
|
+
"inputs": [in_place],
|
|
319
|
+
"outputs": [p_pre],
|
|
320
|
+
"phase": "prep",
|
|
321
|
+
"agent_did": sub_arr.agent_did,
|
|
322
|
+
"v_pre_count": len(sub_arr.v_pre),
|
|
323
|
+
}
|
|
324
|
+
t_exec = {
|
|
325
|
+
"id": f"{prefix}_t_exec",
|
|
326
|
+
"label": f"Exec ({sub_arr.name})",
|
|
327
|
+
"inputs": [p_pre],
|
|
328
|
+
"outputs": [p_exec],
|
|
329
|
+
"phase": "exec",
|
|
330
|
+
"arrow_ref": sub_arr,
|
|
331
|
+
}
|
|
332
|
+
t_post = {
|
|
333
|
+
"id": f"{prefix}_t_post",
|
|
334
|
+
"label": f"Post ({sub_arr.name})",
|
|
335
|
+
"inputs": [p_exec],
|
|
336
|
+
"outputs": [p_post],
|
|
337
|
+
"phase": "post",
|
|
338
|
+
"v_post_count": len(sub_arr.v_post),
|
|
339
|
+
}
|
|
340
|
+
t_seal = {
|
|
341
|
+
"id": f"{prefix}_t_seal",
|
|
342
|
+
"label": f"Seal ({sub_arr.name})",
|
|
343
|
+
"inputs": [p_post],
|
|
344
|
+
"outputs": [p_out],
|
|
345
|
+
"phase": "seal",
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
transitions.extend([t_prep, t_exec, t_post, t_seal])
|
|
349
|
+
prev_out_place = p_out
|
|
350
|
+
if idx == len(arrows) - 1:
|
|
351
|
+
exit_place = p_out
|
|
352
|
+
|
|
353
|
+
# Compute Incidence Matrix D = D^+ - D^-
|
|
354
|
+
place_ids = [p["id"] for p in places]
|
|
355
|
+
trans_ids = [t["id"] for t in transitions]
|
|
356
|
+
place_index = {pid: i for i, pid in enumerate(place_ids)}
|
|
357
|
+
trans_index = {tid: j for j, tid in enumerate(trans_ids)}
|
|
358
|
+
|
|
359
|
+
num_p = len(places)
|
|
360
|
+
num_t = len(transitions)
|
|
361
|
+
d_matrix = [[0 for _ in range(num_t)] for _ in range(num_p)]
|
|
362
|
+
|
|
363
|
+
for j, t in enumerate(transitions):
|
|
364
|
+
for inp in t.get("inputs", []):
|
|
365
|
+
i = place_index[inp]
|
|
366
|
+
d_matrix[i][j] -= 1
|
|
367
|
+
for out in t.get("outputs", []):
|
|
368
|
+
i = place_index[out]
|
|
369
|
+
d_matrix[i][j] += 1
|
|
370
|
+
|
|
371
|
+
return CPNCompiledNet(
|
|
372
|
+
net_id=net_id,
|
|
373
|
+
places=places,
|
|
374
|
+
transitions=transitions,
|
|
375
|
+
incidence_matrix=d_matrix,
|
|
376
|
+
place_index=place_index,
|
|
377
|
+
transition_index=trans_index,
|
|
378
|
+
entry_place=entry_place,
|
|
379
|
+
exit_place=exit_place,
|
|
380
|
+
arrow=arrow,
|
|
381
|
+
steps=arrows,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
class PCardResolver:
|
|
386
|
+
"""Resolve PCard references by content hash, CLM handle, or URL (Layer 1).
|
|
387
|
+
|
|
388
|
+
The resolver is the bridge between symbolic PCard references stored
|
|
389
|
+
in PetriNet transitions and the actual PCard MCards in the collection.
|
|
390
|
+
"""
|
|
391
|
+
|
|
392
|
+
def __init__(
|
|
393
|
+
self,
|
|
394
|
+
collection: Any,
|
|
395
|
+
trusted_urls: list[str] | None = None,
|
|
396
|
+
allow_file_refs: bool = True,
|
|
397
|
+
) -> None:
|
|
398
|
+
self.collection = collection
|
|
399
|
+
self.trusted_urls = trusted_urls or []
|
|
400
|
+
self.allow_file_refs = allow_file_refs
|
|
401
|
+
self._resolution_cache: dict[str, str] = {}
|
|
402
|
+
self._recursion_guard: set[str] = set()
|
|
403
|
+
|
|
404
|
+
def resolve_to_hash(self, ref: str) -> str | None:
|
|
405
|
+
"""Resolve a PCard reference to a content hash."""
|
|
406
|
+
if not ref:
|
|
407
|
+
return None
|
|
408
|
+
|
|
409
|
+
if ref in self._resolution_cache:
|
|
410
|
+
return self._resolution_cache[ref]
|
|
411
|
+
|
|
412
|
+
resolved = None
|
|
413
|
+
|
|
414
|
+
if self._is_hash_ref(ref):
|
|
415
|
+
resolved = self._resolve_hash(ref)
|
|
416
|
+
elif ref.startswith("/") or "/" in ref:
|
|
417
|
+
resolved = self._resolve_handle(ref)
|
|
418
|
+
elif ref.startswith("file://"):
|
|
419
|
+
resolved = self._resolve_file(ref)
|
|
420
|
+
elif ref.startswith("http://") or ref.startswith("https://"):
|
|
421
|
+
resolved = self._resolve_url(ref)
|
|
422
|
+
else:
|
|
423
|
+
resolved = self._resolve_hash(ref)
|
|
424
|
+
|
|
425
|
+
if resolved:
|
|
426
|
+
self._resolution_cache[ref] = resolved
|
|
427
|
+
|
|
428
|
+
return resolved
|
|
429
|
+
|
|
430
|
+
@staticmethod
|
|
431
|
+
def _is_hash_ref(ref: str) -> bool:
|
|
432
|
+
if ref.startswith("sha256:") or ref.startswith("sha512:"):
|
|
433
|
+
return True
|
|
434
|
+
if len(ref) >= 32 and all(c in "0123456789abcdef" for c in ref[:32]):
|
|
435
|
+
return True
|
|
436
|
+
return False
|
|
437
|
+
|
|
438
|
+
def _resolve_hash(self, ref: str) -> str | None:
|
|
439
|
+
hash_val = ref
|
|
440
|
+
if ":" in ref and ref.index(":") < 10:
|
|
441
|
+
hash_val = ref.split(":", 1)[1] if not ref.startswith("clm:") else ref
|
|
442
|
+
|
|
443
|
+
if hasattr(self.collection, "get"):
|
|
444
|
+
card = self.collection.get(hash_val)
|
|
445
|
+
if card:
|
|
446
|
+
return getattr(card, "hash", hash_val)
|
|
447
|
+
|
|
448
|
+
card = self.collection.get(ref)
|
|
449
|
+
if card:
|
|
450
|
+
return getattr(card, "hash", ref)
|
|
451
|
+
|
|
452
|
+
return None
|
|
453
|
+
|
|
454
|
+
def _resolve_handle(self, ref: str) -> str | None:
|
|
455
|
+
if hasattr(self.collection, "resolve_handle"):
|
|
456
|
+
resolved = self.collection.resolve_handle(ref)
|
|
457
|
+
if resolved:
|
|
458
|
+
return resolved
|
|
459
|
+
|
|
460
|
+
if hasattr(self.collection, "get_by_handle"):
|
|
461
|
+
card = self.collection.get_by_handle(ref)
|
|
462
|
+
if card:
|
|
463
|
+
return getattr(card, "hash", None)
|
|
464
|
+
|
|
465
|
+
return None
|
|
466
|
+
|
|
467
|
+
def _resolve_file(self, ref: str) -> str | None:
|
|
468
|
+
if not self.allow_file_refs:
|
|
469
|
+
return None
|
|
470
|
+
|
|
471
|
+
try:
|
|
472
|
+
from pathlib import Path
|
|
473
|
+
|
|
474
|
+
path_str = ref[len("file://") :] if ref.startswith("file://") else ref
|
|
475
|
+
file_path = Path(path_str)
|
|
476
|
+
if not file_path.exists():
|
|
477
|
+
return None
|
|
478
|
+
|
|
479
|
+
content = file_path.read_bytes()
|
|
480
|
+
from clm.layer0.mcard import MCard
|
|
481
|
+
|
|
482
|
+
card = MCard(content)
|
|
483
|
+
if hasattr(self.collection, "add"):
|
|
484
|
+
self.collection.add(card)
|
|
485
|
+
return getattr(card, "hash", None)
|
|
486
|
+
except Exception:
|
|
487
|
+
return None
|
|
488
|
+
|
|
489
|
+
def _resolve_url(self, ref: str) -> str | None:
|
|
490
|
+
if not self._is_trusted_url(ref):
|
|
491
|
+
return None
|
|
492
|
+
|
|
493
|
+
try:
|
|
494
|
+
import urllib.request
|
|
495
|
+
|
|
496
|
+
from clm.layer0.mcard import MCard
|
|
497
|
+
|
|
498
|
+
response = urllib.request.urlopen(ref, timeout=30) # noqa: S310
|
|
499
|
+
content = response.read()
|
|
500
|
+
card = MCard(content)
|
|
501
|
+
if hasattr(self.collection, "add"):
|
|
502
|
+
self.collection.add(card)
|
|
503
|
+
return getattr(card, "hash", None)
|
|
504
|
+
except Exception:
|
|
505
|
+
return None
|
|
506
|
+
|
|
507
|
+
def _is_trusted_url(self, url: str) -> bool:
|
|
508
|
+
return any(url.startswith(prefix) for prefix in self.trusted_urls)
|
|
509
|
+
|
|
510
|
+
def clear_cache(self) -> None:
|
|
511
|
+
self._resolution_cache.clear()
|
|
512
|
+
|
|
513
|
+
def add_trusted_url(self, url_prefix: str) -> None:
|
|
514
|
+
if url_prefix not in self.trusted_urls:
|
|
515
|
+
self.trusted_urls.append(url_prefix)
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
__all__ = [
|
|
519
|
+
"Arrow",
|
|
520
|
+
"CPNCompiledNet",
|
|
521
|
+
"VCardGuard",
|
|
522
|
+
"compile_to_cpn",
|
|
523
|
+
"PCardResolver",
|
|
524
|
+
]
|
|
525
|
+
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Layer 1: Batch Processor for PTREngine execution.
|
|
2
|
+
|
|
3
|
+
Handles sequential and parallel batch execution logic for PTREngine.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import logging
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
|
+
|
|
12
|
+
from clm.layer0.types import ExecutionResult
|
|
13
|
+
from clm.layer1.store import PTRSharedStore
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class BatchProcessor:
|
|
20
|
+
"""
|
|
21
|
+
Composition object handling sequential and parallel batch execution logic for the PTREngine.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, engine: Any):
|
|
25
|
+
self.engine = engine
|
|
26
|
+
self.logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
def execute_batch(
|
|
29
|
+
self,
|
|
30
|
+
pcard_hash: str,
|
|
31
|
+
target_hashes: list[str],
|
|
32
|
+
context: dict[str, Any] | PTRSharedStore = None,
|
|
33
|
+
specification_embedding: list[float] | None = None,
|
|
34
|
+
fail_fast: bool = False,
|
|
35
|
+
) -> list[ExecutionResult]:
|
|
36
|
+
context = context or {}
|
|
37
|
+
results = []
|
|
38
|
+
failed_count = 0
|
|
39
|
+
|
|
40
|
+
self.logger.info(
|
|
41
|
+
f"[BATCH] Starting sequential batch execution: "
|
|
42
|
+
f"PCard {pcard_hash[:16]} against {len(target_hashes)} targets"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
for i, target_hash in enumerate(target_hashes):
|
|
46
|
+
self.logger.debug(
|
|
47
|
+
f"[BATCH] Processing {i + 1}/{len(target_hashes)}: {target_hash[:16]}"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
result = self.engine.execute_pcard(
|
|
51
|
+
pcard_hash, target_hash, context, specification_embedding
|
|
52
|
+
)
|
|
53
|
+
results.append(result)
|
|
54
|
+
|
|
55
|
+
if not result.success:
|
|
56
|
+
failed_count += 1
|
|
57
|
+
self.logger.warning(
|
|
58
|
+
f"[BATCH] Failed {i + 1}/{len(target_hashes)}: {target_hash[:16]} - {result.error_message}"
|
|
59
|
+
)
|
|
60
|
+
if fail_fast:
|
|
61
|
+
self.logger.error(
|
|
62
|
+
"[BATCH] Fail-fast triggered, stopping batch execution"
|
|
63
|
+
)
|
|
64
|
+
break
|
|
65
|
+
|
|
66
|
+
self.engine.correctness_tracker.record_liveness_metric(
|
|
67
|
+
"batch_completed", 1.0 if failed_count == 0 else 0.5
|
|
68
|
+
)
|
|
69
|
+
self.engine.correctness_tracker.record_liveness_metric(
|
|
70
|
+
"batch_success_rate", (len(results) - failed_count) / len(target_hashes)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
self.logger.info(
|
|
74
|
+
f"[BATCH] Completed: {len(results) - failed_count}/{len(target_hashes)} succeeded"
|
|
75
|
+
)
|
|
76
|
+
return results
|
|
77
|
+
|
|
78
|
+
async def execute_batch_parallel(
|
|
79
|
+
self,
|
|
80
|
+
pcard_hash: str,
|
|
81
|
+
target_hashes: list[str],
|
|
82
|
+
context: dict[str, Any] | PTRSharedStore = None,
|
|
83
|
+
specification_embedding: list[float] | None = None,
|
|
84
|
+
max_concurrency: int = 10,
|
|
85
|
+
) -> list[ExecutionResult]:
|
|
86
|
+
context = context or {}
|
|
87
|
+
|
|
88
|
+
self.logger.info(
|
|
89
|
+
f"[BATCH_PARALLEL] Starting parallel batch execution: "
|
|
90
|
+
f"PCard {pcard_hash[:16]} against {len(target_hashes)} targets "
|
|
91
|
+
f"(max_concurrency={max_concurrency})"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
async def execute_single(target_hash: str) -> ExecutionResult:
|
|
95
|
+
loop = asyncio.get_event_loop()
|
|
96
|
+
return await loop.run_in_executor(
|
|
97
|
+
None,
|
|
98
|
+
self.engine.execute_pcard,
|
|
99
|
+
pcard_hash,
|
|
100
|
+
target_hash,
|
|
101
|
+
context,
|
|
102
|
+
specification_embedding,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
semaphore = asyncio.Semaphore(max_concurrency)
|
|
106
|
+
|
|
107
|
+
async def execute_with_limit(target_hash: str) -> ExecutionResult:
|
|
108
|
+
async with semaphore:
|
|
109
|
+
return await execute_single(target_hash)
|
|
110
|
+
|
|
111
|
+
results = await asyncio.gather(
|
|
112
|
+
*[execute_with_limit(th) for th in target_hashes], return_exceptions=True
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
processed_results = []
|
|
116
|
+
failed_count = 0
|
|
117
|
+
for i, result in enumerate(results):
|
|
118
|
+
if isinstance(result, Exception):
|
|
119
|
+
failed_count += 1
|
|
120
|
+
error_result = ExecutionResult(
|
|
121
|
+
success=False,
|
|
122
|
+
output=None,
|
|
123
|
+
verification_vcard=None,
|
|
124
|
+
execution_time_ms=0,
|
|
125
|
+
alignment_score=None,
|
|
126
|
+
invariants_preserved=False,
|
|
127
|
+
safety_violations=[],
|
|
128
|
+
liveness_metrics={},
|
|
129
|
+
error_message=str(result),
|
|
130
|
+
)
|
|
131
|
+
processed_results.append(error_result)
|
|
132
|
+
self.logger.error(f"[BATCH_PARALLEL] Exception in {i + 1}: {result}")
|
|
133
|
+
else:
|
|
134
|
+
processed_results.append(result)
|
|
135
|
+
if not result.success:
|
|
136
|
+
failed_count += 1
|
|
137
|
+
|
|
138
|
+
self.engine.correctness_tracker.record_liveness_metric(
|
|
139
|
+
"batch_parallel_completed", 1.0 if failed_count == 0 else 0.5
|
|
140
|
+
)
|
|
141
|
+
self.engine.correctness_tracker.record_liveness_metric(
|
|
142
|
+
"batch_parallel_success_rate",
|
|
143
|
+
(len(processed_results) - failed_count) / len(target_hashes),
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
self.logger.info(
|
|
147
|
+
f"[BATCH_PARALLEL] Completed: {len(processed_results) - failed_count}/{len(target_hashes)} succeeded"
|
|
148
|
+
)
|
|
149
|
+
return processed_results
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
__all__ = ["BatchProcessor"]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Stratified Runtime CEL Execution in Layer 1 (Sprint 384 / DEL-384-04).
|
|
2
|
+
|
|
3
|
+
Provides runtime store builder and evaluation engines conforming to the
|
|
4
|
+
Six-Layer Stratification architecture.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from clm.layer1.cel.builder import (
|
|
10
|
+
Listener,
|
|
11
|
+
PostMiddleware,
|
|
12
|
+
PreMiddleware,
|
|
13
|
+
action_log_middleware,
|
|
14
|
+
build_store,
|
|
15
|
+
vcard_post_middleware,
|
|
16
|
+
vcard_pre_middleware,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"Listener",
|
|
21
|
+
"PostMiddleware",
|
|
22
|
+
"PreMiddleware",
|
|
23
|
+
"action_log_middleware",
|
|
24
|
+
"build_store",
|
|
25
|
+
"vcard_post_middleware",
|
|
26
|
+
"vcard_pre_middleware",
|
|
27
|
+
]
|