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
clm/layer0/loader.py
ADDED
|
@@ -0,0 +1,826 @@
|
|
|
1
|
+
"""CLM Chapter & YAML Template Loader (Layer 0).
|
|
2
|
+
|
|
3
|
+
Part of Phase 7 (Sprint 383: Layer 0 Kenotic Core & Parser Unification).
|
|
4
|
+
Loads CLM Chapter specifications and templates from YAML, parses dimensional metadata,
|
|
5
|
+
and dynamically constructs executable Chapter objects.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
import yaml
|
|
14
|
+
|
|
15
|
+
from clm.layer0.narrative import Chapter, CLMConfiguration, NarrativeMonad
|
|
16
|
+
|
|
17
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
18
|
+
# Constants
|
|
19
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
CLM_EXTENSIONS = (".clm", ".yaml", ".yml")
|
|
22
|
+
CODE_READABLE_RUNTIMES = ("python", "javascript")
|
|
23
|
+
MODULE_URI_PREFIX = "module://"
|
|
24
|
+
DEFAULT_RUNTIME = "python"
|
|
25
|
+
DEFAULT_TARGET = "dummy_target"
|
|
26
|
+
|
|
27
|
+
# Runtime factory hook for Layer 1 execution engines (Dependency Inversion for INV-383-02)
|
|
28
|
+
_runtime_factory: Any = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def register_runtime_factory(factory: Any) -> None:
|
|
32
|
+
"""Register runtime factory from Layer 1."""
|
|
33
|
+
global _runtime_factory
|
|
34
|
+
_runtime_factory = factory
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _resolve_runtime_factory() -> Any:
|
|
38
|
+
global _runtime_factory
|
|
39
|
+
if _runtime_factory is not None:
|
|
40
|
+
return _runtime_factory
|
|
41
|
+
import sys
|
|
42
|
+
|
|
43
|
+
runtimes_mod = sys.modules.get("clm.layer1.runtimes")
|
|
44
|
+
if runtimes_mod and hasattr(runtimes_mod, "RuntimeFactory"):
|
|
45
|
+
return runtimes_mod.RuntimeFactory
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
51
|
+
# Path Resolution Utilities
|
|
52
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _resolve_path(base_dir: str, path: str) -> str:
|
|
56
|
+
"""Resolve a relative path against a base directory."""
|
|
57
|
+
if os.path.isabs(path):
|
|
58
|
+
return path
|
|
59
|
+
return os.path.join(base_dir, path)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _is_module_uri(path: str) -> bool:
|
|
63
|
+
"""Check if path is a module:// URI."""
|
|
64
|
+
return path.startswith(MODULE_URI_PREFIX)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _resolve_concrete_paths(concrete: dict, base_dir: str, runtime: str) -> None:
|
|
68
|
+
"""Resolve all file paths in concrete config relative to base_dir.
|
|
69
|
+
Modifies concrete dict in-place.
|
|
70
|
+
"""
|
|
71
|
+
# code_file resolution
|
|
72
|
+
if "code_file" in concrete and not _is_module_uri(concrete["code_file"]):
|
|
73
|
+
code_path = _resolve_path(base_dir, concrete["code_file"])
|
|
74
|
+
concrete["code_file"] = code_path
|
|
75
|
+
# Read code content for runtimes that need it
|
|
76
|
+
if runtime in CODE_READABLE_RUNTIMES and os.path.exists(code_path):
|
|
77
|
+
with open(code_path) as f:
|
|
78
|
+
concrete["code"] = f.read()
|
|
79
|
+
|
|
80
|
+
# binary_path resolution
|
|
81
|
+
if "binary_path" in concrete and not os.path.isabs(concrete["binary_path"]):
|
|
82
|
+
concrete["binary_path"] = _resolve_path(base_dir, concrete["binary_path"])
|
|
83
|
+
|
|
84
|
+
# wasm_module resolution
|
|
85
|
+
if "wasm_module" in concrete and not os.path.isabs(concrete["wasm_module"]):
|
|
86
|
+
concrete["wasm_module"] = _resolve_path(base_dir, concrete["wasm_module"])
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _parse_numeric_result(result: Any) -> Any:
|
|
90
|
+
"""Attempt to parse a string result as a number."""
|
|
91
|
+
if isinstance(result, str):
|
|
92
|
+
clean = result.strip()
|
|
93
|
+
# Check if it looks numeric (handles negatives and decimals)
|
|
94
|
+
if clean.lstrip("-").replace(".", "", 1).isdigit():
|
|
95
|
+
try:
|
|
96
|
+
return float(clean) if "." in clean else int(clean)
|
|
97
|
+
except ValueError:
|
|
98
|
+
pass
|
|
99
|
+
return result
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
103
|
+
# YAML Template Loader
|
|
104
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class YAMLTemplateLoader:
|
|
108
|
+
"""Loads CLM YAML templates from a templates directory."""
|
|
109
|
+
|
|
110
|
+
def __init__(self, templates_dir: str | None = None):
|
|
111
|
+
if templates_dir:
|
|
112
|
+
self.templates_dir = templates_dir
|
|
113
|
+
else:
|
|
114
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
115
|
+
candidate_dirs = [
|
|
116
|
+
os.path.join(current_dir, "templates"),
|
|
117
|
+
os.path.abspath(
|
|
118
|
+
os.path.join(
|
|
119
|
+
current_dir,
|
|
120
|
+
"../../../../clm/computable_knowledge/manifests/templates",
|
|
121
|
+
)
|
|
122
|
+
),
|
|
123
|
+
os.path.abspath(
|
|
124
|
+
os.path.join(
|
|
125
|
+
current_dir,
|
|
126
|
+
"../../../../mcard/ptr/clm/templates",
|
|
127
|
+
)
|
|
128
|
+
),
|
|
129
|
+
]
|
|
130
|
+
self.templates_dir = candidate_dirs[0]
|
|
131
|
+
for c_dir in candidate_dirs:
|
|
132
|
+
if os.path.exists(c_dir):
|
|
133
|
+
self.templates_dir = c_dir
|
|
134
|
+
break
|
|
135
|
+
|
|
136
|
+
def load_template(self, template_name: str) -> dict[str, Any]:
|
|
137
|
+
"""Load a specific YAML template by name."""
|
|
138
|
+
for ext in CLM_EXTENSIONS:
|
|
139
|
+
file_path = os.path.join(self.templates_dir, f"{template_name}{ext}")
|
|
140
|
+
if os.path.exists(file_path):
|
|
141
|
+
with open(file_path) as f:
|
|
142
|
+
return yaml.safe_load(f)
|
|
143
|
+
raise FileNotFoundError(f"Template not found: {template_name}")
|
|
144
|
+
|
|
145
|
+
def load_all_templates(self) -> dict[str, dict[str, Any]]:
|
|
146
|
+
"""Load all templates in the directory."""
|
|
147
|
+
templates = {}
|
|
148
|
+
if not os.path.exists(self.templates_dir):
|
|
149
|
+
return templates
|
|
150
|
+
|
|
151
|
+
for filename in os.listdir(self.templates_dir):
|
|
152
|
+
if filename.endswith(CLM_EXTENSIONS):
|
|
153
|
+
name = os.path.splitext(filename)[0]
|
|
154
|
+
with open(os.path.join(self.templates_dir, filename)) as f:
|
|
155
|
+
templates[name] = yaml.safe_load(f)
|
|
156
|
+
return templates
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
160
|
+
# CLM Chapter Loader
|
|
161
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class CLMChapterLoader:
|
|
165
|
+
"""Loads Chapter objects from YAML specifications."""
|
|
166
|
+
|
|
167
|
+
@staticmethod
|
|
168
|
+
def _parse_clm_data(data: dict, yaml_path: str) -> tuple:
|
|
169
|
+
"""Parse CLM data and return (chapter_data, clm_data, clm_config).
|
|
170
|
+
Handles both 'chapter' format and raw PCard format.
|
|
171
|
+
"""
|
|
172
|
+
if "chapter" in data:
|
|
173
|
+
chapter_data = data["chapter"]
|
|
174
|
+
# Provide defaults for optional fields
|
|
175
|
+
chapter_data.setdefault("mvp_card", "CLM Execution")
|
|
176
|
+
chapter_data.setdefault("pkc_task", "Runtime Execution")
|
|
177
|
+
clm_data = data["clm"]
|
|
178
|
+
|
|
179
|
+
# Support aliases for dimensions
|
|
180
|
+
abstract = clm_data.get("abstract_spec", clm_data.get("abstract", {}))
|
|
181
|
+
concrete = clm_data.get("concrete_impl", clm_data.get("concrete", {}))
|
|
182
|
+
balanced = clm_data.get("balanced_exp", clm_data.get("balanced", {}))
|
|
183
|
+
|
|
184
|
+
# Update clm_data for internal use
|
|
185
|
+
clm_data["abstract"] = abstract
|
|
186
|
+
clm_data["concrete"] = concrete
|
|
187
|
+
clm_data["balanced"] = balanced
|
|
188
|
+
|
|
189
|
+
# Default to lambda runtime if none specified
|
|
190
|
+
if "runtime" not in clm_data["concrete"]:
|
|
191
|
+
if "builtin" in clm_data["concrete"]:
|
|
192
|
+
clm_data["concrete"]["runtime"] = "python"
|
|
193
|
+
else:
|
|
194
|
+
clm_data["concrete"]["runtime"] = "lambda"
|
|
195
|
+
|
|
196
|
+
# Synthesise concrete summary from structural fields when no explicit description
|
|
197
|
+
concrete_desc = concrete.get(
|
|
198
|
+
"manifestation", concrete.get("description", None)
|
|
199
|
+
)
|
|
200
|
+
if not concrete_desc:
|
|
201
|
+
parts = []
|
|
202
|
+
if concrete.get("runtime"):
|
|
203
|
+
parts.append(f"runtime={concrete['runtime']}")
|
|
204
|
+
if concrete.get("provider"):
|
|
205
|
+
parts.append(f"provider={concrete['provider']}")
|
|
206
|
+
if concrete.get("model"):
|
|
207
|
+
parts.append(f"model={concrete['model']}")
|
|
208
|
+
if concrete.get("builtin"):
|
|
209
|
+
parts.append(f"builtin={concrete['builtin']}")
|
|
210
|
+
concrete_desc = ", ".join(parts) if parts else "Unknown"
|
|
211
|
+
|
|
212
|
+
# Synthesise balanced summary from test_cases when no explicit description
|
|
213
|
+
balanced_desc = balanced.get(
|
|
214
|
+
"expectation", balanced.get("description", None)
|
|
215
|
+
)
|
|
216
|
+
if not balanced_desc:
|
|
217
|
+
test_cases = balanced.get("test_cases", [])
|
|
218
|
+
if test_cases:
|
|
219
|
+
case_labels = [
|
|
220
|
+
tc.get("description", tc.get("given", f"case {i + 1}"))
|
|
221
|
+
for i, tc in enumerate(test_cases[:3])
|
|
222
|
+
]
|
|
223
|
+
balanced_desc = f"{len(test_cases)} test case(s): {'; '.join(str(c) for c in case_labels)}"
|
|
224
|
+
else:
|
|
225
|
+
balanced_desc = "Unknown"
|
|
226
|
+
|
|
227
|
+
clm = CLMConfiguration(
|
|
228
|
+
abstract=abstract.get("concept", abstract.get("purpose", "Unknown")),
|
|
229
|
+
concrete=concrete_desc,
|
|
230
|
+
balanced=balanced_desc,
|
|
231
|
+
)
|
|
232
|
+
else:
|
|
233
|
+
# Fallback for raw PCards
|
|
234
|
+
metadata = data.get("metadata", {})
|
|
235
|
+
chapter_data = {
|
|
236
|
+
"id": 0,
|
|
237
|
+
"title": metadata.get("name", os.path.basename(yaml_path)),
|
|
238
|
+
"mvp_card": "Raw PCard",
|
|
239
|
+
"pkc_task": "Execution",
|
|
240
|
+
}
|
|
241
|
+
clm_data = data.get("clm", data)
|
|
242
|
+
|
|
243
|
+
# Support aliases for dimensions
|
|
244
|
+
abstract = clm_data.get("abstract_spec", clm_data.get("abstract", {}))
|
|
245
|
+
concrete = clm_data.get("concrete_impl", clm_data.get("concrete", {}))
|
|
246
|
+
balanced = clm_data.get("balanced_expc", clm_data.get("balanced", {}))
|
|
247
|
+
|
|
248
|
+
abstract_val = (
|
|
249
|
+
abstract
|
|
250
|
+
if isinstance(abstract, str)
|
|
251
|
+
else abstract.get("concept", abstract.get("purpose", "Unknown"))
|
|
252
|
+
)
|
|
253
|
+
if isinstance(concrete, str):
|
|
254
|
+
concrete_val = concrete
|
|
255
|
+
else:
|
|
256
|
+
concrete_val = concrete.get(
|
|
257
|
+
"manifestation",
|
|
258
|
+
concrete.get("operation", concrete.get("description", None)),
|
|
259
|
+
)
|
|
260
|
+
if not concrete_val:
|
|
261
|
+
parts = []
|
|
262
|
+
if concrete.get("runtime"):
|
|
263
|
+
parts.append(f"runtime={concrete['runtime']}")
|
|
264
|
+
if concrete.get("provider"):
|
|
265
|
+
parts.append(f"provider={concrete['provider']}")
|
|
266
|
+
if concrete.get("model"):
|
|
267
|
+
parts.append(f"model={concrete['model']}")
|
|
268
|
+
if concrete.get("builtin"):
|
|
269
|
+
parts.append(f"builtin={concrete['builtin']}")
|
|
270
|
+
concrete_val = ", ".join(parts) if parts else "Unknown"
|
|
271
|
+
|
|
272
|
+
if isinstance(balanced, str):
|
|
273
|
+
balanced_val = balanced
|
|
274
|
+
else:
|
|
275
|
+
balanced_val = balanced.get(
|
|
276
|
+
"expectation", balanced.get("description", None)
|
|
277
|
+
)
|
|
278
|
+
if not balanced_val:
|
|
279
|
+
test_cases = balanced.get("test_cases", [])
|
|
280
|
+
if test_cases:
|
|
281
|
+
case_labels = [
|
|
282
|
+
tc.get("description", tc.get("given", f"case {i + 1}"))
|
|
283
|
+
for i, tc in enumerate(test_cases[:3])
|
|
284
|
+
]
|
|
285
|
+
balanced_val = f"{len(test_cases)} test case(s): {'; '.join(str(c) for c in case_labels)}"
|
|
286
|
+
else:
|
|
287
|
+
balanced_val = "Unknown"
|
|
288
|
+
|
|
289
|
+
clm = CLMConfiguration(
|
|
290
|
+
abstract=abstract_val,
|
|
291
|
+
concrete=concrete_val,
|
|
292
|
+
balanced=balanced_val,
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
# Ensure clm_data has normalized keys for the rest of the loader
|
|
296
|
+
clm_data["abstract"] = abstract
|
|
297
|
+
clm_data["concrete"] = concrete
|
|
298
|
+
clm_data["balanced"] = balanced
|
|
299
|
+
|
|
300
|
+
# Resolve engine mapping for backward compatibility
|
|
301
|
+
if (
|
|
302
|
+
"runtime" not in clm_data["concrete"]
|
|
303
|
+
and "engine" in clm_data["concrete"]
|
|
304
|
+
):
|
|
305
|
+
engine_val = str(clm_data["concrete"]["engine"]).lower()
|
|
306
|
+
if "python" in engine_val:
|
|
307
|
+
clm_data["concrete"]["runtime"] = "python"
|
|
308
|
+
elif "node" in engine_val or "javascript" in engine_val:
|
|
309
|
+
clm_data["concrete"]["runtime"] = "javascript"
|
|
310
|
+
else:
|
|
311
|
+
clm_data["concrete"]["runtime"] = clm_data["concrete"]["engine"]
|
|
312
|
+
|
|
313
|
+
# Default runtime selection
|
|
314
|
+
if "runtime" not in clm_data["concrete"]:
|
|
315
|
+
if "builtin" in clm_data["concrete"]:
|
|
316
|
+
clm_data["concrete"]["runtime"] = "python"
|
|
317
|
+
else:
|
|
318
|
+
clm_data["concrete"]["runtime"] = "lambda"
|
|
319
|
+
|
|
320
|
+
return chapter_data, clm_data, clm
|
|
321
|
+
|
|
322
|
+
@staticmethod
|
|
323
|
+
def _is_clm_runtime(runtime: str) -> bool:
|
|
324
|
+
"""Check if runtime is a CLM file reference."""
|
|
325
|
+
return any(runtime.endswith(ext) for ext in CLM_EXTENSIONS)
|
|
326
|
+
|
|
327
|
+
@staticmethod
|
|
328
|
+
def _create_recursive_executor(
|
|
329
|
+
yaml_path: str, runtime: str, chapter_data: dict, clm_data: dict
|
|
330
|
+
):
|
|
331
|
+
"""Create an executor for recursive CLM runtime."""
|
|
332
|
+
base_dir = os.path.dirname(yaml_path)
|
|
333
|
+
runtime_path = _resolve_path(base_dir, runtime)
|
|
334
|
+
|
|
335
|
+
def execute(concrete, target, ctx):
|
|
336
|
+
if not os.path.exists(runtime_path):
|
|
337
|
+
return f"Error: Recursive runtime file not found: {runtime_path}"
|
|
338
|
+
|
|
339
|
+
meta_context = ctx.copy()
|
|
340
|
+
meta_context["source_pcard_title"] = chapter_data.get("title", "Unknown")
|
|
341
|
+
meta_context["concrete"] = concrete
|
|
342
|
+
meta_context["abstract"] = clm_data.get("abstract", {})
|
|
343
|
+
meta_context["__input_content__"] = target.get_content()
|
|
344
|
+
meta_context["__skip_tests__"] = True
|
|
345
|
+
|
|
346
|
+
meta_chapter = CLMChapterLoader.load_from_yaml(runtime_path)
|
|
347
|
+
result_tuple = meta_chapter.action.execute(
|
|
348
|
+
meta_context, {"collection": None}
|
|
349
|
+
)
|
|
350
|
+
return result_tuple[0]
|
|
351
|
+
|
|
352
|
+
class RecursiveExecutor:
|
|
353
|
+
def execute(self, concrete, target, ctx):
|
|
354
|
+
return execute(concrete, target, ctx)
|
|
355
|
+
|
|
356
|
+
return RecursiveExecutor()
|
|
357
|
+
|
|
358
|
+
@staticmethod
|
|
359
|
+
def _execute_single_run(
|
|
360
|
+
executor, concrete: dict, config_ctx: dict, runtime: str
|
|
361
|
+
) -> NarrativeMonad:
|
|
362
|
+
"""Execute a single run (no test cases)."""
|
|
363
|
+
from clm.layer0.model.card import MCard
|
|
364
|
+
|
|
365
|
+
target_content = config_ctx.get("__input_content__", DEFAULT_TARGET)
|
|
366
|
+
|
|
367
|
+
try:
|
|
368
|
+
target = MCard(target_content)
|
|
369
|
+
result = executor.execute(concrete, target, config_ctx)
|
|
370
|
+
|
|
371
|
+
if isinstance(result, NarrativeMonad):
|
|
372
|
+
|
|
373
|
+
def run_with_config(_, state):
|
|
374
|
+
return result.run(config_ctx, state)
|
|
375
|
+
|
|
376
|
+
return NarrativeMonad(run_with_config)
|
|
377
|
+
|
|
378
|
+
result = _parse_numeric_result(result)
|
|
379
|
+
|
|
380
|
+
return NarrativeMonad.log(f"Executed {runtime} (Single): {result}").bind(
|
|
381
|
+
lambda _: NarrativeMonad.unit(result)
|
|
382
|
+
)
|
|
383
|
+
except (ValueError, TypeError, RuntimeError) as e:
|
|
384
|
+
return NarrativeMonad.log(f"Execution failed: {e}").bind(
|
|
385
|
+
lambda _: NarrativeMonad.unit(None)
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
@staticmethod
|
|
389
|
+
def _execute_test_cases(
|
|
390
|
+
executor, concrete: dict, config_ctx: dict, test_cases: list[dict]
|
|
391
|
+
) -> NarrativeMonad:
|
|
392
|
+
"""Execute all test cases and return a report."""
|
|
393
|
+
from clm.layer0.model.card import MCard
|
|
394
|
+
|
|
395
|
+
results = []
|
|
396
|
+
log_entries = [f"Running {len(test_cases)} test cases..."]
|
|
397
|
+
all_passed = True
|
|
398
|
+
|
|
399
|
+
try:
|
|
400
|
+
for i, case in enumerate(test_cases):
|
|
401
|
+
given_raw = case.get("given", "")
|
|
402
|
+
given_ctx = {}
|
|
403
|
+
if isinstance(given_raw, dict):
|
|
404
|
+
ctx = given_raw.get("context", {})
|
|
405
|
+
cond = given_raw.get("condition", {})
|
|
406
|
+
if isinstance(ctx, dict) and isinstance(cond, dict):
|
|
407
|
+
given_ctx = {**ctx, **cond}
|
|
408
|
+
elif isinstance(cond, dict):
|
|
409
|
+
given_ctx = cond
|
|
410
|
+
elif isinstance(ctx, dict):
|
|
411
|
+
given_ctx = ctx
|
|
412
|
+
if "description" in given_raw:
|
|
413
|
+
given = str(given_raw.get("description", ""))
|
|
414
|
+
elif "name" in given_raw:
|
|
415
|
+
given = str(given_raw.get("name", ""))
|
|
416
|
+
else:
|
|
417
|
+
given = str(given_raw)
|
|
418
|
+
else:
|
|
419
|
+
given = str(given_raw)
|
|
420
|
+
when = case.get("when", {})
|
|
421
|
+
then = case.get("then", {})
|
|
422
|
+
|
|
423
|
+
test_ctx = config_ctx.copy()
|
|
424
|
+
if isinstance(given_ctx, dict):
|
|
425
|
+
test_ctx.update(given_ctx)
|
|
426
|
+
test_ctx.update(when)
|
|
427
|
+
if "params" in when and isinstance(when["params"], dict):
|
|
428
|
+
test_ctx["params"] = when["params"]
|
|
429
|
+
test_ctx.update(when["params"])
|
|
430
|
+
|
|
431
|
+
test_concrete = concrete.copy()
|
|
432
|
+
case_executor = executor
|
|
433
|
+
if "assumption" in case and isinstance(case["assumption"], dict):
|
|
434
|
+
assumption = case["assumption"]
|
|
435
|
+
if "clm" in assumption and isinstance(assumption["clm"], dict):
|
|
436
|
+
if "concrete" in assumption["clm"] and isinstance(
|
|
437
|
+
assumption["clm"]["concrete"], dict
|
|
438
|
+
):
|
|
439
|
+
test_concrete.update(assumption["clm"]["concrete"])
|
|
440
|
+
|
|
441
|
+
if "runtime" in test_concrete and test_concrete[
|
|
442
|
+
"runtime"
|
|
443
|
+
] != concrete.get("runtime"):
|
|
444
|
+
rf = _resolve_runtime_factory()
|
|
445
|
+
if rf:
|
|
446
|
+
tmp_executor = rf.get_executor(test_concrete["runtime"])
|
|
447
|
+
if tmp_executor:
|
|
448
|
+
case_executor = tmp_executor
|
|
449
|
+
|
|
450
|
+
args = when.get("arguments", when.get("context", {}))
|
|
451
|
+
if (
|
|
452
|
+
not args
|
|
453
|
+
and "assumption" in case
|
|
454
|
+
and isinstance(case["assumption"], dict)
|
|
455
|
+
):
|
|
456
|
+
args = case["assumption"].get("input_arguments", {})
|
|
457
|
+
if isinstance(args, dict):
|
|
458
|
+
test_ctx.update(args)
|
|
459
|
+
|
|
460
|
+
builtin = test_concrete.get("builtin")
|
|
461
|
+
if builtin in (
|
|
462
|
+
"isomorphism_pre",
|
|
463
|
+
"isomorphism_transition",
|
|
464
|
+
"isomorphism_post",
|
|
465
|
+
):
|
|
466
|
+
rf = _resolve_runtime_factory()
|
|
467
|
+
if rf:
|
|
468
|
+
js_executor = rf.get_executor("javascript")
|
|
469
|
+
if js_executor:
|
|
470
|
+
case_executor = js_executor
|
|
471
|
+
test_concrete["runtime"] = "javascript"
|
|
472
|
+
|
|
473
|
+
if "name" in case and not given:
|
|
474
|
+
given = case["name"]
|
|
475
|
+
test_target = MCard(given)
|
|
476
|
+
res = case_executor.execute(test_concrete, test_target, test_ctx)
|
|
477
|
+
|
|
478
|
+
expected = then.get("result")
|
|
479
|
+
result_contains = then.get("result_contains")
|
|
480
|
+
|
|
481
|
+
if result_contains:
|
|
482
|
+
match = CLMChapterLoader._compare_contains(res, result_contains)
|
|
483
|
+
expected = f"contains '{result_contains}'"
|
|
484
|
+
else:
|
|
485
|
+
match = CLMChapterLoader._compare_results(
|
|
486
|
+
res, expected, then.get("epsilon")
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
if not match:
|
|
490
|
+
all_passed = False
|
|
491
|
+
|
|
492
|
+
status = "✅" if match else "❌"
|
|
493
|
+
log_entries.append(
|
|
494
|
+
f"Test {i + 1} [{given}]: {status} Got {res} (Expected {expected})"
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
results.append(
|
|
498
|
+
{
|
|
499
|
+
"case": i + 1,
|
|
500
|
+
"input": given,
|
|
501
|
+
"result": res,
|
|
502
|
+
"expected": expected,
|
|
503
|
+
"match": match,
|
|
504
|
+
}
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
report = {
|
|
508
|
+
"success": all_passed,
|
|
509
|
+
"total": len(results),
|
|
510
|
+
"passed_count": sum(1 for r in results if r["match"]),
|
|
511
|
+
"results": results,
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return NarrativeMonad.log("\n".join(log_entries)).bind(
|
|
515
|
+
lambda _: NarrativeMonad.unit(report)
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
except (ValueError, TypeError, RuntimeError) as e:
|
|
519
|
+
return NarrativeMonad.log(f"Test Execution Failed: {e}").bind(
|
|
520
|
+
lambda _: NarrativeMonad.unit(None)
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
@staticmethod
|
|
524
|
+
def _compare_results(
|
|
525
|
+
actual: Any, expected: Any, epsilon: float | None = None
|
|
526
|
+
) -> bool:
|
|
527
|
+
"""Compare actual vs expected results with optional epsilon for floats."""
|
|
528
|
+
if expected is None:
|
|
529
|
+
return not (isinstance(actual, str) and actual.startswith("Error:"))
|
|
530
|
+
|
|
531
|
+
try:
|
|
532
|
+
if isinstance(expected, dict) and isinstance(actual, dict):
|
|
533
|
+
for k, v in expected.items():
|
|
534
|
+
if k not in actual:
|
|
535
|
+
return False
|
|
536
|
+
if not CLMChapterLoader._compare_results(actual[k], v, epsilon):
|
|
537
|
+
return False
|
|
538
|
+
return True
|
|
539
|
+
|
|
540
|
+
if isinstance(expected, int | float) and isinstance(actual, str):
|
|
541
|
+
actual_parsed = _parse_numeric_result(actual)
|
|
542
|
+
if isinstance(actual_parsed, int | float):
|
|
543
|
+
actual = actual_parsed
|
|
544
|
+
|
|
545
|
+
if isinstance(expected, int | float) and isinstance(actual, int | float):
|
|
546
|
+
if epsilon:
|
|
547
|
+
return abs(actual - expected) < float(epsilon)
|
|
548
|
+
return actual == expected
|
|
549
|
+
|
|
550
|
+
actual_str = str(actual).lower().strip()
|
|
551
|
+
expected_str = str(expected).lower().strip()
|
|
552
|
+
|
|
553
|
+
if actual_str in ("true", "false") and expected_str in ("true", "false"):
|
|
554
|
+
return actual_str == expected_str
|
|
555
|
+
|
|
556
|
+
return str(actual) == str(expected)
|
|
557
|
+
except Exception:
|
|
558
|
+
return str(actual) == str(expected)
|
|
559
|
+
|
|
560
|
+
@staticmethod
|
|
561
|
+
def _compare_contains(actual: Any, substring: str) -> bool:
|
|
562
|
+
"""Check if actual result contains the expected substring."""
|
|
563
|
+
try:
|
|
564
|
+
return substring in str(actual)
|
|
565
|
+
except Exception:
|
|
566
|
+
return False
|
|
567
|
+
|
|
568
|
+
@staticmethod
|
|
569
|
+
def load_from_yaml(yaml_path: str) -> Chapter:
|
|
570
|
+
"""Load a Chapter from a YAML file."""
|
|
571
|
+
with open(yaml_path) as f:
|
|
572
|
+
raw_content = f.read()
|
|
573
|
+
data = yaml.safe_load(raw_content)
|
|
574
|
+
|
|
575
|
+
from clm.layer0.model.pcard import PCard
|
|
576
|
+
pcard = PCard(raw_content)
|
|
577
|
+
base_dir = os.path.dirname(yaml_path)
|
|
578
|
+
chapter_data, clm_data, clm = CLMChapterLoader._parse_clm_data(data, yaml_path)
|
|
579
|
+
|
|
580
|
+
concrete = clm_data["concrete"]
|
|
581
|
+
runtime = concrete.get("runtime", DEFAULT_RUNTIME)
|
|
582
|
+
builtin = concrete.get("builtin")
|
|
583
|
+
|
|
584
|
+
def logic_func(_: Any) -> NarrativeMonad:
|
|
585
|
+
return NarrativeMonad.get_context().bind(
|
|
586
|
+
lambda ctx: NarrativeMonad.get_state().bind(
|
|
587
|
+
lambda state: process_with_petri_net(ctx, state)
|
|
588
|
+
)
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
def process_with_petri_net(ctx: dict, state: dict) -> NarrativeMonad:
|
|
592
|
+
collection = state.get("collection")
|
|
593
|
+
|
|
594
|
+
if collection:
|
|
595
|
+
try:
|
|
596
|
+
collection.add(pcard)
|
|
597
|
+
handle = pcard.get_transition_handle()
|
|
598
|
+
try:
|
|
599
|
+
collection.update_handle(handle, pcard)
|
|
600
|
+
except (KeyError, ValueError, AttributeError, TypeError):
|
|
601
|
+
try:
|
|
602
|
+
collection.add_with_handle(pcard, handle)
|
|
603
|
+
except (KeyError, ValueError, AttributeError, TypeError):
|
|
604
|
+
pass
|
|
605
|
+
except (KeyError, ValueError, AttributeError, TypeError):
|
|
606
|
+
pass
|
|
607
|
+
|
|
608
|
+
config_ctx = {k: v for k, v in concrete.items() if k != "logic_source"}
|
|
609
|
+
if not ctx.get("__skip_tests__"):
|
|
610
|
+
config_ctx["balanced"] = clm_data.get("balanced", {})
|
|
611
|
+
|
|
612
|
+
config_ctx.update(ctx)
|
|
613
|
+
config_ctx["pcard_dir"] = base_dir
|
|
614
|
+
|
|
615
|
+
_resolve_concrete_paths(concrete, base_dir, runtime)
|
|
616
|
+
|
|
617
|
+
execution_monad = _execute_core_logic(
|
|
618
|
+
ctx=ctx,
|
|
619
|
+
config_ctx=config_ctx,
|
|
620
|
+
concrete=concrete,
|
|
621
|
+
clm_data=clm_data,
|
|
622
|
+
chapter_data=chapter_data,
|
|
623
|
+
runtime=runtime,
|
|
624
|
+
builtin=builtin,
|
|
625
|
+
yaml_path=yaml_path,
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
def handle_result(result_tuple):
|
|
629
|
+
result = result_tuple
|
|
630
|
+
if collection:
|
|
631
|
+
try:
|
|
632
|
+
verified = True
|
|
633
|
+
if isinstance(result, dict) and result.get("success") is False:
|
|
634
|
+
verified = False
|
|
635
|
+
|
|
636
|
+
previous_vcard = None
|
|
637
|
+
previous_hash = ctx.get("previous_hash") or ctx.get(
|
|
638
|
+
"previousHash"
|
|
639
|
+
)
|
|
640
|
+
if previous_hash:
|
|
641
|
+
try:
|
|
642
|
+
prev_card = collection.get(previous_hash)
|
|
643
|
+
if prev_card:
|
|
644
|
+
from clm.layer0.model.vcard import VCard as VCardCls
|
|
645
|
+
|
|
646
|
+
previous_vcard = VCardCls(
|
|
647
|
+
prev_card.get_content(as_text=True)
|
|
648
|
+
)
|
|
649
|
+
except (KeyError, ValueError, AttributeError, TypeError):
|
|
650
|
+
pass
|
|
651
|
+
|
|
652
|
+
from clm.layer0.model.vcard import VCard
|
|
653
|
+
vcard = VCard.create_verification_vcard(
|
|
654
|
+
pcard=pcard,
|
|
655
|
+
execution_result=(
|
|
656
|
+
result
|
|
657
|
+
if isinstance(
|
|
658
|
+
result, dict | list | str | int | float | bool
|
|
659
|
+
)
|
|
660
|
+
else str(result)
|
|
661
|
+
),
|
|
662
|
+
previous_vcard=previous_vcard,
|
|
663
|
+
verified=verified,
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
collection.add(vcard)
|
|
667
|
+
b_handle = pcard.get_balanced_handle()
|
|
668
|
+
try:
|
|
669
|
+
collection.update_handle(b_handle, vcard)
|
|
670
|
+
except (KeyError, ValueError, AttributeError, TypeError):
|
|
671
|
+
try:
|
|
672
|
+
collection.add_with_handle(vcard, b_handle)
|
|
673
|
+
except (KeyError, ValueError, AttributeError, TypeError):
|
|
674
|
+
pass
|
|
675
|
+
|
|
676
|
+
if isinstance(result, dict):
|
|
677
|
+
result["petriNet"] = {
|
|
678
|
+
"pcardHash": pcard.hash,
|
|
679
|
+
"vcardHash": vcard.hash,
|
|
680
|
+
"handle": vcard.get_token_handle(),
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
except (KeyError, ValueError, AttributeError, TypeError):
|
|
684
|
+
pass
|
|
685
|
+
|
|
686
|
+
return NarrativeMonad.unit(result)
|
|
687
|
+
|
|
688
|
+
return execution_monad.bind(handle_result)
|
|
689
|
+
|
|
690
|
+
def _execute_core_logic(
|
|
691
|
+
ctx,
|
|
692
|
+
config_ctx,
|
|
693
|
+
concrete,
|
|
694
|
+
clm_data,
|
|
695
|
+
chapter_data,
|
|
696
|
+
runtime,
|
|
697
|
+
builtin,
|
|
698
|
+
yaml_path,
|
|
699
|
+
) -> NarrativeMonad:
|
|
700
|
+
handle_builtins = ("handle_version", "handle_prune")
|
|
701
|
+
if builtin in handle_builtins:
|
|
702
|
+
rf = _resolve_runtime_factory()
|
|
703
|
+
executor = rf.get_executor("python") if rf else None
|
|
704
|
+
if not executor:
|
|
705
|
+
return NarrativeMonad.log(
|
|
706
|
+
"Error: Python runtime not available"
|
|
707
|
+
).bind(
|
|
708
|
+
lambda _: NarrativeMonad.unit(
|
|
709
|
+
{"success": False, "error": "Python runtime not available"}
|
|
710
|
+
)
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
try:
|
|
714
|
+
from clm.layer0.model.card import MCard
|
|
715
|
+
|
|
716
|
+
target = MCard(ctx.get("__input_content__", DEFAULT_TARGET))
|
|
717
|
+
result = executor.execute(concrete, target, config_ctx)
|
|
718
|
+
return NarrativeMonad.log(f"Executed builtin {builtin}").bind(
|
|
719
|
+
lambda _, r=result: NarrativeMonad.unit(r)
|
|
720
|
+
)
|
|
721
|
+
except (ValueError, TypeError, RuntimeError) as exc:
|
|
722
|
+
error_msg = str(exc)
|
|
723
|
+
return NarrativeMonad.log(
|
|
724
|
+
f"Builtin execution failed: {error_msg}"
|
|
725
|
+
).bind(
|
|
726
|
+
lambda _, err=error_msg: NarrativeMonad.unit(
|
|
727
|
+
{"success": False, "error": err}
|
|
728
|
+
)
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
if builtin and (not runtime or runtime == "network"):
|
|
732
|
+
rf = _resolve_runtime_factory()
|
|
733
|
+
executor = rf.get_executor("network") if rf else None
|
|
734
|
+
if not executor:
|
|
735
|
+
return NarrativeMonad.log(
|
|
736
|
+
"Error: Network runtime not available"
|
|
737
|
+
).bind(
|
|
738
|
+
lambda _: NarrativeMonad.unit(
|
|
739
|
+
{"success": False, "error": "Network runtime not available"}
|
|
740
|
+
)
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
test_cases = clm_data.get("balanced", {}).get("test_cases", [])
|
|
744
|
+
has_explicit_input = ctx.get("signaling_url") or ctx.get("params")
|
|
745
|
+
|
|
746
|
+
if (
|
|
747
|
+
test_cases
|
|
748
|
+
and not ctx.get("__skip_tests__")
|
|
749
|
+
and not has_explicit_input
|
|
750
|
+
):
|
|
751
|
+
return CLMChapterLoader._execute_test_cases(
|
|
752
|
+
executor, concrete, config_ctx, test_cases
|
|
753
|
+
)
|
|
754
|
+
else:
|
|
755
|
+
try:
|
|
756
|
+
from clm.layer0.model.card import MCard
|
|
757
|
+
|
|
758
|
+
target = MCard(ctx.get("__input_content__", DEFAULT_TARGET))
|
|
759
|
+
result = executor.execute(concrete, target, config_ctx)
|
|
760
|
+
return NarrativeMonad.log(f"Executed builtin {builtin}").bind(
|
|
761
|
+
lambda _, r=result: NarrativeMonad.unit(r)
|
|
762
|
+
)
|
|
763
|
+
except (ValueError, TypeError, RuntimeError) as exc:
|
|
764
|
+
error_msg = str(exc)
|
|
765
|
+
return NarrativeMonad.log(
|
|
766
|
+
f"Builtin execution failed: {error_msg}"
|
|
767
|
+
).bind(
|
|
768
|
+
lambda _, err=error_msg: NarrativeMonad.unit(
|
|
769
|
+
{"success": False, "error": err}
|
|
770
|
+
)
|
|
771
|
+
)
|
|
772
|
+
|
|
773
|
+
if CLMChapterLoader._is_clm_runtime(runtime):
|
|
774
|
+
executor = CLMChapterLoader._create_recursive_executor(
|
|
775
|
+
yaml_path, runtime, chapter_data, clm_data
|
|
776
|
+
)
|
|
777
|
+
else:
|
|
778
|
+
rf = _resolve_runtime_factory()
|
|
779
|
+
executor = rf.get_executor(runtime) if rf else None
|
|
780
|
+
if not executor:
|
|
781
|
+
return NarrativeMonad.log(
|
|
782
|
+
f"Error: Runtime {runtime} not available"
|
|
783
|
+
).bind(lambda _: NarrativeMonad.unit(None))
|
|
784
|
+
|
|
785
|
+
balanced_section = clm_data.get(
|
|
786
|
+
"balanced_expc",
|
|
787
|
+
clm_data.get("balanced_exp", clm_data.get("balanced", {})),
|
|
788
|
+
)
|
|
789
|
+
test_cases = balanced_section.get("test_cases", [])
|
|
790
|
+
if not test_cases and "examples" in data:
|
|
791
|
+
test_cases = data["examples"]
|
|
792
|
+
|
|
793
|
+
if not test_cases or ctx.get("__skip_tests__"):
|
|
794
|
+
return CLMChapterLoader._execute_single_run(
|
|
795
|
+
executor, concrete, config_ctx, runtime
|
|
796
|
+
)
|
|
797
|
+
else:
|
|
798
|
+
return CLMChapterLoader._execute_test_cases(
|
|
799
|
+
executor, concrete, config_ctx, test_cases
|
|
800
|
+
)
|
|
801
|
+
|
|
802
|
+
action = NarrativeMonad.unit(None).bind(logic_func)
|
|
803
|
+
|
|
804
|
+
return Chapter(
|
|
805
|
+
id=chapter_data["id"],
|
|
806
|
+
title=chapter_data["title"],
|
|
807
|
+
clm=clm,
|
|
808
|
+
mvp_card=chapter_data["mvp_card"],
|
|
809
|
+
pkc_task=chapter_data["pkc_task"],
|
|
810
|
+
action=action,
|
|
811
|
+
)
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
__all__ = [
|
|
815
|
+
"CLMChapterLoader",
|
|
816
|
+
"CLM_EXTENSIONS",
|
|
817
|
+
"CODE_READABLE_RUNTIMES",
|
|
818
|
+
"DEFAULT_RUNTIME",
|
|
819
|
+
"DEFAULT_TARGET",
|
|
820
|
+
"MODULE_URI_PREFIX",
|
|
821
|
+
"YAMLTemplateLoader",
|
|
822
|
+
"_is_module_uri",
|
|
823
|
+
"_parse_numeric_result",
|
|
824
|
+
"_resolve_concrete_paths",
|
|
825
|
+
"_resolve_path",
|
|
826
|
+
]
|