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
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"""Task classification based on prompt content and context."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from enum import StrEnum
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TaskType(StrEnum):
|
|
11
|
+
"""Task type categories for routing."""
|
|
12
|
+
|
|
13
|
+
CODING = "coding"
|
|
14
|
+
REASONING = "reasoning"
|
|
15
|
+
SUMMARIZATION = "summarization"
|
|
16
|
+
GENERAL = "general"
|
|
17
|
+
EMBEDDING = "embedding"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ComplexityLevel(StrEnum):
|
|
21
|
+
"""Task complexity estimation."""
|
|
22
|
+
|
|
23
|
+
LOW = "low"
|
|
24
|
+
MEDIUM = "medium"
|
|
25
|
+
HIGH = "high"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class TaskProfile:
|
|
30
|
+
"""Profile of a task for routing decisions."""
|
|
31
|
+
|
|
32
|
+
task_type: TaskType
|
|
33
|
+
complexity: ComplexityLevel
|
|
34
|
+
latency_sensitive: bool
|
|
35
|
+
requires_long_context: bool
|
|
36
|
+
requires_tools: bool
|
|
37
|
+
estimated_tokens: int = 0
|
|
38
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TaskClassifier:
|
|
42
|
+
"""Classifies prompts and context into task profiles for routing."""
|
|
43
|
+
|
|
44
|
+
# Coding-related keywords
|
|
45
|
+
CODING_KEYWORDS = {
|
|
46
|
+
"implement",
|
|
47
|
+
"write",
|
|
48
|
+
"code",
|
|
49
|
+
"function",
|
|
50
|
+
"class",
|
|
51
|
+
"method",
|
|
52
|
+
"fix",
|
|
53
|
+
"bug",
|
|
54
|
+
"error",
|
|
55
|
+
"debug",
|
|
56
|
+
"refactor",
|
|
57
|
+
"rewrite",
|
|
58
|
+
"modify",
|
|
59
|
+
"change",
|
|
60
|
+
"update",
|
|
61
|
+
"add",
|
|
62
|
+
"remove",
|
|
63
|
+
"delete",
|
|
64
|
+
"script",
|
|
65
|
+
"program",
|
|
66
|
+
"algorithm",
|
|
67
|
+
"library",
|
|
68
|
+
"framework",
|
|
69
|
+
"test",
|
|
70
|
+
"unittest",
|
|
71
|
+
"pytest",
|
|
72
|
+
"jest",
|
|
73
|
+
"mocha",
|
|
74
|
+
"sql",
|
|
75
|
+
"query",
|
|
76
|
+
"database",
|
|
77
|
+
"schema",
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# Reasoning-related keywords
|
|
81
|
+
REASONING_KEYWORDS = {
|
|
82
|
+
"explain",
|
|
83
|
+
"why",
|
|
84
|
+
"how",
|
|
85
|
+
"analyze",
|
|
86
|
+
"reason",
|
|
87
|
+
"infer",
|
|
88
|
+
"deduce",
|
|
89
|
+
"conclude",
|
|
90
|
+
"derive",
|
|
91
|
+
"solve",
|
|
92
|
+
"think",
|
|
93
|
+
"understand",
|
|
94
|
+
"interpret",
|
|
95
|
+
"clarify",
|
|
96
|
+
"elaborate",
|
|
97
|
+
"compare",
|
|
98
|
+
"contrast",
|
|
99
|
+
"difference",
|
|
100
|
+
"similarity",
|
|
101
|
+
"logic",
|
|
102
|
+
"argument",
|
|
103
|
+
"evidence",
|
|
104
|
+
"proof",
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# Summarization-related keywords
|
|
108
|
+
SUMMARIZATION_KEYWORDS = {
|
|
109
|
+
"summarize",
|
|
110
|
+
"summary",
|
|
111
|
+
"tldr",
|
|
112
|
+
"brief",
|
|
113
|
+
"overview",
|
|
114
|
+
"outline",
|
|
115
|
+
"abstract",
|
|
116
|
+
"condense",
|
|
117
|
+
"digest",
|
|
118
|
+
"recap",
|
|
119
|
+
"highlight",
|
|
120
|
+
"extract",
|
|
121
|
+
"key",
|
|
122
|
+
"main",
|
|
123
|
+
"point",
|
|
124
|
+
"synopsis",
|
|
125
|
+
"gist",
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# Quick question patterns (low complexity)
|
|
129
|
+
QUICK_PATTERNS = {
|
|
130
|
+
"what is",
|
|
131
|
+
"what's",
|
|
132
|
+
"who is",
|
|
133
|
+
"who's",
|
|
134
|
+
"when is",
|
|
135
|
+
"where is",
|
|
136
|
+
"how many",
|
|
137
|
+
"how much",
|
|
138
|
+
"why",
|
|
139
|
+
"define",
|
|
140
|
+
"meaning",
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
def classify(self, prompt: str, context: dict[str, Any] | None = None) -> TaskProfile:
|
|
144
|
+
"""Classify a prompt into a task profile.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
prompt: The user prompt/question
|
|
148
|
+
context: Optional context dict with keys like:
|
|
149
|
+
- code: Code snippets present
|
|
150
|
+
- retrieved_context: Retrieved knowledge base content
|
|
151
|
+
- context_tokens: Estimated context token count
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
TaskProfile with task type, complexity, and routing hints
|
|
155
|
+
"""
|
|
156
|
+
context = context or {}
|
|
157
|
+
|
|
158
|
+
# Estimate prompt length in tokens (rough: 1 token ~= 4 chars)
|
|
159
|
+
prompt_tokens = max(1, len(prompt) // 4)
|
|
160
|
+
context_tokens = context.get("context_tokens", 0)
|
|
161
|
+
total_tokens = prompt_tokens + context_tokens
|
|
162
|
+
|
|
163
|
+
# Classify task type
|
|
164
|
+
task_type = self._classify_task_type(prompt)
|
|
165
|
+
|
|
166
|
+
# Estimate complexity
|
|
167
|
+
complexity = self._estimate_complexity(prompt, task_type, total_tokens, context)
|
|
168
|
+
|
|
169
|
+
# Determine if latency sensitive
|
|
170
|
+
latency_sensitive = self._is_latency_sensitive(prompt, task_type, complexity)
|
|
171
|
+
|
|
172
|
+
# Check if long context needed
|
|
173
|
+
requires_long_context = total_tokens > 8000
|
|
174
|
+
|
|
175
|
+
# Check if tools needed
|
|
176
|
+
requires_tools = self._requires_tools(prompt, context)
|
|
177
|
+
|
|
178
|
+
return TaskProfile(
|
|
179
|
+
task_type=task_type,
|
|
180
|
+
complexity=complexity,
|
|
181
|
+
latency_sensitive=latency_sensitive,
|
|
182
|
+
requires_long_context=requires_long_context,
|
|
183
|
+
requires_tools=requires_tools,
|
|
184
|
+
estimated_tokens=total_tokens,
|
|
185
|
+
metadata={
|
|
186
|
+
"prompt_tokens": prompt_tokens,
|
|
187
|
+
"context_tokens": context_tokens,
|
|
188
|
+
},
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
def _classify_task_type(self, prompt: str) -> TaskType:
|
|
192
|
+
"""Classify the primary task type from prompt keywords."""
|
|
193
|
+
lower = prompt.lower()
|
|
194
|
+
|
|
195
|
+
# Check coding keywords
|
|
196
|
+
coding_matches = sum(1 for kw in self.CODING_KEYWORDS if kw in lower)
|
|
197
|
+
reasoning_matches = sum(1 for kw in self.REASONING_KEYWORDS if kw in lower)
|
|
198
|
+
summarization_matches = sum(1 for kw in self.SUMMARIZATION_KEYWORDS if kw in lower)
|
|
199
|
+
|
|
200
|
+
# Return task type with most matches
|
|
201
|
+
max_matches = max(coding_matches, reasoning_matches, summarization_matches)
|
|
202
|
+
|
|
203
|
+
if max_matches == 0:
|
|
204
|
+
return TaskType.GENERAL
|
|
205
|
+
|
|
206
|
+
if coding_matches == max_matches:
|
|
207
|
+
return TaskType.CODING
|
|
208
|
+
if reasoning_matches == max_matches:
|
|
209
|
+
return TaskType.REASONING
|
|
210
|
+
if summarization_matches == max_matches:
|
|
211
|
+
return TaskType.SUMMARIZATION
|
|
212
|
+
|
|
213
|
+
return TaskType.GENERAL
|
|
214
|
+
|
|
215
|
+
def _estimate_complexity(
|
|
216
|
+
self,
|
|
217
|
+
prompt: str,
|
|
218
|
+
task_type: TaskType,
|
|
219
|
+
total_tokens: int,
|
|
220
|
+
context: dict[str, Any],
|
|
221
|
+
) -> ComplexityLevel:
|
|
222
|
+
"""Estimate task complexity based on prompt and context."""
|
|
223
|
+
prompt.lower()
|
|
224
|
+
|
|
225
|
+
# Short prompts with no code/context => LOW
|
|
226
|
+
if len(prompt) < 50 and total_tokens < 500:
|
|
227
|
+
if task_type == TaskType.GENERAL and "code" not in context:
|
|
228
|
+
return ComplexityLevel.LOW
|
|
229
|
+
|
|
230
|
+
# Long prompts with code/context => HIGH
|
|
231
|
+
if total_tokens > 4000 and "code" in context:
|
|
232
|
+
return ComplexityLevel.HIGH
|
|
233
|
+
|
|
234
|
+
# Reasoning/complex analysis => at least MEDIUM
|
|
235
|
+
if task_type in (TaskType.REASONING, TaskType.SUMMARIZATION):
|
|
236
|
+
if total_tokens > 2000:
|
|
237
|
+
return ComplexityLevel.HIGH
|
|
238
|
+
return ComplexityLevel.MEDIUM
|
|
239
|
+
|
|
240
|
+
# Coding with substantial context => HIGH
|
|
241
|
+
if task_type == TaskType.CODING and total_tokens > 3000:
|
|
242
|
+
return ComplexityLevel.HIGH
|
|
243
|
+
|
|
244
|
+
# Default to MEDIUM
|
|
245
|
+
return ComplexityLevel.MEDIUM
|
|
246
|
+
|
|
247
|
+
def _is_latency_sensitive(
|
|
248
|
+
self,
|
|
249
|
+
prompt: str,
|
|
250
|
+
task_type: TaskType,
|
|
251
|
+
complexity: ComplexityLevel,
|
|
252
|
+
) -> bool:
|
|
253
|
+
"""Determine if response latency is critical."""
|
|
254
|
+
# Short quick questions are latency sensitive
|
|
255
|
+
if complexity == ComplexityLevel.LOW:
|
|
256
|
+
lower = prompt.lower()
|
|
257
|
+
return any(pattern in lower for pattern in self.QUICK_PATTERNS)
|
|
258
|
+
|
|
259
|
+
# Complex reasoning/analysis can wait
|
|
260
|
+
if complexity == ComplexityLevel.HIGH:
|
|
261
|
+
return False
|
|
262
|
+
|
|
263
|
+
# Quick questions/definitions
|
|
264
|
+
lower = prompt.lower()
|
|
265
|
+
quick_match = any(pattern in lower for pattern in self.QUICK_PATTERNS)
|
|
266
|
+
if quick_match:
|
|
267
|
+
return True
|
|
268
|
+
|
|
269
|
+
# Default: not latency sensitive for medium complexity
|
|
270
|
+
return False
|
|
271
|
+
|
|
272
|
+
def _requires_tools(self, prompt: str, context: dict[str, Any]) -> bool:
|
|
273
|
+
"""Determine if the task likely needs external tools/APIs."""
|
|
274
|
+
tool_keywords = {
|
|
275
|
+
"api",
|
|
276
|
+
"request",
|
|
277
|
+
"fetch",
|
|
278
|
+
"call",
|
|
279
|
+
"network",
|
|
280
|
+
"http",
|
|
281
|
+
"search",
|
|
282
|
+
"browse",
|
|
283
|
+
"lookup",
|
|
284
|
+
"query",
|
|
285
|
+
"database",
|
|
286
|
+
}
|
|
287
|
+
lower = prompt.lower()
|
|
288
|
+
return any(kw in lower for kw in tool_keywords)
|
velune/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Repository Cognition Engine for AST parsing, Git tracking, and dependency graphs."""
|
|
2
|
+
|
|
3
|
+
from velune.repository.ast_parser import ASTParser, Symbol, SymbolKind
|
|
4
|
+
from velune.repository.cognition import RepositoryCognitionService
|
|
5
|
+
from velune.repository.parser import RepositorySnapshotParser
|
|
6
|
+
from velune.repository.rename_journal import RenameJournal
|
|
7
|
+
from velune.repository.schemas import (
|
|
8
|
+
RepositoryEdge,
|
|
9
|
+
RepositoryFile,
|
|
10
|
+
RepositoryLanguage,
|
|
11
|
+
RepositorySnapshot,
|
|
12
|
+
RepositorySymbol,
|
|
13
|
+
RepositorySymbolKind,
|
|
14
|
+
)
|
|
15
|
+
from velune.repository.symbol_registry import SymbolRegistry
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"RepositoryCognitionService",
|
|
19
|
+
"RepositoryLanguage",
|
|
20
|
+
"RepositorySymbolKind",
|
|
21
|
+
"RepositorySymbol",
|
|
22
|
+
"RepositoryFile",
|
|
23
|
+
"RepositoryEdge",
|
|
24
|
+
"RepositorySnapshot",
|
|
25
|
+
# Async AST parser (canonical for SymbolRegistry / RenameJournal)
|
|
26
|
+
"ASTParser",
|
|
27
|
+
"Symbol",
|
|
28
|
+
"SymbolKind",
|
|
29
|
+
"SymbolRegistry",
|
|
30
|
+
"RenameJournal",
|
|
31
|
+
# Sync snapshot parser (used by indexer / incremental indexer / tools)
|
|
32
|
+
"RepositorySnapshotParser",
|
|
33
|
+
]
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Architectural layer classifier and design pattern analyzer."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CodebaseAnalyzer:
|
|
7
|
+
"""Analyzes the repository's architectural layered structure and checks for design violations."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, root_path: Path) -> None:
|
|
10
|
+
self.root_path = root_path.resolve()
|
|
11
|
+
|
|
12
|
+
def classify_architecture_layers(self, file_paths: list[str]) -> dict[str, list[str]]:
|
|
13
|
+
"""Groups files into functional architectural layers based on folder topology."""
|
|
14
|
+
layers: dict[str, list[str]] = {
|
|
15
|
+
"kernel": [],
|
|
16
|
+
"providers": [],
|
|
17
|
+
"models": [],
|
|
18
|
+
"memory": [],
|
|
19
|
+
"context": [],
|
|
20
|
+
"intent": [],
|
|
21
|
+
"repository": [],
|
|
22
|
+
"retrieval": [],
|
|
23
|
+
"execution": [],
|
|
24
|
+
"cognition": [],
|
|
25
|
+
"cli": [],
|
|
26
|
+
"tools": [],
|
|
27
|
+
"plugins": [],
|
|
28
|
+
"telemetry": [],
|
|
29
|
+
"other": [],
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for path in file_paths:
|
|
33
|
+
# Normalize to forward slashes
|
|
34
|
+
norm = path.replace("\\", "/")
|
|
35
|
+
classified = False
|
|
36
|
+
|
|
37
|
+
for layer in layers.keys():
|
|
38
|
+
if f"velune/{layer}/" in norm or norm.startswith(f"{layer}/"):
|
|
39
|
+
layers[layer].append(norm)
|
|
40
|
+
classified = True
|
|
41
|
+
break
|
|
42
|
+
|
|
43
|
+
if not classified:
|
|
44
|
+
layers["other"].append(norm)
|
|
45
|
+
|
|
46
|
+
return layers
|
|
47
|
+
|
|
48
|
+
def detect_dependency_violations(
|
|
49
|
+
self, layers: dict[str, list[str]], import_edges: list[tuple]
|
|
50
|
+
) -> list[dict[str, str]]:
|
|
51
|
+
"""Identifies circular dependencies or violations of layered architectural boundaries.
|
|
52
|
+
|
|
53
|
+
Rules:
|
|
54
|
+
- Kernel layer MUST NOT import higher levels (cli, cognition, retrieval).
|
|
55
|
+
- Providers layer MUST NOT import cli or cognition.
|
|
56
|
+
- Higher levels can import kernel/providers freely.
|
|
57
|
+
"""
|
|
58
|
+
violations: list[dict[str, str]] = []
|
|
59
|
+
|
|
60
|
+
# Build mapping of file to its layer
|
|
61
|
+
layer_by_file: dict[str, str] = {}
|
|
62
|
+
for layer, files in layers.items():
|
|
63
|
+
for f in files:
|
|
64
|
+
layer_by_file[f] = layer
|
|
65
|
+
|
|
66
|
+
# Layer importance hierarchy (lower number is more fundamental/lower layer)
|
|
67
|
+
hierarchy = {
|
|
68
|
+
"kernel": 0,
|
|
69
|
+
"providers": 1,
|
|
70
|
+
"models": 2,
|
|
71
|
+
"memory": 3,
|
|
72
|
+
"context": 4,
|
|
73
|
+
"intent": 5,
|
|
74
|
+
"repository": 5,
|
|
75
|
+
"retrieval": 6,
|
|
76
|
+
"tools": 7,
|
|
77
|
+
"execution": 8,
|
|
78
|
+
"cognition": 9,
|
|
79
|
+
"plugins": 9,
|
|
80
|
+
"cli": 10,
|
|
81
|
+
"telemetry": 0, # Telemetry can be imported everywhere
|
|
82
|
+
"other": 10,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
for source, target in import_edges:
|
|
86
|
+
src_layer = layer_by_file.get(source)
|
|
87
|
+
tgt_layer = layer_by_file.get(target)
|
|
88
|
+
|
|
89
|
+
if src_layer and tgt_layer:
|
|
90
|
+
src_val = hierarchy.get(src_layer, 10)
|
|
91
|
+
tgt_val = hierarchy.get(tgt_layer, 10)
|
|
92
|
+
|
|
93
|
+
# Violation: Lower layer imports a strictly higher layer (excluding telemetry)
|
|
94
|
+
if src_val < tgt_val and tgt_layer != "telemetry":
|
|
95
|
+
violations.append(
|
|
96
|
+
{
|
|
97
|
+
"source": source,
|
|
98
|
+
"target": target,
|
|
99
|
+
"source_layer": src_layer,
|
|
100
|
+
"target_layer": tgt_layer,
|
|
101
|
+
"rule": f"Layer violation: Fundamental '{src_layer}' layer imports higher-level '{tgt_layer}' layer.",
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return violations
|
|
106
|
+
|
|
107
|
+
def detect_framework_footprint(self, code_files: dict[str, str]) -> list[str]:
|
|
108
|
+
"""Detects specific framework libraries and dependencies active in the codebase."""
|
|
109
|
+
footprints: set[str] = set()
|
|
110
|
+
|
|
111
|
+
for code in code_files.values():
|
|
112
|
+
if "import typer" in code or "from typer" in code:
|
|
113
|
+
footprints.add("Typer (CLI)")
|
|
114
|
+
if "import langgraph" in code or "from langgraph" in code:
|
|
115
|
+
footprints.add("LangGraph (Orchestration)")
|
|
116
|
+
if "import qdrant_client" in code or "from qdrant_client" in code:
|
|
117
|
+
footprints.add("Qdrant (Vector DB)")
|
|
118
|
+
if "import sqlite3" in code or "from sqlite3" in code:
|
|
119
|
+
footprints.add("SQLite (Episodic Storage)")
|
|
120
|
+
if "import tree_sitter" in code or "from tree_sitter" in code:
|
|
121
|
+
footprints.add("Tree-sitter (AST Parsing)")
|
|
122
|
+
if "import networkx" in code or "from networkx" in code:
|
|
123
|
+
footprints.add("NetworkX (Graph Traversal)")
|
|
124
|
+
if "import psutil" in code or "from psutil" in code:
|
|
125
|
+
footprints.add("psutil (Subprocess Limits)")
|
|
126
|
+
|
|
127
|
+
return list(footprints)
|