velune-cli 0.9.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 (279) hide show
  1. velune/__init__.py +5 -0
  2. velune/__main__.py +6 -0
  3. velune/cli/__init__.py +5 -0
  4. velune/cli/app.py +208 -0
  5. velune/cli/autocomplete.py +80 -0
  6. velune/cli/banner.py +60 -0
  7. velune/cli/commands/__init__.py +32 -0
  8. velune/cli/commands/ask.py +175 -0
  9. velune/cli/commands/base.py +16 -0
  10. velune/cli/commands/chat.py +228 -0
  11. velune/cli/commands/config.py +224 -0
  12. velune/cli/commands/daemon.py +88 -0
  13. velune/cli/commands/doctor.py +721 -0
  14. velune/cli/commands/init.py +170 -0
  15. velune/cli/commands/mcp.py +82 -0
  16. velune/cli/commands/memory.py +293 -0
  17. velune/cli/commands/models.py +683 -0
  18. velune/cli/commands/preflight.py +95 -0
  19. velune/cli/commands/run.py +270 -0
  20. velune/cli/commands/setup.py +184 -0
  21. velune/cli/commands/workspace.py +249 -0
  22. velune/cli/context.py +36 -0
  23. velune/cli/councilmodel_ui.py +199 -0
  24. velune/cli/display/council_view.py +254 -0
  25. velune/cli/display/memory_view.py +126 -0
  26. velune/cli/display/panels.py +35 -0
  27. velune/cli/display/progress.py +25 -0
  28. velune/cli/display/themes.py +25 -0
  29. velune/cli/main.py +15 -0
  30. velune/cli/model_selector.py +51 -0
  31. velune/cli/modes.py +86 -0
  32. velune/cli/pull_ui.py +123 -0
  33. velune/cli/registry.py +80 -0
  34. velune/cli/rendering/__init__.py +5 -0
  35. velune/cli/rendering/error_panel.py +79 -0
  36. velune/cli/rendering/markdown.py +63 -0
  37. velune/cli/repl.py +1855 -0
  38. velune/cli/session_manager.py +71 -0
  39. velune/cli/slash_commands.py +37 -0
  40. velune/cli/theme.py +8 -0
  41. velune/cognition/__init__.py +23 -0
  42. velune/cognition/agents/__init__.py +7 -0
  43. velune/cognition/agents/coder.py +209 -0
  44. velune/cognition/agents/planner.py +156 -0
  45. velune/cognition/agents/reviewer.py +195 -0
  46. velune/cognition/arbitrator.py +220 -0
  47. velune/cognition/architecture.py +415 -0
  48. velune/cognition/budget.py +65 -0
  49. velune/cognition/council/__init__.py +47 -0
  50. velune/cognition/council/base.py +217 -0
  51. velune/cognition/council/challenger.py +74 -0
  52. velune/cognition/council/coder.py +79 -0
  53. velune/cognition/council/critic_agent.py +43 -0
  54. velune/cognition/council/critic_configs.py +111 -0
  55. velune/cognition/council/critics.py +41 -0
  56. velune/cognition/council/debate.py +46 -0
  57. velune/cognition/council/factory.py +140 -0
  58. velune/cognition/council/messages.py +56 -0
  59. velune/cognition/council/planner.py +124 -0
  60. velune/cognition/council/reviewer.py +74 -0
  61. velune/cognition/council/synthesizer.py +67 -0
  62. velune/cognition/council/tiers.py +188 -0
  63. velune/cognition/council_orchestrator.py +282 -0
  64. velune/cognition/firewall.py +354 -0
  65. velune/cognition/module.py +46 -0
  66. velune/cognition/orchestrator.py +1205 -0
  67. velune/cognition/personality.py +238 -0
  68. velune/cognition/state.py +104 -0
  69. velune/cognition/style_resolver.py +64 -0
  70. velune/cognition/verification.py +205 -0
  71. velune/context/__init__.py +28 -0
  72. velune/context/assembler.py +240 -0
  73. velune/context/budget.py +97 -0
  74. velune/context/extractive.py +95 -0
  75. velune/context/prompt_adaptation.py +480 -0
  76. velune/context/sections.py +99 -0
  77. velune/context/token_counter.py +134 -0
  78. velune/context/utilization.py +33 -0
  79. velune/context/window.py +63 -0
  80. velune/core/__init__.py +89 -0
  81. velune/core/background.py +5 -0
  82. velune/core/config/__init__.py +37 -0
  83. velune/core/errors/__init__.py +90 -0
  84. velune/core/errors/catalog.py +188 -0
  85. velune/core/errors/execution.py +31 -0
  86. velune/core/errors/memory.py +25 -0
  87. velune/core/errors/orchestration.py +31 -0
  88. velune/core/errors/provider.py +37 -0
  89. velune/core/event_loop.py +35 -0
  90. velune/core/logging.py +83 -0
  91. velune/core/paths.py +165 -0
  92. velune/core/runtime.py +113 -0
  93. velune/core/startup_profiler.py +56 -0
  94. velune/core/task_registry.py +117 -0
  95. velune/core/trace.py +83 -0
  96. velune/core/types/__init__.py +48 -0
  97. velune/core/types/agent.py +53 -0
  98. velune/core/types/context.py +42 -0
  99. velune/core/types/inference.py +38 -0
  100. velune/core/types/memory.py +42 -0
  101. velune/core/types/model.py +70 -0
  102. velune/core/types/provider.py +62 -0
  103. velune/core/types/repository.py +38 -0
  104. velune/core/types/task.py +61 -0
  105. velune/core/types/workspace.py +28 -0
  106. velune/daemon/client.py +13 -0
  107. velune/daemon/server.py +127 -0
  108. velune/daemon/transport.py +179 -0
  109. velune/events.py +204 -0
  110. velune/execution/__init__.py +22 -0
  111. velune/execution/benchmarker.py +315 -0
  112. velune/execution/cancellation.py +53 -0
  113. velune/execution/checkpointer.py +130 -0
  114. velune/execution/command_spec.py +165 -0
  115. velune/execution/diff_preview.py +197 -0
  116. velune/execution/executor.py +181 -0
  117. velune/execution/module.py +18 -0
  118. velune/execution/multi_diff.py +67 -0
  119. velune/execution/path_guard.py +74 -0
  120. velune/execution/planner.py +91 -0
  121. velune/execution/rollback.py +89 -0
  122. velune/execution/sandbox.py +268 -0
  123. velune/execution/validator.py +115 -0
  124. velune/hardware/__init__.py +1 -0
  125. velune/hardware/detector.py +192 -0
  126. velune/kernel/__init__.py +55 -0
  127. velune/kernel/bootstrap.py +125 -0
  128. velune/kernel/config.py +426 -0
  129. velune/kernel/entrypoint.py +78 -0
  130. velune/kernel/health.py +54 -0
  131. velune/kernel/lifecycle.py +143 -0
  132. velune/kernel/module.py +17 -0
  133. velune/kernel/modules.py +23 -0
  134. velune/kernel/registry.py +96 -0
  135. velune/kernel/schemas.py +28 -0
  136. velune/main.py +9 -0
  137. velune/mcp/__init__.py +9 -0
  138. velune/mcp/client.py +115 -0
  139. velune/mcp/config.py +19 -0
  140. velune/mcp/server.py +624 -0
  141. velune/memory/__init__.py +32 -0
  142. velune/memory/compaction.py +506 -0
  143. velune/memory/embedding_pipeline.py +241 -0
  144. velune/memory/lifecycle.py +680 -0
  145. velune/memory/module.py +218 -0
  146. velune/memory/prioritizer.py +67 -0
  147. velune/memory/storage/episodic_schema.sql +53 -0
  148. velune/memory/storage/lancedb_store.py +282 -0
  149. velune/memory/storage/sqlite_manager.py +369 -0
  150. velune/memory/storage/sqlite_pool.py +149 -0
  151. velune/memory/tiers/episodic.py +588 -0
  152. velune/memory/tiers/graph.py +378 -0
  153. velune/memory/tiers/lineage.py +416 -0
  154. velune/memory/tiers/semantic.py +475 -0
  155. velune/memory/tiers/working.py +168 -0
  156. velune/memory/vitality.py +132 -0
  157. velune/models/__init__.py +15 -0
  158. velune/models/family.py +76 -0
  159. velune/models/module.py +20 -0
  160. velune/models/probes.py +192 -0
  161. velune/models/profile_cache.py +84 -0
  162. velune/models/profiler.py +108 -0
  163. velune/models/registry.py +251 -0
  164. velune/models/scorer.py +233 -0
  165. velune/models/specializations.py +205 -0
  166. velune/orchestration/__init__.py +19 -0
  167. velune/orchestration/engine.py +239 -0
  168. velune/orchestration/module.py +15 -0
  169. velune/orchestration/role_assignments.py +82 -0
  170. velune/orchestration/schemas.py +98 -0
  171. velune/plugins/__init__.py +20 -0
  172. velune/plugins/hooks.py +50 -0
  173. velune/plugins/loader.py +161 -0
  174. velune/plugins/registry.py +56 -0
  175. velune/plugins/schemas.py +21 -0
  176. velune/providers/__init__.py +23 -0
  177. velune/providers/adapters/anthropic.py +257 -0
  178. velune/providers/adapters/fireworks.py +115 -0
  179. velune/providers/adapters/google.py +234 -0
  180. velune/providers/adapters/groq.py +151 -0
  181. velune/providers/adapters/huggingface.py +210 -0
  182. velune/providers/adapters/llamacpp.py +208 -0
  183. velune/providers/adapters/lmstudio.py +175 -0
  184. velune/providers/adapters/ollama.py +233 -0
  185. velune/providers/adapters/openai.py +213 -0
  186. velune/providers/adapters/openrouter.py +81 -0
  187. velune/providers/adapters/together.py +134 -0
  188. velune/providers/adapters/xai.py +60 -0
  189. velune/providers/base.py +86 -0
  190. velune/providers/benchmarker.py +138 -0
  191. velune/providers/discovery/__init__.py +33 -0
  192. velune/providers/discovery/anthropic.py +79 -0
  193. velune/providers/discovery/benchmarks.py +44 -0
  194. velune/providers/discovery/classifier.py +69 -0
  195. velune/providers/discovery/fireworks.py +95 -0
  196. velune/providers/discovery/gguf.py +88 -0
  197. velune/providers/discovery/google.py +95 -0
  198. velune/providers/discovery/gpu.py +117 -0
  199. velune/providers/discovery/groq.py +21 -0
  200. velune/providers/discovery/huggingface.py +67 -0
  201. velune/providers/discovery/lmstudio.py +80 -0
  202. velune/providers/discovery/ollama.py +162 -0
  203. velune/providers/discovery/openai.py +96 -0
  204. velune/providers/discovery/openrouter.py +113 -0
  205. velune/providers/discovery/scanner.py +115 -0
  206. velune/providers/discovery/together.py +114 -0
  207. velune/providers/discovery/xai.py +57 -0
  208. velune/providers/health.py +67 -0
  209. velune/providers/health_monitor.py +169 -0
  210. velune/providers/keystore.py +142 -0
  211. velune/providers/local_paths.py +49 -0
  212. velune/providers/local_resolver.py +229 -0
  213. velune/providers/module.py +51 -0
  214. velune/providers/ollama_manager.py +193 -0
  215. velune/providers/registry.py +220 -0
  216. velune/providers/router.py +255 -0
  217. velune/providers/task_classifier.py +288 -0
  218. velune/py.typed +0 -0
  219. velune/repository/__init__.py +33 -0
  220. velune/repository/analyzer.py +127 -0
  221. velune/repository/ast_parser.py +822 -0
  222. velune/repository/blast_radius.py +298 -0
  223. velune/repository/boundary_classifier.py +295 -0
  224. velune/repository/cognition.py +316 -0
  225. velune/repository/grapher.py +179 -0
  226. velune/repository/import_graph.py +263 -0
  227. velune/repository/incremental_indexer.py +275 -0
  228. velune/repository/index_state.py +96 -0
  229. velune/repository/indexer.py +243 -0
  230. velune/repository/module.py +17 -0
  231. velune/repository/parser.py +474 -0
  232. velune/repository/project_type.py +300 -0
  233. velune/repository/rename_journal.py +287 -0
  234. velune/repository/scanner.py +193 -0
  235. velune/repository/schemas.py +102 -0
  236. velune/repository/symbol_registry.py +365 -0
  237. velune/repository/tracker.py +252 -0
  238. velune/retrieval/__init__.py +27 -0
  239. velune/retrieval/cache.py +110 -0
  240. velune/retrieval/fast_path.py +391 -0
  241. velune/retrieval/graph.py +124 -0
  242. velune/retrieval/hybrid.py +271 -0
  243. velune/retrieval/keyword.py +131 -0
  244. velune/retrieval/module.py +26 -0
  245. velune/retrieval/pipeline.py +303 -0
  246. velune/retrieval/reranker.py +102 -0
  247. velune/retrieval/schemas.py +59 -0
  248. velune/retrieval/slow_path.py +364 -0
  249. velune/retrieval/vector.py +203 -0
  250. velune/telemetry/__init__.py +59 -0
  251. velune/telemetry/cognition.py +267 -0
  252. velune/telemetry/cost_estimator.py +92 -0
  253. velune/telemetry/debug.py +304 -0
  254. velune/telemetry/doctor.py +244 -0
  255. velune/telemetry/logging.py +286 -0
  256. velune/telemetry/spans.py +277 -0
  257. velune/telemetry/token_tracker.py +140 -0
  258. velune/telemetry/usage_tracker.py +340 -0
  259. velune/tools/__init__.py +41 -0
  260. velune/tools/base/registry.py +87 -0
  261. velune/tools/base/tool.py +63 -0
  262. velune/tools/code/navigate.py +116 -0
  263. velune/tools/code/search.py +123 -0
  264. velune/tools/filesystem/read.py +75 -0
  265. velune/tools/filesystem/search.py +136 -0
  266. velune/tools/filesystem/write.py +163 -0
  267. velune/tools/git/history.py +177 -0
  268. velune/tools/git/operations.py +122 -0
  269. velune/tools/git/state.py +121 -0
  270. velune/tools/module.py +81 -0
  271. velune/tools/terminal/execute.py +72 -0
  272. velune/tools/terminal/history.py +47 -0
  273. velune/tools/web/fetch.py +55 -0
  274. velune/tools/web/validator.py +122 -0
  275. velune_cli-0.9.0.dist-info/METADATA +518 -0
  276. velune_cli-0.9.0.dist-info/RECORD +279 -0
  277. velune_cli-0.9.0.dist-info/WHEEL +4 -0
  278. velune_cli-0.9.0.dist-info/entry_points.txt +2 -0
  279. velune_cli-0.9.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,54 @@
1
+ """Subsystem health monitoring and status collection."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import time
7
+ from collections.abc import Callable
8
+
9
+ from velune.kernel.lifecycle import ComponentStatus, LifecycleCoordinator
10
+ from velune.kernel.schemas import HealthReport
11
+
12
+ logger = logging.getLogger("velune.kernel.health")
13
+
14
+
15
+ class SubsystemHealthMonitor:
16
+ """Monitors system-wide component health, API response, and storage status."""
17
+
18
+ def __init__(self, coordinator: LifecycleCoordinator) -> None:
19
+ self._coordinator = coordinator
20
+ self._custom_checks: dict[str, Callable[[], dict]] = {}
21
+
22
+ def register_health_hook(self, name: str, hook: Callable[[], dict]) -> None:
23
+ """Register a custom diagnostic function for a subsystem."""
24
+ self._custom_checks[name] = hook
25
+
26
+ def check_subsystem(self, name: str) -> HealthReport:
27
+ """Evaluate and report a single subsystem's status and latency."""
28
+ status = self._coordinator.get_status(name)
29
+
30
+ start_time = time.perf_counter()
31
+ details = {}
32
+
33
+ if name in self._custom_checks:
34
+ try:
35
+ details = self._custom_checks[name]()
36
+ except Exception as e:
37
+ logger.error("Health hook failed for subsystem '%s': %s", name, e)
38
+ details = {"error": str(e)}
39
+ status = ComponentStatus.DEGRADED
40
+
41
+ latency_ms = (time.perf_counter() - start_time) * 1000.0
42
+
43
+ return HealthReport(
44
+ status=status,
45
+ latency_ms=latency_ms,
46
+ details=details,
47
+ )
48
+
49
+ def check_all(self) -> dict[str, HealthReport]:
50
+ """Aggregate health reports for all managed subsystems."""
51
+ reports = {}
52
+ for name in self._coordinator._components.keys():
53
+ reports[name] = self.check_subsystem(name)
54
+ return reports
@@ -0,0 +1,143 @@
1
+ """Subsystem lifecycle manager and transition tracker."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import logging
7
+ from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
8
+
9
+ from velune.kernel.schemas import ComponentStatus
10
+
11
+ if TYPE_CHECKING:
12
+ from velune.kernel.config import VeluneConfig
13
+
14
+ logger = logging.getLogger("velune.kernel.lifecycle")
15
+
16
+
17
+ @runtime_checkable
18
+ class Subsystem(Protocol):
19
+ """Lifecycle protocol for subsystems that need explicit startup or shutdown."""
20
+
21
+ async def initialize(self) -> None:
22
+ """Startup procedures for the component."""
23
+ ...
24
+
25
+ async def shutdown(self) -> None:
26
+ """Teardown procedures for the component."""
27
+ ...
28
+
29
+
30
+ class LifecycleCoordinator:
31
+ """Orchestrates structured startup, health transitions, and graceful shutdown of subsystems."""
32
+
33
+ def __init__(self) -> None:
34
+ self._components: dict[str, Subsystem] = {}
35
+ self._states: dict[str, ComponentStatus] = {}
36
+ self._started = False
37
+ self.container: Any = None
38
+ self._config: VeluneConfig | None = None
39
+
40
+ def set_config(self, config: VeluneConfig) -> None:
41
+ """Attach a VeluneConfig so startup() can validate it before initialising subsystems."""
42
+ self._config = config
43
+
44
+ def register(self, name: str, component: Subsystem) -> None:
45
+ """Register a component for active lifecycle tracking."""
46
+ self._components[name] = component
47
+ self._states[name] = ComponentStatus.UNINITIALIZED
48
+ logger.debug("Component '%s' registered for lifecycle tracking.", name)
49
+
50
+ def get_status(self, name: str) -> ComponentStatus:
51
+ """Retrieve the current state of a registered component."""
52
+ return self._states.get(name, ComponentStatus.UNINITIALIZED)
53
+
54
+ def set_status(self, name: str, status: ComponentStatus) -> None:
55
+ """Explicitly set a component status."""
56
+ if name in self._states:
57
+ self._states[name] = status
58
+ logger.debug("Component '%s' transitioned to state: %s", name, status.value)
59
+
60
+ async def startup(self) -> None:
61
+ """Initialize all registered subsystems sequentially.
62
+
63
+ If a :class:`~velune.kernel.config.VeluneConfig` was attached via
64
+ :meth:`set_config`, it is validated first. Any ``CRITICAL`` validation
65
+ errors abort startup immediately with a descriptive exception.
66
+ """
67
+ if self._started:
68
+ return
69
+
70
+ if self._config is not None:
71
+ from velune.kernel.config import ConfigValidationError # noqa: F401
72
+
73
+ errors = self._config.validate()
74
+ critical = [e for e in errors if e.severity == "CRITICAL"]
75
+ if critical:
76
+ for err in critical:
77
+ logger.critical(
78
+ "Config validation failed — %s = %r: %s",
79
+ err.field,
80
+ err.value,
81
+ err.reason,
82
+ )
83
+ summary = "; ".join(f"{e.field}: {e.reason}" for e in critical)
84
+ raise RuntimeError(
85
+ f"VeluneConfig has {len(critical)} critical error(s). "
86
+ f"Startup aborted. Details: {summary}"
87
+ )
88
+ for warn in errors:
89
+ if warn.severity != "CRITICAL":
90
+ logger.warning(
91
+ "Config warning — %s = %r: %s",
92
+ warn.field,
93
+ warn.value,
94
+ warn.reason,
95
+ )
96
+
97
+ self._started = True
98
+ logger.info("Initializing Velune systems...")
99
+ for name, comp in self._components.items():
100
+ if self._states[name] != ComponentStatus.UNINITIALIZED:
101
+ continue
102
+
103
+ self._states[name] = ComponentStatus.INITIALIZING
104
+ try:
105
+ if hasattr(comp, "initialize") and callable(comp.initialize):
106
+ await comp.initialize()
107
+ self._states[name] = ComponentStatus.HEALTHY
108
+ logger.info("Subsystem '%s' initialized successfully.", name)
109
+ except Exception as e:
110
+ self._states[name] = ComponentStatus.FAILED
111
+ logger.critical("Subsystem '%s' failed to initialize: %s", name, e)
112
+ raise e
113
+
114
+ async def shutdown(self) -> None:
115
+ """Shut down all registered subsystems gracefully in reverse order."""
116
+ if self.container and self.container.has("runtime.task_registry"):
117
+ try:
118
+ task_registry = self.container.get("runtime.task_registry")
119
+ await task_registry.cancel_all(timeout=10.0)
120
+ except Exception as e:
121
+ logger.error("Failed to cancel background tasks during shutdown: %s", e)
122
+
123
+ logger.info("Shutting down Velune systems...")
124
+ for name in reversed(list(self._components.keys())):
125
+ comp = self._components[name]
126
+ self._states[name] = ComponentStatus.SHUTTING_DOWN
127
+ try:
128
+ if hasattr(comp, "shutdown") and callable(comp.shutdown):
129
+ await comp.shutdown()
130
+ elif hasattr(comp, "stop") and callable(comp.stop):
131
+ if asyncio.iscoroutinefunction(comp.stop):
132
+ await comp.stop()
133
+ else:
134
+ comp.stop()
135
+ self._states[name] = ComponentStatus.SHUTDOWN
136
+ logger.info("Subsystem '%s' shut down successfully.", name)
137
+ except Exception as e:
138
+ self._states[name] = ComponentStatus.FAILED
139
+ logger.error("Error shutting down subsystem '%s': %s", name, e)
140
+
141
+ self._components.clear()
142
+ self._states.clear()
143
+ self._started = False
@@ -0,0 +1,17 @@
1
+ from velune.kernel.bootstrap import RuntimeEnvironment, SubsystemModule
2
+
3
+
4
+ def _create_cognitive_bus(env: RuntimeEnvironment):
5
+ from velune.events import CognitiveBus
6
+
7
+ return CognitiveBus()
8
+
9
+
10
+ KERNEL_MODULES = [
11
+ SubsystemModule(
12
+ name="cognitive_bus",
13
+ factory=_create_cognitive_bus,
14
+ container_key="runtime.bus",
15
+ lifecycle_key="bus",
16
+ )
17
+ ]
@@ -0,0 +1,23 @@
1
+ from velune.cognition.module import COGNITION_MODULES
2
+ from velune.execution.module import EXECUTION_MODULES
3
+ from velune.kernel.module import KERNEL_MODULES
4
+ from velune.memory.module import MEMORY_MODULES
5
+ from velune.models.module import MODEL_MODULES
6
+ from velune.orchestration.module import ORCHESTRATION_MODULES
7
+ from velune.providers.module import PROVIDER_MODULES
8
+ from velune.repository.module import REPOSITORY_MODULES
9
+ from velune.retrieval.module import RETRIEVAL_MODULES
10
+ from velune.tools.module import TOOL_MODULES
11
+
12
+ ALL_MODULES = (
13
+ KERNEL_MODULES
14
+ + PROVIDER_MODULES
15
+ + MODEL_MODULES
16
+ + REPOSITORY_MODULES
17
+ + MEMORY_MODULES
18
+ + RETRIEVAL_MODULES
19
+ + EXECUTION_MODULES
20
+ + TOOL_MODULES
21
+ + COGNITION_MODULES
22
+ + ORCHESTRATION_MODULES
23
+ )
@@ -0,0 +1,96 @@
1
+ """Kernel component registry — string-keyed ServiceContainer with lazy factories."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from collections.abc import Callable
7
+ from functools import wraps
8
+ from typing import Any, TypeVar
9
+
10
+ logger = logging.getLogger("velune.kernel.registry")
11
+
12
+ T = TypeVar("T")
13
+
14
+
15
+ class ServiceContainer:
16
+ """Component registry container supporting string-based lazy factories."""
17
+
18
+ def __init__(self) -> None:
19
+ self._services: dict[str, Any] = {}
20
+ self._factories: dict[str, Callable[[], Any]] = {}
21
+ self._singletons: dict[str, Any] = {}
22
+
23
+ def register(self, name: str, factory: Callable[[], T], singleton: bool = True) -> None:
24
+ """Register a service factory by key."""
25
+ if singleton:
26
+ self._factories[name] = factory
27
+ else:
28
+ self._services[name] = factory
29
+ logger.debug("Registered factory for service: '%s' (singleton=%s)", name, singleton)
30
+
31
+ def register_instance(self, name: str, instance: Any) -> None:
32
+ """Register a concrete service instance directly."""
33
+ self._singletons[name] = instance
34
+ logger.debug("Registered direct instance for service: '%s'", name)
35
+
36
+ def hot_swap(self, name: str, replacement: Any) -> None:
37
+ """Dynamically replace an active service instance or registry factory."""
38
+ logger.info("Hot-swapping service: '%s'", name)
39
+ self._singletons.pop(name, None)
40
+ self._factories.pop(name, None)
41
+ self._services.pop(name, None) # Clear cached singleton too
42
+ self._singletons[name] = replacement
43
+
44
+ def get(self, name: str) -> Any:
45
+ """Retrieve a service instance by key. Resolves lazy factories if needed."""
46
+ if name in self._singletons:
47
+ return self._singletons[name]
48
+
49
+ if name in self._factories:
50
+ if name not in self._services:
51
+ factory = self._factories[name]
52
+ self._services[name] = factory()
53
+ return self._services[name]
54
+
55
+ if name in self._services:
56
+ callable_srv = self._services[name]
57
+ if callable(callable_srv):
58
+ return callable_srv()
59
+ return callable_srv
60
+
61
+ raise KeyError(f"Kernel Service not registered: {name}")
62
+
63
+ def has(self, name: str) -> bool:
64
+ """Check if a service is registered in any tier."""
65
+ return name in self._singletons or name in self._factories or name in self._services
66
+
67
+ def clear(self) -> None:
68
+ """Purge all registrations."""
69
+ self._services.clear()
70
+ self._factories.clear()
71
+ self._singletons.clear()
72
+ logger.debug("Kernel component registry cleared.")
73
+
74
+
75
+ # Global system container
76
+ _container = ServiceContainer()
77
+
78
+
79
+ def inject(service_name: str):
80
+ """Decorator to inject a registered kernel component into function arguments."""
81
+
82
+ def decorator(func: Callable) -> Callable:
83
+ @wraps(func)
84
+ def wrapper(*args, **kwargs):
85
+ if service_name not in kwargs:
86
+ kwargs[service_name] = _container.get(service_name)
87
+ return func(*args, **kwargs)
88
+
89
+ return wrapper
90
+
91
+ return decorator
92
+
93
+
94
+ def get_container() -> ServiceContainer:
95
+ """Access the global system service container."""
96
+ return _container
@@ -0,0 +1,28 @@
1
+ """Strictly-typed schemas for the Cognitive Kernel."""
2
+
3
+ from datetime import UTC, datetime
4
+ from enum import StrEnum
5
+ from typing import Any
6
+
7
+ from pydantic import BaseModel, Field
8
+
9
+
10
+ class ComponentStatus(StrEnum):
11
+ """Execution status of registered kernel components."""
12
+
13
+ UNINITIALIZED = "uninitialized"
14
+ INITIALIZING = "initializing"
15
+ HEALTHY = "healthy"
16
+ DEGRADED = "degraded"
17
+ FAILED = "failed"
18
+ SHUTTING_DOWN = "shutting_down"
19
+ SHUTDOWN = "shutdown"
20
+
21
+
22
+ class HealthReport(BaseModel):
23
+ """A report for individual subsystem health."""
24
+
25
+ status: ComponentStatus
26
+ latency_ms: float = 0.0
27
+ details: dict[str, Any] = Field(default_factory=dict)
28
+ last_check: datetime = Field(default_factory=lambda: datetime.now(tz=UTC))
velune/main.py ADDED
@@ -0,0 +1,9 @@
1
+ """Public CLI entry point for Velune."""
2
+
3
+ from velune.cli.app import app
4
+
5
+ __all__ = ["app"]
6
+
7
+
8
+ if __name__ == "__main__":
9
+ app()
velune/mcp/__init__.py ADDED
@@ -0,0 +1,9 @@
1
+ """Model Context Protocol (MCP) support for Velune."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from velune.mcp.client import VeluneMCPClient
6
+ from velune.mcp.config import load_mcp_servers
7
+ from velune.mcp.server import VeluneMCPServer
8
+
9
+ __all__ = ["VeluneMCPServer", "VeluneMCPClient", "load_mcp_servers"]
velune/mcp/client.py ADDED
@@ -0,0 +1,115 @@
1
+ """MCP client consuming external MCP servers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from mcp.types import Tool
8
+
9
+ from velune.tools.base.tool import BaseTool
10
+
11
+
12
+ class MCPToolWrapper(BaseTool):
13
+ """Wraps an external MCP tool as a Velune BaseTool."""
14
+
15
+ def __init__(self, client: VeluneMCPClient, name: str, description: str, input_schema: dict):
16
+ self.client = client
17
+ self.name = name
18
+ self.description = description
19
+ self.input_schema = input_schema
20
+
21
+ def get_name(self) -> str:
22
+ # Namespace tool with the server name prefix to avoid collisions
23
+ return f"{self.client.server_name}_{self.name}"
24
+
25
+ def get_description(self) -> str:
26
+ return self.description
27
+
28
+ def get_schema(self) -> dict[str, Any]:
29
+ return self.input_schema
30
+
31
+ async def execute(self, **kwargs) -> Any:
32
+ if not self.client.session:
33
+ raise RuntimeError(f"Client for tool {self.get_name()} is not connected.")
34
+ result = await self.client.session.call_tool(self.name, arguments=kwargs)
35
+
36
+ # CallToolResult structure holds content. Let's parse text contents.
37
+ text_contents = []
38
+ if hasattr(result, "content") and result.content:
39
+ for content in result.content:
40
+ if hasattr(content, "text"):
41
+ text_contents.append(content.text)
42
+ elif isinstance(content, dict) and "text" in content:
43
+ text_contents.append(content["text"])
44
+ else:
45
+ text_contents.append(str(content))
46
+ return "\n".join(text_contents) if text_contents else str(result)
47
+
48
+
49
+ class VeluneMCPClient:
50
+ """Connects to external MCP servers and exposes them as Velune tools."""
51
+
52
+ def __init__(self, server_url: str, server_name: str):
53
+ self.server_url = server_url
54
+ self.server_name = server_name
55
+ self._sse_ctx = None
56
+ self._session_ctx = None
57
+ self.session = None
58
+ self.raw_tools: list[Tool] = []
59
+
60
+ async def connect(self) -> list[dict]:
61
+ """Connect and return available tools."""
62
+ from mcp import ClientSession
63
+ from mcp.client.sse import sse_client
64
+
65
+ self._sse_ctx = sse_client(self.server_url)
66
+ self._read, self._write = await self._sse_ctx.__aenter__()
67
+
68
+ self._session_ctx = ClientSession(self._read, self._write)
69
+ self.session = await self._session_ctx.__aenter__()
70
+ await self.session.initialize()
71
+
72
+ tools_result = await self.session.list_tools()
73
+ self.raw_tools = tools_result.tools
74
+
75
+ # Convert tools to list of dict
76
+ result = []
77
+ for tool in self.raw_tools:
78
+ result.append(
79
+ {
80
+ "name": tool.name,
81
+ "description": tool.description or "",
82
+ "inputSchema": tool.inputSchema,
83
+ }
84
+ )
85
+ return result
86
+
87
+ def to_velune_tools(self) -> list[BaseTool]:
88
+ """Convert MCP tools to Velune BaseTool wrappers."""
89
+ velune_tools = []
90
+ for tool in self.raw_tools:
91
+ velune_tools.append(
92
+ MCPToolWrapper(
93
+ client=self,
94
+ name=tool.name,
95
+ description=tool.description or "",
96
+ input_schema=tool.inputSchema,
97
+ )
98
+ )
99
+ return velune_tools
100
+
101
+ async def disconnect(self) -> None:
102
+ """Disconnect and clean up resources."""
103
+ if self._session_ctx:
104
+ try:
105
+ await self._session_ctx.__aexit__(None, None, None)
106
+ except Exception:
107
+ pass
108
+ self._session_ctx = None
109
+ self.session = None
110
+ if self._sse_ctx:
111
+ try:
112
+ await self._sse_ctx.__aexit__(None, None, None)
113
+ except Exception:
114
+ pass
115
+ self._sse_ctx = None
velune/mcp/config.py ADDED
@@ -0,0 +1,19 @@
1
+ """Configuration utility for loading external MCP servers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from velune.kernel.config import ConfigLoader
8
+
9
+
10
+ def load_mcp_servers(config_path: Path | None = None) -> dict[str, str]:
11
+ """Load external MCP server configurations from velune.toml."""
12
+ try:
13
+ loader = ConfigLoader(config_path)
14
+ config = loader.load()
15
+ if hasattr(config, "mcp") and config.mcp:
16
+ return config.mcp.servers
17
+ except Exception:
18
+ pass
19
+ return {}