mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-06-01 14:31:14 +00:00
feat: show active task count in /status output
This commit is contained in:
parent
b60e8dc0ba
commit
634f4b45c1
@ -262,3 +262,11 @@ class SubagentManager:
|
|||||||
def get_running_count(self) -> int:
|
def get_running_count(self) -> int:
|
||||||
"""Return the number of currently running subagents."""
|
"""Return the number of currently running subagents."""
|
||||||
return len(self._running_tasks)
|
return len(self._running_tasks)
|
||||||
|
|
||||||
|
def get_running_count_by_session(self, session_key: str) -> int:
|
||||||
|
"""Return the number of currently running subagents for a session."""
|
||||||
|
tids = self._session_tasks.get(session_key, set())
|
||||||
|
return sum(
|
||||||
|
1 for tid in tids
|
||||||
|
if tid in self._running_tasks and not self._running_tasks[tid].done()
|
||||||
|
)
|
||||||
|
|||||||
@ -74,6 +74,12 @@ async def cmd_status(ctx: CommandContext) -> OutboundMessage:
|
|||||||
search_usage_text = usage.format()
|
search_usage_text = usage.format()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass # Never let usage fetch break /status
|
pass # Never let usage fetch break /status
|
||||||
|
active_tasks = loop._active_tasks.get(ctx.key, [])
|
||||||
|
task_count = sum(1 for t in active_tasks if not t.done())
|
||||||
|
try:
|
||||||
|
task_count += loop.subagents.get_running_count_by_session(ctx.key)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
return OutboundMessage(
|
return OutboundMessage(
|
||||||
channel=ctx.msg.channel,
|
channel=ctx.msg.channel,
|
||||||
chat_id=ctx.msg.chat_id,
|
chat_id=ctx.msg.chat_id,
|
||||||
@ -84,6 +90,7 @@ async def cmd_status(ctx: CommandContext) -> OutboundMessage:
|
|||||||
session_msg_count=len(session.get_history(max_messages=0)),
|
session_msg_count=len(session.get_history(max_messages=0)),
|
||||||
context_tokens_estimate=ctx_est,
|
context_tokens_estimate=ctx_est,
|
||||||
search_usage_text=search_usage_text,
|
search_usage_text=search_usage_text,
|
||||||
|
active_task_count=task_count,
|
||||||
),
|
),
|
||||||
metadata={**dict(ctx.msg.metadata or {}), "render_as": "text"},
|
metadata={**dict(ctx.msg.metadata or {}), "render_as": "text"},
|
||||||
)
|
)
|
||||||
|
|||||||
@ -400,6 +400,7 @@ def build_status_content(
|
|||||||
session_msg_count: int,
|
session_msg_count: int,
|
||||||
context_tokens_estimate: int,
|
context_tokens_estimate: int,
|
||||||
search_usage_text: str | None = None,
|
search_usage_text: str | None = None,
|
||||||
|
active_task_count: int = 0,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Build a human-readable runtime status snapshot.
|
"""Build a human-readable runtime status snapshot.
|
||||||
|
|
||||||
@ -431,6 +432,7 @@ def build_status_content(
|
|||||||
f"\U0001f4da Context: {ctx_used_str}/{ctx_total_str} ({ctx_pct}%)",
|
f"\U0001f4da Context: {ctx_used_str}/{ctx_total_str} ({ctx_pct}%)",
|
||||||
f"\U0001f4ac Session: {session_msg_count} messages",
|
f"\U0001f4ac Session: {session_msg_count} messages",
|
||||||
f"\u23f1 Uptime: {uptime}",
|
f"\u23f1 Uptime: {uptime}",
|
||||||
|
f"\u26a1 Tasks: {active_task_count} active",
|
||||||
]
|
]
|
||||||
if search_usage_text:
|
if search_usage_text:
|
||||||
lines.append(search_usage_text)
|
lines.append(search_usage_text)
|
||||||
|
|||||||
@ -140,6 +140,7 @@ class TestRestartCommand:
|
|||||||
loop.consolidator.estimate_session_prompt_tokens = MagicMock(
|
loop.consolidator.estimate_session_prompt_tokens = MagicMock(
|
||||||
return_value=(20500, "tiktoken")
|
return_value=(20500, "tiktoken")
|
||||||
)
|
)
|
||||||
|
loop.subagents.get_running_count_by_session.return_value = 0
|
||||||
|
|
||||||
msg = InboundMessage(channel="telegram", sender_id="u1", chat_id="c1", content="/status")
|
msg = InboundMessage(channel="telegram", sender_id="u1", chat_id="c1", content="/status")
|
||||||
|
|
||||||
@ -151,6 +152,7 @@ class TestRestartCommand:
|
|||||||
assert "Context: 20k/65k (31%)" in response.content
|
assert "Context: 20k/65k (31%)" in response.content
|
||||||
assert "Session: 3 messages" in response.content
|
assert "Session: 3 messages" in response.content
|
||||||
assert "Uptime: 2m 5s" in response.content
|
assert "Uptime: 2m 5s" in response.content
|
||||||
|
assert "Tasks: 0 active" in response.content
|
||||||
assert response.metadata == {"render_as": "text"}
|
assert response.metadata == {"render_as": "text"}
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@ -179,6 +181,7 @@ class TestRestartCommand:
|
|||||||
loop.consolidator.estimate_session_prompt_tokens = MagicMock(
|
loop.consolidator.estimate_session_prompt_tokens = MagicMock(
|
||||||
return_value=(0, "none")
|
return_value=(0, "none")
|
||||||
)
|
)
|
||||||
|
loop.subagents.get_running_count_by_session.return_value = 0
|
||||||
|
|
||||||
response = await loop._process_message(
|
response = await loop._process_message(
|
||||||
InboundMessage(channel="telegram", sender_id="u1", chat_id="c1", content="/status")
|
InboundMessage(channel="telegram", sender_id="u1", chat_id="c1", content="/status")
|
||||||
@ -187,6 +190,7 @@ class TestRestartCommand:
|
|||||||
assert response is not None
|
assert response is not None
|
||||||
assert "Tokens: 1200 in / 34 out" in response.content
|
assert "Tokens: 1200 in / 34 out" in response.content
|
||||||
assert "Context: 1k/65k (1%)" in response.content
|
assert "Context: 1k/65k (1%)" in response.content
|
||||||
|
assert "Tasks: 0 active" in response.content
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_process_direct_preserves_render_metadata(self):
|
async def test_process_direct_preserves_render_metadata(self):
|
||||||
@ -195,6 +199,7 @@ class TestRestartCommand:
|
|||||||
session.get_history.return_value = []
|
session.get_history.return_value = []
|
||||||
loop.sessions.get_or_create.return_value = session
|
loop.sessions.get_or_create.return_value = session
|
||||||
loop.subagents.get_running_count.return_value = 0
|
loop.subagents.get_running_count.return_value = 0
|
||||||
|
loop.subagents.get_running_count_by_session.return_value = 0
|
||||||
|
|
||||||
response = await loop.process_direct("/status", session_key="cli:test")
|
response = await loop.process_direct("/status", session_key="cli:test")
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ def test_status_shows_cache_hit_rate():
|
|||||||
)
|
)
|
||||||
assert "60% cached" in content
|
assert "60% cached" in content
|
||||||
assert "2000 in / 300 out" in content
|
assert "2000 in / 300 out" in content
|
||||||
|
assert "Tasks: 0 active" in content
|
||||||
|
|
||||||
|
|
||||||
def test_status_no_cache_info():
|
def test_status_no_cache_info():
|
||||||
@ -30,6 +31,7 @@ def test_status_no_cache_info():
|
|||||||
)
|
)
|
||||||
assert "cached" not in content.lower()
|
assert "cached" not in content.lower()
|
||||||
assert "2000 in / 300 out" in content
|
assert "2000 in / 300 out" in content
|
||||||
|
assert "Tasks: 0 active" in content
|
||||||
|
|
||||||
|
|
||||||
def test_status_zero_cached_tokens():
|
def test_status_zero_cached_tokens():
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user