perf(dashboard): keep tools in focused analytics usage (#18511)
This commit is contained in:
parent
c1639322c2
commit
e3ce092f8d
|
|
@ -196,17 +196,23 @@ class InsightsEngine:
|
|||
"top_sessions": top_sessions,
|
||||
}
|
||||
|
||||
def get_skill_breakdown(self, days: int = 30, source: str = None) -> Dict[str, Any]:
|
||||
"""Return only the skills section without running a full generate().
|
||||
def get_usage_breakdown(self, days: int = 30, source: str = None) -> Dict[str, Any]:
|
||||
"""Return the analytics-usage payload without running a full generate().
|
||||
|
||||
Uses the instr()-prefiltered _get_skill_usage query so only messages
|
||||
that reference skill_view or skill_manage are loaded from SQLite,
|
||||
avoiding the cost of _get_sessions, _get_tool_usage, and the other
|
||||
compute passes that generate() performs.
|
||||
that reference skill_view or skill_manage are loaded from SQLite, while
|
||||
still preserving the per-tool breakdown used by the dashboard route.
|
||||
"""
|
||||
cutoff = time.time() - (days * 86400)
|
||||
tool_usage = self._get_tool_usage(cutoff, source)
|
||||
skill_usage = self._get_skill_usage(cutoff, source)
|
||||
return self._compute_skill_breakdown(skill_usage)
|
||||
return {
|
||||
"tools": self._compute_tool_breakdown(tool_usage),
|
||||
"skills": self._compute_skill_breakdown(skill_usage),
|
||||
}
|
||||
|
||||
def get_skill_breakdown(self, days: int = 30, source: str = None) -> Dict[str, Any]:
|
||||
return self.get_usage_breakdown(days=days, source=source)["skills"]
|
||||
|
||||
# =========================================================================
|
||||
# Data gathering (SQL queries)
|
||||
|
|
|
|||
|
|
@ -14073,7 +14073,7 @@ def _get_usage_analytics(days: int = 30, profile: Optional[str] = None):
|
|||
FROM sessions WHERE started_at > ?
|
||||
""", (cutoff,))
|
||||
totals = dict(cur3.fetchone())
|
||||
skills = InsightsEngine(db).get_skill_breakdown(days=days)
|
||||
usage = InsightsEngine(db).get_usage_breakdown(days=days)
|
||||
|
||||
return {
|
||||
"daily": daily,
|
||||
|
|
@ -14083,10 +14083,10 @@ def _get_usage_analytics(days: int = 30, profile: Optional[str] = None):
|
|||
"by_task": _aux_task_summary(aux_rows),
|
||||
"totals": totals,
|
||||
"period_days": days,
|
||||
"skills": skills,
|
||||
"skills": usage["skills"],
|
||||
# Per-tool-name call counts (already computed by InsightsEngine);
|
||||
# the desktop Capabilities page aggregates these per toolset.
|
||||
"tools": insights_report.get("tools", []),
|
||||
"tools": usage["tools"],
|
||||
}
|
||||
finally:
|
||||
db.close()
|
||||
|
|
|
|||
|
|
@ -479,6 +479,13 @@ class TestInsightsPopulated:
|
|||
focused = engine.get_skill_breakdown(days=30)
|
||||
assert focused == full["skills"]
|
||||
|
||||
def test_get_usage_breakdown_matches_full_generate(self, populated_db):
|
||||
engine = InsightsEngine(populated_db)
|
||||
full = engine.generate(days=30)
|
||||
focused = engine.get_usage_breakdown(days=30)
|
||||
assert focused["skills"] == full["skills"]
|
||||
assert focused["tools"] == full["tools"]
|
||||
|
||||
def test_get_skill_breakdown_respects_source_filter(self, populated_db):
|
||||
engine = InsightsEngine(populated_db)
|
||||
# Only s1 (cli) has skill_view "github-pr-workflow"
|
||||
|
|
|
|||
|
|
@ -2226,11 +2226,34 @@ class TestNewEndpoints:
|
|||
"""get_usage_analytics must call get_skill_breakdown, not generate()."""
|
||||
from unittest.mock import patch
|
||||
from agent.insights import InsightsEngine
|
||||
from hermes_state import SessionDB
|
||||
|
||||
db = SessionDB()
|
||||
try:
|
||||
db.create_session(
|
||||
session_id="usage-tools-test",
|
||||
source="cli",
|
||||
model="anthropic/claude-sonnet-4",
|
||||
)
|
||||
db.update_token_counts(
|
||||
"usage-tools-test",
|
||||
input_tokens=10,
|
||||
output_tokens=5,
|
||||
)
|
||||
db.append_message(
|
||||
"usage-tools-test",
|
||||
role="tool",
|
||||
content="read output",
|
||||
tool_name="read_file",
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
with patch.object(InsightsEngine, "generate") as mock_generate:
|
||||
resp = self.client.get("/api/analytics/usage?days=7")
|
||||
assert resp.status_code == 200
|
||||
mock_generate.assert_not_called()
|
||||
assert any(tool["tool"] == "read_file" for tool in resp.json()["tools"])
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Model context length: normalize/denormalize + /api/model/info
|
||||
|
|
|
|||
Loading…
Reference in New Issue