feat: tier source badge + VM resource suggestions

- Tier badge (#9): addMessage() gains source param; sourceBadge() maps source string to KB/GROQ/CLAUDE/OLLAMA pill rendered after typing finishes; sendMessage() passes data.source through; CSS badges styled with domain colors
- VM suggestions (#10): vm_suggestions intent queries 24h avg CPU+MEM per agent, flags hosts with >80% avg CPU, <5% CPU on 2GB+ RAM, >85% avg MEM, or <25% MEM on 2GB+ RAM; 8 KB intents; say "VM resource suggestions" or "optimize VMs"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 02:38:24 +00:00
parent b024e51f3d
commit bde8909490
2 changed files with 76 additions and 3 deletions
+49 -1
View File
@@ -1647,6 +1647,54 @@ if (!$reply) {
if ($matched && $matched['action'] === 'action') {
switch ($matched['intent']) {
case 'vm_suggestions': {
$rows = JarvisDB::query(
"SELECT a.hostname, a.agent_type,
ROUND(AVG(CAST(JSON_EXTRACT(m.metric_data,'$.cpu_percent') AS DECIMAL(5,1))),1) as avg_cpu,
ROUND(AVG(CAST(JSON_EXTRACT(m.metric_data,'$.memory.percent') AS DECIMAL(5,1))),1) as avg_mem,
ROUND(MAX(CAST(JSON_EXTRACT(m.metric_data,'$.memory.total_mb') AS UNSIGNED))/1024,1) as ram_gb,
COUNT(*) as samples
FROM agent_metrics m
JOIN registered_agents a ON a.agent_id = m.agent_id
WHERE m.recorded_at > DATE_SUB(NOW(), INTERVAL 24 HOUR)
AND a.agent_type IN ('linux','proxmox')
GROUP BY a.hostname, a.agent_type
HAVING samples > 20
ORDER BY avg_mem DESC"
) ?? [];
$suggestions = [];
foreach ($rows as $r) {
$cpu = (float)($r['avg_cpu'] ?? 0);
$mem = (float)($r['avg_mem'] ?? 0);
$ram = (float)($r['ram_gb'] ?? 0);
$host = $r['hostname'];
if (!$ram) continue;
// High CPU
if ($cpu >= 80)
$suggestions[] = "{$host} is averaging {$cpu}% CPU — consider increasing vCPU allocation or investigating load.";
// Low CPU + reasonable RAM (suggest checking allocation)
elseif ($cpu < 5 && $ram >= 2)
$suggestions[] = "{$host} averages only {$cpu}% CPU — vCPU allocation may be generous.";
// High MEM
if ($mem >= 85)
$suggestions[] = "{$host} is averaging {$mem}% memory use ({$ram}GB allocated) — consider increasing RAM.";
// Low MEM
elseif ($mem < 25 && $ram >= 2)
$suggestions[] = "{$host} uses only {$mem}% of its {$ram}GB RAM on average — allocation could be reduced.";
}
if (!$suggestions) {
$reply = "All VM resources look well-balanced, {$userAddr}. No over or under-allocation detected across the past 24 hours.";
} else {
$reply = "Resource analysis for the past 24 hours, {$userAddr}: " . implode(' ', $suggestions);
}
$source = 'intent:vm_suggestions';
break;
}
case 'ha_scene': {
// Fetch all HA scenes and fuzzy-match against the message
$haScenes = @json_decode(@file_get_contents(
@@ -1681,7 +1729,7 @@ if (!$reply) {
false,
stream_context_create(['http' => [
'method' => 'POST',
'header' => "Authorization: Bearer " . HA_TOKEN . "
'header' => "Authorization: Bearer " . HA_TOKEN . "
Content-Type: application/json",
'content' => json_encode(['entity_id' => $best['entity_id']]),
'timeout' => 5,