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.
- velune/__init__.py +5 -0
- velune/__main__.py +6 -0
- velune/cli/__init__.py +5 -0
- velune/cli/app.py +208 -0
- velune/cli/autocomplete.py +80 -0
- velune/cli/banner.py +60 -0
- velune/cli/commands/__init__.py +32 -0
- velune/cli/commands/ask.py +175 -0
- velune/cli/commands/base.py +16 -0
- velune/cli/commands/chat.py +228 -0
- velune/cli/commands/config.py +224 -0
- velune/cli/commands/daemon.py +88 -0
- velune/cli/commands/doctor.py +721 -0
- velune/cli/commands/init.py +170 -0
- velune/cli/commands/mcp.py +82 -0
- velune/cli/commands/memory.py +293 -0
- velune/cli/commands/models.py +683 -0
- velune/cli/commands/preflight.py +95 -0
- velune/cli/commands/run.py +270 -0
- velune/cli/commands/setup.py +184 -0
- velune/cli/commands/workspace.py +249 -0
- velune/cli/context.py +36 -0
- velune/cli/councilmodel_ui.py +199 -0
- velune/cli/display/council_view.py +254 -0
- velune/cli/display/memory_view.py +126 -0
- velune/cli/display/panels.py +35 -0
- velune/cli/display/progress.py +25 -0
- velune/cli/display/themes.py +25 -0
- velune/cli/main.py +15 -0
- velune/cli/model_selector.py +51 -0
- velune/cli/modes.py +86 -0
- velune/cli/pull_ui.py +123 -0
- velune/cli/registry.py +80 -0
- velune/cli/rendering/__init__.py +5 -0
- velune/cli/rendering/error_panel.py +79 -0
- velune/cli/rendering/markdown.py +63 -0
- velune/cli/repl.py +1855 -0
- velune/cli/session_manager.py +71 -0
- velune/cli/slash_commands.py +37 -0
- velune/cli/theme.py +8 -0
- velune/cognition/__init__.py +23 -0
- velune/cognition/agents/__init__.py +7 -0
- velune/cognition/agents/coder.py +209 -0
- velune/cognition/agents/planner.py +156 -0
- velune/cognition/agents/reviewer.py +195 -0
- velune/cognition/arbitrator.py +220 -0
- velune/cognition/architecture.py +415 -0
- velune/cognition/budget.py +65 -0
- velune/cognition/council/__init__.py +47 -0
- velune/cognition/council/base.py +217 -0
- velune/cognition/council/challenger.py +74 -0
- velune/cognition/council/coder.py +79 -0
- velune/cognition/council/critic_agent.py +43 -0
- velune/cognition/council/critic_configs.py +111 -0
- velune/cognition/council/critics.py +41 -0
- velune/cognition/council/debate.py +46 -0
- velune/cognition/council/factory.py +140 -0
- velune/cognition/council/messages.py +56 -0
- velune/cognition/council/planner.py +124 -0
- velune/cognition/council/reviewer.py +74 -0
- velune/cognition/council/synthesizer.py +67 -0
- velune/cognition/council/tiers.py +188 -0
- velune/cognition/council_orchestrator.py +282 -0
- velune/cognition/firewall.py +354 -0
- velune/cognition/module.py +46 -0
- velune/cognition/orchestrator.py +1205 -0
- velune/cognition/personality.py +238 -0
- velune/cognition/state.py +104 -0
- velune/cognition/style_resolver.py +64 -0
- velune/cognition/verification.py +205 -0
- velune/context/__init__.py +28 -0
- velune/context/assembler.py +240 -0
- velune/context/budget.py +97 -0
- velune/context/extractive.py +95 -0
- velune/context/prompt_adaptation.py +480 -0
- velune/context/sections.py +99 -0
- velune/context/token_counter.py +134 -0
- velune/context/utilization.py +33 -0
- velune/context/window.py +63 -0
- velune/core/__init__.py +89 -0
- velune/core/background.py +5 -0
- velune/core/config/__init__.py +37 -0
- velune/core/errors/__init__.py +90 -0
- velune/core/errors/catalog.py +188 -0
- velune/core/errors/execution.py +31 -0
- velune/core/errors/memory.py +25 -0
- velune/core/errors/orchestration.py +31 -0
- velune/core/errors/provider.py +37 -0
- velune/core/event_loop.py +35 -0
- velune/core/logging.py +83 -0
- velune/core/paths.py +165 -0
- velune/core/runtime.py +113 -0
- velune/core/startup_profiler.py +56 -0
- velune/core/task_registry.py +117 -0
- velune/core/trace.py +83 -0
- velune/core/types/__init__.py +48 -0
- velune/core/types/agent.py +53 -0
- velune/core/types/context.py +42 -0
- velune/core/types/inference.py +38 -0
- velune/core/types/memory.py +42 -0
- velune/core/types/model.py +70 -0
- velune/core/types/provider.py +62 -0
- velune/core/types/repository.py +38 -0
- velune/core/types/task.py +61 -0
- velune/core/types/workspace.py +28 -0
- velune/daemon/client.py +13 -0
- velune/daemon/server.py +127 -0
- velune/daemon/transport.py +179 -0
- velune/events.py +204 -0
- velune/execution/__init__.py +22 -0
- velune/execution/benchmarker.py +315 -0
- velune/execution/cancellation.py +53 -0
- velune/execution/checkpointer.py +130 -0
- velune/execution/command_spec.py +165 -0
- velune/execution/diff_preview.py +197 -0
- velune/execution/executor.py +181 -0
- velune/execution/module.py +18 -0
- velune/execution/multi_diff.py +67 -0
- velune/execution/path_guard.py +74 -0
- velune/execution/planner.py +91 -0
- velune/execution/rollback.py +89 -0
- velune/execution/sandbox.py +268 -0
- velune/execution/validator.py +115 -0
- velune/hardware/__init__.py +1 -0
- velune/hardware/detector.py +192 -0
- velune/kernel/__init__.py +55 -0
- velune/kernel/bootstrap.py +125 -0
- velune/kernel/config.py +426 -0
- velune/kernel/entrypoint.py +78 -0
- velune/kernel/health.py +54 -0
- velune/kernel/lifecycle.py +143 -0
- velune/kernel/module.py +17 -0
- velune/kernel/modules.py +23 -0
- velune/kernel/registry.py +96 -0
- velune/kernel/schemas.py +28 -0
- velune/main.py +9 -0
- velune/mcp/__init__.py +9 -0
- velune/mcp/client.py +115 -0
- velune/mcp/config.py +19 -0
- velune/mcp/server.py +624 -0
- velune/memory/__init__.py +32 -0
- velune/memory/compaction.py +506 -0
- velune/memory/embedding_pipeline.py +241 -0
- velune/memory/lifecycle.py +680 -0
- velune/memory/module.py +218 -0
- velune/memory/prioritizer.py +67 -0
- velune/memory/storage/episodic_schema.sql +53 -0
- velune/memory/storage/lancedb_store.py +282 -0
- velune/memory/storage/sqlite_manager.py +369 -0
- velune/memory/storage/sqlite_pool.py +149 -0
- velune/memory/tiers/episodic.py +588 -0
- velune/memory/tiers/graph.py +378 -0
- velune/memory/tiers/lineage.py +416 -0
- velune/memory/tiers/semantic.py +475 -0
- velune/memory/tiers/working.py +168 -0
- velune/memory/vitality.py +132 -0
- velune/models/__init__.py +15 -0
- velune/models/family.py +76 -0
- velune/models/module.py +20 -0
- velune/models/probes.py +192 -0
- velune/models/profile_cache.py +84 -0
- velune/models/profiler.py +108 -0
- velune/models/registry.py +251 -0
- velune/models/scorer.py +233 -0
- velune/models/specializations.py +205 -0
- velune/orchestration/__init__.py +19 -0
- velune/orchestration/engine.py +239 -0
- velune/orchestration/module.py +15 -0
- velune/orchestration/role_assignments.py +82 -0
- velune/orchestration/schemas.py +98 -0
- velune/plugins/__init__.py +20 -0
- velune/plugins/hooks.py +50 -0
- velune/plugins/loader.py +161 -0
- velune/plugins/registry.py +56 -0
- velune/plugins/schemas.py +21 -0
- velune/providers/__init__.py +23 -0
- velune/providers/adapters/anthropic.py +257 -0
- velune/providers/adapters/fireworks.py +115 -0
- velune/providers/adapters/google.py +234 -0
- velune/providers/adapters/groq.py +151 -0
- velune/providers/adapters/huggingface.py +210 -0
- velune/providers/adapters/llamacpp.py +208 -0
- velune/providers/adapters/lmstudio.py +175 -0
- velune/providers/adapters/ollama.py +233 -0
- velune/providers/adapters/openai.py +213 -0
- velune/providers/adapters/openrouter.py +81 -0
- velune/providers/adapters/together.py +134 -0
- velune/providers/adapters/xai.py +60 -0
- velune/providers/base.py +86 -0
- velune/providers/benchmarker.py +138 -0
- velune/providers/discovery/__init__.py +33 -0
- velune/providers/discovery/anthropic.py +79 -0
- velune/providers/discovery/benchmarks.py +44 -0
- velune/providers/discovery/classifier.py +69 -0
- velune/providers/discovery/fireworks.py +95 -0
- velune/providers/discovery/gguf.py +88 -0
- velune/providers/discovery/google.py +95 -0
- velune/providers/discovery/gpu.py +117 -0
- velune/providers/discovery/groq.py +21 -0
- velune/providers/discovery/huggingface.py +67 -0
- velune/providers/discovery/lmstudio.py +80 -0
- velune/providers/discovery/ollama.py +162 -0
- velune/providers/discovery/openai.py +96 -0
- velune/providers/discovery/openrouter.py +113 -0
- velune/providers/discovery/scanner.py +115 -0
- velune/providers/discovery/together.py +114 -0
- velune/providers/discovery/xai.py +57 -0
- velune/providers/health.py +67 -0
- velune/providers/health_monitor.py +169 -0
- velune/providers/keystore.py +142 -0
- velune/providers/local_paths.py +49 -0
- velune/providers/local_resolver.py +229 -0
- velune/providers/module.py +51 -0
- velune/providers/ollama_manager.py +193 -0
- velune/providers/registry.py +220 -0
- velune/providers/router.py +255 -0
- velune/providers/task_classifier.py +288 -0
- velune/py.typed +0 -0
- velune/repository/__init__.py +33 -0
- velune/repository/analyzer.py +127 -0
- velune/repository/ast_parser.py +822 -0
- velune/repository/blast_radius.py +298 -0
- velune/repository/boundary_classifier.py +295 -0
- velune/repository/cognition.py +316 -0
- velune/repository/grapher.py +179 -0
- velune/repository/import_graph.py +263 -0
- velune/repository/incremental_indexer.py +275 -0
- velune/repository/index_state.py +96 -0
- velune/repository/indexer.py +243 -0
- velune/repository/module.py +17 -0
- velune/repository/parser.py +474 -0
- velune/repository/project_type.py +300 -0
- velune/repository/rename_journal.py +287 -0
- velune/repository/scanner.py +193 -0
- velune/repository/schemas.py +102 -0
- velune/repository/symbol_registry.py +365 -0
- velune/repository/tracker.py +252 -0
- velune/retrieval/__init__.py +27 -0
- velune/retrieval/cache.py +110 -0
- velune/retrieval/fast_path.py +391 -0
- velune/retrieval/graph.py +124 -0
- velune/retrieval/hybrid.py +271 -0
- velune/retrieval/keyword.py +131 -0
- velune/retrieval/module.py +26 -0
- velune/retrieval/pipeline.py +303 -0
- velune/retrieval/reranker.py +102 -0
- velune/retrieval/schemas.py +59 -0
- velune/retrieval/slow_path.py +364 -0
- velune/retrieval/vector.py +203 -0
- velune/telemetry/__init__.py +59 -0
- velune/telemetry/cognition.py +267 -0
- velune/telemetry/cost_estimator.py +92 -0
- velune/telemetry/debug.py +304 -0
- velune/telemetry/doctor.py +244 -0
- velune/telemetry/logging.py +286 -0
- velune/telemetry/spans.py +277 -0
- velune/telemetry/token_tracker.py +140 -0
- velune/telemetry/usage_tracker.py +340 -0
- velune/tools/__init__.py +41 -0
- velune/tools/base/registry.py +87 -0
- velune/tools/base/tool.py +63 -0
- velune/tools/code/navigate.py +116 -0
- velune/tools/code/search.py +123 -0
- velune/tools/filesystem/read.py +75 -0
- velune/tools/filesystem/search.py +136 -0
- velune/tools/filesystem/write.py +163 -0
- velune/tools/git/history.py +177 -0
- velune/tools/git/operations.py +122 -0
- velune/tools/git/state.py +121 -0
- velune/tools/module.py +81 -0
- velune/tools/terminal/execute.py +72 -0
- velune/tools/terminal/history.py +47 -0
- velune/tools/web/fetch.py +55 -0
- velune/tools/web/validator.py +122 -0
- velune_cli-0.9.0.dist-info/METADATA +518 -0
- velune_cli-0.9.0.dist-info/RECORD +279 -0
- velune_cli-0.9.0.dist-info/WHEEL +4 -0
- velune_cli-0.9.0.dist-info/entry_points.txt +2 -0
- velune_cli-0.9.0.dist-info/licenses/LICENSE +201 -0
velune/kernel/health.py
ADDED
|
@@ -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
|
velune/kernel/module.py
ADDED
|
@@ -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
|
+
]
|
velune/kernel/modules.py
ADDED
|
@@ -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
|
velune/kernel/schemas.py
ADDED
|
@@ -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
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 {}
|