velune-cli 1.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.
- velune/__init__.py +5 -0
- velune/__main__.py +6 -0
- velune/cli/__init__.py +5 -0
- velune/cli/app.py +212 -0
- velune/cli/autocomplete.py +76 -0
- velune/cli/banner.py +98 -0
- velune/cli/commands/__init__.py +32 -0
- velune/cli/commands/ask.py +149 -0
- velune/cli/commands/base.py +16 -0
- velune/cli/commands/chat.py +188 -0
- velune/cli/commands/config.py +182 -0
- velune/cli/commands/daemon.py +85 -0
- velune/cli/commands/doctor.py +373 -0
- velune/cli/commands/init.py +160 -0
- velune/cli/commands/mcp.py +80 -0
- velune/cli/commands/memory.py +269 -0
- velune/cli/commands/models.py +462 -0
- velune/cli/commands/preflight.py +95 -0
- velune/cli/commands/run.py +171 -0
- velune/cli/commands/setup.py +182 -0
- velune/cli/commands/workspace.py +217 -0
- velune/cli/context.py +37 -0
- velune/cli/councilmodel_ui.py +171 -0
- velune/cli/display/council_view.py +240 -0
- velune/cli/display/memory_view.py +93 -0
- velune/cli/display/panels.py +35 -0
- velune/cli/display/progress.py +25 -0
- velune/cli/display/themes.py +21 -0
- velune/cli/main.py +15 -0
- velune/cli/model_selector.py +44 -0
- velune/cli/modes.py +86 -0
- velune/cli/pull_ui.py +118 -0
- velune/cli/registry.py +81 -0
- velune/cli/repl.py +1178 -0
- velune/cli/session_manager.py +69 -0
- velune/cli/slash_commands.py +37 -0
- velune/cognition/__init__.py +19 -0
- velune/cognition/arbitrator.py +216 -0
- velune/cognition/architecture.py +398 -0
- velune/cognition/council/__init__.py +47 -0
- velune/cognition/council/base.py +216 -0
- velune/cognition/council/challenger.py +70 -0
- velune/cognition/council/coder.py +79 -0
- velune/cognition/council/critic_agent.py +39 -0
- velune/cognition/council/critic_configs.py +111 -0
- velune/cognition/council/critics.py +41 -0
- velune/cognition/council/debate.py +44 -0
- velune/cognition/council/factory.py +140 -0
- velune/cognition/council/messages.py +53 -0
- velune/cognition/council/planner.py +119 -0
- velune/cognition/council/reviewer.py +72 -0
- velune/cognition/council/synthesizer.py +67 -0
- velune/cognition/council/tiers.py +181 -0
- velune/cognition/firewall.py +256 -0
- velune/cognition/module.py +38 -0
- velune/cognition/orchestrator.py +886 -0
- velune/cognition/personality.py +236 -0
- velune/cognition/style_resolver.py +62 -0
- velune/cognition/verification.py +201 -0
- velune/context/__init__.py +11 -0
- velune/context/extractive.py +94 -0
- velune/context/window.py +62 -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 +53 -0
- velune/core/errors/execution.py +26 -0
- velune/core/errors/memory.py +21 -0
- velune/core/errors/orchestration.py +26 -0
- velune/core/errors/provider.py +31 -0
- velune/core/event_loop.py +30 -0
- velune/core/logging.py +83 -0
- velune/core/runtime.py +106 -0
- velune/core/task_registry.py +120 -0
- velune/core/trace.py +80 -0
- velune/core/types/__init__.py +48 -0
- velune/core/types/agent.py +49 -0
- velune/core/types/context.py +39 -0
- velune/core/types/inference.py +35 -0
- velune/core/types/memory.py +39 -0
- velune/core/types/model.py +64 -0
- velune/core/types/provider.py +35 -0
- velune/core/types/repository.py +35 -0
- velune/core/types/task.py +56 -0
- velune/core/types/workspace.py +28 -0
- velune/daemon/client.py +13 -0
- velune/daemon/server.py +115 -0
- velune/daemon/transport.py +169 -0
- velune/events.py +194 -0
- velune/execution/__init__.py +22 -0
- velune/execution/benchmarker.py +311 -0
- velune/execution/cancellation.py +53 -0
- velune/execution/checkpointer.py +128 -0
- velune/execution/command_spec.py +140 -0
- velune/execution/diff_preview.py +172 -0
- velune/execution/executor.py +173 -0
- velune/execution/module.py +16 -0
- velune/execution/multi_diff.py +70 -0
- velune/execution/path_guard.py +19 -0
- velune/execution/planner.py +91 -0
- velune/execution/rollback.py +80 -0
- velune/execution/sandbox.py +257 -0
- velune/execution/validator.py +113 -0
- velune/hardware/__init__.py +1 -0
- velune/hardware/detector.py +162 -0
- velune/kernel/__init__.py +58 -0
- velune/kernel/bootstrap.py +107 -0
- velune/kernel/config.py +252 -0
- velune/kernel/health.py +54 -0
- velune/kernel/lifecycle.py +102 -0
- velune/kernel/module.py +15 -0
- velune/kernel/modules.py +23 -0
- velune/kernel/registry.py +93 -0
- velune/kernel/schemas.py +28 -0
- velune/main.py +9 -0
- velune/mcp/__init__.py +9 -0
- velune/mcp/client.py +113 -0
- velune/mcp/config.py +19 -0
- velune/mcp/server.py +90 -0
- velune/memory/__init__.py +28 -0
- velune/memory/lifecycle.py +154 -0
- velune/memory/module.py +94 -0
- velune/memory/prioritizer.py +65 -0
- velune/memory/storage/sqlite_manager.py +368 -0
- velune/memory/tiers/episodic.py +156 -0
- velune/memory/tiers/graph.py +282 -0
- velune/memory/tiers/lineage.py +367 -0
- velune/memory/tiers/semantic.py +198 -0
- velune/memory/tiers/working.py +165 -0
- velune/models/__init__.py +16 -0
- velune/models/module.py +18 -0
- velune/models/probes.py +182 -0
- velune/models/profile_cache.py +82 -0
- velune/models/profiler.py +105 -0
- velune/models/registry.py +201 -0
- velune/models/scorer.py +225 -0
- velune/models/specializations.py +196 -0
- velune/orchestration/__init__.py +15 -0
- velune/orchestration/module.py +14 -0
- velune/orchestration/role_assignments.py +78 -0
- velune/orchestration/schemas.py +99 -0
- velune/plugins/__init__.py +13 -0
- velune/plugins/hooks.py +49 -0
- velune/plugins/loader.py +95 -0
- velune/plugins/registry.py +54 -0
- velune/plugins/schemas.py +19 -0
- velune/providers/__init__.py +18 -0
- velune/providers/adapters/anthropic.py +231 -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 +203 -0
- velune/providers/adapters/llamacpp.py +202 -0
- velune/providers/adapters/lmstudio.py +173 -0
- velune/providers/adapters/ollama.py +186 -0
- velune/providers/adapters/openai.py +207 -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 +135 -0
- velune/providers/discovery/__init__.py +33 -0
- velune/providers/discovery/anthropic.py +77 -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 +87 -0
- velune/providers/discovery/google.py +95 -0
- velune/providers/discovery/gpu.py +109 -0
- velune/providers/discovery/groq.py +20 -0
- velune/providers/discovery/huggingface.py +67 -0
- velune/providers/discovery/lmstudio.py +80 -0
- velune/providers/discovery/ollama.py +165 -0
- velune/providers/discovery/openai.py +96 -0
- velune/providers/discovery/openrouter.py +113 -0
- velune/providers/discovery/scanner.py +114 -0
- velune/providers/discovery/together.py +114 -0
- velune/providers/discovery/xai.py +57 -0
- velune/providers/keystore.py +83 -0
- velune/providers/local_paths.py +49 -0
- velune/providers/local_resolver.py +208 -0
- velune/providers/module.py +15 -0
- velune/providers/ollama_manager.py +193 -0
- velune/providers/registry.py +173 -0
- velune/providers/router.py +82 -0
- velune/py.typed +0 -0
- velune/repository/__init__.py +21 -0
- velune/repository/analyzer.py +123 -0
- velune/repository/cognition.py +172 -0
- velune/repository/grapher.py +182 -0
- velune/repository/indexer.py +229 -0
- velune/repository/module.py +15 -0
- velune/repository/parser.py +378 -0
- velune/repository/project_type.py +293 -0
- velune/repository/scanner.py +177 -0
- velune/repository/schemas.py +102 -0
- velune/repository/tracker.py +233 -0
- velune/retrieval/__init__.py +27 -0
- velune/retrieval/graph.py +117 -0
- velune/retrieval/hybrid.py +250 -0
- velune/retrieval/keyword.py +113 -0
- velune/retrieval/module.py +19 -0
- velune/retrieval/reranker.py +68 -0
- velune/retrieval/schemas.py +59 -0
- velune/retrieval/vector.py +163 -0
- velune/telemetry/__init__.py +7 -0
- velune/telemetry/cognition.py +260 -0
- velune/telemetry/token_tracker.py +133 -0
- velune/tools/__init__.py +41 -0
- velune/tools/base/registry.py +84 -0
- velune/tools/base/tool.py +63 -0
- velune/tools/code/navigate.py +107 -0
- velune/tools/code/search.py +112 -0
- velune/tools/filesystem/read.py +75 -0
- velune/tools/filesystem/search.py +123 -0
- velune/tools/filesystem/write.py +160 -0
- velune/tools/git/history.py +185 -0
- velune/tools/git/operations.py +132 -0
- velune/tools/git/state.py +134 -0
- velune/tools/module.py +65 -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 +96 -0
- velune_cli-1.0.0.dist-info/METADATA +497 -0
- velune_cli-1.0.0.dist-info/RECORD +229 -0
- velune_cli-1.0.0.dist-info/WHEEL +4 -0
- velune_cli-1.0.0.dist-info/entry_points.txt +2 -0
- velune_cli-1.0.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""Interactive two-stage TUI for assigning models to council agent roles."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from prompt_toolkit.application import Application
|
|
8
|
+
from prompt_toolkit.formatted_text import FormattedText
|
|
9
|
+
from prompt_toolkit.key_binding import KeyBindings
|
|
10
|
+
from prompt_toolkit.layout import Layout
|
|
11
|
+
from prompt_toolkit.layout.containers import Window
|
|
12
|
+
from prompt_toolkit.layout.controls import FormattedTextControl
|
|
13
|
+
|
|
14
|
+
from velune.orchestration.role_assignments import (
|
|
15
|
+
COUNCIL_ROLES,
|
|
16
|
+
ROLE_DESCRIPTIONS,
|
|
17
|
+
CouncilRoleMap,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from rich.console import Console
|
|
22
|
+
|
|
23
|
+
from velune.core.types.model import ModelDescriptor
|
|
24
|
+
|
|
25
|
+
# Sentinel to distinguish "user pressed Escape" from "user selected clear"
|
|
26
|
+
_CANCELLED = object()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def run_councilmodel_ui(
|
|
30
|
+
role_map: CouncilRoleMap,
|
|
31
|
+
available_models: list[ModelDescriptor],
|
|
32
|
+
console: Console,
|
|
33
|
+
) -> CouncilRoleMap | None:
|
|
34
|
+
"""Two-stage interactive UI: select a role, then select a model for it.
|
|
35
|
+
|
|
36
|
+
Returns the updated role_map, or None if cancelled at the role-select stage.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# ── Stage 1: Role selection ────────────────────────────────────────
|
|
40
|
+
selected_role_idx = [0]
|
|
41
|
+
role_result: list[str | None] = [None]
|
|
42
|
+
|
|
43
|
+
def render_role_list() -> FormattedText:
|
|
44
|
+
lines: list[tuple[str, str]] = []
|
|
45
|
+
lines.append(("bold", " Assign model to council role\n"))
|
|
46
|
+
lines.append(("fg:ansibrightblack", " ↑↓ navigate · Enter select · Esc cancel\n\n"))
|
|
47
|
+
for i, role in enumerate(COUNCIL_ROLES):
|
|
48
|
+
is_active = i == selected_role_idx[0]
|
|
49
|
+
prefix = "❯ " if is_active else " "
|
|
50
|
+
row_style = "bold fg:cyan" if is_active else ""
|
|
51
|
+
desc = ROLE_DESCRIPTIONS.get(role, "")
|
|
52
|
+
lines.append((row_style, f" {prefix}{role:<14} {desc}\n"))
|
|
53
|
+
current = role_map.get(role)
|
|
54
|
+
if current:
|
|
55
|
+
lines.append(("fg:ansibrightblack", f" currently: {current.model_id}\n"))
|
|
56
|
+
return FormattedText(lines)
|
|
57
|
+
|
|
58
|
+
kb1 = KeyBindings()
|
|
59
|
+
|
|
60
|
+
@kb1.add("up")
|
|
61
|
+
def _up(event) -> None:
|
|
62
|
+
selected_role_idx[0] = (selected_role_idx[0] - 1) % len(COUNCIL_ROLES)
|
|
63
|
+
|
|
64
|
+
@kb1.add("down")
|
|
65
|
+
def _down(event) -> None:
|
|
66
|
+
selected_role_idx[0] = (selected_role_idx[0] + 1) % len(COUNCIL_ROLES)
|
|
67
|
+
|
|
68
|
+
@kb1.add("enter")
|
|
69
|
+
def _select_role(event) -> None:
|
|
70
|
+
role_result[0] = COUNCIL_ROLES[selected_role_idx[0]]
|
|
71
|
+
event.app.exit()
|
|
72
|
+
|
|
73
|
+
@kb1.add("escape")
|
|
74
|
+
@kb1.add("c-c")
|
|
75
|
+
def _cancel_role(event) -> None:
|
|
76
|
+
event.app.exit() # role_result stays None
|
|
77
|
+
|
|
78
|
+
app1 = Application(
|
|
79
|
+
layout=Layout(Window(
|
|
80
|
+
content=FormattedTextControl(render_role_list, focusable=True),
|
|
81
|
+
)),
|
|
82
|
+
key_bindings=kb1,
|
|
83
|
+
full_screen=False,
|
|
84
|
+
mouse_support=False,
|
|
85
|
+
)
|
|
86
|
+
await app1.run_async()
|
|
87
|
+
|
|
88
|
+
selected_role = role_result[0]
|
|
89
|
+
if selected_role is None:
|
|
90
|
+
return None # cancelled at role stage
|
|
91
|
+
|
|
92
|
+
# ── Stage 2: Model selection for chosen role ───────────────────────
|
|
93
|
+
# First entry is None = "clear assignment"
|
|
94
|
+
model_options: list[ModelDescriptor | None] = [None] + list(available_models)
|
|
95
|
+
selected_model_idx = [0]
|
|
96
|
+
model_result: list[object] = [_CANCELLED]
|
|
97
|
+
|
|
98
|
+
def render_model_list() -> FormattedText:
|
|
99
|
+
lines: list[tuple[str, str]] = []
|
|
100
|
+
lines.append(("bold", f" Select model for [{selected_role}]\n"))
|
|
101
|
+
lines.append(("fg:ansibrightblack", " ↑↓ navigate · Enter select · Esc back\n\n"))
|
|
102
|
+
|
|
103
|
+
for i, model in enumerate(model_options):
|
|
104
|
+
is_active = i == selected_model_idx[0]
|
|
105
|
+
prefix = "❯ " if is_active else " "
|
|
106
|
+
row_style = "bold fg:cyan" if is_active else ""
|
|
107
|
+
|
|
108
|
+
if model is None:
|
|
109
|
+
lines.append((row_style, f" {prefix}(clear — use default routing)\n"))
|
|
110
|
+
continue
|
|
111
|
+
|
|
112
|
+
current = role_map.get(selected_role)
|
|
113
|
+
is_current = current is not None and current.model_id == model.model_id
|
|
114
|
+
current_marker = " ← current" if is_current else ""
|
|
115
|
+
local_cloud = "local" if model.is_local else "cloud"
|
|
116
|
+
cost = getattr(model, "cost_per_1k_tokens", None)
|
|
117
|
+
free_str = " free" if cost == 0.0 else ""
|
|
118
|
+
lines.append((
|
|
119
|
+
row_style,
|
|
120
|
+
f" {prefix}{model.model_id:<42}"
|
|
121
|
+
f" [{local_cloud}{free_str} · {model.speed_tier}]"
|
|
122
|
+
f"{current_marker}\n",
|
|
123
|
+
))
|
|
124
|
+
return FormattedText(lines)
|
|
125
|
+
|
|
126
|
+
kb2 = KeyBindings()
|
|
127
|
+
|
|
128
|
+
@kb2.add("up")
|
|
129
|
+
def _up2(event) -> None:
|
|
130
|
+
selected_model_idx[0] = (selected_model_idx[0] - 1) % len(model_options)
|
|
131
|
+
|
|
132
|
+
@kb2.add("down")
|
|
133
|
+
def _down2(event) -> None:
|
|
134
|
+
selected_model_idx[0] = (selected_model_idx[0] + 1) % len(model_options)
|
|
135
|
+
|
|
136
|
+
@kb2.add("enter")
|
|
137
|
+
def _select_model(event) -> None:
|
|
138
|
+
model_result[0] = model_options[selected_model_idx[0]]
|
|
139
|
+
event.app.exit()
|
|
140
|
+
|
|
141
|
+
@kb2.add("escape")
|
|
142
|
+
@kb2.add("c-c")
|
|
143
|
+
def _cancel_model(event) -> None:
|
|
144
|
+
event.app.exit() # model_result stays _CANCELLED
|
|
145
|
+
|
|
146
|
+
app2 = Application(
|
|
147
|
+
layout=Layout(Window(
|
|
148
|
+
content=FormattedTextControl(render_model_list, focusable=True),
|
|
149
|
+
)),
|
|
150
|
+
key_bindings=kb2,
|
|
151
|
+
full_screen=False,
|
|
152
|
+
mouse_support=False,
|
|
153
|
+
)
|
|
154
|
+
await app2.run_async()
|
|
155
|
+
|
|
156
|
+
if model_result[0] is _CANCELLED:
|
|
157
|
+
return role_map # user backed out — return map unchanged
|
|
158
|
+
|
|
159
|
+
chosen_model = model_result[0]
|
|
160
|
+
if chosen_model is None:
|
|
161
|
+
role_map.clear_role(selected_role)
|
|
162
|
+
console.print(f"[yellow]✓ Cleared assignment for [{selected_role}][/yellow]")
|
|
163
|
+
else:
|
|
164
|
+
role_map.assign(selected_role, chosen_model.model_id, chosen_model.provider_id)
|
|
165
|
+
console.print(
|
|
166
|
+
f"[green]✓ [{selected_role}][/green] → "
|
|
167
|
+
f"[cyan]{chosen_model.model_id}[/cyan] "
|
|
168
|
+
f"[dim]({chosen_model.provider_id})[/dim]"
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
return role_map
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""Rich terminal visualization of the Reasoning Council debate, scoring, and votes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from rich.box import ROUNDED
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.panel import Panel
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
|
|
13
|
+
from velune.cognition.council.planner import TaskPlan
|
|
14
|
+
from velune.models.specializations import CouncilRole
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CouncilDisplayView:
|
|
18
|
+
"""Beautiful Rich-based UI components to visualize Reasoning Council deliberations."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, console: Console) -> None:
|
|
21
|
+
self.console = console
|
|
22
|
+
|
|
23
|
+
def render_header(self, task: str) -> None:
|
|
24
|
+
"""Display an eye-catching header for the council run."""
|
|
25
|
+
self.console.print()
|
|
26
|
+
self.console.print(
|
|
27
|
+
Panel(
|
|
28
|
+
Text.assemble(
|
|
29
|
+
("[bold magenta]VELUNE COGNITIVE OS[/bold magenta] — [cyan]Reasoning Council Active[/cyan]\n"),
|
|
30
|
+
("[dim]Objective:[/dim] ", "[italic white]" + task + "[/italic white]")
|
|
31
|
+
),
|
|
32
|
+
border_style="magenta",
|
|
33
|
+
box=ROUNDED,
|
|
34
|
+
title="[bold white]🧠 Cognitive Deliberation[/bold white]",
|
|
35
|
+
title_align="left"
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def render_role_assignments(self, assignments: dict[CouncilRole, Any]) -> None:
|
|
40
|
+
"""Render a table displaying mapped specialized models for the council."""
|
|
41
|
+
table = Table(
|
|
42
|
+
title="[bold cyan]Mapped Council Specializations[/bold cyan]",
|
|
43
|
+
box=ROUNDED,
|
|
44
|
+
border_style="dim",
|
|
45
|
+
expand=True
|
|
46
|
+
)
|
|
47
|
+
table.add_column("Council Seat", style="bold yellow")
|
|
48
|
+
table.add_column("Provider / Endpoint", style="green")
|
|
49
|
+
table.add_column("Target Model", style="cyan")
|
|
50
|
+
table.add_column("Key Skills / Tags", style="magenta")
|
|
51
|
+
|
|
52
|
+
for role, desc in assignments.items():
|
|
53
|
+
caps = []
|
|
54
|
+
if hasattr(desc, "capabilities") and desc.capabilities:
|
|
55
|
+
for cap_name in ["coding", "reasoning", "planning", "summarization", "tool_use"]:
|
|
56
|
+
level = getattr(desc.capabilities, cap_name, None)
|
|
57
|
+
if level and level > 0:
|
|
58
|
+
caps.append(f"{cap_name} ({level.name})")
|
|
59
|
+
tags = ", ".join(caps) if caps else ", ".join(desc.tags) if getattr(desc, "tags", None) else "reasoning"
|
|
60
|
+
table.add_row(
|
|
61
|
+
role.value.upper(),
|
|
62
|
+
desc.provider_id.capitalize(),
|
|
63
|
+
desc.model_id,
|
|
64
|
+
tags
|
|
65
|
+
)
|
|
66
|
+
self.console.print(table)
|
|
67
|
+
self.console.print()
|
|
68
|
+
|
|
69
|
+
def render_step_header(self, step_name: str, agent_emoji: str = "🤖") -> None:
|
|
70
|
+
"""Draw an elegant boundary indicating a change in agent active deliberation."""
|
|
71
|
+
self.console.print(f"\n[bold magenta]●[/bold magenta] [bold white]{agent_emoji} {step_name}[/bold white] is deliberating...")
|
|
72
|
+
|
|
73
|
+
def render_planner_dag(self, plan: TaskPlan) -> None:
|
|
74
|
+
"""Render the Planner's task plan DAG as a neat hierarchical or sequential table."""
|
|
75
|
+
table = Table(
|
|
76
|
+
title="[bold yellow]Execution Plan Compiled by Council Planner[/bold yellow]",
|
|
77
|
+
box=ROUNDED,
|
|
78
|
+
border_style="yellow",
|
|
79
|
+
expand=True
|
|
80
|
+
)
|
|
81
|
+
table.add_column("ID", style="bold cyan", width=8)
|
|
82
|
+
table.add_column("Description", style="white")
|
|
83
|
+
table.add_column("Dependencies", style="magenta")
|
|
84
|
+
table.add_column("Validation Strategy", style="green")
|
|
85
|
+
|
|
86
|
+
for step in plan.steps:
|
|
87
|
+
deps = ", ".join(step.dependencies) if step.dependencies else "[dim]None[/dim]"
|
|
88
|
+
val = step.metadata.get("test_command") or "Syntax check + File existence"
|
|
89
|
+
table.add_row(
|
|
90
|
+
step.id,
|
|
91
|
+
step.description,
|
|
92
|
+
deps,
|
|
93
|
+
str(val)
|
|
94
|
+
)
|
|
95
|
+
self.console.print(table)
|
|
96
|
+
self.console.print()
|
|
97
|
+
|
|
98
|
+
def render_code_proposal(self, code_proposal: str) -> None:
|
|
99
|
+
"""Format the coder's proposed implementation code inside a syntax-focused block."""
|
|
100
|
+
self.console.print(
|
|
101
|
+
Panel(
|
|
102
|
+
Text(code_proposal, style="green"),
|
|
103
|
+
title="[bold green]💻 Coder Proposed Patch[/bold green]",
|
|
104
|
+
border_style="green",
|
|
105
|
+
box=ROUNDED,
|
|
106
|
+
expand=True
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
def render_reviewer_report(self, report: Any) -> None:
|
|
111
|
+
"""Render the Reviewer's static audit, showing passed status and critical issues."""
|
|
112
|
+
if isinstance(report, dict):
|
|
113
|
+
passed = report.get("passed", True)
|
|
114
|
+
confidence = report.get("confidence_rating", 0.8)
|
|
115
|
+
issues = report.get("critical_issues", [])
|
|
116
|
+
else:
|
|
117
|
+
passed = report.passed
|
|
118
|
+
confidence = report.confidence_rating
|
|
119
|
+
issues = report.critical_issues
|
|
120
|
+
|
|
121
|
+
status_text = "[bold green]PASS[/bold green]" if passed else "[bold red]FAIL / BLOCKED[/bold red]"
|
|
122
|
+
border_style = "green" if passed else "red"
|
|
123
|
+
|
|
124
|
+
content = []
|
|
125
|
+
content.append(f"[bold]Verification Status:[/bold] {status_text}")
|
|
126
|
+
content.append(f"[bold]Confidence Rating:[/bold] {confidence:.2f}")
|
|
127
|
+
|
|
128
|
+
if issues:
|
|
129
|
+
content.append("\n[bold red]⚠️ Critical Issues Detected:[/bold red]")
|
|
130
|
+
for issue in issues:
|
|
131
|
+
content.append(f" [red]•[/red] {issue}")
|
|
132
|
+
else:
|
|
133
|
+
content.append("\n[green]✓ Static static checks passed. No syntactical or safety concerns raised.[/green]")
|
|
134
|
+
|
|
135
|
+
self.console.print(
|
|
136
|
+
Panel(
|
|
137
|
+
"\n".join(content),
|
|
138
|
+
title="[bold]🔍 Reviewer Audit[/bold]",
|
|
139
|
+
border_style=border_style,
|
|
140
|
+
box=ROUNDED,
|
|
141
|
+
expand=True
|
|
142
|
+
)
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def render_challenger_report(self, report: Any) -> None:
|
|
146
|
+
"""Render the Challenger's adversarial audit and failure vector probes."""
|
|
147
|
+
if isinstance(report, dict):
|
|
148
|
+
severity = report.get("severity_rating", 0.0)
|
|
149
|
+
vectors = report.get("failure_vectors", [])
|
|
150
|
+
else:
|
|
151
|
+
severity = report.severity_rating
|
|
152
|
+
vectors = report.failure_vectors
|
|
153
|
+
|
|
154
|
+
border_style = "yellow" if severity > 0.4 else "dim"
|
|
155
|
+
|
|
156
|
+
content = []
|
|
157
|
+
content.append(f"[bold]Adversarial Severity Rating:[/bold] [bold red]{severity:.2f}[/bold red] / 1.00")
|
|
158
|
+
|
|
159
|
+
if vectors:
|
|
160
|
+
content.append("\n[bold yellow]⚡ Failure Vectors Simulated:[/bold yellow]")
|
|
161
|
+
for vec in vectors:
|
|
162
|
+
content.append(f" [yellow]•[/yellow] {vec}")
|
|
163
|
+
else:
|
|
164
|
+
content.append("\n[dim]No significant failure vectors or edge-case gaps simulated.[/dim]")
|
|
165
|
+
|
|
166
|
+
self.console.print(
|
|
167
|
+
Panel(
|
|
168
|
+
"\n".join(content),
|
|
169
|
+
title="[bold]⚡ Challenger Adversarial Check[/bold]",
|
|
170
|
+
border_style=border_style,
|
|
171
|
+
box=ROUNDED,
|
|
172
|
+
expand=True
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def render_arbitration_result(self, res: Any) -> None:
|
|
177
|
+
"""Display calibrated confidence score, contradiction matches, and human-review flags."""
|
|
178
|
+
if isinstance(res, dict):
|
|
179
|
+
confidence = res.get("overall_confidence", 0.8)
|
|
180
|
+
review_required = res.get("requires_human_review", False)
|
|
181
|
+
flags = res.get("flags", [])
|
|
182
|
+
winning_claims = res.get("winning_claims", [])
|
|
183
|
+
synthesis_inst = res.get("synthesis_instructions", "")
|
|
184
|
+
else:
|
|
185
|
+
confidence = res.overall_confidence
|
|
186
|
+
review_required = res.requires_human_review
|
|
187
|
+
flags = res.flags
|
|
188
|
+
winning_claims = res.winning_claims
|
|
189
|
+
synthesis_inst = res.synthesis_instructions
|
|
190
|
+
|
|
191
|
+
# Color calibrated confidence based on score
|
|
192
|
+
if confidence > 0.75:
|
|
193
|
+
conf_str = f"[bold green]{confidence * 100:.1f}% (High Confidence)[/bold green]"
|
|
194
|
+
border_style = "green"
|
|
195
|
+
elif confidence > 0.55:
|
|
196
|
+
conf_str = f"[bold yellow]{confidence * 100:.1f}% (Medium Confidence)[/bold yellow]"
|
|
197
|
+
border_style = "yellow"
|
|
198
|
+
else:
|
|
199
|
+
conf_str = f"[bold red]{confidence * 100:.1f}% (Low Confidence / High Volatility)[/bold red]"
|
|
200
|
+
border_style = "red"
|
|
201
|
+
|
|
202
|
+
status_text = "[bold red]YES (Blocked / Escalate)[/bold red]" if review_required else "[bold green]NO (Autonomous Pass)[/bold green]"
|
|
203
|
+
|
|
204
|
+
content = []
|
|
205
|
+
content.append(f"[bold]Calibrated Council Confidence Score:[/bold] {conf_str}")
|
|
206
|
+
content.append(f"[bold]Escalate to Human-in-the-Loop Review:[/bold] {status_text}")
|
|
207
|
+
|
|
208
|
+
if flags:
|
|
209
|
+
content.append(f"[bold red]System Flags Raised:[/bold red] {', '.join(flags)}")
|
|
210
|
+
|
|
211
|
+
if winning_claims:
|
|
212
|
+
content.append("\n[bold cyan]Winning Claims & Arbitration Compromise:[/bold cyan]")
|
|
213
|
+
for claim in winning_claims:
|
|
214
|
+
content.append(f" [cyan]✓[/cyan] {claim}")
|
|
215
|
+
|
|
216
|
+
if synthesis_inst:
|
|
217
|
+
content.append("\n[bold dim]Arbitrator Instructions for Synthesizer:[/bold dim]")
|
|
218
|
+
content.append(f"[dim]{synthesis_inst}[/dim]")
|
|
219
|
+
|
|
220
|
+
self.console.print(
|
|
221
|
+
Panel(
|
|
222
|
+
"\n".join(content),
|
|
223
|
+
title="[bold white]⚖️ Council Arbitration Engine[/bold white]",
|
|
224
|
+
border_style=border_style,
|
|
225
|
+
box=ROUNDED,
|
|
226
|
+
expand=True
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
def render_synthesized_response(self, text: str) -> None:
|
|
231
|
+
"""Display final walker walkthrough summary and accomplishments."""
|
|
232
|
+
self.console.print(
|
|
233
|
+
Panel(
|
|
234
|
+
Text(text, style="white"),
|
|
235
|
+
title="[bold magenta]🚀 Deliberated Walkthrough & Accomplishments[/bold magenta]",
|
|
236
|
+
border_style="magenta",
|
|
237
|
+
box=ROUNDED,
|
|
238
|
+
expand=True
|
|
239
|
+
)
|
|
240
|
+
)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Rich terminal rendering of hierarchical memory tiers, priority decays, and graph entities."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from rich.box import ROUNDED
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.panel import Panel
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
from rich.tree import Tree
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MemoryDisplayView:
|
|
16
|
+
"""Beautiful Rich-based UI components to visualize Velune's 5-tier Hierarchical Memory system."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, console: Console) -> None:
|
|
19
|
+
self.console = console
|
|
20
|
+
|
|
21
|
+
def render_memory_architecture(self, stats: dict[str, Any]) -> None:
|
|
22
|
+
"""Render a magnificent visual map of the memory tiers and active index statistics."""
|
|
23
|
+
self.console.print(
|
|
24
|
+
Panel(
|
|
25
|
+
Text.assemble(
|
|
26
|
+
("[bold magenta]VELUNE CORE HIERARCHICAL MEMORY MAP[/bold magenta]\n"),
|
|
27
|
+
("[dim]Active Workspace:[/dim] [italic cyan]" + str(stats.get("workspace", "current")) + "[/italic cyan]\n\n"),
|
|
28
|
+
("[bold yellow]Tier 1: Working Memory[/bold yellow] ──► In-memory state, fast lookups (TTL: " + str(stats.get("working_memory_ttl", 3600)) + "s)\n"),
|
|
29
|
+
("[bold green]Tier 2: Episodic SQLite[/bold green] ──► Task runs, step histories (Retention: " + str(stats.get("episodic_retention_days", 30)) + " days)\n"),
|
|
30
|
+
("[bold blue]Tier 3: Semantic Qdrant[/bold blue] ──► Vector code snippet indices (Similarity Threshold: " + str(stats.get("semantic_threshold", 0.85)) + ")\n"),
|
|
31
|
+
("[bold cyan]Tier 4: Graphiti Graph[/bold cyan] ──► Entity relationships & AST Dependency Graph (Graphiti Enabled: " + str(stats.get("graph_enabled", True)) + ")\n"),
|
|
32
|
+
("[bold red]Tier 5: Archive Storage[/bold red] ──► Long-term zstd-compressed cold files")
|
|
33
|
+
),
|
|
34
|
+
title="[bold white]🧠 Memory Architecture Map[/bold white]",
|
|
35
|
+
border_style="magenta",
|
|
36
|
+
box=ROUNDED,
|
|
37
|
+
title_align="left"
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def render_memory_records_table(self, records: list[dict[str, Any]], memory_type: str) -> None:
|
|
42
|
+
"""Render a structured table showing registered records across specific memory tiers."""
|
|
43
|
+
table = Table(
|
|
44
|
+
title=f"[bold green]Registered Memory Records ({memory_type.capitalize()})[/bold green]",
|
|
45
|
+
box=ROUNDED,
|
|
46
|
+
border_style="green",
|
|
47
|
+
expand=True
|
|
48
|
+
)
|
|
49
|
+
table.add_column("Record ID / Key", style="bold cyan", width=25)
|
|
50
|
+
table.add_column("Memory Tier", style="magenta")
|
|
51
|
+
table.add_column("Importance Score", style="yellow")
|
|
52
|
+
table.add_column("Content Preview", style="white")
|
|
53
|
+
table.add_column("Age (s) / Status", style="blue")
|
|
54
|
+
|
|
55
|
+
for rec in records:
|
|
56
|
+
importance = rec.get("importance", 1.0)
|
|
57
|
+
importance_bar = "★" * int(importance * 5)
|
|
58
|
+
table.add_row(
|
|
59
|
+
rec.get("id", "N/A"),
|
|
60
|
+
rec.get("tier", memory_type),
|
|
61
|
+
f"{importance:.2f} ({importance_bar})",
|
|
62
|
+
rec.get("content_preview", ""),
|
|
63
|
+
rec.get("status", "Active")
|
|
64
|
+
)
|
|
65
|
+
self.console.print(table)
|
|
66
|
+
self.console.print()
|
|
67
|
+
|
|
68
|
+
def render_knowledge_graph(self, entities: list[dict[str, Any]], relations: list[dict[str, Any]]) -> None:
|
|
69
|
+
"""Render a beautiful hierarchical tree of knowledge graph entities and their relational links."""
|
|
70
|
+
root = Tree("[bold cyan]🌐 Graphiti Knowledge Graph Root[/bold cyan]")
|
|
71
|
+
|
|
72
|
+
# Index entities by type for rendering
|
|
73
|
+
by_type: dict[str, list[dict[str, Any]]] = {}
|
|
74
|
+
for ent in entities:
|
|
75
|
+
etype = ent.get("type", "entity").upper()
|
|
76
|
+
if etype not in by_type:
|
|
77
|
+
by_type[etype] = []
|
|
78
|
+
by_type[etype].append(ent)
|
|
79
|
+
|
|
80
|
+
for etype, items in by_type.items():
|
|
81
|
+
type_node = root.add(f"[bold yellow]🏷️ {etype}[/bold yellow]")
|
|
82
|
+
for item in items:
|
|
83
|
+
name = item.get("name", item.get("id", "Unknown"))
|
|
84
|
+
importance = item.get("importance", 1.0)
|
|
85
|
+
item_node = type_node.add(f"[cyan]{name}[/cyan] [dim](imp: {importance:.2f})[/dim]")
|
|
86
|
+
|
|
87
|
+
# Find relations where this item is the source
|
|
88
|
+
for rel in relations:
|
|
89
|
+
if rel.get("source") == item.get("id"):
|
|
90
|
+
item_node.add(f"──[magenta]{rel.get('relation', 'connected')}[/magenta]──► [white]{rel.get('target')}[/white]")
|
|
91
|
+
|
|
92
|
+
self.console.print(Panel(root, title="[bold white]Knowledge Graph Visualization[/bold white]", border_style="cyan", box=ROUNDED))
|
|
93
|
+
self.console.print()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Rich-based terminal panels."""
|
|
2
|
+
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from rich.panel import Panel
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DisplayPanels:
|
|
9
|
+
"""Utility class for creating rich display panels."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, console: Console):
|
|
12
|
+
self.console = console
|
|
13
|
+
|
|
14
|
+
def info_panel(self, title: str, content: str) -> None:
|
|
15
|
+
"""Display an info panel."""
|
|
16
|
+
self.console.print(Panel(content, title=title, border_style="blue"))
|
|
17
|
+
|
|
18
|
+
def success_panel(self, title: str, content: str) -> None:
|
|
19
|
+
"""Display a success panel."""
|
|
20
|
+
self.console.print(Panel(content, title=title, border_style="green"))
|
|
21
|
+
|
|
22
|
+
def warning_panel(self, title: str, content: str) -> None:
|
|
23
|
+
"""Display a warning panel."""
|
|
24
|
+
self.console.print(Panel(content, title=title, border_style="yellow"))
|
|
25
|
+
|
|
26
|
+
def error_panel(self, title: str, content: str) -> None:
|
|
27
|
+
"""Display an error panel."""
|
|
28
|
+
self.console.print(Panel(content, title=title, border_style="red"))
|
|
29
|
+
|
|
30
|
+
def create_table(self, title: str, columns: list[str]) -> Table:
|
|
31
|
+
"""Create a rich table."""
|
|
32
|
+
table = Table(title=title)
|
|
33
|
+
for column in columns:
|
|
34
|
+
table.add_column(column)
|
|
35
|
+
return table
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Rich-based progress display."""
|
|
2
|
+
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ProgressDisplay:
|
|
8
|
+
"""Utility class for displaying progress."""
|
|
9
|
+
|
|
10
|
+
def __init__(self, console: Console):
|
|
11
|
+
self.console = console
|
|
12
|
+
|
|
13
|
+
def create_progress(self) -> Progress:
|
|
14
|
+
"""Create a rich progress bar."""
|
|
15
|
+
return Progress(
|
|
16
|
+
SpinnerColumn(),
|
|
17
|
+
TextColumn("[progress.description]{task.description}"),
|
|
18
|
+
BarColumn(),
|
|
19
|
+
TaskProgressColumn(),
|
|
20
|
+
console=self.console,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
def spinner(self, description: str):
|
|
24
|
+
"""Create a spinner context manager."""
|
|
25
|
+
return self.console.status(description)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Rich terminal themes."""
|
|
2
|
+
|
|
3
|
+
from rich.theme import Theme
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class VeluneTheme:
|
|
7
|
+
"""Velune CLI theme."""
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def get_theme() -> Theme:
|
|
11
|
+
"""Get the Velune theme."""
|
|
12
|
+
return Theme({
|
|
13
|
+
"info": "cyan",
|
|
14
|
+
"warning": "yellow",
|
|
15
|
+
"error": "red",
|
|
16
|
+
"success": "green",
|
|
17
|
+
"title": "bold blue",
|
|
18
|
+
"subtitle": "bold cyan",
|
|
19
|
+
"key": "magenta",
|
|
20
|
+
"value": "green",
|
|
21
|
+
})
|
velune/cli/main.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Backward-compatible CLI entry point."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
if sys.platform == "win32":
|
|
6
|
+
try:
|
|
7
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
8
|
+
sys.stderr.reconfigure(encoding="utf-8")
|
|
9
|
+
except Exception:
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
from velune.cli.app import app
|
|
13
|
+
|
|
14
|
+
if __name__ == "__main__":
|
|
15
|
+
app()
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from velune.cli.modes import ModeConfig
|
|
2
|
+
from velune.core.types.model import ModelDescriptor
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ModeAwareModelSelector:
|
|
6
|
+
def __init__(self, model_registry, provider_registry) -> None:
|
|
7
|
+
self.model_registry = model_registry
|
|
8
|
+
self.provider_registry = provider_registry
|
|
9
|
+
|
|
10
|
+
def select_for_mode(
|
|
11
|
+
self,
|
|
12
|
+
config: ModeConfig,
|
|
13
|
+
current_model: ModelDescriptor | None,
|
|
14
|
+
) -> ModelDescriptor | None:
|
|
15
|
+
all_models = [
|
|
16
|
+
m for m in self.model_registry.list_all()
|
|
17
|
+
if self.provider_registry.get(m.provider_id) is not None
|
|
18
|
+
]
|
|
19
|
+
if not all_models:
|
|
20
|
+
return current_model
|
|
21
|
+
|
|
22
|
+
if config.use_fastest_model:
|
|
23
|
+
# Smallest context window = fastest / lightest
|
|
24
|
+
# Prefer local 3B/7B over cloud in optimus
|
|
25
|
+
local = [m for m in all_models if m.is_local]
|
|
26
|
+
pool = local if local else all_models
|
|
27
|
+
return min(pool, key=lambda m: m.context_length)
|
|
28
|
+
|
|
29
|
+
if config.use_largest_model:
|
|
30
|
+
# Largest context + highest capability score
|
|
31
|
+
def capability_score(m: ModelDescriptor) -> int:
|
|
32
|
+
caps = m.capabilities
|
|
33
|
+
if not caps:
|
|
34
|
+
return 0
|
|
35
|
+
return sum([
|
|
36
|
+
getattr(caps, f, 0).value if hasattr(
|
|
37
|
+
getattr(caps, f, None), "value"
|
|
38
|
+
) else 0
|
|
39
|
+
for f in ["coding", "reasoning", "planning",
|
|
40
|
+
"summarization", "instruction_following"]
|
|
41
|
+
])
|
|
42
|
+
return max(all_models, key=capability_score)
|
|
43
|
+
|
|
44
|
+
return current_model
|