Skip to content

Commit 9e0e50c

Browse files
author
Paul C
committed
fix: AI context includes all nodes, dedup container/VM calls
- Fix docker format template escaping ({{.Names}} was mangled by format!()) - All online nodes now listed under "All Nodes" (Proxmox nodes no longer excluded from the AI context listing) - Deduplicate docker/lxc/vm calls: fetch once, reuse for counts + names - Remove redundant container/VM fetches in user_context block
1 parent 1656cd4 commit 9e0e50c

2 files changed

Lines changed: 25 additions & 37 deletions

File tree

src/ai/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1397,7 +1397,7 @@ fn build_system_prompt(knowledge: &str, server_context: &str) -> String {
13971397
## Containers, VMs & Docker\n\
13981398
- The server state below includes Docker container names, LXC container names, and VM names for the local node\n\
13991399
- For remote nodes, you can discover containers/VMs by using [EXEC_ALL] with commands like:\n\
1400-
`docker ps --format '{{.Names}} {{.Status}}'` for Docker containers\n\
1400+
`docker ps --format '{{{{.Names}}}} {{{{.Status}}}}'` for Docker containers\n\
14011401
`lxc-ls -f` for LXC containers\n\
14021402
- When the user asks about a specific container by name, identify which node it's on and target that node\n\
14031403
- When proposing actions for containers, be specific about which container and which node\n\n\

src/api/mod.rs

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5000,29 +5000,27 @@ pub async fn ai_chat(
50005000
.map(|h| h.to_string_lossy().to_string())
50015001
.unwrap_or_else(|_| "unknown".to_string());
50025002

5003-
let docker_count = crate::containers::docker_list_all().len();
5004-
let lxc_count = crate::containers::lxc_list_all().len();
5005-
let vm_count = state.vms.lock().unwrap().list_vms().len();
5003+
// Get local container/VM lists once (reuse for counts and names)
5004+
let local_docker_list = crate::containers::docker_list_all();
5005+
let local_lxc_list = crate::containers::lxc_list_all();
5006+
let local_vm_list = state.vms.lock().unwrap().list_vms();
5007+
let docker_count = local_docker_list.len();
5008+
let lxc_count = local_lxc_list.len();
5009+
let vm_count = local_vm_list.len();
50065010
let components = crate::installer::get_all_status();
50075011

5012+
let local_docker: Vec<String> = local_docker_list.iter().map(|c| format!("{} [{}]", c.name, c.state)).collect();
5013+
let local_lxc: Vec<String> = local_lxc_list.iter().map(|c| format!("{} [{}]", c.name, c.state)).collect();
5014+
let local_vms: Vec<String> = local_vm_list.iter().map(|v| format!("{} [{}]", v.name, if v.running { "running" } else { "stopped" })).collect();
5015+
50085016
let nodes = state.cluster.get_all_nodes();
50095017

5010-
// WolfStack nodes summary
5011-
let ws_nodes: Vec<&crate::agent::Node> = nodes.iter().filter(|n| n.node_type != "proxmox").collect();
5018+
// All nodes (including Proxmox nodes that have WolfStack agents)
5019+
let all_online: Vec<&crate::agent::Node> = nodes.iter().filter(|n| n.online).collect();
5020+
// PVE nodes for separate cluster grouping display
50125021
let pve_nodes: Vec<&crate::agent::Node> = nodes.iter().filter(|n| n.node_type == "proxmox").collect();
50135022

5014-
// For the local node, get actual container/VM names
5015-
let local_docker: Vec<String> = crate::containers::docker_list_all().iter().map(|c| {
5016-
format!("{} [{}]", c.name, c.state)
5017-
}).collect();
5018-
let local_lxc: Vec<String> = crate::containers::lxc_list_all().iter().map(|c| {
5019-
format!("{} [{}]", c.name, c.state)
5020-
}).collect();
5021-
let local_vms: Vec<String> = state.vms.lock().unwrap().list_vms().iter().map(|v| {
5022-
format!("{} [{}]", v.name, if v.running { "running" } else { "stopped" })
5023-
}).collect();
5024-
5025-
let node_info = ws_nodes.iter().map(|n| {
5023+
let node_info = all_online.iter().map(|n| {
50265024
let mut line = format!(" - {} ({}) [{}]", n.hostname, n.address,
50275025
if n.online { "online" } else { "offline" });
50285026
if n.docker_count > 0 { line.push_str(&format!(" — {} Docker", n.docker_count)); }
@@ -5105,31 +5103,21 @@ pub async fn ai_chat(
51055103
ctx.push_str(&format!(", Context: {}", name));
51065104
}
51075105
}
5108-
// Enumerate containers/VMs on the viewed node so the AI knows what's there
5109-
// For self node: use local data. For remote: the frontend sends its own infra cache.
5106+
// Enumerate containers/VMs on the viewed node (reuse already-fetched local data)
51105107
if body.node_id.as_ref().map(|s| !s.is_empty()).unwrap_or(false) {
51115108
let is_self_node = nodes.iter().any(|n| n.is_self && Some(&n.id) == body.node_id.as_ref());
51125109
if is_self_node {
5113-
let docker = crate::containers::docker_list_all();
5114-
if !docker.is_empty() {
5110+
if !local_docker.is_empty() {
51155111
ctx.push_str("\n\nDocker containers on this node:");
5116-
for c in &docker {
5117-
ctx.push_str(&format!("\n - {} [{}]", c.name, c.state));
5118-
}
5112+
for name in &local_docker { ctx.push_str(&format!("\n - {}", name)); }
51195113
}
5120-
let lxc = crate::containers::lxc_list_all();
5121-
if !lxc.is_empty() {
5114+
if !local_lxc.is_empty() {
51225115
ctx.push_str("\n\nLXC containers on this node:");
5123-
for c in &lxc {
5124-
ctx.push_str(&format!("\n - {} [{}]", c.name, c.state));
5125-
}
5116+
for name in &local_lxc { ctx.push_str(&format!("\n - {}", name)); }
51265117
}
5127-
let vms = state.vms.lock().unwrap().list_vms();
5128-
if !vms.is_empty() {
5118+
if !local_vms.is_empty() {
51295119
ctx.push_str("\n\nVMs on this node:");
5130-
for v in &vms {
5131-
ctx.push_str(&format!("\n - {} [{}]", v.name, if v.running { "running" } else { "stopped" }));
5132-
}
5120+
for name in &local_vms { ctx.push_str(&format!("\n - {}", name)); }
51335121
}
51345122
} else {
51355123
ctx.push_str("\n\n(Container/VM list for remote nodes is provided by the frontend target picker)");
@@ -5144,10 +5132,10 @@ pub async fn ai_chat(
51445132

51455133
format!(
51465134
"Hostname: {}\nLocal Docker containers: {}\nLocal LXC containers: {}\nLocal VMs: {}\n\
5147-
Components: {}\n\nWolfStack Nodes ({}):\n{}\n\nProxmox Clusters:\n{}{}",
5135+
Components: {}\n\nAll Nodes ({}):\n{}\n\nProxmox Clusters:\n{}{}",
51485136
hostname, docker_count, lxc_count, vm_count,
51495137
components.iter().map(|c| format!("{:?}: {}", c.component, if c.running { "running" } else { "stopped" })).collect::<Vec<_>>().join(", "),
5150-
ws_nodes.len(), node_info,
5138+
all_online.len(), node_info,
51515139
pve_info,
51525140
user_context,
51535141
)

0 commit comments

Comments
 (0)