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,262 @@
|
|
|
1
|
+
"""Layer 0 Verifier Core — Precondition validation and Execution MCard emission.
|
|
2
|
+
|
|
3
|
+
Extracted from clm.layer1.verifier to resolve Girard paradox violation V-1:
|
|
4
|
+
algebra_baldwin.py (Layer 0) was importing emit_exec_mcard_py and
|
|
5
|
+
verify_precondition from verifier.py (Layer 1), creating an upward
|
|
6
|
+
dependency that breaks the Type Lattice SSOT principle (type_lattice.md §4.5).
|
|
7
|
+
|
|
8
|
+
These functions depend only on Layer 0 primitives:
|
|
9
|
+
- clm.layer0.db (CLMDB, CLMRepository, resolve_clm_path)
|
|
10
|
+
- clm.layer0.utils (clm_hash_file, clm_hash_string, detect_host_os)
|
|
11
|
+
- clm.layer0.algebra (load_normalized_manifest_str — lazy)
|
|
12
|
+
- clm.layer0.type_registry (TypeRegistry — lazy)
|
|
13
|
+
- clm.cross_cutting.clm_logger (get_logger)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
import sys
|
|
18
|
+
import time
|
|
19
|
+
|
|
20
|
+
import yaml
|
|
21
|
+
|
|
22
|
+
from clm.cross_cutting.clm_logger import get_logger
|
|
23
|
+
from clm.layer0.utils import (
|
|
24
|
+
clm_hash_file,
|
|
25
|
+
clm_hash_string,
|
|
26
|
+
detect_host_os,
|
|
27
|
+
resolve_clm_path,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _get_storage():
|
|
32
|
+
target = "clm.layer2.storage"
|
|
33
|
+
mod = sys.modules.get(target)
|
|
34
|
+
if mod is None:
|
|
35
|
+
__import__(target)
|
|
36
|
+
mod = sys.modules.get(target)
|
|
37
|
+
return mod
|
|
38
|
+
|
|
39
|
+
_logger = get_logger("clm.layer0.verifier_core")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _get_load_manifest_str():
|
|
43
|
+
from clm.layer0.algebra import load_normalized_manifest_str
|
|
44
|
+
return load_normalized_manifest_str
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def verify_precondition(clm_root: str, clm_file: str) -> bool:
|
|
48
|
+
"""
|
|
49
|
+
Validates the precondition bounds and zero-trust identity before executing a PCard.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
clm_root (str): The root directory of the CLM workspace.
|
|
53
|
+
clm_file (str): The path to the CLM PCard file.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
bool: True if authorized and validated, False otherwise.
|
|
57
|
+
"""
|
|
58
|
+
load_normalized_manifest_str_fn = _get_load_manifest_str()
|
|
59
|
+
clm_file = resolve_clm_path(clm_root, clm_file)
|
|
60
|
+
stub = os.path.join(clm_root, "kernel", "scripts", "clmEval")
|
|
61
|
+
eval_yaml = os.path.join(clm_root, "computable_knowledge/Abstract/layer5_system/bootstrap/evaluator/clmEval.yaml")
|
|
62
|
+
if os.path.exists(stub) and os.path.exists(eval_yaml):
|
|
63
|
+
stub_cert = ""
|
|
64
|
+
with open(stub, encoding="utf-8") as f:
|
|
65
|
+
for line in f:
|
|
66
|
+
if "_EXPECTED_HASH=" in line:
|
|
67
|
+
parts = line.split('="')
|
|
68
|
+
if len(parts) > 1:
|
|
69
|
+
stub_cert = parts[1].split('"')[0]
|
|
70
|
+
break
|
|
71
|
+
yaml_hash = clm_hash_file(eval_yaml)
|
|
72
|
+
if stub_cert and stub_cert != yaml_hash:
|
|
73
|
+
import hashlib
|
|
74
|
+
with open(eval_yaml, "rb") as yf:
|
|
75
|
+
norm_yaml = yf.read().replace(b"\r\n", b"\n").replace(b"\r", b"\n")
|
|
76
|
+
if stub_cert != hashlib.sha256(norm_yaml).hexdigest():
|
|
77
|
+
_logger.error("[VERIFY] ❌ Integrity failure: stub certificate mismatch")
|
|
78
|
+
_logger.info(f"[VERIFY] Stub expects: {stub_cert}")
|
|
79
|
+
_logger.info(f"[VERIFY] YAML actual: {yaml_hash}")
|
|
80
|
+
return False
|
|
81
|
+
_logger.info("[VERIFY] ✅ Bidirectional integrity verified")
|
|
82
|
+
_logger.info(f"[VERIFY] YAML hash: {yaml_hash}")
|
|
83
|
+
_logger.info(f"[VERIFY] Stub hash: {clm_hash_file(stub)}")
|
|
84
|
+
|
|
85
|
+
host_os = detect_host_os()
|
|
86
|
+
|
|
87
|
+
root_did = f"did:pkc:ca:{host_os}:root-agent"
|
|
88
|
+
storage = _get_storage()
|
|
89
|
+
db = storage.CLMDB(clm_root) if storage else None
|
|
90
|
+
repo = storage.CLMRepository(db, clm_root) if storage and db else None
|
|
91
|
+
|
|
92
|
+
if not repo.has_card(root_did, "agent_identities"):
|
|
93
|
+
_logger.info(f"[VERIFY] Bootstrapping root CA agent: {root_did}")
|
|
94
|
+
g_time = time.strftime("%Y-%m-%dT%H:%M:%S%z")
|
|
95
|
+
yaml_content = f"""agent:
|
|
96
|
+
did: "{root_did}"
|
|
97
|
+
type: "CA"
|
|
98
|
+
metadata:
|
|
99
|
+
os: "{host_os}"
|
|
100
|
+
name: "Root CA Agent"
|
|
101
|
+
capabilities:
|
|
102
|
+
- "admin"
|
|
103
|
+
- "exec"
|
|
104
|
+
"""
|
|
105
|
+
repo.save_card_and_handle("agent:root", root_did, yaml_content, "agent_identities", timestamp=g_time, replace=False)
|
|
106
|
+
|
|
107
|
+
virtual_dids = ["did:clm:evaluator", "did:clm:cache_manager", "did:clm:iam_agent", "did:clm:ci_pipeline"]
|
|
108
|
+
for v_did in virtual_dids:
|
|
109
|
+
if not repo.has_card(v_did, "agent_identities"):
|
|
110
|
+
g_time = time.strftime("%Y-%m-%dT%H:%M:%S%z")
|
|
111
|
+
yaml_content = f"""agent:
|
|
112
|
+
did: "{v_did}"
|
|
113
|
+
type: "CA"
|
|
114
|
+
metadata:
|
|
115
|
+
name: "Virtual {v_did} Agent"
|
|
116
|
+
capabilities:
|
|
117
|
+
- "admin"
|
|
118
|
+
- "exec"
|
|
119
|
+
"""
|
|
120
|
+
repo.save_card(v_did, yaml_content, "agent_identities", timestamp=g_time, replace=False)
|
|
121
|
+
|
|
122
|
+
if "ACTIVE_AGENT_DID" not in os.environ:
|
|
123
|
+
os.environ["ACTIVE_AGENT_DID"] = root_did
|
|
124
|
+
|
|
125
|
+
active_did = os.environ["ACTIVE_AGENT_DID"]
|
|
126
|
+
_logger.info(f"[VERIFY] Active Agent DID: {active_did}")
|
|
127
|
+
|
|
128
|
+
agent_card_res = repo.get_card_content(active_did, "agent_identities")
|
|
129
|
+
if not agent_card_res or not agent_card_res.strip():
|
|
130
|
+
_logger.error(f"[VERIFY] ❌ Unauthorized agent: {active_did} is not registered.")
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
agent_data = yaml.safe_load(agent_card_res.strip())
|
|
135
|
+
caps = agent_data.get("agent", {}).get("capabilities", [])
|
|
136
|
+
if not any(c in caps for c in ["exec", "admin"]):
|
|
137
|
+
_logger.error(f"[VERIFY] ❌ Agent {active_did} lacks required 'exec' or 'admin' capability.")
|
|
138
|
+
return False
|
|
139
|
+
except Exception as e:
|
|
140
|
+
_logger.error(f"[VERIFY] ❌ Failed to parse agent capabilities: {e}")
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
_logger.info("[VERIFY] ✅ Agent authorized successfully")
|
|
144
|
+
|
|
145
|
+
strict_types = os.environ.get("CLM_STRICT_TYPES", "").lower() == "true"
|
|
146
|
+
if clm_file and os.path.exists(clm_file):
|
|
147
|
+
content_str = load_normalized_manifest_str_fn(clm_root, clm_file)
|
|
148
|
+
type_name = "mcard"
|
|
149
|
+
extract_key = None
|
|
150
|
+
if "_meta:" in content_str:
|
|
151
|
+
type_name = "clm_manifest"
|
|
152
|
+
extract_key = "_meta"
|
|
153
|
+
elif "vpre:" in content_str or "vpost:" in content_str or "vcard_token:" in content_str:
|
|
154
|
+
type_name = "vcard_token"
|
|
155
|
+
extract_key = "vcard_token"
|
|
156
|
+
elif "abstract_spec:" in content_str:
|
|
157
|
+
type_name = "a_mcard"
|
|
158
|
+
extract_key = "abstract_spec"
|
|
159
|
+
|
|
160
|
+
payload_content = content_str
|
|
161
|
+
try:
|
|
162
|
+
parsed = yaml.safe_load(content_str)
|
|
163
|
+
inner = parsed.get("clm", parsed)
|
|
164
|
+
if extract_key and isinstance(inner, dict) and extract_key in inner:
|
|
165
|
+
payload_content = yaml.dump(inner[extract_key])
|
|
166
|
+
except Exception:
|
|
167
|
+
pass
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
from clm.layer0.type_registry import TypeRegistry
|
|
171
|
+
registry = TypeRegistry(clm_root)
|
|
172
|
+
if type_name == "mcard" and not registry.get_type_info("mcard"):
|
|
173
|
+
_logger.warning("[VERIFY] ⚠️ Skipping type validation: 'mcard' is not registered.")
|
|
174
|
+
else:
|
|
175
|
+
registry.validate(type_name, payload_content)
|
|
176
|
+
_logger.info(f"[VERIFY] ✅ Type validation passed (type: {type_name})")
|
|
177
|
+
except Exception as e:
|
|
178
|
+
if strict_types:
|
|
179
|
+
_logger.error(f"[VERIFY] ❌ TYPE_ERROR: Type validation failed for '{clm_file}'!")
|
|
180
|
+
_logger.error(f"Error: {e}")
|
|
181
|
+
return False
|
|
182
|
+
else:
|
|
183
|
+
_logger.warning(f"[VERIFY] ⚠️ TYPE_WARNING: Type validation failed for '{clm_file}' (Strict mode is OFF)")
|
|
184
|
+
_logger.info(f"Details: {e}")
|
|
185
|
+
return True
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def emit_exec_mcard_py(
|
|
189
|
+
clm_root: str,
|
|
190
|
+
operation: str,
|
|
191
|
+
content_hash: str,
|
|
192
|
+
extra_yaml: str = "",
|
|
193
|
+
a_hash: str = "",
|
|
194
|
+
c_hash: str = "",
|
|
195
|
+
) -> str:
|
|
196
|
+
"""
|
|
197
|
+
Emits an Execution MCard to the Tri-Database to track state changes.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
clm_root (str): The root directory of the CLM workspace.
|
|
201
|
+
operation (str): The operation being performed.
|
|
202
|
+
content_hash (str): The hash of the content involved.
|
|
203
|
+
extra_yaml (str): Additional YAML content to append.
|
|
204
|
+
a_hash (str): The Abstract Specification hash.
|
|
205
|
+
c_hash (str): The Concrete Implementation hash.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
str: The path to the emitted MCard file.
|
|
209
|
+
"""
|
|
210
|
+
storage = _get_storage()
|
|
211
|
+
db = storage.CLMDB(clm_root) if storage else None
|
|
212
|
+
zero_fs = db.zero_fs if db else (os.environ.get("CLM_ZERO_FS", "").lower() in ("true", "1"))
|
|
213
|
+
mcard_dir = os.path.join(clm_root, "data/execution_logs")
|
|
214
|
+
if zero_fs:
|
|
215
|
+
mcard_dir = os.path.join(os.environ.get("TMPDIR", "/tmp"), "data/execution_logs")
|
|
216
|
+
os.makedirs(mcard_dir, exist_ok=True)
|
|
217
|
+
|
|
218
|
+
g_time = time.strftime("%Y-%m-%dT%H:%M:%S%z")
|
|
219
|
+
unix_ts = int(time.time())
|
|
220
|
+
mcard_file = os.path.join(mcard_dir, f"exec_{unix_ts}_{content_hash[:8]}.yaml")
|
|
221
|
+
|
|
222
|
+
lines = []
|
|
223
|
+
lines.append("mcard:")
|
|
224
|
+
lines.append(f' g_time: "{g_time}"')
|
|
225
|
+
lines.append(f' content_hash: "{content_hash}"')
|
|
226
|
+
lines.append(f' operation: "{operation}"')
|
|
227
|
+
lines.append(f' agent_did: "{os.environ.get("ACTIVE_AGENT_DID", "")}"')
|
|
228
|
+
|
|
229
|
+
if extra_yaml:
|
|
230
|
+
for line in extra_yaml.strip().splitlines():
|
|
231
|
+
lines.append(f" {line}")
|
|
232
|
+
|
|
233
|
+
if a_hash or c_hash:
|
|
234
|
+
lines.append(" source:")
|
|
235
|
+
if a_hash:
|
|
236
|
+
lines.append(f' a_hash: "{a_hash}"')
|
|
237
|
+
if c_hash:
|
|
238
|
+
lines.append(f' c_hash: "{c_hash}"')
|
|
239
|
+
lines.append(f' operation: "{operation}"')
|
|
240
|
+
facts = os.environ.get("CLM_RUNTIME_FACTS")
|
|
241
|
+
if facts:
|
|
242
|
+
lines.append(" runtime_facts:")
|
|
243
|
+
for line in facts.strip().splitlines():
|
|
244
|
+
lines.append(f" {line}")
|
|
245
|
+
else:
|
|
246
|
+
facts = os.environ.get("CLM_RUNTIME_FACTS")
|
|
247
|
+
if facts:
|
|
248
|
+
lines.append(" runtime_facts:")
|
|
249
|
+
for line in facts.strip().splitlines():
|
|
250
|
+
lines.append(f" {line}")
|
|
251
|
+
|
|
252
|
+
mcard_content = "\n".join(lines) + "\n"
|
|
253
|
+
|
|
254
|
+
if not zero_fs:
|
|
255
|
+
with open(mcard_file, "w", encoding="utf-8") as f:
|
|
256
|
+
f.write(mcard_content)
|
|
257
|
+
|
|
258
|
+
mcard_hash = clm_hash_string(mcard_content)
|
|
259
|
+
if storage and db:
|
|
260
|
+
repo = storage.CLMRepository(db, clm_root)
|
|
261
|
+
repo.save_card(mcard_hash, mcard_content, "execution_log", timestamp=g_time, replace=False)
|
|
262
|
+
return mcard_file
|
clm/layer1/__init__.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""CLM layer1 layer package."""
|
|
2
|
+
|
|
3
|
+
from clm.layer1.gate_composer import compose_conditions_cmd, run_nested_evaluate
|
|
4
|
+
from clm.layer1.matrix import IncidenceMatrix, math_gcd, math_lcm, solve_nullspace
|
|
5
|
+
from clm.layer1.petri import ColoredPetriVM, petri_run_net
|
|
6
|
+
from clm.layer1.runtime_adapter import (
|
|
7
|
+
evaluate_compose,
|
|
8
|
+
evaluate_polyglot,
|
|
9
|
+
run_internal_reference_test,
|
|
10
|
+
)
|
|
11
|
+
from clm.layer1.test_runner import run_algebra_op, test_baldwin_operators
|
|
12
|
+
from clm.layer1.verifier import (
|
|
13
|
+
check_precondition_py,
|
|
14
|
+
emit_exec_mcard_py,
|
|
15
|
+
get_load_manifest,
|
|
16
|
+
get_load_manifest_str,
|
|
17
|
+
normalize_output_py,
|
|
18
|
+
resolve_active_hashes_py,
|
|
19
|
+
validate_postcondition,
|
|
20
|
+
verify_precondition,
|
|
21
|
+
)
|
|
22
|
+
from clm.layer1.vm import PetriVM
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
'petri_run_net', 'ColoredPetriVM', 'PetriVM', 'IncidenceMatrix', 'solve_nullspace', 'math_gcd', 'math_lcm',
|
|
26
|
+
'compose_conditions_cmd', 'run_nested_evaluate', 'run_internal_reference_test', 'evaluate_polyglot',
|
|
27
|
+
'evaluate_compose', 'get_load_manifest_str', 'get_load_manifest', 'verify_precondition',
|
|
28
|
+
'validate_postcondition', 'emit_exec_mcard_py', 'resolve_active_hashes_py', 'check_precondition_py',
|
|
29
|
+
'normalize_output_py', 'run_algebra_op', 'test_baldwin_operators'
|
|
30
|
+
]
|
|
31
|
+
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""Action Execution Dispatcher (Sprint 393 / DEL-393-01).
|
|
2
|
+
|
|
3
|
+
Bridges declarative Action manifests (clm.action.v1) to the Layer 1 algebraic execution core.
|
|
4
|
+
Resolves content-addressed MCards, dispatches to domain-aware Baldwin manipulators,
|
|
5
|
+
enforces the Zero Mutation Invariant on inputs, and registers output MCards in CAS.
|
|
6
|
+
|
|
7
|
+
Acceptance Criteria:
|
|
8
|
+
- AC-393-01-01: ActionDispatcher.execute() resolves inputs from CAS and returns typed ExecutionResult.
|
|
9
|
+
- AC-393-01-02: Zero Mutation Invariant on input MCards (input hash unchanged post-execution).
|
|
10
|
+
- AC-393-01-03: Unresolved input URIs raise InputResolutionError prior to operator execution.
|
|
11
|
+
- AC-393-01-04: Execution metrics (wall time, bytes processed) recorded in ExecutionResult.
|
|
12
|
+
|
|
13
|
+
Layer 1 module (no imports above L1).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import time
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
from clm.cross_cutting.domain_models import MCard
|
|
24
|
+
from clm.layer0.exceptions import CLMError
|
|
25
|
+
from clm.layer0.model.action import ActionManifest
|
|
26
|
+
from clm.layer0.model.card_triad import compute_canonical_hash
|
|
27
|
+
from clm.layer0.utils import clm_hash_string
|
|
28
|
+
from clm.layer1.operations.manipulators import (
|
|
29
|
+
DocumentManipulator,
|
|
30
|
+
MediaManipulator,
|
|
31
|
+
StructuredManipulator,
|
|
32
|
+
TabularManipulator,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class InputResolutionError(CLMError):
|
|
37
|
+
"""Raised when an action input cannot be resolved from bindings or CAS."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True)
|
|
41
|
+
class ExecutionResult:
|
|
42
|
+
"""Certified outcome of an Action execution."""
|
|
43
|
+
|
|
44
|
+
status: str
|
|
45
|
+
operator: str
|
|
46
|
+
outputs: dict[str, MCard] = field(default_factory=dict)
|
|
47
|
+
input_hashes: dict[str, str] = field(default_factory=dict)
|
|
48
|
+
output_hashes: dict[str, str] = field(default_factory=dict)
|
|
49
|
+
metrics: dict[str, Any] = field(default_factory=dict)
|
|
50
|
+
error: str | None = None
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def is_success(self) -> bool:
|
|
54
|
+
return self.status == "success"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ActionDispatcher:
|
|
58
|
+
"""Dispatches declarative Action manifests to Baldwin modality manipulators."""
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def execute(
|
|
62
|
+
cls,
|
|
63
|
+
manifest: ActionManifest | str | Path | dict[str, Any],
|
|
64
|
+
input_bindings: dict[str, Any] | None = None,
|
|
65
|
+
fs: Any = None,
|
|
66
|
+
) -> ExecutionResult:
|
|
67
|
+
"""Execute an Action manifest against content-addressed MCards."""
|
|
68
|
+
t_start = time.perf_counter()
|
|
69
|
+
bindings = dict(input_bindings or {})
|
|
70
|
+
|
|
71
|
+
# 1. Parse manifest if string, Path, or dict
|
|
72
|
+
if isinstance(manifest, (str, Path)):
|
|
73
|
+
action = ActionManifest.from_yaml(manifest)
|
|
74
|
+
elif isinstance(manifest, dict):
|
|
75
|
+
action = ActionManifest.from_dict(manifest)
|
|
76
|
+
elif isinstance(manifest, ActionManifest):
|
|
77
|
+
action = manifest
|
|
78
|
+
else:
|
|
79
|
+
raise TypeError(f"Invalid manifest type: {type(manifest)}")
|
|
80
|
+
|
|
81
|
+
operator = action.operation.operator
|
|
82
|
+
params = action.operation.parameters or {}
|
|
83
|
+
|
|
84
|
+
# 2. Resolve inputs and check AC-393-01-03 (InputResolutionError)
|
|
85
|
+
resolved_inputs: dict[str, MCard] = {}
|
|
86
|
+
input_hashes_before: dict[str, str] = {}
|
|
87
|
+
original_card_hashes: dict[str, str] = {}
|
|
88
|
+
bytes_processed = 0
|
|
89
|
+
|
|
90
|
+
for inp in action.inputs:
|
|
91
|
+
card: MCard | None = None
|
|
92
|
+
# Check direct binding by input name or URI
|
|
93
|
+
if inp.name in bindings:
|
|
94
|
+
bound = bindings[inp.name]
|
|
95
|
+
if isinstance(bound, MCard):
|
|
96
|
+
card = bound
|
|
97
|
+
else:
|
|
98
|
+
# Raw payload provided, wrap in transient MCard
|
|
99
|
+
digest = clm_hash_string(bound) if isinstance(bound, str) else compute_canonical_hash(bound)
|
|
100
|
+
card = MCard.create(
|
|
101
|
+
uri=f"mcard:{digest[:16]}",
|
|
102
|
+
payload=bound,
|
|
103
|
+
author="did:clm:user",
|
|
104
|
+
)
|
|
105
|
+
elif inp.uri and inp.uri in bindings:
|
|
106
|
+
bound = bindings[inp.uri]
|
|
107
|
+
if isinstance(bound, MCard):
|
|
108
|
+
card = bound
|
|
109
|
+
else:
|
|
110
|
+
digest = clm_hash_string(bound) if isinstance(bound, str) else compute_canonical_hash(bound)
|
|
111
|
+
card = MCard.create(
|
|
112
|
+
uri=inp.uri,
|
|
113
|
+
payload=bound,
|
|
114
|
+
author="did:clm:user",
|
|
115
|
+
)
|
|
116
|
+
elif inp.uri and fs is not None:
|
|
117
|
+
# Attempt resolution from CAS filesystem
|
|
118
|
+
try:
|
|
119
|
+
clean_uri = inp.uri
|
|
120
|
+
if clean_uri.startswith("mcard://"):
|
|
121
|
+
clean_uri = "mcard:" + clean_uri[len("mcard://"):]
|
|
122
|
+
card = fs.read_file(clean_uri)
|
|
123
|
+
except Exception:
|
|
124
|
+
card = None
|
|
125
|
+
|
|
126
|
+
if card is None and inp.required:
|
|
127
|
+
raise InputResolutionError(
|
|
128
|
+
f"Required input '{inp.name}' (uri={inp.uri}) could not be resolved"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if card is not None:
|
|
132
|
+
resolved_inputs[inp.name] = card
|
|
133
|
+
original_card_hashes[inp.name] = str(card.hash)
|
|
134
|
+
if isinstance(card.payload, str):
|
|
135
|
+
h_str = clm_hash_string(card.payload)
|
|
136
|
+
elif isinstance(card.payload, bytes):
|
|
137
|
+
from clm.layer0.utils import clm_hash_bytes
|
|
138
|
+
h_str = clm_hash_bytes(card.payload)
|
|
139
|
+
else:
|
|
140
|
+
h_str = str(card.hash)
|
|
141
|
+
input_hashes_before[inp.name] = h_str
|
|
142
|
+
# Count bytes
|
|
143
|
+
if isinstance(card.payload, (str, bytes)):
|
|
144
|
+
bytes_processed += len(card.payload)
|
|
145
|
+
elif card.content:
|
|
146
|
+
bytes_processed += len(card.content)
|
|
147
|
+
|
|
148
|
+
# 3. Select Modality Manipulator
|
|
149
|
+
primary_input = next(iter(resolved_inputs.values())) if resolved_inputs else None
|
|
150
|
+
primary_format = action.inputs[0].format if action.inputs else None
|
|
151
|
+
|
|
152
|
+
if primary_format is None and primary_input and hasattr(primary_input, "metadata"):
|
|
153
|
+
primary_format = primary_input.metadata.get("format")
|
|
154
|
+
|
|
155
|
+
manipulator = cls._select_manipulator(primary_format, primary_input)
|
|
156
|
+
|
|
157
|
+
# 4. Invoke Algebraic Operator
|
|
158
|
+
try:
|
|
159
|
+
payload = primary_input.payload if primary_input else None
|
|
160
|
+
op_method = getattr(manipulator, operator, None)
|
|
161
|
+
if not op_method:
|
|
162
|
+
raise ValueError(f"Manipulator {manipulator.__name__} does not support operator '{operator}'")
|
|
163
|
+
|
|
164
|
+
op_out = op_method(payload, params)
|
|
165
|
+
|
|
166
|
+
# 5. AC-393-01-02: Enforce Zero Mutation Invariant on inputs
|
|
167
|
+
for name, card in resolved_inputs.items():
|
|
168
|
+
expected_hash = original_card_hashes[name]
|
|
169
|
+
current_hash = str(card.hash)
|
|
170
|
+
if current_hash != expected_hash:
|
|
171
|
+
raise AssertionError(
|
|
172
|
+
f"Zero Mutation Invariant violated for input '{name}': hash mutated from {expected_hash} to {current_hash}"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# 6. Map operator output to ActionOutputs
|
|
176
|
+
outputs: dict[str, MCard] = {}
|
|
177
|
+
output_hashes: dict[str, str] = {}
|
|
178
|
+
|
|
179
|
+
if isinstance(op_out, dict) and len(action.outputs) > 1:
|
|
180
|
+
# Multi-output split
|
|
181
|
+
for out_spec in action.outputs:
|
|
182
|
+
out_name = out_spec.name
|
|
183
|
+
part_payload = op_out.get(out_name)
|
|
184
|
+
if part_payload is None:
|
|
185
|
+
# Fallback to key index
|
|
186
|
+
part_payload = next(iter(op_out.values()), "")
|
|
187
|
+
|
|
188
|
+
digest = clm_hash_string(part_payload) if isinstance(part_payload, str) else compute_canonical_hash(part_payload)
|
|
189
|
+
handle = out_spec.handle or f"mcard:{out_name}"
|
|
190
|
+
clean_uri = handle if handle.startswith("mcard:") else f"mcard:{handle}"
|
|
191
|
+
out_card = MCard.create(
|
|
192
|
+
uri=clean_uri,
|
|
193
|
+
payload=part_payload,
|
|
194
|
+
author=action.clm.author,
|
|
195
|
+
metadata={"format": out_spec.format or primary_format, "generator": action.clm.name},
|
|
196
|
+
)
|
|
197
|
+
outputs[out_name] = out_card
|
|
198
|
+
output_hashes[out_name] = digest
|
|
199
|
+
if fs is not None:
|
|
200
|
+
fs.write_file(uri=clean_uri, payload=part_payload, author=action.clm.author, metadata=out_card.metadata)
|
|
201
|
+
|
|
202
|
+
else:
|
|
203
|
+
# Single output or undivided result
|
|
204
|
+
out_spec = action.outputs[0]
|
|
205
|
+
out_name = out_spec.name
|
|
206
|
+
part_payload = op_out.get(out_name, op_out) if isinstance(op_out, dict) else op_out
|
|
207
|
+
|
|
208
|
+
digest = clm_hash_string(part_payload) if isinstance(part_payload, str) else compute_canonical_hash(part_payload)
|
|
209
|
+
handle = out_spec.handle or f"mcard:{out_name}"
|
|
210
|
+
clean_uri = handle if handle.startswith("mcard:") else f"mcard:{handle}"
|
|
211
|
+
out_card = MCard.create(
|
|
212
|
+
uri=clean_uri,
|
|
213
|
+
payload=part_payload,
|
|
214
|
+
author=action.clm.author,
|
|
215
|
+
metadata={"format": out_spec.format or primary_format, "generator": action.clm.name},
|
|
216
|
+
)
|
|
217
|
+
outputs[out_name] = out_card
|
|
218
|
+
output_hashes[out_name] = digest
|
|
219
|
+
if fs is not None:
|
|
220
|
+
fs.write_file(uri=clean_uri, payload=part_payload, author=action.clm.author, metadata=out_card.metadata)
|
|
221
|
+
|
|
222
|
+
t_elapsed = (time.perf_counter() - t_start) * 1000.0
|
|
223
|
+
|
|
224
|
+
return ExecutionResult(
|
|
225
|
+
status="success",
|
|
226
|
+
operator=operator,
|
|
227
|
+
outputs=outputs,
|
|
228
|
+
input_hashes=input_hashes_before,
|
|
229
|
+
output_hashes=output_hashes,
|
|
230
|
+
metrics={
|
|
231
|
+
"wall_time_ms": round(t_elapsed, 3),
|
|
232
|
+
"bytes_processed": bytes_processed,
|
|
233
|
+
"operator": operator,
|
|
234
|
+
"action_name": action.clm.name,
|
|
235
|
+
},
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
except Exception as e:
|
|
239
|
+
t_elapsed = (time.perf_counter() - t_start) * 1000.0
|
|
240
|
+
return ExecutionResult(
|
|
241
|
+
status="failed",
|
|
242
|
+
operator=operator,
|
|
243
|
+
input_hashes=input_hashes_before,
|
|
244
|
+
metrics={"wall_time_ms": round(t_elapsed, 3), "bytes_processed": bytes_processed},
|
|
245
|
+
error=str(e),
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
@staticmethod
|
|
249
|
+
def _select_manipulator(fmt: str | None, primary_input: MCard | None) -> type:
|
|
250
|
+
"""Select appropriate modality manipulator."""
|
|
251
|
+
if fmt in ("csv", "tsv", "tabular"):
|
|
252
|
+
return TabularManipulator
|
|
253
|
+
if fmt in ("markdown", "text", "txt", "md"):
|
|
254
|
+
return DocumentManipulator
|
|
255
|
+
if fmt in ("json", "yaml", "yml"):
|
|
256
|
+
return StructuredManipulator
|
|
257
|
+
if fmt in ("image", "audio", "media", "binary", "model3d"):
|
|
258
|
+
return MediaManipulator
|
|
259
|
+
|
|
260
|
+
# Infer from payload
|
|
261
|
+
if primary_input and primary_input.payload is not None:
|
|
262
|
+
p = primary_input.payload
|
|
263
|
+
if isinstance(p, (dict, list)):
|
|
264
|
+
return StructuredManipulator
|
|
265
|
+
if isinstance(p, bytes):
|
|
266
|
+
return MediaManipulator
|
|
267
|
+
if isinstance(p, str):
|
|
268
|
+
if "\n" in p and ("," in p.splitlines()[0] or "\t" in p.splitlines()[0]):
|
|
269
|
+
return TabularManipulator
|
|
270
|
+
if p.startswith("#") or "##" in p:
|
|
271
|
+
return DocumentManipulator
|
|
272
|
+
if p.strip().startswith("{") or p.strip().startswith("["):
|
|
273
|
+
return StructuredManipulator
|
|
274
|
+
|
|
275
|
+
return TabularManipulator
|