Private
Public Access
some organization pass, still need to review a bunch
This commit is contained in:
+1
-1
@@ -4170,7 +4170,7 @@ def render_operations_hub(app: App) -> None:
|
|||||||
with imscope.tab_item("Vendor State") as (exp, _):
|
with imscope.tab_item("Vendor State") as (exp, _):
|
||||||
if exp: render_vendor_state(app)
|
if exp: render_vendor_state(app)
|
||||||
|
|
||||||
def render_vendor_state(app: App) -> None:
|
def render_vendor_state(app: App) -> None: # TODO(Ed): Shouldn't this just be a part of usage analytics? We can show all used vendors at once...
|
||||||
"""Render the Operations Hub > Vendor State panel.
|
"""Render the Operations Hub > Vendor State panel.
|
||||||
[C: src/vendor_state.py:get_vendor_state]
|
[C: src/vendor_state.py:get_vendor_state]
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ def parse_ts(s: str) -> Optional[datetime.datetime]:
|
|||||||
|
|
||||||
def entry_to_str(entry: dict[str, Any]) -> str:
|
def entry_to_str(entry: dict[str, Any]) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Serialise a disc entry dict -> stored string.
|
Serialise a disc entry dict -> stored string.
|
||||||
[C: tests/test_thinking_persistence.py:test_entry_to_str_with_thinking]
|
[C: tests/test_thinking_persistence.py:test_entry_to_str_with_thinking]
|
||||||
"""
|
"""
|
||||||
@@ -62,7 +61,6 @@ def format_discussion(entries: list[dict[str, Any]]) -> str:
|
|||||||
|
|
||||||
def str_to_entry(raw: str, roles: list[str]) -> dict[str, Any]:
|
def str_to_entry(raw: str, roles: list[str]) -> dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Parse a stored string back to a disc entry dict.
|
Parse a stored string back to a disc entry dict.
|
||||||
[C: tests/test_thinking_persistence.py:test_str_to_entry_with_thinking]
|
[C: tests/test_thinking_persistence.py:test_str_to_entry_with_thinking]
|
||||||
"""
|
"""
|
||||||
|
|||||||
+17
-19
@@ -6,9 +6,13 @@ import sys
|
|||||||
|
|
||||||
from typing import List, Dict, Any, Optional
|
from typing import List, Dict, Any, Optional
|
||||||
|
|
||||||
|
from src import ai_client
|
||||||
from src import models
|
from src import models
|
||||||
from src import mcp_client
|
from src import mcp_client
|
||||||
|
|
||||||
|
from src.file_cache import ASTParser
|
||||||
|
|
||||||
|
|
||||||
_SENTENCE_TRANSFORMERS = None
|
_SENTENCE_TRANSFORMERS = None
|
||||||
_GOOGLE_GENAI = None
|
_GOOGLE_GENAI = None
|
||||||
_CHROMADB = None
|
_CHROMADB = None
|
||||||
@@ -74,15 +78,14 @@ class GeminiEmbeddingProvider(BaseEmbeddingProvider):
|
|||||||
if google_module is None:
|
if google_module is None:
|
||||||
raise ImportError("google-genai is not installed")
|
raise ImportError("google-genai is not installed")
|
||||||
genai_pkg, types = google_module
|
genai_pkg, types = google_module
|
||||||
from src import ai_client
|
|
||||||
ai_client._ensure_gemini_client()
|
ai_client._ensure_gemini_client()
|
||||||
client = ai_client._gemini_client
|
client = ai_client._gemini_client
|
||||||
if not client:
|
if not client:
|
||||||
raise ValueError("Gemini client not initialized")
|
raise ValueError("Gemini client not initialized")
|
||||||
res = client.models.embed_content(
|
res = client.models.embed_content(
|
||||||
model=self.model_name,
|
model = self.model_name,
|
||||||
contents=texts,
|
contents = texts,
|
||||||
config=types.EmbedContentConfig(task_type="RETRIEVAL_DOCUMENT")
|
config = types.EmbedContentConfig(task_type="RETRIEVAL_DOCUMENT")
|
||||||
)
|
)
|
||||||
return [e.values for e in res.embeddings]
|
return [e.values for e in res.embeddings]
|
||||||
|
|
||||||
@@ -94,8 +97,7 @@ class RAGEngine:
|
|||||||
self.collection = None
|
self.collection = None
|
||||||
self.embedding_provider = None
|
self.embedding_provider = None
|
||||||
|
|
||||||
if not self.config.enabled:
|
if not self.config.enabled: return
|
||||||
return
|
|
||||||
|
|
||||||
self._init_embedding_provider()
|
self._init_embedding_provider()
|
||||||
self._init_vector_store()
|
self._init_vector_store()
|
||||||
@@ -143,10 +145,10 @@ class RAGEngine:
|
|||||||
return
|
return
|
||||||
embeddings = self.embedding_provider.embed(texts)
|
embeddings = self.embedding_provider.embed(texts)
|
||||||
self.collection.upsert(
|
self.collection.upsert(
|
||||||
ids=ids,
|
ids = ids,
|
||||||
embeddings=embeddings,
|
embeddings = embeddings,
|
||||||
documents=texts,
|
documents = texts,
|
||||||
metadatas=metadatas
|
metadatas = metadatas
|
||||||
)
|
)
|
||||||
|
|
||||||
def _chunk_text(self, content: str) -> List[str]:
|
def _chunk_text(self, content: str) -> List[str]:
|
||||||
@@ -168,7 +170,6 @@ class RAGEngine:
|
|||||||
def _chunk_code(self, content: str, file_path: str) -> List[str]:
|
def _chunk_code(self, content: str, file_path: str) -> List[str]:
|
||||||
"""AST-aware chunking for Python code."""
|
"""AST-aware chunking for Python code."""
|
||||||
try:
|
try:
|
||||||
from src.file_cache import ASTParser
|
|
||||||
parser = ASTParser("python")
|
parser = ASTParser("python")
|
||||||
tree = parser.parse(content)
|
tree = parser.parse(content)
|
||||||
chunks = []
|
chunks = []
|
||||||
@@ -246,17 +247,14 @@ class RAGEngine:
|
|||||||
"""
|
"""
|
||||||
[C: tests/mock_concurrent_mma.py:main, tests/test_rag_engine.py:test_rag_engine_chroma]
|
[C: tests/mock_concurrent_mma.py:main, tests/test_rag_engine.py:test_rag_engine_chroma]
|
||||||
"""
|
"""
|
||||||
if not self.config.enabled:
|
if not self.config.enabled: return []
|
||||||
return []
|
if self.config.vector_store.provider == 'mcp': return self._search_mcp(query, top_k)
|
||||||
if self.config.vector_store.provider == 'mcp':
|
if self.collection == "mock": return []
|
||||||
return self._search_mcp(query, top_k)
|
|
||||||
if self.collection == "mock":
|
|
||||||
return []
|
|
||||||
|
|
||||||
query_embedding = self.embedding_provider.embed([query])[0]
|
query_embedding = self.embedding_provider.embed([query])[0]
|
||||||
results = self.collection.query(
|
results = self.collection.query(
|
||||||
query_embeddings=[query_embedding],
|
query_embeddings = [query_embedding],
|
||||||
n_results=top_k
|
n_results = top_k
|
||||||
)
|
)
|
||||||
|
|
||||||
ret = []
|
ret = []
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ def open_session(label: Optional[str] = None) -> None:
|
|||||||
|
|
||||||
def close_session() -> None:
|
def close_session() -> None:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Flush and close all log files. Called on clean exit.
|
Flush and close all log files. Called on clean exit.
|
||||||
[C: tests/test_app_controller_offloading.py:tmp_session_dir, tests/test_logging_e2e.py:e2e_setup, tests/test_logging_e2e.py:test_logging_e2e, tests/test_session_logger_optimization.py:temp_session_setup, tests/test_session_logger_reset.py:temp_logs, tests/test_session_logging.py:temp_logs]
|
[C: tests/test_app_controller_offloading.py:tmp_session_dir, tests/test_logging_e2e.py:e2e_setup, tests/test_logging_e2e.py:test_logging_e2e, tests/test_session_logger_optimization.py:temp_session_setup, tests/test_session_logger_reset.py:temp_logs, tests/test_session_logging.py:temp_logs]
|
||||||
"""
|
"""
|
||||||
|
|||||||
+1
-4
@@ -8,8 +8,7 @@ def draw_soft_shadow(draw_list: imgui.ImDrawList, p_min: imgui.ImVec2, p_max: im
|
|||||||
"""
|
"""
|
||||||
r, g, b, a = color.x, color.y, color.z, color.w
|
r, g, b, a = color.x, color.y, color.z, color.w
|
||||||
steps = int(shadow_size)
|
steps = int(shadow_size)
|
||||||
if steps <= 0:
|
if steps <= 0: return
|
||||||
return
|
|
||||||
|
|
||||||
alpha_step = a / steps
|
alpha_step = a / steps
|
||||||
|
|
||||||
@@ -17,12 +16,10 @@ def draw_soft_shadow(draw_list: imgui.ImDrawList, p_min: imgui.ImVec2, p_max: im
|
|||||||
current_alpha = a - (i * alpha_step)
|
current_alpha = a - (i * alpha_step)
|
||||||
# Apply an easing function (e.g., cubic) for a smoother shadow falloff
|
# Apply an easing function (e.g., cubic) for a smoother shadow falloff
|
||||||
current_alpha = current_alpha * (1.0 - (i / steps)**2)
|
current_alpha = current_alpha * (1.0 - (i / steps)**2)
|
||||||
|
|
||||||
if current_alpha <= 0.01:
|
if current_alpha <= 0.01:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
expand = float(i)
|
expand = float(i)
|
||||||
|
|
||||||
c_min = imgui.ImVec2(p_min.x - expand, p_min.y - expand)
|
c_min = imgui.ImVec2(p_min.x - expand, p_min.y - expand)
|
||||||
c_max = imgui.ImVec2(p_max.x + expand, p_max.y + expand)
|
c_max = imgui.ImVec2(p_max.x + expand, p_max.y + expand)
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -70,9 +70,9 @@ def run_powershell(script: str, base_dir: str, qa_callback: Optional[Callable[[s
|
|||||||
try:
|
try:
|
||||||
process = subprocess.Popen(
|
process = subprocess.Popen(
|
||||||
[exe, "-NoProfile", "-NonInteractive", "-Command", full_script],
|
[exe, "-NoProfile", "-NonInteractive", "-Command", full_script],
|
||||||
stdin=subprocess.DEVNULL,
|
stdin = subprocess.DEVNULL,
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
|
stdout = subprocess.PIPE, stderr=subprocess.PIPE, text=True,
|
||||||
cwd=base_dir, env=_build_subprocess_env(),
|
cwd = base_dir, env=_build_subprocess_env(),
|
||||||
)
|
)
|
||||||
stdout, stderr = process.communicate(timeout=TIMEOUT_SECONDS)
|
stdout, stderr = process.communicate(timeout=TIMEOUT_SECONDS)
|
||||||
parts: list[str] = []
|
parts: list[str] = []
|
||||||
|
|||||||
+6
-22
@@ -1,16 +1,5 @@
|
|||||||
# summarize.py
|
# summarize.py
|
||||||
"""
|
"""
|
||||||
Note(Gemini):
|
|
||||||
Local heuristic summariser. Doesn't use any AI or network.
|
|
||||||
Uses Python's AST to reliably pull out classes, methods, and functions.
|
|
||||||
Regex is used for TOML and Markdown.
|
|
||||||
|
|
||||||
The rationale here is simple: giving the AI the *structure* of a codebase is 90%
|
|
||||||
as good as giving it the full source, but costs 1% of the tokens.
|
|
||||||
If it needs the full source of a file after reading the summary, it can just call read_file.
|
|
||||||
"""
|
|
||||||
# summarize.py
|
|
||||||
"""
|
|
||||||
Local symbolic summariser — no AI calls, no network.
|
Local symbolic summariser — no AI calls, no network.
|
||||||
|
|
||||||
For each file, extracts structural information:
|
For each file, extracts structural information:
|
||||||
@@ -28,6 +17,8 @@ import re
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable, Any
|
from typing import Callable, Any
|
||||||
|
|
||||||
|
from src import ai_client
|
||||||
|
|
||||||
from src.summary_cache import SummaryCache, get_file_hash
|
from src.summary_cache import SummaryCache, get_file_hash
|
||||||
|
|
||||||
|
|
||||||
@@ -73,16 +64,13 @@ def _summarise_python(path: Path, content: str) -> str:
|
|||||||
n.name for n in ast.iter_child_nodes(node)
|
n.name for n in ast.iter_child_nodes(node)
|
||||||
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))
|
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))
|
||||||
]
|
]
|
||||||
if methods:
|
if methods: parts.append(f"class {node.name}: {', '.join(methods)}")
|
||||||
parts.append(f"class {node.name}: {', '.join(methods)}")
|
else: parts.append(f"class {node.name}")
|
||||||
else:
|
|
||||||
parts.append(f"class {node.name}")
|
|
||||||
top_fns = [
|
top_fns = [
|
||||||
node.name for node in ast.iter_child_nodes(tree)
|
node.name for node in ast.iter_child_nodes(tree)
|
||||||
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
|
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
|
||||||
]
|
]
|
||||||
if top_fns:
|
if top_fns: parts.append(f"functions: {', '.join(top_fns)}")
|
||||||
parts.append(f"functions: {', '.join(top_fns)}")
|
|
||||||
return "\n".join(parts)
|
return "\n".join(parts)
|
||||||
|
|
||||||
def _summarise_toml(path: Path, content: str) -> str:
|
def _summarise_toml(path: Path, content: str) -> str:
|
||||||
@@ -168,16 +156,13 @@ _SUMMARISERS: dict[str, Callable[[Path, str], str]] = {
|
|||||||
|
|
||||||
def summarise_file(path: Path, content: str) -> str:
|
def summarise_file(path: Path, content: str) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
Return a compact markdown summary string for a single file.
|
Return a compact markdown summary string for a single file.
|
||||||
`content` is the already-read file text (or an error string).
|
`content` is the already-read file text (or an error string).
|
||||||
[C: tests/test_subagent_summarization.py:test_summarise_file_integration]
|
[C: tests/test_subagent_summarization.py:test_summarise_file_integration]
|
||||||
"""
|
"""
|
||||||
content_hash = get_file_hash(content)
|
content_hash = get_file_hash(content)
|
||||||
cached = _summary_cache.get_summary(str(path), content_hash)
|
cached = _summary_cache.get_summary(str(path), content_hash)
|
||||||
if cached:
|
if cached: return cached
|
||||||
return cached
|
|
||||||
suffix = path.suffix.lower() if hasattr(path, "suffix") else ""
|
suffix = path.suffix.lower() if hasattr(path, "suffix") else ""
|
||||||
fn = _SUMMARISERS.get(suffix, _summarise_generic)
|
fn = _SUMMARISERS.get(suffix, _summarise_generic)
|
||||||
try:
|
try:
|
||||||
@@ -185,7 +170,6 @@ def summarise_file(path: Path, content: str) -> str:
|
|||||||
# Smart AI Summarization
|
# Smart AI Summarization
|
||||||
is_code = suffix in [".py", ".ps1", ".js", ".ts", ".cpp", ".c", ".h", ".cs", ".go", ".rs", ".lua"]
|
is_code = suffix in [".py", ".ps1", ".js", ".ts", ".cpp", ".c", ".h", ".cs", ".go", ".rs", ".lua"]
|
||||||
try:
|
try:
|
||||||
from src import ai_client
|
|
||||||
smart_summary = ai_client.run_subagent_summarization(
|
smart_summary = ai_client.run_subagent_summarization(
|
||||||
file_path=str(path),
|
file_path=str(path),
|
||||||
content=content[:10000],
|
content=content[:10000],
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from typing import Optional, Dict
|
|||||||
|
|
||||||
def get_file_hash(content: str) -> str:
|
def get_file_hash(content: str) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Returns SHA256 hash of the content.
|
Returns SHA256 hash of the content.
|
||||||
[C: tests/test_summary_cache.py:test_get_file_hash, tests/test_summary_cache.py:test_summary_cache]
|
[C: tests/test_summary_cache.py:test_get_file_hash, tests/test_summary_cache.py:test_summary_cache]
|
||||||
"""
|
"""
|
||||||
@@ -15,8 +14,6 @@ def get_file_hash(content: str) -> str:
|
|||||||
|
|
||||||
class SummaryCache:
|
class SummaryCache:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
A hash-based cache for file summaries to avoid redundant processing.
|
A hash-based cache for file summaries to avoid redundant processing.
|
||||||
Invalidates when content hash changes.
|
Invalidates when content hash changes.
|
||||||
"""
|
"""
|
||||||
@@ -32,7 +29,6 @@ class SummaryCache:
|
|||||||
|
|
||||||
def load(self) -> None:
|
def load(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Loads cache from disk.
|
Loads cache from disk.
|
||||||
[C: src/tool_presets.py:ToolPresetManager._read_raw, src/workspace_manager.py:WorkspaceManager._load_file, tests/test_gui_phase3.py:test_create_track, tests/test_history_management.py:test_save_separation, tests/test_session_logging.py:test_open_session_creates_subdir_and_registry]
|
[C: src/tool_presets.py:ToolPresetManager._read_raw, src/workspace_manager.py:WorkspaceManager._load_file, tests/test_gui_phase3.py:test_create_track, tests/test_history_management.py:test_save_separation, tests/test_session_logging.py:test_open_session_creates_subdir_and_registry]
|
||||||
"""
|
"""
|
||||||
@@ -54,7 +50,6 @@ class SummaryCache:
|
|||||||
|
|
||||||
def get_summary(self, file_path: str, content_hash: str) -> Optional[str]:
|
def get_summary(self, file_path: str, content_hash: str) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Returns cached summary if hash matches, otherwise None.
|
Returns cached summary if hash matches, otherwise None.
|
||||||
[C: tests/test_summary_cache.py:test_summary_cache, tests/test_summary_cache.py:test_summary_cache_lru]
|
[C: tests/test_summary_cache.py:test_summary_cache, tests/test_summary_cache.py:test_summary_cache_lru]
|
||||||
"""
|
"""
|
||||||
@@ -68,7 +63,6 @@ class SummaryCache:
|
|||||||
|
|
||||||
def set_summary(self, file_path: str, content_hash: str, summary: str) -> None:
|
def set_summary(self, file_path: str, content_hash: str, summary: str) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Stores summary in cache and saves to disk.
|
Stores summary in cache and saves to disk.
|
||||||
[C: tests/test_summary_cache.py:test_summary_cache, tests/test_summary_cache.py:test_summary_cache_lru]
|
[C: tests/test_summary_cache.py:test_summary_cache, tests/test_summary_cache.py:test_summary_cache_lru]
|
||||||
"""
|
"""
|
||||||
@@ -87,7 +81,6 @@ class SummaryCache:
|
|||||||
|
|
||||||
def clear(self) -> None:
|
def clear(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Clears the cache both in-memory and on disk.
|
Clears the cache both in-memory and on disk.
|
||||||
[C: tests/conftest.py:reset_ai_client]
|
[C: tests/conftest.py:reset_ai_client]
|
||||||
"""
|
"""
|
||||||
|
|||||||
+1
-2
@@ -14,7 +14,7 @@ from contextlib import nullcontext
|
|||||||
from imgui_bundle import imgui, hello_imgui
|
from imgui_bundle import imgui, hello_imgui
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
import src.theme_nerv
|
from src import theme_nerv
|
||||||
|
|
||||||
from src import imgui_scopes as imscope
|
from src import imgui_scopes as imscope
|
||||||
from src.theme_nerv import DATA_GREEN
|
from src.theme_nerv import DATA_GREEN
|
||||||
@@ -213,7 +213,6 @@ def apply(palette_name: str) -> None:
|
|||||||
global _current_palette
|
global _current_palette
|
||||||
_current_palette = palette_name
|
_current_palette = palette_name
|
||||||
if palette_name == 'NERV':
|
if palette_name == 'NERV':
|
||||||
from src import theme_nerv
|
|
||||||
theme_nerv.apply_nerv()
|
theme_nerv.apply_nerv()
|
||||||
apply_syntax_palette(get_syntax_palette_for_theme(palette_name))
|
apply_syntax_palette(get_syntax_palette_for_theme(palette_name))
|
||||||
return
|
return
|
||||||
|
|||||||
+18
-18
@@ -124,12 +124,12 @@ class ThemeFile:
|
|||||||
|
|
||||||
def with_scope(self, scope: str) -> ThemeFile:
|
def with_scope(self, scope: str) -> ThemeFile:
|
||||||
return ThemeFile(
|
return ThemeFile(
|
||||||
name=self.name,
|
name = self.name,
|
||||||
palette=self.palette,
|
palette = self.palette,
|
||||||
syntax_palette=self.syntax_palette,
|
syntax_palette = self.syntax_palette,
|
||||||
source_path=self.source_path,
|
source_path = self.source_path,
|
||||||
scope=scope,
|
scope = scope,
|
||||||
description=self.description,
|
description = self.description,
|
||||||
)
|
)
|
||||||
|
|
||||||
def to_dict(self) -> dict[str, Any]:
|
def to_dict(self) -> dict[str, Any]:
|
||||||
@@ -152,12 +152,12 @@ class ThemeFile:
|
|||||||
f"must be one of {VALID_SYNTAX_PALETTES}"
|
f"must be one of {VALID_SYNTAX_PALETTES}"
|
||||||
)
|
)
|
||||||
return cls(
|
return cls(
|
||||||
name=name,
|
name = name,
|
||||||
palette=ThemePalette.from_dict(data["colors"]),
|
palette = ThemePalette.from_dict(data["colors"]),
|
||||||
syntax_palette=syntax_palette,
|
syntax_palette = syntax_palette,
|
||||||
source_path=source_path,
|
source_path = source_path,
|
||||||
scope=scope,
|
scope = scope,
|
||||||
description=str(data.get("description", "")),
|
description = str(data.get("description", "")),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -204,14 +204,14 @@ def load_themes_from_toml(path: Path, scope: str) -> dict[str, ThemeFile]:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"warning: failed to parse {path}: {e}", file=sys.stderr)
|
print(f"warning: failed to parse {path}: {e}", file=sys.stderr)
|
||||||
return out
|
return out
|
||||||
if not isinstance(data, dict):
|
|
||||||
return out
|
if not isinstance(data, dict): return out
|
||||||
|
|
||||||
themes_sec = data.get("themes", {})
|
themes_sec = data.get("themes", {})
|
||||||
if not isinstance(themes_sec, dict):
|
if not isinstance(themes_sec, dict): return out
|
||||||
return out
|
|
||||||
for name, theme_data in themes_sec.items():
|
for name, theme_data in themes_sec.items():
|
||||||
if not isinstance(theme_data, dict):
|
if not isinstance(theme_data, dict): continue
|
||||||
continue
|
|
||||||
try:
|
try:
|
||||||
theme = ThemeFile.from_dict(name, theme_data, source_path=path, scope=scope)
|
theme = ThemeFile.from_dict(name, theme_data, source_path=path, scope=scope)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ NERV_PALETTE = {
|
|||||||
|
|
||||||
def apply_nerv() -> None:
|
def apply_nerv() -> None:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Apply NERV theme with hard edges and specific palette.
|
Apply NERV theme with hard edges and specific palette.
|
||||||
[C: tests/test_theme_nerv.py:test_apply_nerv_sets_rounding_and_colors]
|
[C: tests/test_theme_nerv.py:test_apply_nerv_sets_rounding_and_colors]
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ class CRTFilter:
|
|||||||
"""
|
"""
|
||||||
[C: tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render]
|
[C: tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render]
|
||||||
"""
|
"""
|
||||||
if not self.enabled:
|
if not self.enabled: return
|
||||||
return
|
|
||||||
draw_list = imgui.get_foreground_draw_list()
|
draw_list = imgui.get_foreground_draw_list()
|
||||||
|
|
||||||
# 1. Enhanced Scanlines (Horizontal)
|
# 1. Enhanced Scanlines (Horizontal)
|
||||||
@@ -40,7 +39,6 @@ class CRTFilter:
|
|||||||
# Exponential alpha for smoother falloff
|
# Exponential alpha for smoother falloff
|
||||||
alpha = (i / v_steps) ** 3.0 * 0.25
|
alpha = (i / v_steps) ** 3.0 * 0.25
|
||||||
v_color = imgui.get_color_u32((0.0, 0.0, 0.0, alpha))
|
v_color = imgui.get_color_u32((0.0, 0.0, 0.0, alpha))
|
||||||
|
|
||||||
# Inset and rounding grow to simulate tube curvature
|
# Inset and rounding grow to simulate tube curvature
|
||||||
inset = (v_steps - i) * 4.5
|
inset = (v_steps - i) * 4.5
|
||||||
rounding = 60.0 + (v_steps - i) * 8.0
|
rounding = 60.0 + (v_steps - i) * 8.0
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ from src.models import ThinkingSegment
|
|||||||
|
|
||||||
def parse_thinking_trace(text: str) -> Tuple[List[ThinkingSegment], str]:
|
def parse_thinking_trace(text: str) -> Tuple[List[ThinkingSegment], str]:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
Parses thinking segments from text and returns (segments, response_content).
|
Parses thinking segments from text and returns (segments, response_content).
|
||||||
Support extraction of thinking traces from <thinking>...</thinking>, <thought>...</thought>,
|
Support extraction of thinking traces from <thinking>...</thinking>, <thought>...</thought>,
|
||||||
and blocks prefixed with Thinking:.
|
and blocks prefixed with Thinking:.
|
||||||
@@ -18,7 +16,6 @@ def parse_thinking_trace(text: str) -> Tuple[List[ThinkingSegment], str]:
|
|||||||
|
|
||||||
# 1. Extract <thinking> and <thought> tags
|
# 1. Extract <thinking> and <thought> tags
|
||||||
current_text = text
|
current_text = text
|
||||||
|
|
||||||
# Combined pattern for tags
|
# Combined pattern for tags
|
||||||
tag_pattern = re.compile(r'<(thinking|thought)>(.*?)</\1>', re.DOTALL | re.IGNORECASE)
|
tag_pattern = re.compile(r'<(thinking|thought)>(.*?)</\1>', re.DOTALL | re.IGNORECASE)
|
||||||
|
|
||||||
@@ -46,8 +43,7 @@ def parse_thinking_trace(text: str) -> Tuple[List[ThinkingSegment], str]:
|
|||||||
|
|
||||||
def replace_func(match):
|
def replace_func(match):
|
||||||
content = match.group(1).strip()
|
content = match.group(1).strip()
|
||||||
if content:
|
if content: found_segments.append(ThinkingSegment(content=content, marker="Thinking:"))
|
||||||
found_segments.append(ThinkingSegment(content=content, marker="Thinking:"))
|
|
||||||
return "\n\n"
|
return "\n\n"
|
||||||
|
|
||||||
res = thinking_colon_pattern.sub(replace_func, txt)
|
res = thinking_colon_pattern.sub(replace_func, txt)
|
||||||
@@ -55,5 +51,4 @@ def parse_thinking_trace(text: str) -> Tuple[List[ThinkingSegment], str]:
|
|||||||
|
|
||||||
colon_segments, final_remaining = extract_colon_blocks(remaining)
|
colon_segments, final_remaining = extract_colon_blocks(remaining)
|
||||||
segments.extend(colon_segments)
|
segments.extend(colon_segments)
|
||||||
|
|
||||||
return segments, final_remaining.strip()
|
return segments, final_remaining.strip()
|
||||||
+6
-12
@@ -51,19 +51,13 @@ class ToolBiasEngine:
|
|||||||
for cat_tools in preset.categories.values():
|
for cat_tools in preset.categories.values():
|
||||||
for t in cat_tools:
|
for t in cat_tools:
|
||||||
if not isinstance(t, Tool): continue
|
if not isinstance(t, Tool): continue
|
||||||
if t.weight >= 5:
|
if t.weight >= 5: preferred.append(f"{t.name} [HIGH PRIORITY]")
|
||||||
preferred.append(f"{t.name} [HIGH PRIORITY]")
|
elif t.weight == 4: preferred.append(f"{t.name} [PREFERRED]")
|
||||||
elif t.weight == 4:
|
elif t.weight == 2: low_priority.append(f"{t.name} [NOT RECOMMENDED]")
|
||||||
preferred.append(f"{t.name} [PREFERRED]")
|
elif t.weight <= 1: low_priority.append(f"{t.name} [LOW PRIORITY]")
|
||||||
elif t.weight == 2:
|
|
||||||
low_priority.append(f"{t.name} [NOT RECOMMENDED]")
|
|
||||||
elif t.weight <= 1:
|
|
||||||
low_priority.append(f"{t.name} [LOW PRIORITY]")
|
|
||||||
|
|
||||||
if preferred:
|
if preferred: lines.append(f"Preferred tools: {', '.join(preferred)}.")
|
||||||
lines.append(f"Preferred tools: {', '.join(preferred)}.")
|
if low_priority: lines.append(f"Low-priority tools: {', '.join(low_priority)}.")
|
||||||
if low_priority:
|
|
||||||
lines.append(f"Low-priority tools: {', '.join(low_priority)}.")
|
|
||||||
|
|
||||||
if global_bias.category_multipliers:
|
if global_bias.category_multipliers:
|
||||||
lines.append("Category focus multipliers:")
|
lines.append("Category focus multipliers:")
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ class ToolPresetManager:
|
|||||||
|
|
||||||
def load_all(self) -> Dict[str, ToolPreset]:
|
def load_all(self) -> Dict[str, ToolPreset]:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Backward compatibility for load_all().
|
Backward compatibility for load_all().
|
||||||
[C: tests/test_persona_manager.py:test_delete_persona, tests/test_persona_manager.py:test_load_all_merged, tests/test_persona_manager.py:test_save_persona, tests/test_preset_manager.py:test_delete_preset, tests/test_preset_manager.py:test_load_all_merged, tests/test_preset_manager.py:test_save_preset_global, tests/test_preset_manager.py:test_save_preset_project, tests/test_presets.py:TestPresetManager.test_delete_preset, tests/test_presets.py:TestPresetManager.test_project_overwrites_global, tests/test_presets.py:TestPresetManager.test_save_and_load_global, tests/test_presets.py:TestPresetManager.test_save_and_load_project]
|
[C: tests/test_persona_manager.py:test_delete_persona, tests/test_persona_manager.py:test_load_all_merged, tests/test_persona_manager.py:test_save_persona, tests/test_preset_manager.py:test_delete_preset, tests/test_preset_manager.py:test_load_all_merged, tests/test_preset_manager.py:test_save_preset_global, tests/test_preset_manager.py:test_save_preset_project, tests/test_presets.py:TestPresetManager.test_delete_preset, tests/test_presets.py:TestPresetManager.test_project_overwrites_global, tests/test_presets.py:TestPresetManager.test_save_and_load_global, tests/test_presets.py:TestPresetManager.test_save_and_load_project]
|
||||||
"""
|
"""
|
||||||
|
|||||||
+28
-28
@@ -18,11 +18,11 @@ def get_vendor_state(app) -> list[VendorMetric]:
|
|||||||
"""
|
"""
|
||||||
out: list[VendorMetric] = []
|
out: list[VendorMetric] = []
|
||||||
out.append(VendorMetric(
|
out.append(VendorMetric(
|
||||||
key="provider_model",
|
key = "provider_model",
|
||||||
label="Provider / Model",
|
label = "Provider / Model",
|
||||||
value=f"{app.current_provider} / {app.current_model}",
|
value = f"{app.current_provider} / {app.current_model}",
|
||||||
state="info",
|
state = "info",
|
||||||
tooltip="The vendor and model that will handle the next request."
|
tooltip = "The vendor and model that will handle the next request."
|
||||||
))
|
))
|
||||||
ctrl = getattr(app, "controller", None)
|
ctrl = getattr(app, "controller", None)
|
||||||
tt = getattr(ctrl, "token_tracker", None) if ctrl else None
|
tt = getattr(ctrl, "token_tracker", None) if ctrl else None
|
||||||
@@ -30,16 +30,16 @@ def get_vendor_state(app) -> list[VendorMetric]:
|
|||||||
pct = 100.0 * getattr(tt, "used", 0) / tt.limit
|
pct = 100.0 * getattr(tt, "used", 0) / tt.limit
|
||||||
state = "warn" if pct > 75 else "ok"
|
state = "warn" if pct > 75 else "ok"
|
||||||
out.append(VendorMetric(
|
out.append(VendorMetric(
|
||||||
key="context_window",
|
key = "context_window",
|
||||||
label="Context Window",
|
label = "Context Window",
|
||||||
value=f"{tt.used:,} / {tt.limit:,} ({pct:.0f}%)",
|
value = f"{tt.used:,} / {tt.limit:,} ({pct:.0f}%)",
|
||||||
state=state,
|
state = state,
|
||||||
tooltip="Used vs total context window for the current session."
|
tooltip = "Used vs total context window for the current session."
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
out.append(VendorMetric(
|
out.append(VendorMetric(
|
||||||
key="context_window", label="Context Window", value="—", state="info",
|
key = "context_window", label="Context Window", value="—", state="info",
|
||||||
tooltip="No token tracker attached for the current provider."
|
tooltip = "No token tracker attached for the current provider."
|
||||||
))
|
))
|
||||||
if tt is not None:
|
if tt is not None:
|
||||||
hits = getattr(tt, "cache_hits", 0)
|
hits = getattr(tt, "cache_hits", 0)
|
||||||
@@ -47,35 +47,35 @@ def get_vendor_state(app) -> list[VendorMetric]:
|
|||||||
total = hits + miss
|
total = hits + miss
|
||||||
rate = (100.0 * hits / total) if total else 0.0
|
rate = (100.0 * hits / total) if total else 0.0
|
||||||
out.append(VendorMetric(
|
out.append(VendorMetric(
|
||||||
key="cache", label="Cache Hit Rate",
|
key = "cache", label="Cache Hit Rate",
|
||||||
value=f"{rate:.0f}% ({hits:,}/{total:,})",
|
value = f"{rate:.0f}% ({hits:,}/{total:,})",
|
||||||
state="ok" if rate > 50 else "info",
|
state = "ok" if rate > 50 else "info",
|
||||||
tooltip="Server-side prompt cache hit rate for the current session."
|
tooltip = "Server-side prompt cache hit rate for the current session."
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
out.append(VendorMetric(
|
out.append(VendorMetric(
|
||||||
key="cache", label="Cache Hit Rate", value="—", state="info",
|
key = "cache", label="Cache Hit Rate", value="—", state="info",
|
||||||
tooltip="No token tracker attached for the current provider."
|
tooltip = "No token tracker attached for the current provider."
|
||||||
))
|
))
|
||||||
quota = (getattr(ctrl, "vendor_quota", {}) or {}) if ctrl else {}
|
quota = (getattr(ctrl, "vendor_quota", {}) or {}) if ctrl else {}
|
||||||
pct_left = quota.get("remaining_pct")
|
pct_left = quota.get("remaining_pct")
|
||||||
if pct_left is None:
|
if pct_left is None:
|
||||||
out.append(VendorMetric(
|
out.append(VendorMetric(
|
||||||
key="quota", label="Vendor Quota", value="—", state="info",
|
key = "quota", label="Vendor Quota", value="—", state="info",
|
||||||
tooltip="Vendor did not report quota for the current billing period."
|
tooltip = "Vendor did not report quota for the current billing period."
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
out.append(VendorMetric(
|
out.append(VendorMetric(
|
||||||
key="quota", label="Vendor Quota",
|
key = "quota", label="Vendor Quota",
|
||||||
value=f"{pct_left}% remaining",
|
value = f"{pct_left}% remaining",
|
||||||
state="ok" if pct_left > 25 else "warn",
|
state = "ok" if pct_left > 25 else "warn",
|
||||||
tooltip="Approximate quota remaining for the current billing period."
|
tooltip = "Approximate quota remaining for the current billing period."
|
||||||
))
|
))
|
||||||
err = getattr(ctrl, "last_error", None) if ctrl else None
|
err = getattr(ctrl, "last_error", None) if ctrl else None
|
||||||
out.append(VendorMetric(
|
out.append(VendorMetric(
|
||||||
key="last_error", label="Last Error",
|
key = "last_error", label="Last Error",
|
||||||
value=err.get("class", "none") if err else "none",
|
value = err.get("class", "none") if err else "none",
|
||||||
state="error" if err else "ok",
|
state = "error" if err else "ok",
|
||||||
tooltip=err.get("message", "No error since session start.") if err else "No error since session start."
|
tooltip = err.get("message", "No error since session start.") if err else "No error since session start."
|
||||||
))
|
))
|
||||||
return out
|
return out
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ class WorkspaceManager:
|
|||||||
|
|
||||||
def load_all_profiles(self) -> Dict[str, WorkspaceProfile]:
|
def load_all_profiles(self) -> Dict[str, WorkspaceProfile]:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Merges global and project profiles into a single dictionary.
|
Merges global and project profiles into a single dictionary.
|
||||||
[C: tests/test_workspace_manager.py:test_delete_profile, tests/test_workspace_manager.py:test_load_all_profiles_merged, tests/test_workspace_manager.py:test_save_profile_global_and_project]
|
[C: tests/test_workspace_manager.py:test_delete_profile, tests/test_workspace_manager.py:test_load_all_profiles_merged, tests/test_workspace_manager.py:test_save_profile_global_and_project]
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user