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, _):
|
||||
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.
|
||||
[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:
|
||||
"""
|
||||
|
||||
Serialise a disc entry dict -> stored string.
|
||||
[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]:
|
||||
"""
|
||||
|
||||
Parse a stored string back to a disc entry dict.
|
||||
[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 src import ai_client
|
||||
from src import models
|
||||
from src import mcp_client
|
||||
|
||||
from src.file_cache import ASTParser
|
||||
|
||||
|
||||
_SENTENCE_TRANSFORMERS = None
|
||||
_GOOGLE_GENAI = None
|
||||
_CHROMADB = None
|
||||
@@ -74,15 +78,14 @@ class GeminiEmbeddingProvider(BaseEmbeddingProvider):
|
||||
if google_module is None:
|
||||
raise ImportError("google-genai is not installed")
|
||||
genai_pkg, types = google_module
|
||||
from src import ai_client
|
||||
ai_client._ensure_gemini_client()
|
||||
client = ai_client._gemini_client
|
||||
if not client:
|
||||
raise ValueError("Gemini client not initialized")
|
||||
res = client.models.embed_content(
|
||||
model=self.model_name,
|
||||
contents=texts,
|
||||
config=types.EmbedContentConfig(task_type="RETRIEVAL_DOCUMENT")
|
||||
model = self.model_name,
|
||||
contents = texts,
|
||||
config = types.EmbedContentConfig(task_type="RETRIEVAL_DOCUMENT")
|
||||
)
|
||||
return [e.values for e in res.embeddings]
|
||||
|
||||
@@ -94,8 +97,7 @@ class RAGEngine:
|
||||
self.collection = None
|
||||
self.embedding_provider = None
|
||||
|
||||
if not self.config.enabled:
|
||||
return
|
||||
if not self.config.enabled: return
|
||||
|
||||
self._init_embedding_provider()
|
||||
self._init_vector_store()
|
||||
@@ -143,10 +145,10 @@ class RAGEngine:
|
||||
return
|
||||
embeddings = self.embedding_provider.embed(texts)
|
||||
self.collection.upsert(
|
||||
ids=ids,
|
||||
embeddings=embeddings,
|
||||
documents=texts,
|
||||
metadatas=metadatas
|
||||
ids = ids,
|
||||
embeddings = embeddings,
|
||||
documents = texts,
|
||||
metadatas = metadatas
|
||||
)
|
||||
|
||||
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]:
|
||||
"""AST-aware chunking for Python code."""
|
||||
try:
|
||||
from src.file_cache import ASTParser
|
||||
parser = ASTParser("python")
|
||||
tree = parser.parse(content)
|
||||
chunks = []
|
||||
@@ -246,17 +247,14 @@ class RAGEngine:
|
||||
"""
|
||||
[C: tests/mock_concurrent_mma.py:main, tests/test_rag_engine.py:test_rag_engine_chroma]
|
||||
"""
|
||||
if not self.config.enabled:
|
||||
return []
|
||||
if self.config.vector_store.provider == 'mcp':
|
||||
return self._search_mcp(query, top_k)
|
||||
if self.collection == "mock":
|
||||
return []
|
||||
if not self.config.enabled: return []
|
||||
if self.config.vector_store.provider == 'mcp': return self._search_mcp(query, top_k)
|
||||
if self.collection == "mock": return []
|
||||
|
||||
query_embedding = self.embedding_provider.embed([query])[0]
|
||||
results = self.collection.query(
|
||||
query_embeddings=[query_embedding],
|
||||
n_results=top_k
|
||||
query_embeddings = [query_embedding],
|
||||
n_results = top_k
|
||||
)
|
||||
|
||||
ret = []
|
||||
|
||||
@@ -90,7 +90,6 @@ def open_session(label: Optional[str] = None) -> None:
|
||||
|
||||
def close_session() -> None:
|
||||
"""
|
||||
|
||||
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]
|
||||
"""
|
||||
|
||||
+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
|
||||
steps = int(shadow_size)
|
||||
if steps <= 0:
|
||||
return
|
||||
if steps <= 0: return
|
||||
|
||||
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)
|
||||
# Apply an easing function (e.g., cubic) for a smoother shadow falloff
|
||||
current_alpha = current_alpha * (1.0 - (i / steps)**2)
|
||||
|
||||
if current_alpha <= 0.01:
|
||||
continue
|
||||
|
||||
expand = float(i)
|
||||
|
||||
c_min = imgui.ImVec2(p_min.x - expand, p_min.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:
|
||||
process = subprocess.Popen(
|
||||
[exe, "-NoProfile", "-NonInteractive", "-Command", full_script],
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
|
||||
cwd=base_dir, env=_build_subprocess_env(),
|
||||
stdin = subprocess.DEVNULL,
|
||||
stdout = subprocess.PIPE, stderr=subprocess.PIPE, text=True,
|
||||
cwd = base_dir, env=_build_subprocess_env(),
|
||||
)
|
||||
stdout, stderr = process.communicate(timeout=TIMEOUT_SECONDS)
|
||||
parts: list[str] = []
|
||||
|
||||
+6
-22
@@ -1,16 +1,5 @@
|
||||
# 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.
|
||||
|
||||
For each file, extracts structural information:
|
||||
@@ -28,6 +17,8 @@ import re
|
||||
from pathlib import Path
|
||||
from typing import Callable, Any
|
||||
|
||||
from src import ai_client
|
||||
|
||||
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)
|
||||
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))
|
||||
]
|
||||
if methods:
|
||||
parts.append(f"class {node.name}: {', '.join(methods)}")
|
||||
else:
|
||||
parts.append(f"class {node.name}")
|
||||
if methods: parts.append(f"class {node.name}: {', '.join(methods)}")
|
||||
else: parts.append(f"class {node.name}")
|
||||
top_fns = [
|
||||
node.name for node in ast.iter_child_nodes(tree)
|
||||
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
|
||||
]
|
||||
if top_fns:
|
||||
parts.append(f"functions: {', '.join(top_fns)}")
|
||||
if top_fns: parts.append(f"functions: {', '.join(top_fns)}")
|
||||
return "\n".join(parts)
|
||||
|
||||
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:
|
||||
"""
|
||||
|
||||
|
||||
Return a compact markdown summary string for a single file.
|
||||
`content` is the already-read file text (or an error string).
|
||||
[C: tests/test_subagent_summarization.py:test_summarise_file_integration]
|
||||
"""
|
||||
content_hash = get_file_hash(content)
|
||||
cached = _summary_cache.get_summary(str(path), content_hash)
|
||||
if cached:
|
||||
return cached
|
||||
if cached: return cached
|
||||
suffix = path.suffix.lower() if hasattr(path, "suffix") else ""
|
||||
fn = _SUMMARISERS.get(suffix, _summarise_generic)
|
||||
try:
|
||||
@@ -185,7 +170,6 @@ def summarise_file(path: Path, content: str) -> str:
|
||||
# Smart AI Summarization
|
||||
is_code = suffix in [".py", ".ps1", ".js", ".ts", ".cpp", ".c", ".h", ".cs", ".go", ".rs", ".lua"]
|
||||
try:
|
||||
from src import ai_client
|
||||
smart_summary = ai_client.run_subagent_summarization(
|
||||
file_path=str(path),
|
||||
content=content[:10000],
|
||||
|
||||
@@ -7,7 +7,6 @@ from typing import Optional, Dict
|
||||
|
||||
def get_file_hash(content: str) -> str:
|
||||
"""
|
||||
|
||||
Returns SHA256 hash of the content.
|
||||
[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:
|
||||
"""
|
||||
|
||||
|
||||
A hash-based cache for file summaries to avoid redundant processing.
|
||||
Invalidates when content hash changes.
|
||||
"""
|
||||
@@ -32,7 +29,6 @@ class SummaryCache:
|
||||
|
||||
def load(self) -> None:
|
||||
"""
|
||||
|
||||
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]
|
||||
"""
|
||||
@@ -54,7 +50,6 @@ class SummaryCache:
|
||||
|
||||
def get_summary(self, file_path: str, content_hash: str) -> Optional[str]:
|
||||
"""
|
||||
|
||||
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]
|
||||
"""
|
||||
@@ -68,7 +63,6 @@ class SummaryCache:
|
||||
|
||||
def set_summary(self, file_path: str, content_hash: str, summary: str) -> None:
|
||||
"""
|
||||
|
||||
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]
|
||||
"""
|
||||
@@ -87,7 +81,6 @@ class SummaryCache:
|
||||
|
||||
def clear(self) -> None:
|
||||
"""
|
||||
|
||||
Clears the cache both in-memory and on disk.
|
||||
[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 typing import Any, Optional
|
||||
|
||||
import src.theme_nerv
|
||||
from src import theme_nerv
|
||||
|
||||
from src import imgui_scopes as imscope
|
||||
from src.theme_nerv import DATA_GREEN
|
||||
@@ -213,7 +213,6 @@ def apply(palette_name: str) -> None:
|
||||
global _current_palette
|
||||
_current_palette = palette_name
|
||||
if palette_name == 'NERV':
|
||||
from src import theme_nerv
|
||||
theme_nerv.apply_nerv()
|
||||
apply_syntax_palette(get_syntax_palette_for_theme(palette_name))
|
||||
return
|
||||
|
||||
+18
-18
@@ -124,12 +124,12 @@ class ThemeFile:
|
||||
|
||||
def with_scope(self, scope: str) -> ThemeFile:
|
||||
return ThemeFile(
|
||||
name=self.name,
|
||||
palette=self.palette,
|
||||
syntax_palette=self.syntax_palette,
|
||||
source_path=self.source_path,
|
||||
scope=scope,
|
||||
description=self.description,
|
||||
name = self.name,
|
||||
palette = self.palette,
|
||||
syntax_palette = self.syntax_palette,
|
||||
source_path = self.source_path,
|
||||
scope = scope,
|
||||
description = self.description,
|
||||
)
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
@@ -152,12 +152,12 @@ class ThemeFile:
|
||||
f"must be one of {VALID_SYNTAX_PALETTES}"
|
||||
)
|
||||
return cls(
|
||||
name=name,
|
||||
palette=ThemePalette.from_dict(data["colors"]),
|
||||
syntax_palette=syntax_palette,
|
||||
source_path=source_path,
|
||||
scope=scope,
|
||||
description=str(data.get("description", "")),
|
||||
name = name,
|
||||
palette = ThemePalette.from_dict(data["colors"]),
|
||||
syntax_palette = syntax_palette,
|
||||
source_path = source_path,
|
||||
scope = scope,
|
||||
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:
|
||||
print(f"warning: failed to parse {path}: {e}", file=sys.stderr)
|
||||
return out
|
||||
if not isinstance(data, dict):
|
||||
return out
|
||||
|
||||
if not isinstance(data, dict): return out
|
||||
|
||||
themes_sec = data.get("themes", {})
|
||||
if not isinstance(themes_sec, dict):
|
||||
return out
|
||||
if not isinstance(themes_sec, dict): return out
|
||||
|
||||
for name, theme_data in themes_sec.items():
|
||||
if not isinstance(theme_data, dict):
|
||||
continue
|
||||
if not isinstance(theme_data, dict): continue
|
||||
try:
|
||||
theme = ThemeFile.from_dict(name, theme_data, source_path=path, scope=scope)
|
||||
except ValueError as e:
|
||||
|
||||
@@ -64,7 +64,6 @@ NERV_PALETTE = {
|
||||
|
||||
def apply_nerv() -> None:
|
||||
"""
|
||||
|
||||
Apply NERV theme with hard edges and specific palette.
|
||||
[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]
|
||||
"""
|
||||
if not self.enabled:
|
||||
return
|
||||
if not self.enabled: return
|
||||
draw_list = imgui.get_foreground_draw_list()
|
||||
|
||||
# 1. Enhanced Scanlines (Horizontal)
|
||||
@@ -40,7 +39,6 @@ class CRTFilter:
|
||||
# Exponential alpha for smoother falloff
|
||||
alpha = (i / v_steps) ** 3.0 * 0.25
|
||||
v_color = imgui.get_color_u32((0.0, 0.0, 0.0, alpha))
|
||||
|
||||
# Inset and rounding grow to simulate tube curvature
|
||||
inset = (v_steps - i) * 4.5
|
||||
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]:
|
||||
"""
|
||||
|
||||
|
||||
Parses thinking segments from text and returns (segments, response_content).
|
||||
Support extraction of thinking traces from <thinking>...</thinking>, <thought>...</thought>,
|
||||
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
|
||||
current_text = text
|
||||
|
||||
# Combined pattern for tags
|
||||
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):
|
||||
content = match.group(1).strip()
|
||||
if content:
|
||||
found_segments.append(ThinkingSegment(content=content, marker="Thinking:"))
|
||||
if content: found_segments.append(ThinkingSegment(content=content, marker="Thinking:"))
|
||||
return "\n\n"
|
||||
|
||||
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)
|
||||
segments.extend(colon_segments)
|
||||
|
||||
return segments, final_remaining.strip()
|
||||
+6
-12
@@ -51,19 +51,13 @@ class ToolBiasEngine:
|
||||
for cat_tools in preset.categories.values():
|
||||
for t in cat_tools:
|
||||
if not isinstance(t, Tool): continue
|
||||
if t.weight >= 5:
|
||||
preferred.append(f"{t.name} [HIGH PRIORITY]")
|
||||
elif t.weight == 4:
|
||||
preferred.append(f"{t.name} [PREFERRED]")
|
||||
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 t.weight >= 5: preferred.append(f"{t.name} [HIGH PRIORITY]")
|
||||
elif t.weight == 4: preferred.append(f"{t.name} [PREFERRED]")
|
||||
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:
|
||||
lines.append(f"Preferred tools: {', '.join(preferred)}.")
|
||||
if low_priority:
|
||||
lines.append(f"Low-priority tools: {', '.join(low_priority)}.")
|
||||
if preferred: lines.append(f"Preferred tools: {', '.join(preferred)}.")
|
||||
if low_priority: lines.append(f"Low-priority tools: {', '.join(low_priority)}.")
|
||||
|
||||
if global_bias.category_multipliers:
|
||||
lines.append("Category focus multipliers:")
|
||||
|
||||
@@ -62,7 +62,6 @@ class ToolPresetManager:
|
||||
|
||||
def load_all(self) -> Dict[str, ToolPreset]:
|
||||
"""
|
||||
|
||||
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]
|
||||
"""
|
||||
|
||||
+28
-28
@@ -18,11 +18,11 @@ def get_vendor_state(app) -> list[VendorMetric]:
|
||||
"""
|
||||
out: list[VendorMetric] = []
|
||||
out.append(VendorMetric(
|
||||
key="provider_model",
|
||||
label="Provider / Model",
|
||||
value=f"{app.current_provider} / {app.current_model}",
|
||||
state="info",
|
||||
tooltip="The vendor and model that will handle the next request."
|
||||
key = "provider_model",
|
||||
label = "Provider / Model",
|
||||
value = f"{app.current_provider} / {app.current_model}",
|
||||
state = "info",
|
||||
tooltip = "The vendor and model that will handle the next request."
|
||||
))
|
||||
ctrl = getattr(app, "controller", 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
|
||||
state = "warn" if pct > 75 else "ok"
|
||||
out.append(VendorMetric(
|
||||
key="context_window",
|
||||
label="Context Window",
|
||||
value=f"{tt.used:,} / {tt.limit:,} ({pct:.0f}%)",
|
||||
state=state,
|
||||
tooltip="Used vs total context window for the current session."
|
||||
key = "context_window",
|
||||
label = "Context Window",
|
||||
value = f"{tt.used:,} / {tt.limit:,} ({pct:.0f}%)",
|
||||
state = state,
|
||||
tooltip = "Used vs total context window for the current session."
|
||||
))
|
||||
else:
|
||||
out.append(VendorMetric(
|
||||
key="context_window", label="Context Window", value="—", state="info",
|
||||
tooltip="No token tracker attached for the current provider."
|
||||
key = "context_window", label="Context Window", value="—", state="info",
|
||||
tooltip = "No token tracker attached for the current provider."
|
||||
))
|
||||
if tt is not None:
|
||||
hits = getattr(tt, "cache_hits", 0)
|
||||
@@ -47,35 +47,35 @@ def get_vendor_state(app) -> list[VendorMetric]:
|
||||
total = hits + miss
|
||||
rate = (100.0 * hits / total) if total else 0.0
|
||||
out.append(VendorMetric(
|
||||
key="cache", label="Cache Hit Rate",
|
||||
value=f"{rate:.0f}% ({hits:,}/{total:,})",
|
||||
state="ok" if rate > 50 else "info",
|
||||
tooltip="Server-side prompt cache hit rate for the current session."
|
||||
key = "cache", label="Cache Hit Rate",
|
||||
value = f"{rate:.0f}% ({hits:,}/{total:,})",
|
||||
state = "ok" if rate > 50 else "info",
|
||||
tooltip = "Server-side prompt cache hit rate for the current session."
|
||||
))
|
||||
else:
|
||||
out.append(VendorMetric(
|
||||
key="cache", label="Cache Hit Rate", value="—", state="info",
|
||||
tooltip="No token tracker attached for the current provider."
|
||||
key = "cache", label="Cache Hit Rate", value="—", state="info",
|
||||
tooltip = "No token tracker attached for the current provider."
|
||||
))
|
||||
quota = (getattr(ctrl, "vendor_quota", {}) or {}) if ctrl else {}
|
||||
pct_left = quota.get("remaining_pct")
|
||||
if pct_left is None:
|
||||
out.append(VendorMetric(
|
||||
key="quota", label="Vendor Quota", value="—", state="info",
|
||||
tooltip="Vendor did not report quota for the current billing period."
|
||||
key = "quota", label="Vendor Quota", value="—", state="info",
|
||||
tooltip = "Vendor did not report quota for the current billing period."
|
||||
))
|
||||
else:
|
||||
out.append(VendorMetric(
|
||||
key="quota", label="Vendor Quota",
|
||||
value=f"{pct_left}% remaining",
|
||||
state="ok" if pct_left > 25 else "warn",
|
||||
tooltip="Approximate quota remaining for the current billing period."
|
||||
key = "quota", label="Vendor Quota",
|
||||
value = f"{pct_left}% remaining",
|
||||
state = "ok" if pct_left > 25 else "warn",
|
||||
tooltip = "Approximate quota remaining for the current billing period."
|
||||
))
|
||||
err = getattr(ctrl, "last_error", None) if ctrl else None
|
||||
out.append(VendorMetric(
|
||||
key="last_error", label="Last Error",
|
||||
value=err.get("class", "none") if err else "none",
|
||||
state="error" if err else "ok",
|
||||
tooltip=err.get("message", "No error since session start.") if err else "No error since session start."
|
||||
key = "last_error", label="Last Error",
|
||||
value = err.get("class", "none") if err else "none",
|
||||
state = "error" if err else "ok",
|
||||
tooltip = err.get("message", "No error since session start.") if err else "No error since session start."
|
||||
))
|
||||
return out
|
||||
|
||||
@@ -29,7 +29,6 @@ class WorkspaceManager:
|
||||
|
||||
def load_all_profiles(self) -> Dict[str, WorkspaceProfile]:
|
||||
"""
|
||||
|
||||
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]
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user