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.
Files changed (333) hide show
  1. clm/__init__.py +230 -0
  2. clm/__main__.py +25 -0
  3. clm/cross_cutting/__init__.py +50 -0
  4. clm/cross_cutting/_test_runner.py +36 -0
  5. clm/cross_cutting/async_utils.py +25 -0
  6. clm/cross_cutting/cel_builder.py +123 -0
  7. clm/cross_cutting/cel_converter.py +697 -0
  8. clm/cross_cutting/cel_engine.py +252 -0
  9. clm/cross_cutting/cel_ops.py +72 -0
  10. clm/cross_cutting/cel_ops_db.py +157 -0
  11. clm/cross_cutting/cel_ops_files.py +99 -0
  12. clm/cross_cutting/cel_ops_state.py +71 -0
  13. clm/cross_cutting/cel_ops_strings.py +64 -0
  14. clm/cross_cutting/cel_resolver.py +63 -0
  15. clm/cross_cutting/cel_slices.py +56 -0
  16. clm/cross_cutting/cel_store.py +330 -0
  17. clm/cross_cutting/clm_logger.py +140 -0
  18. clm/cross_cutting/config/__init__.py +155 -0
  19. clm/cross_cutting/config/config_constants.py +392 -0
  20. clm/cross_cutting/config/env_parameters.py +90 -0
  21. clm/cross_cutting/config/logging.py +378 -0
  22. clm/cross_cutting/config/settings.py +504 -0
  23. clm/cross_cutting/context.py +5 -0
  24. clm/cross_cutting/domain_models.py +55 -0
  25. clm/cross_cutting/domain_types.py +61 -0
  26. clm/cross_cutting/effects.py +67 -0
  27. clm/cross_cutting/errors.py +153 -0
  28. clm/cross_cutting/governance.py +331 -0
  29. clm/cross_cutting/guards.py +41 -0
  30. clm/cross_cutting/mcard.py +321 -0
  31. clm/cross_cutting/native.py +185 -0
  32. clm/cross_cutting/observability.py +289 -0
  33. clm/cross_cutting/pcard.py +434 -0
  34. clm/cross_cutting/registry.py +71 -0
  35. clm/cross_cutting/resources.py +84 -0
  36. clm/cross_cutting/secrets.py +44 -0
  37. clm/cross_cutting/subprocess_utils.py +118 -0
  38. clm/cross_cutting/telemetry.py +164 -0
  39. clm/cross_cutting/testing/__init__.py +8 -0
  40. clm/cross_cutting/testing/comparator.py +187 -0
  41. clm/cross_cutting/testing/datasets.py +87 -0
  42. clm/cross_cutting/timeutil.py +20 -0
  43. clm/cross_cutting/types/new_type_check.py +0 -0
  44. clm/cross_cutting/utils/__init__.py +1 -0
  45. clm/cross_cutting/utils/url_safety.py +50 -0
  46. clm/cross_cutting/vcard.py +560 -0
  47. clm/cross_cutting/writer.py +118 -0
  48. clm/layer0/README.md +50 -0
  49. clm/layer0/__init__.py +223 -0
  50. clm/layer0/algebra.py +60 -0
  51. clm/layer0/algebra_baldwin.py +133 -0
  52. clm/layer0/algebra_baldwin_generators.py +36 -0
  53. clm/layer0/algebra_equivalence.py +262 -0
  54. clm/layer0/algebra_ingest.py +306 -0
  55. clm/layer0/algebra_manifest_io.py +156 -0
  56. clm/layer0/assembler.py +481 -0
  57. clm/layer0/cel/__init__.py +23 -0
  58. clm/layer0/cel_eval.py +491 -0
  59. clm/layer0/codec.py +106 -0
  60. clm/layer0/context.py +122 -0
  61. clm/layer0/db.py +160 -0
  62. clm/layer0/exceptions.py +73 -0
  63. clm/layer0/fibration.py +106 -0
  64. clm/layer0/gates.py +50 -0
  65. clm/layer0/hash.py +80 -0
  66. clm/layer0/loader.py +826 -0
  67. clm/layer0/math_utils.py +163 -0
  68. clm/layer0/mcard.py +96 -0
  69. clm/layer0/model/__init__.py +0 -0
  70. clm/layer0/model/action.py +223 -0
  71. clm/layer0/model/card.py +280 -0
  72. clm/layer0/model/card_triad.py +427 -0
  73. clm/layer0/model/compatibility.py +60 -0
  74. clm/layer0/model/dictionary.py +46 -0
  75. clm/layer0/model/dots.py +331 -0
  76. clm/layer0/model/event_producer.py +88 -0
  77. clm/layer0/model/g_time.py +89 -0
  78. clm/layer0/model/handle.py +163 -0
  79. clm/layer0/model/hash/__init__.py +6 -0
  80. clm/layer0/model/hash/algorithms/__init__.py +3 -0
  81. clm/layer0/model/hash/algorithms/custom_hash.py +7 -0
  82. clm/layer0/model/hash/algorithms/local_sha256.py +178 -0
  83. clm/layer0/model/hash/constants.py +53 -0
  84. clm/layer0/model/hash/enums.py +60 -0
  85. clm/layer0/model/hash/validator.py +307 -0
  86. clm/layer0/model/interpreter.py +275 -0
  87. clm/layer0/model/pagination.py +45 -0
  88. clm/layer0/model/pcard.py +716 -0
  89. clm/layer0/model/schema.py +44 -0
  90. clm/layer0/model/utils/__init__.py +1 -0
  91. clm/layer0/model/utils/content_analyzer.py +152 -0
  92. clm/layer0/model/validators/__init__.py +1 -0
  93. clm/layer0/model/validators/base_validator.py +44 -0
  94. clm/layer0/model/validators/binary_validator.py +66 -0
  95. clm/layer0/model/validators/text_validator.py +82 -0
  96. clm/layer0/model/validators/validation_registry.py +49 -0
  97. clm/layer0/model/vcard.py +823 -0
  98. clm/layer0/model/vcard_ext/__init__.py +39 -0
  99. clm/layer0/model/vcard_ext/core.py +60 -0
  100. clm/layer0/model/vcard_ext/network.py +23 -0
  101. clm/layer0/model/vcard_ext/observability.py +63 -0
  102. clm/layer0/model/vcard_ext/storage.py +60 -0
  103. clm/layer0/model/vcard_ext/vendors.py +211 -0
  104. clm/layer0/model/vcard_sandwich.py +119 -0
  105. clm/layer0/model/vcard_vocabulary.py +463 -0
  106. clm/layer0/model/workflow.py +274 -0
  107. clm/layer0/narrative.py +431 -0
  108. clm/layer0/ontology/collection.py +125 -0
  109. clm/layer0/parser.py +429 -0
  110. clm/layer0/relations.py +73 -0
  111. clm/layer0/rule_evaluator.py +251 -0
  112. clm/layer0/schema/__init__.py +38 -0
  113. clm/layer0/schema/ddl.py +97 -0
  114. clm/layer0/templates/abstract.yaml +99 -0
  115. clm/layer0/templates/balanced.yaml +253 -0
  116. clm/layer0/templates/concrete.yaml +172 -0
  117. clm/layer0/type_registry.py +223 -0
  118. clm/layer0/types.py +204 -0
  119. clm/layer0/utils.py +886 -0
  120. clm/layer0/verifier_core.py +262 -0
  121. clm/layer1/__init__.py +31 -0
  122. clm/layer1/action_dispatcher.py +275 -0
  123. clm/layer1/adapter_registry.py +233 -0
  124. clm/layer1/algebra_baldwin_generators.py +192 -0
  125. clm/layer1/arrow_compiler.py +525 -0
  126. clm/layer1/batch_processor.py +152 -0
  127. clm/layer1/cel/__init__.py +27 -0
  128. clm/layer1/cel/builder.py +173 -0
  129. clm/layer1/engine.py +759 -0
  130. clm/layer1/engine_async.py +89 -0
  131. clm/layer1/eval_callback.py +49 -0
  132. clm/layer1/gate_composer.py +229 -0
  133. clm/layer1/hoare_sandwich.py +298 -0
  134. clm/layer1/identity_guard.py +348 -0
  135. clm/layer1/matrix.py +138 -0
  136. clm/layer1/neural_runtime.py +221 -0
  137. clm/layer1/operations/__init__.py +123 -0
  138. clm/layer1/operations/augment.py +156 -0
  139. clm/layer1/operations/base.py +109 -0
  140. clm/layer1/operations/builtins.py +338 -0
  141. clm/layer1/operations/dispatch.py +144 -0
  142. clm/layer1/operations/exclude.py +107 -0
  143. clm/layer1/operations/handle.py +197 -0
  144. clm/layer1/operations/identity.py +139 -0
  145. clm/layer1/operations/invert.py +66 -0
  146. clm/layer1/operations/loader.py +116 -0
  147. clm/layer1/operations/manipulators/__init__.py +23 -0
  148. clm/layer1/operations/manipulators/document.py +211 -0
  149. clm/layer1/operations/manipulators/media.py +134 -0
  150. clm/layer1/operations/manipulators/structured.py +159 -0
  151. clm/layer1/operations/manipulators/tabular.py +334 -0
  152. clm/layer1/operations/port.py +65 -0
  153. clm/layer1/operations/services.py +529 -0
  154. clm/layer1/operations/split.py +117 -0
  155. clm/layer1/operations/substitute.py +75 -0
  156. clm/layer1/petri.py +1052 -0
  157. clm/layer1/pocketflow_orchestrator.py +408 -0
  158. clm/layer1/pocketflow_scheduler.py +519 -0
  159. clm/layer1/protocols.py +197 -0
  160. clm/layer1/runtime_adapter.py +718 -0
  161. clm/layer1/runtimes/__init__.py +71 -0
  162. clm/layer1/runtimes/_generated_assertions.py +125 -0
  163. clm/layer1/runtimes/_generated_types.py +42 -0
  164. clm/layer1/runtimes/base.py +265 -0
  165. clm/layer1/runtimes/binary.py +101 -0
  166. clm/layer1/runtimes/factory.py +203 -0
  167. clm/layer1/runtimes/javascript.py +321 -0
  168. clm/layer1/runtimes/javascript_runtime.js +33 -0
  169. clm/layer1/runtimes/js_scripts/isomorphism.mjs +22 -0
  170. clm/layer1/runtimes/js_scripts/loader.mjs +52 -0
  171. clm/layer1/runtimes/lambda_calc.py +126 -0
  172. clm/layer1/runtimes/python.py +288 -0
  173. clm/layer1/runtimes/python_runtime.py +41 -0
  174. clm/layer1/runtimes/script.py +114 -0
  175. clm/layer1/sandbox.py +334 -0
  176. clm/layer1/savepoint_guard.py +136 -0
  177. clm/layer1/sparse_net.py +122 -0
  178. clm/layer1/spec_tester.py +165 -0
  179. clm/layer1/store.py +295 -0
  180. clm/layer1/test_runner.py +498 -0
  181. clm/layer1/type_inspector.py +166 -0
  182. clm/layer1/verifier.py +772 -0
  183. clm/layer1/vm.py +91 -0
  184. clm/layer2/__init__.py +78 -0
  185. clm/layer2/card_collection.py +678 -0
  186. clm/layer2/classifier.py +479 -0
  187. clm/layer2/engine/__init__.py +1 -0
  188. clm/layer2/engine/abstract_sql_engine.py +320 -0
  189. clm/layer2/engine/base.py +193 -0
  190. clm/layer2/engine/duckdb_engine.py +576 -0
  191. clm/layer2/engine/sqlite_engine.py +518 -0
  192. clm/layer2/fiber/__init__.py +31 -0
  193. clm/layer2/fiber/context.py +89 -0
  194. clm/layer2/fiber/disposable.py +163 -0
  195. clm/layer2/fiber/fiber.py +125 -0
  196. clm/layer2/fiber/hoare.py +191 -0
  197. clm/layer2/file_io.py +376 -0
  198. clm/layer2/file_registrar.py +77 -0
  199. clm/layer2/ingest.py +202 -0
  200. clm/layer2/lineage.py +322 -0
  201. clm/layer2/mcard_fs.py +262 -0
  202. clm/layer2/merkle.py +11 -0
  203. clm/layer2/mime.py +244 -0
  204. clm/layer2/mime_detector.py +245 -0
  205. clm/layer2/private_collection.py +118 -0
  206. clm/layer2/shared_store.py +11 -0
  207. clm/layer2/storage/__init__.py +190 -0
  208. clm/layer2/storage/db_bitemporal.py +141 -0
  209. clm/layer2/storage/db_driver.py +331 -0
  210. clm/layer2/storage/db_hyperlinks.py +214 -0
  211. clm/layer2/storage/db_pool.py +139 -0
  212. clm/layer2/storage/db_repository.py +229 -0
  213. clm/layer2/storage/savepoint.py +78 -0
  214. clm/layer2/storage/sqlite_wal.py +74 -0
  215. clm/layer2/tridb.py +245 -0
  216. clm/layer2/vcard_receipt.py +71 -0
  217. clm/layer2/vfs.py +17 -0
  218. clm/layer3/__init__.py +141 -0
  219. clm/layer3/agency/__init__.py +19 -0
  220. clm/layer3/agency/config.py +220 -0
  221. clm/layer3/agency/providers/__init__.py +15 -0
  222. clm/layer3/agency/providers/base.py +91 -0
  223. clm/layer3/agency/providers/mlc_llm.py +169 -0
  224. clm/layer3/agency/providers/ollama.py +203 -0
  225. clm/layer3/agency/router.py +71 -0
  226. clm/layer3/agency/runtime.py +524 -0
  227. clm/layer3/gateway.py +353 -0
  228. clm/layer3/open_interpreter_adapter.py +159 -0
  229. clm/layer3/rag/__init__.py +70 -0
  230. clm/layer3/rag/cli.py +228 -0
  231. clm/layer3/rag/config.py +153 -0
  232. clm/layer3/rag/embeddings/__init__.py +16 -0
  233. clm/layer3/rag/embeddings/base.py +79 -0
  234. clm/layer3/rag/embeddings/ollama.py +211 -0
  235. clm/layer3/rag/embeddings/vision.py +290 -0
  236. clm/layer3/rag/engine.py +276 -0
  237. clm/layer3/rag/graph/__init__.py +22 -0
  238. clm/layer3/rag/graph/community.py +198 -0
  239. clm/layer3/rag/graph/engine.py +433 -0
  240. clm/layer3/rag/graph/extractor.py +255 -0
  241. clm/layer3/rag/graph/schema.py +115 -0
  242. clm/layer3/rag/graph/store.py +641 -0
  243. clm/layer3/rag/indexer.py +333 -0
  244. clm/layer3/rag/llm_providers.json +52 -0
  245. clm/layer3/rag/semantic_versioning.py +313 -0
  246. clm/layer3/rag/vector/__init__.py +28 -0
  247. clm/layer3/rag/vector/handle_vector_store.py +749 -0
  248. clm/layer3/rag/vector/schema.py +151 -0
  249. clm/layer3/rag/vector/store.py +632 -0
  250. clm/layer3/reticulum/__init__.py +55 -0
  251. clm/layer3/reticulum/bridge.py +74 -0
  252. clm/layer3/reticulum/daemon.py +88 -0
  253. clm/layer3/reticulum/discovery.py +164 -0
  254. clm/layer3/reticulum/identity.py +107 -0
  255. clm/layer3/reticulum/media.py +148 -0
  256. clm/layer3/reticulum/transport.py +133 -0
  257. clm/layer3/satori/__init__.py +209 -0
  258. clm/layer3/satori/alpha_conversion.py +168 -0
  259. clm/layer3/satori/beta_reduction.py +359 -0
  260. clm/layer3/satori/continuation.py +38 -0
  261. clm/layer3/satori/eta_conversion.py +205 -0
  262. clm/layer3/satori/free_variables.py +163 -0
  263. clm/layer3/satori/io_effects.py +370 -0
  264. clm/layer3/satori/lambda_runtime.py +726 -0
  265. clm/layer3/satori/lambda_term.py +230 -0
  266. clm/layer3/satori/sheaf_audit.py +261 -0
  267. clm/layer3/satori/speech_act.py +279 -0
  268. clm/layer4/README.md +9 -0
  269. clm/layer4/__init__.py +67 -0
  270. clm/layer4/_sovereign_executor.py +580 -0
  271. clm/layer4/action_commands.py +719 -0
  272. clm/layer4/api.py +236 -0
  273. clm/layer4/bootstrap/__init__.py +13 -0
  274. clm/layer4/bootstrap/bootstrapper.py +118 -0
  275. clm/layer4/bootstrap/cleanup.py +44 -0
  276. clm/layer4/bootstrap/clm_hooks.py +49 -0
  277. clm/layer4/bootstrap/genesis.py +77 -0
  278. clm/layer4/bootstrap/injection.py +125 -0
  279. clm/layer4/cli.py +549 -0
  280. clm/layer4/collection_manager.py +553 -0
  281. clm/layer4/commands/__init__.py +81 -0
  282. clm/layer4/commands/action_cmd.py +42 -0
  283. clm/layer4/commands/check_cmd.py +82 -0
  284. clm/layer4/commands/envelope.py +73 -0
  285. clm/layer4/commands/evaluate_cmd.py +224 -0
  286. clm/layer4/commands/mcard_cmd.py +24 -0
  287. clm/layer4/commands/petri_cmd.py +92 -0
  288. clm/layer4/commands/tridb_cmd.py +319 -0
  289. clm/layer4/commands/version_cmd.py +27 -0
  290. clm/layer4/diff_commands.py +167 -0
  291. clm/layer4/eoa_sponsor.py +232 -0
  292. clm/layer4/events.py +69 -0
  293. clm/layer4/flux_gateway.py +133 -0
  294. clm/layer4/gateway.py +91 -0
  295. clm/layer4/loader.py +307 -0
  296. clm/layer4/mcard_commands.py +166 -0
  297. clm/layer4/membrane.py +273 -0
  298. clm/layer4/navigation.py +50 -0
  299. clm/layer4/protocol/protocol_entry_python.py +108 -0
  300. clm/layer4/protocol/python_sync.py +126 -0
  301. clm/layer4/runner.py +150 -0
  302. clm/layer4/satori.py +262 -0
  303. clm/layer4/server/python_sync_client.py +150 -0
  304. clm/layer4/server/python_sync_server.py +139 -0
  305. clm/layer4/server/service_api.py +315 -0
  306. clm/layer4/server/start_servers.py +107 -0
  307. clm/layer4/server/stop_servers.py +24 -0
  308. clm/layer4/simulated_optimization_fix.py +0 -0
  309. clm/layer4/storage.py +463 -0
  310. clm/layer4/tridb_admin.py +268 -0
  311. clm/layer4/websocket/test_ws_client.py +104 -0
  312. clm/layer4/websocket/test_ws_server.py +138 -0
  313. clm/layer4/websocket/ws_server_local.py +160 -0
  314. clm/layer5/__init__.py +51 -0
  315. clm/layer5/async_batch_writer.py +133 -0
  316. clm/layer5/learning.py +276 -0
  317. clm/layer5/telemetry_hook.py +107 -0
  318. clm/layer5/type_lattice.py +238 -0
  319. clm/py.typed +0 -0
  320. clm/schemas/__init__.py +652 -0
  321. clm/schemas/abstract_face_schema.yaml +77 -0
  322. clm/schemas/cel_schema.yaml +416 -0
  323. clm/schemas/lifecycle/__init__.py +45 -0
  324. clm/schemas/lifecycle/models.py +123 -0
  325. clm/schemas/lifecycle/service.py +364 -0
  326. clm/schemas/mcard_schema.sql +192 -0
  327. clm/schemas/mcard_vector_schema.sql +283 -0
  328. clm/schemas/mlp-context-v1.json +91 -0
  329. clm_kernel-0.0.0.dist-info/METADATA +121 -0
  330. clm_kernel-0.0.0.dist-info/RECORD +333 -0
  331. clm_kernel-0.0.0.dist-info/WHEEL +4 -0
  332. clm_kernel-0.0.0.dist-info/entry_points.txt +3 -0
  333. clm_kernel-0.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,289 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from collections.abc import Callable
5
+ from contextlib import contextmanager
6
+ from datetime import datetime, timezone
7
+ from functools import wraps
8
+ from typing import Any
9
+
10
+ # OpenTelemetry imports (with fallback if not installed)
11
+ try:
12
+ from opentelemetry import metrics, trace
13
+ from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
14
+ OTLPMetricExporter,
15
+ )
16
+ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
17
+ from opentelemetry.metrics import Meter
18
+ from opentelemetry.sdk.metrics import MeterProvider
19
+ from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
20
+ from opentelemetry.sdk.trace import TracerProvider
21
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
22
+ from opentelemetry.trace import SpanKind, Status, StatusCode, Tracer
23
+
24
+ OTEL_AVAILABLE = True
25
+ except ImportError:
26
+ OTEL_AVAILABLE = False
27
+ Tracer = None
28
+ Span = None
29
+ Meter = None
30
+
31
+
32
+ class REPLPhase:
33
+ """Enum-like class for REPL phases."""
34
+
35
+ PREP = "prep"
36
+ EXEC = "exec"
37
+ POST = "post"
38
+ AWAIT = "await"
39
+
40
+
41
+ class OpenTelemetrySidecar:
42
+ """
43
+ OpenTelemetry Sidecar for PTR Engine observability.
44
+
45
+ Provides instrumentation hooks for each REPL phase:
46
+ - prep: Artifact loading, V_pre validation
47
+ - exec: CLM execution, sandbox runtime
48
+ - post: Balanced verification, VCard generation
49
+ - await: Handle history recording, state transition
50
+
51
+ Usage:
52
+ sidecar = OpenTelemetrySidecar.get_instance()
53
+ sidecar.initialize({
54
+ "endpoint": "http://localhost:4317",
55
+ "service_name": "ptr-runtime",
56
+ "service_version": "1.0.0"
57
+ })
58
+
59
+ with sidecar.trace_phase(REPLPhase.PREP, pcard_hash="abc123"):
60
+ # do prep work
61
+ pass
62
+ """
63
+
64
+ _instance: OpenTelemetrySidecar | None = None
65
+
66
+ def __init__(self):
67
+ self.logger = logging.getLogger(__name__)
68
+ self._initialized = False
69
+ self._tracer: Tracer | None = None
70
+ self._meter: Meter | None = None
71
+ self._config: dict[str, Any] = {}
72
+
73
+ # Metrics
74
+ self._phase_duration_histogram = None
75
+ self._phase_counter = None
76
+ self._error_counter = None
77
+
78
+ @classmethod
79
+ def get_instance(cls) -> OpenTelemetrySidecar:
80
+ """Get singleton instance of OpenTelemetrySidecar."""
81
+ if cls._instance is None:
82
+ cls._instance = OpenTelemetrySidecar()
83
+ return cls._instance
84
+
85
+ def initialize(self, config: dict[str, Any]) -> bool:
86
+ """
87
+ Initialize OpenTelemetry with provided configuration.
88
+
89
+ Args:
90
+ config: Configuration dictionary with keys:
91
+ - endpoint: OTLP endpoint (e.g., "http://localhost:4317")
92
+ - service_name: Name of the service
93
+ - service_version: Version of the service
94
+ - namespace: Optional namespace prefix
95
+
96
+ Returns:
97
+ True if initialization succeeded, False otherwise
98
+ """
99
+ if not OTEL_AVAILABLE:
100
+ self.logger.warning(
101
+ "[OpenTelemetrySidecar] OpenTelemetry SDK not installed. "
102
+ "Install with: pip install opentelemetry-sdk opentelemetry-exporter-otlp"
103
+ )
104
+ return False
105
+
106
+ if self._initialized:
107
+ self.logger.warning("[OpenTelemetrySidecar] Already initialized")
108
+ return True
109
+
110
+ self._config = config
111
+ endpoint = config.get("endpoint", "http://localhost:4317")
112
+ service_name = config.get("service_name", "ptr-runtime")
113
+ service_version = config.get("service_version", "1.0.0")
114
+ namespace = config.get("namespace", "ptr")
115
+
116
+ try:
117
+ # Set up tracer provider
118
+ tracer_provider = TracerProvider()
119
+ span_exporter = OTLPSpanExporter(endpoint=endpoint, insecure=True)
120
+ tracer_provider.add_span_processor(BatchSpanProcessor(span_exporter))
121
+ trace.set_tracer_provider(tracer_provider)
122
+ self._tracer = trace.get_tracer(service_name, service_version)
123
+
124
+ # Set up meter provider
125
+ metric_exporter = OTLPMetricExporter(endpoint=endpoint, insecure=True)
126
+ metric_reader = PeriodicExportingMetricReader(metric_exporter)
127
+ meter_provider = MeterProvider(metric_readers=[metric_reader])
128
+ metrics.set_meter_provider(meter_provider)
129
+ self._meter = metrics.get_meter(service_name, service_version)
130
+
131
+ # Create metrics instruments
132
+ self._phase_duration_histogram = self._meter.create_histogram(
133
+ name=f"{namespace}.repl.phase.duration",
134
+ description="Duration of REPL phase execution in milliseconds",
135
+ unit="ms",
136
+ )
137
+
138
+ self._phase_counter = self._meter.create_counter(
139
+ name=f"{namespace}.repl.phase.count",
140
+ description="Count of REPL phase executions",
141
+ )
142
+
143
+ self._error_counter = self._meter.create_counter(
144
+ name=f"{namespace}.repl.error.count",
145
+ description="Count of errors during REPL execution",
146
+ )
147
+
148
+ self._initialized = True
149
+ self.logger.info(
150
+ f"[OpenTelemetrySidecar] Initialized for {service_name}@{service_version} "
151
+ f"-> {endpoint}"
152
+ )
153
+ return True
154
+
155
+ except (OSError, ValueError, ImportError) as e:
156
+ self.logger.error(
157
+ f"[OpenTelemetrySidecar] Initialization failed: {e}", exc_info=True
158
+ )
159
+ return False
160
+
161
+ @contextmanager
162
+ def trace_phase(
163
+ self,
164
+ phase: str,
165
+ pcard_hash: str | None = None,
166
+ target_hash: str | None = None,
167
+ attributes: dict[str, Any] | None = None,
168
+ ):
169
+ """
170
+ Context manager for tracing a REPL phase.
171
+
172
+ Args:
173
+ phase: One of REPLPhase.PREP, EXEC, POST, AWAIT
174
+ pcard_hash: Optional PCard hash for context
175
+ target_hash: Optional target hash for context
176
+ attributes: Optional additional attributes
177
+
178
+ Yields:
179
+ The active span (or None if not initialized)
180
+ """
181
+ if not self._initialized or not self._tracer:
182
+ yield None
183
+ return
184
+
185
+ span_name = f"ptr.repl.{phase}"
186
+ span_attributes = {
187
+ "repl.phase": phase,
188
+ "ptr.pcard_hash": pcard_hash or "",
189
+ "ptr.target_hash": target_hash or "",
190
+ }
191
+ if attributes:
192
+ span_attributes.update(attributes)
193
+
194
+ start_time = datetime.now(timezone.utc)
195
+
196
+ with self._tracer.start_as_current_span(
197
+ span_name, kind=SpanKind.INTERNAL, attributes=span_attributes
198
+ ) as span:
199
+ try:
200
+ yield span
201
+ span.set_status(Status(StatusCode.OK))
202
+ except (ValueError, TypeError, RuntimeError, OSError) as e:
203
+ span.set_status(Status(StatusCode.ERROR, str(e)))
204
+ span.record_exception(e)
205
+ self._record_error(phase)
206
+ raise
207
+ finally:
208
+ end_time = datetime.now(timezone.utc)
209
+ duration_ms = (end_time - start_time).total_seconds() * 1000
210
+ self._record_phase_duration(phase, duration_ms)
211
+ self._record_phase_count(phase)
212
+
213
+ def log_event(
214
+ self, phase: str, event_name: str, attributes: dict[str, Any] | None = None
215
+ ):
216
+ """
217
+ Log an event within a REPL phase.
218
+
219
+ Args:
220
+ phase: The current REPL phase
221
+ event_name: Name of the event
222
+ attributes: Optional event attributes
223
+ """
224
+ if not self._initialized or not self._tracer:
225
+ self.logger.info(f"[{phase}] {event_name}: {attributes}")
226
+ return
227
+
228
+ current_span = trace.get_current_span()
229
+ if current_span:
230
+ current_span.add_event(event_name, attributes=attributes or {})
231
+
232
+ def _record_phase_duration(self, phase: str, duration_ms: float):
233
+ """Record phase duration metric."""
234
+ if self._phase_duration_histogram:
235
+ self._phase_duration_histogram.record(
236
+ duration_ms, attributes={"repl.phase": phase}
237
+ )
238
+
239
+ def _record_phase_count(self, phase: str):
240
+ """Record phase execution count."""
241
+ if self._phase_counter:
242
+ self._phase_counter.add(1, attributes={"repl.phase": phase})
243
+
244
+ def _record_error(self, phase: str):
245
+ """Record error count."""
246
+ if self._error_counter:
247
+ self._error_counter.add(1, attributes={"repl.phase": phase})
248
+
249
+ def is_initialized(self) -> bool:
250
+ """Check if sidecar is initialized."""
251
+ return self._initialized
252
+
253
+ def get_tracer(self) -> Tracer | None:
254
+ """Get the underlying tracer instance."""
255
+ return self._tracer
256
+
257
+ def get_meter(self) -> Meter | None:
258
+ """Get the underlying meter instance."""
259
+ return self._meter
260
+
261
+
262
+ # Convenience decorator for phase instrumentation
263
+ def instrument_phase(phase: str):
264
+ """
265
+ Decorator to instrument a function as a REPL phase.
266
+
267
+ Usage:
268
+ @instrument_phase(REPLPhase.PREP)
269
+ def _prep(self, pcard_hash, target_hash):
270
+ ...
271
+ """
272
+
273
+ def decorator(func: Callable):
274
+ @wraps(func)
275
+ def wrapper(self, *args, **kwargs):
276
+ sidecar = OpenTelemetrySidecar.get_instance()
277
+
278
+ # Extract common attributes
279
+ pcard_hash = kwargs.get("pcard_hash") or (args[0] if args else None)
280
+ target_hash = kwargs.get("target_hash") or (
281
+ args[1] if len(args) > 1 else None
282
+ )
283
+
284
+ with sidecar.trace_phase(phase, pcard_hash, target_hash):
285
+ return func(self, *args, **kwargs)
286
+
287
+ return wrapper
288
+
289
+ return decorator