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,220 @@
1
+ """
2
+ LLM Configuration Module
3
+
4
+ Centralized configuration for LLM providers and execution parameters.
5
+ """
6
+
7
+ from dataclasses import asdict, dataclass, field
8
+ from typing import Any
9
+
10
+ from clm.cross_cutting.config.config_constants import (
11
+ LLM_DEFAULT_RETRY_COUNT,
12
+ LLM_DEFAULT_RETRY_DELAY_SECS,
13
+ LLM_DEFAULT_TIMEOUT_SECS,
14
+ )
15
+
16
+ # ─────────────────────────────────────────────────────────────────────────────
17
+ # Provider Configurations
18
+ # ─────────────────────────────────────────────────────────────────────────────
19
+
20
+ LLM_PROVIDERS = {
21
+ "ollama": {
22
+ "base_url": "http://localhost:11434",
23
+ "api_path": "/api/generate",
24
+ "chat_path": "/api/chat",
25
+ "models_path": "/api/tags",
26
+ "default_model": "qwen3:0.6b",
27
+ "available_models": ["qwen3:0.6b", "llama3:latest", "qwen3:latest"],
28
+ },
29
+ "mlc-llm": {
30
+ "base_url": "http://localhost:8000",
31
+ "api_path": "/v1/completions",
32
+ "chat_path": "/v1/chat/completions",
33
+ "models_path": "/v1/models",
34
+ "default_model": "Llama-3-8B-Instruct-q4f16_1-MLC",
35
+ "available_models": [],
36
+ },
37
+ "lmstudio": {
38
+ "base_url": "http://localhost:1234",
39
+ "api_path": "/v1/completions",
40
+ "chat_path": "/v1/chat/completions",
41
+ "models_path": "/v1/models",
42
+ "default_model": "local-model",
43
+ "available_models": [],
44
+ },
45
+ "openai": {
46
+ "base_url": "https://api.openai.com",
47
+ "api_path": "/v1/completions",
48
+ "chat_path": "/v1/chat/completions",
49
+ "models_path": "/v1/models",
50
+ "default_model": "gpt-4",
51
+ "available_models": ["gpt-4", "gpt-4-turbo", "gpt-3.5-turbo"],
52
+ },
53
+ "anthropic": {
54
+ "base_url": "https://api.anthropic.com",
55
+ "api_path": "/v1/messages",
56
+ "chat_path": "/v1/messages",
57
+ "models_path": None,
58
+ "default_model": "claude-3-sonnet-20240229",
59
+ "available_models": ["claude-3-opus", "claude-3-sonnet", "claude-3-haiku"],
60
+ },
61
+ }
62
+
63
+ # ─────────────────────────────────────────────────────────────────────────────
64
+ # Default Configuration
65
+ # ─────────────────────────────────────────────────────────────────────────────
66
+
67
+ DEFAULT_LLM_CONFIG = {
68
+ "temperature": 0.7,
69
+ "max_tokens": 2048,
70
+ "top_p": 1.0,
71
+ "top_k": 40,
72
+ "timeout": LLM_DEFAULT_TIMEOUT_SECS,
73
+ "stream": False,
74
+ "response_format": "text",
75
+ "retry_count": LLM_DEFAULT_RETRY_COUNT,
76
+ "retry_delay": LLM_DEFAULT_RETRY_DELAY_SECS,
77
+ }
78
+
79
+ # Output format options
80
+ RESPONSE_FORMATS = ("text", "json", "structured", "markdown")
81
+
82
+ # Default provider
83
+ DEFAULT_PROVIDER = "ollama"
84
+
85
+
86
+ # ─────────────────────────────────────────────────────────────────────────────
87
+ # LLMConfig Dataclass
88
+ # ─────────────────────────────────────────────────────────────────────────────
89
+
90
+
91
+ @dataclass
92
+ class LLMConfig:
93
+ """
94
+ Configuration for LLM execution.
95
+
96
+ Supports multiple providers (Ollama, LMStudio, OpenAI, Anthropic) with
97
+ unified configuration interface.
98
+ """
99
+
100
+ # Provider settings
101
+ provider: str = DEFAULT_PROVIDER
102
+ model: str | None = None # Uses provider default if None
103
+ endpoint_url: str | None = None # Override provider base_url
104
+ api_key: str | None = None # For authenticated providers
105
+
106
+ # Prompt structure
107
+ system_prompt: str = ""
108
+ assistant_instruction: str = ""
109
+
110
+ # Generation parameters
111
+ temperature: float = DEFAULT_LLM_CONFIG["temperature"]
112
+ max_tokens: int = DEFAULT_LLM_CONFIG["max_tokens"]
113
+ top_p: float = DEFAULT_LLM_CONFIG["top_p"]
114
+ top_k: int | None = DEFAULT_LLM_CONFIG["top_k"]
115
+ frequency_penalty: float = 0.0
116
+ presence_penalty: float = 0.0
117
+ stop_sequences: list[str] = field(default_factory=list)
118
+
119
+ # Response handling
120
+ response_format: str = DEFAULT_LLM_CONFIG["response_format"]
121
+ json_schema: dict | None = None # For structured output
122
+
123
+ # Execution settings
124
+ timeout: int = DEFAULT_LLM_CONFIG["timeout"]
125
+ retry_count: int = DEFAULT_LLM_CONFIG["retry_count"]
126
+ retry_delay: float = DEFAULT_LLM_CONFIG["retry_delay"]
127
+ stream: bool = DEFAULT_LLM_CONFIG["stream"]
128
+
129
+ def __post_init__(self):
130
+ """Validate configuration after initialization."""
131
+ if self.provider not in LLM_PROVIDERS:
132
+ raise ValueError(
133
+ f"Unknown provider: {self.provider}. "
134
+ f"Available: {list(LLM_PROVIDERS.keys())}"
135
+ )
136
+
137
+ if self.response_format not in RESPONSE_FORMATS:
138
+ raise ValueError(
139
+ f"Unknown response format: {self.response_format}. "
140
+ f"Available: {RESPONSE_FORMATS}"
141
+ )
142
+
143
+ @property
144
+ def effective_model(self) -> str:
145
+ """Get the model to use, falling back to provider default."""
146
+ if self.model:
147
+ return self.model
148
+ return LLM_PROVIDERS[self.provider]["default_model"]
149
+
150
+ @property
151
+ def effective_base_url(self) -> str:
152
+ """Get the base URL, with optional override."""
153
+ if self.endpoint_url:
154
+ return self.endpoint_url.rstrip("/")
155
+ return LLM_PROVIDERS[self.provider]["base_url"]
156
+
157
+ def to_provider_params(self) -> dict[str, Any]:
158
+ """Convert to provider-specific generation parameters."""
159
+ params = {
160
+ "model": self.effective_model,
161
+ "temperature": self.temperature,
162
+ }
163
+
164
+ # Provider-specific parameter names
165
+ if self.provider == "ollama":
166
+ params["options"] = {
167
+ "num_predict": self.max_tokens,
168
+ "top_p": self.top_p,
169
+ "top_k": self.top_k,
170
+ "temperature": self.temperature,
171
+ }
172
+ if self.stop_sequences:
173
+ params["options"]["stop"] = self.stop_sequences
174
+ else:
175
+ # OpenAI-compatible format
176
+ params["max_tokens"] = self.max_tokens
177
+ params["top_p"] = self.top_p
178
+ if self.stop_sequences:
179
+ params["stop"] = self.stop_sequences
180
+ if self.frequency_penalty:
181
+ params["frequency_penalty"] = self.frequency_penalty
182
+ if self.presence_penalty:
183
+ params["presence_penalty"] = self.presence_penalty
184
+
185
+ return params
186
+
187
+ def to_dict(self) -> dict[str, Any]:
188
+ """Convert to dictionary for serialization."""
189
+ return asdict(self)
190
+
191
+ @classmethod
192
+ def from_dict(cls, data: dict[str, Any]) -> "LLMConfig":
193
+ """Create from dictionary."""
194
+ return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__})
195
+
196
+ @classmethod
197
+ def from_concrete(
198
+ cls, concrete: dict[str, Any], context: dict[str, Any] = None
199
+ ) -> "LLMConfig":
200
+ """
201
+ Create LLMConfig from concrete implementation and context.
202
+
203
+ Merges llm_config from concrete with context overrides.
204
+ """
205
+ context = context or {}
206
+
207
+ # Start with defaults from concrete's llm_config
208
+ config_data = concrete.get("llm_config", {}).copy()
209
+
210
+ # Override with top-level concrete fields
211
+ for key in ["provider", "model", "system_prompt", "temperature", "max_tokens"]:
212
+ if key in concrete:
213
+ config_data[key] = concrete[key]
214
+
215
+ # Override with context
216
+ for key in cls.__dataclass_fields__:
217
+ if key in context:
218
+ config_data[key] = context[key]
219
+
220
+ return cls.from_dict(config_data)
@@ -0,0 +1,15 @@
1
+ """
2
+ LLM Providers Package
3
+
4
+ Provider implementations for different LLM services.
5
+ """
6
+
7
+ from .base import LLMProvider
8
+ from .mlc_llm import MLCLLMProvider
9
+ from .ollama import OllamaProvider
10
+
11
+ __all__ = [
12
+ "LLMProvider",
13
+ "OllamaProvider",
14
+ "MLCLLMProvider",
15
+ ]
@@ -0,0 +1,91 @@
1
+ """
2
+ LLM Provider Base Class
3
+
4
+ Abstract base class defining the interface for LLM providers.
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+ from typing import Any
9
+
10
+ from clm.layer0.narrative import Either, Left
11
+
12
+
13
+ class LLMProvider(ABC):
14
+ """
15
+ Abstract base class for LLM providers.
16
+
17
+ Implementations should handle:
18
+ - Connection to the LLM service
19
+ - Request formatting for the specific API
20
+ - Response parsing
21
+ - Error handling
22
+ """
23
+
24
+ provider_name: str = "base"
25
+
26
+ @abstractmethod
27
+ def complete(self, prompt: str, params: dict[str, Any]) -> Either[str, str]:
28
+ """
29
+ Generate text completion for a prompt.
30
+
31
+ Args:
32
+ prompt: The input prompt
33
+ params: Provider-specific generation parameters
34
+
35
+ Returns:
36
+ Either[Error, Response]: Left(error) or Right(completion)
37
+ """
38
+ pass
39
+
40
+ @abstractmethod
41
+ def chat(
42
+ self, messages: list[dict[str, str]], params: dict[str, Any]
43
+ ) -> Either[str, dict[str, Any]]:
44
+ """
45
+ Generate chat completion from messages.
46
+
47
+ Args:
48
+ messages: List of message dicts with 'role' and 'content'
49
+ params: Provider-specific generation parameters
50
+
51
+ Returns:
52
+ Either[Error, Response]: Left(error) or Right(response dict)
53
+ """
54
+ pass
55
+
56
+ @abstractmethod
57
+ def validate_connection(self) -> bool:
58
+ """
59
+ Check if the provider service is available.
60
+
61
+ Returns:
62
+ True if connection is valid, False otherwise
63
+ """
64
+ pass
65
+
66
+ @abstractmethod
67
+ def list_models(self) -> Either[str, list[str]]:
68
+ """
69
+ List available models from the provider.
70
+
71
+ Returns:
72
+ Either[Error, ModelList]: Left(error) or Right(list of model names)
73
+ """
74
+ pass
75
+
76
+ def get_status(self) -> dict[str, Any]:
77
+ """
78
+ Get provider status information.
79
+
80
+ Returns:
81
+ Dictionary with provider status details
82
+ """
83
+ available = self.validate_connection()
84
+ models_result = self.list_models() if available else Left("Not connected")
85
+
86
+ return {
87
+ "provider": self.provider_name,
88
+ "available": available,
89
+ "models": models_result.value if models_result.is_right() else [],
90
+ "error": models_result.value if models_result.is_left() else None,
91
+ }
@@ -0,0 +1,169 @@
1
+ """
2
+ MLC LLM Provider Implementation
3
+
4
+ Provider for the MLC LLM REST API (OpenAI compatible).
5
+ Running via `mlc_llm serve`.
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ import urllib.error
11
+ import urllib.request
12
+ from typing import Any
13
+
14
+ from clm.cross_cutting.config.config_constants import LLM_DEFAULT_TIMEOUT_SECS
15
+ from clm.cross_cutting.utils.url_safety import safe_urlopen
16
+ from clm.layer0.narrative import Either, Left, Right
17
+
18
+ from ..config import LLM_PROVIDERS
19
+ from .base import LLMProvider
20
+
21
+
22
+ class MLCLLMProvider(LLMProvider):
23
+ """
24
+ MLC LLM provider for local execution via OpenAI-compatible API.
25
+
26
+ Compatible with:
27
+ - /v1/completions
28
+ - /v1/chat/completions
29
+ - /v1/models
30
+ """
31
+
32
+ provider_name = "mlc-llm"
33
+
34
+ def __init__(self, base_url: str = None, timeout: int = LLM_DEFAULT_TIMEOUT_SECS):
35
+ self.logger = logging.getLogger(__name__)
36
+ self.config = LLM_PROVIDERS["mlc-llm"]
37
+ self.base_url = (base_url or self.config["base_url"]).rstrip("/")
38
+ self.timeout = timeout
39
+
40
+ def _make_request(
41
+ self, endpoint: str, data: dict = None, method: str = "POST"
42
+ ) -> Either[str, dict]:
43
+ """Make HTTP request to MLC LLM API."""
44
+ url = f"{self.base_url}{endpoint}"
45
+
46
+ try:
47
+ if data:
48
+ request = urllib.request.Request(
49
+ url,
50
+ data=json.dumps(data).encode("utf-8"),
51
+ headers={"Content-Type": "application/json"},
52
+ method=method,
53
+ )
54
+ else:
55
+ request = urllib.request.Request(url, method=method)
56
+
57
+ with safe_urlopen(request, timeout=self.timeout) as response:
58
+ response_text = response.read().decode("utf-8")
59
+ return Right(json.loads(response_text))
60
+
61
+ except urllib.error.HTTPError as e:
62
+ error_body = e.read().decode("utf-8") if e.fp else str(e)
63
+ return Left(f"MLC-LLM HTTP error {e.code}: {error_body}")
64
+ except urllib.error.URLError as e:
65
+ return Left(f"MLC-LLM connection error: {e.reason}")
66
+ except json.JSONDecodeError as e:
67
+ return Left(f"MLC-LLM response parse error: {e}")
68
+ except (OSError, ConnectionError, ValueError) as e:
69
+ return Left(f"MLC-LLM request failed: {e}")
70
+
71
+ def complete(self, prompt: str, params: dict[str, Any]) -> Either[str, str]:
72
+ """
73
+ Generate text completion.
74
+ Using /v1/completions style.
75
+ """
76
+ data = {
77
+ "model": params.get("model", self.config["default_model"]),
78
+ "prompt": prompt,
79
+ "max_tokens": params.get("max_tokens", 128),
80
+ "temperature": params.get("temperature", 0.7),
81
+ "top_p": params.get("top_p", 1.0),
82
+ "stream": False,
83
+ }
84
+
85
+ if "stop" in params:
86
+ data["stop"] = params["stop"]
87
+
88
+ self.logger.debug(f"MLC-LLM complete request: model={data['model']}")
89
+
90
+ result = self._make_request(self.config["api_path"], data)
91
+
92
+ if result.is_left():
93
+ return result
94
+
95
+ response = result.value
96
+ # OpenAI format: {'choices': [{'text': '...'}]}
97
+ if "choices" in response and len(response["choices"]) > 0:
98
+ return Right(response["choices"][0].get("text", ""))
99
+ elif "error" in response:
100
+ return Left(f"MLC-LLM error: {response['error']}")
101
+ else:
102
+ return Left(f"Unexpected MLC-LLM response format: {response}")
103
+
104
+ def chat(
105
+ self, messages: list[dict[str, str]], params: dict[str, Any]
106
+ ) -> Either[str, dict[str, Any]]:
107
+ """
108
+ Generate chat completion.
109
+ Using /v1/chat/completions style.
110
+ """
111
+ data = {
112
+ "model": params.get("model", self.config["default_model"]),
113
+ "messages": messages,
114
+ "max_tokens": params.get("max_tokens", 128),
115
+ "temperature": params.get("temperature", 0.7),
116
+ "top_p": params.get("top_p", 1.0),
117
+ "stream": False,
118
+ }
119
+
120
+ if "stop" in params:
121
+ data["stop"] = params["stop"]
122
+
123
+ self.logger.debug(f"MLC-LLM chat request: model={data['model']}")
124
+
125
+ result = self._make_request(self.config["chat_path"], data)
126
+
127
+ if result.is_left():
128
+ return result
129
+
130
+ response = result.value
131
+ # OpenAI format: {'choices': [{'message': {'role': 'assistant', 'content': '...'}}]}
132
+ if "choices" in response and len(response["choices"]) > 0:
133
+ choice = response["choices"][0]
134
+ message = choice.get("message", {})
135
+ return Right(
136
+ {
137
+ "content": message.get("content", ""),
138
+ "role": message.get("role", "assistant"),
139
+ "model": response.get("model", data["model"]),
140
+ "usage": response.get("usage", {}),
141
+ }
142
+ )
143
+ elif "error" in response:
144
+ return Left(f"MLC-LLM error: {response['error']}")
145
+ else:
146
+ return Left(f"Unexpected MLC-LLM response format: {response}")
147
+
148
+ def validate_connection(self) -> bool:
149
+ """Check if MLC-LLM service is running via models endpoint."""
150
+ try:
151
+ result = self._make_request(self.config["models_path"], method="GET")
152
+ return result.is_right()
153
+ except (OSError, ConnectionError):
154
+ return False
155
+
156
+ def list_models(self) -> Either[str, list[str]]:
157
+ """List available api models."""
158
+ result = self._make_request(self.config["models_path"], method="GET")
159
+
160
+ if result.is_left():
161
+ return result
162
+
163
+ response = result.value
164
+ # OpenAI format: {'data': [{'id': 'model-id', ...}]}
165
+ if "data" in response and isinstance(response["data"], list):
166
+ models = [m.get("id", "unknown") for m in response["data"]]
167
+ return Right(models)
168
+ else:
169
+ return Left(f"Unexpected models response: {response}")