From 247dca508b16c974c5eb26ff77b3d9f2efb16203 Mon Sep 17 00:00:00 2001 From: jaberjaber23 Date: Tue, 12 May 2026 14:55:27 +0300 Subject: [PATCH] inferencing flag --- crates/openfang-api/static/css/components.css | 6 ++ .../tests/api_integration_test.rs | 80 +++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/crates/openfang-api/static/css/components.css b/crates/openfang-api/static/css/components.css index 74ba0d6d..c3e2148b 100644 --- a/crates/openfang-api/static/css/components.css +++ b/crates/openfang-api/static/css/components.css @@ -312,6 +312,12 @@ tr:hover td { background: var(--surface2); } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } +/* Issue #1026: live indicator for agents currently calling the LLM */ +@keyframes agent-inferencing-pulse { + 0%, 100% { transform: scale(1); opacity: 1; box-shadow: 0 0 0 0 var(--accent); } + 50% { transform: scale(1.25); opacity: 0.85; box-shadow: 0 0 0 4px rgba(255, 92, 0, 0); } +} + .message.user { flex-direction: row-reverse; } diff --git a/crates/openfang-api/tests/api_integration_test.rs b/crates/openfang-api/tests/api_integration_test.rs index d2b98900..4f3421b6 100644 --- a/crates/openfang-api/tests/api_integration_test.rs +++ b/crates/openfang-api/tests/api_integration_test.rs @@ -306,6 +306,86 @@ async fn test_spawn_list_kill_agent() { assert_eq!(agents[0]["name"], "assistant"); } +/// Regression test for issue #1026: GET /api/agents returns `is_inferencing` +/// reflecting whether the agent has an in-flight LLM task. This drives the +/// live dashboard indicator that shows which agents are calling the LLM. +#[tokio::test] +async fn test_list_agents_includes_inferencing_flag() { + let server = start_test_server().await; + let client = reqwest::Client::new(); + + // Spawn a test agent. + let resp = client + .post(format!("{}/api/agents", server.base_url)) + .json(&serde_json::json!({"manifest_toml": TEST_MANIFEST})) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 201); + let body: serde_json::Value = resp.json().await.unwrap(); + let agent_id_str = body["agent_id"].as_str().unwrap().to_string(); + let agent_id: openfang_types::agent::AgentId = agent_id_str.parse().unwrap(); + + // Baseline: idle agent must report is_inferencing = false. + let resp = client + .get(format!("{}/api/agents", server.base_url)) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let agents: Vec = resp.json().await.unwrap(); + let test_agent = agents + .iter() + .find(|a| a["id"] == agent_id_str) + .expect("spawned agent should appear in list"); + assert_eq!( + test_agent["is_inferencing"], false, + "freshly spawned agent should not be inferencing" + ); + + // Simulate an in-flight LLM call by inserting a real AbortHandle into + // the kernel's running_tasks map. This is exactly what the agent loop + // does when it starts processing a message. + let handle = tokio::spawn(async { + // Long-lived task we will abort at end of test. + tokio::time::sleep(std::time::Duration::from_secs(60)).await; + }); + server + .state + .kernel + .running_tasks + .insert(agent_id, handle.abort_handle()); + + // Now list_agents should report is_inferencing = true for that agent. + let resp = client + .get(format!("{}/api/agents", server.base_url)) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let agents: Vec = resp.json().await.unwrap(); + let test_agent = agents + .iter() + .find(|a| a["id"] == agent_id_str) + .expect("spawned agent should still appear in list"); + assert_eq!( + test_agent["is_inferencing"], true, + "agent with an entry in running_tasks must be flagged is_inferencing" + ); + + // Other agents (the default assistant) must NOT be flagged. + if let Some(other) = agents.iter().find(|a| a["id"] != agent_id_str) { + assert_eq!( + other["is_inferencing"], false, + "agents without a running task must not be flagged" + ); + } + + // Cleanup so the spawned future does not outlive the test. + server.state.kernel.running_tasks.remove(&agent_id); + handle.abort(); +} + #[tokio::test] async fn test_agent_session_empty() { let server = start_test_server().await;