@@ -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