Skip to content

Commit 8e0eaa2

Browse files
committed
Add progress notifications to ChatAgent tool execution and cleanup
- Add emitter parameter to ChatAgent._build_workflow() to enable progress notifications - Emit tool_call updates in ChatAgent.tool_node() before executing tools - Remove duplicate imports in docket_tasks.py (process_chat_turn, process_knowledge_query) - Remove unused API_BASE_URL and os import from mcp_server/server.py - Fix ASGI app initialization in mcp_server/server.py for uvicorn - Remove unused ThreadManager import from progress.py - Fix import ordering and remove unused imports across test files - Apply ruff format to all modified files
1 parent 70ead4b commit 8e0eaa2

19 files changed

+162
-140
lines changed

redis_sre_agent/agent/chat_agent.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,9 @@ async def agent_node(state: ChatAgentState) -> Dict[str, Any]:
260260
return {
261261
"messages": new_messages,
262262
"iteration_count": iteration_count + 1,
263-
"current_tool_calls": response.tool_calls if hasattr(response, "tool_calls") else [],
263+
"current_tool_calls": response.tool_calls
264+
if hasattr(response, "tool_calls")
265+
else [],
264266
}
265267

266268
async def tool_node(state: ChatAgentState) -> Dict[str, Any]:
@@ -277,7 +279,9 @@ async def tool_node(state: ChatAgentState) -> Dict[str, Any]:
277279
# Emit progress updates for each tool call
278280
if emitter and tool_calls:
279281
for tc in tool_calls:
280-
tool_name = tc.get("name") if isinstance(tc, dict) else getattr(tc, "name", None)
282+
tool_name = (
283+
tc.get("name") if isinstance(tc, dict) else getattr(tc, "name", None)
284+
)
281285
tool_args = (
282286
tc.get("args") if isinstance(tc, dict) else getattr(tc, "args", {})
283287
) or {}
@@ -299,7 +303,9 @@ async def tool_node(state: ChatAgentState) -> Dict[str, Any]:
299303

300304
# Build envelopes for each tool call result
301305
for idx, tc in enumerate(tool_calls):
302-
tool_name = tc.get("name") if isinstance(tc, dict) else getattr(tc, "name", None)
306+
tool_name = (
307+
tc.get("name") if isinstance(tc, dict) else getattr(tc, "name", None)
308+
)
303309
tool_args = (
304310
tc.get("args") if isinstance(tc, dict) else getattr(tc, "args", {})
305311
) or {}
@@ -445,9 +451,7 @@ async def process_query(
445451
thread_config = {"configurable": {"thread_id": session_id}}
446452

447453
try:
448-
await emitter.emit(
449-
"Chat agent processing your question...", "agent_start"
450-
)
454+
await emitter.emit("Chat agent processing your question...", "agent_start")
451455

452456
final_state = await app.ainvoke(initial_state, config=thread_config)
453457

redis_sre_agent/agent/knowledge_agent.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -510,9 +510,7 @@ async def process_query(
510510
logger.error(f"Knowledge agent processing failed: {e}")
511511
error_response = f"I encountered an error while processing your knowledge query: {str(e)}. Please try asking a more specific question about SRE practices, troubleshooting methodologies, or system reliability concepts."
512512

513-
await emitter.emit(
514-
f"Knowledge agent encountered an error: {str(e)}", "agent_error"
515-
)
513+
await emitter.emit(f"Knowledge agent encountered an error: {str(e)}", "agent_error")
516514

517515
return error_response
518516

redis_sre_agent/agent/langgraph_agent.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -598,8 +598,7 @@ async def _summarize_envelopes_for_reasoning(
598598
batch_prompt += "\n\n"
599599

600600
batch_prompt += (
601-
"Return JSON array format: "
602-
'[{"summary": "key findings..."}, {"summary": "..."}]'
601+
'Return JSON array format: [{"summary": "key findings..."}, {"summary": "..."}]'
603602
)
604603

605604
try:
@@ -623,9 +622,7 @@ async def _summarize_envelopes_for_reasoning(
623622
pass
624623

625624
# Apply summaries to envelopes
626-
for j, (orig_idx, env) in enumerate(
627-
zip(to_summarize_indices, to_summarize)
628-
):
625+
for j, (orig_idx, env) in enumerate(zip(to_summarize_indices, to_summarize)):
629626
summary_text = (
630627
summaries[j].get("summary", "")
631628
if j < len(summaries) and isinstance(summaries[j], dict)
@@ -1236,9 +1233,7 @@ def _sev_score(t: dict) -> int:
12361233
)
12371234
# Use summarized envelopes for recommendation workers
12381235
# LLM can call expand_evidence to get full details if needed
1239-
env_by_key = {
1240-
e.get("tool_key"): e for e in summarized_envelopes
1241-
}
1236+
env_by_key = {e.get("tool_key"): e for e in summarized_envelopes}
12421237
for t in topics:
12431238
ev_keys = [k for k in (t.get("evidence_keys") or []) if isinstance(k, str)]
12441239
ev = [env_by_key[k] for k in ev_keys if k in env_by_key]

redis_sre_agent/cli/query.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,9 @@ async def _query():
150150

151151
# Show thread ID for follow-up queries
152152
console.print("\n[dim]💡 To continue this conversation:[/dim]")
153-
console.print(f"[dim] redis-sre-agent query --thread-id {active_thread_id} \"your follow-up\"[/dim]")
153+
console.print(
154+
f'[dim] redis-sre-agent query --thread-id {active_thread_id} "your follow-up"[/dim]'
155+
)
154156

155157
except Exception as e:
156158
console.print(f"[red]❌ Error: {e}[/red]")

redis_sre_agent/core/docket_tasks.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,13 @@ async def process_chat_turn(
220220
# Add response to thread as assistant message
221221
await thread_manager.append_messages(
222222
thread_id,
223-
[{"role": "assistant", "content": response, "metadata": {"task_id": task_id, "agent": "chat"}}],
223+
[
224+
{
225+
"role": "assistant",
226+
"content": response,
227+
"metadata": {"task_id": task_id, "agent": "chat"},
228+
}
229+
],
224230
)
225231

226232
return result
@@ -290,7 +296,13 @@ async def process_knowledge_query(
290296
# Add response to thread as assistant message
291297
await thread_manager.append_messages(
292298
thread_id,
293-
[{"role": "assistant", "content": response, "metadata": {"task_id": task_id, "agent": "knowledge"}}],
299+
[
300+
{
301+
"role": "assistant",
302+
"content": response,
303+
"metadata": {"task_id": task_id, "agent": "knowledge"},
304+
}
305+
],
294306
)
295307

296308
return result
@@ -653,7 +665,9 @@ async def process_agent_turn(
653665
agent = get_sre_agent()
654666
elif agent_type == AgentType.REDIS_CHAT:
655667
# Get the target instance for the chat agent
656-
target_instance = await get_instance_by_id(active_instance_id) if active_instance_id else None
668+
target_instance = (
669+
await get_instance_by_id(active_instance_id) if active_instance_id else None
670+
)
657671
agent = get_chat_agent(redis_instance=target_instance)
658672
else:
659673
agent = get_knowledge_agent()

redis_sre_agent/core/progress.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,9 +222,7 @@ async def emit(
222222
) -> None:
223223
"""Emit notification to task storage."""
224224
try:
225-
await self._task_manager.add_task_update(
226-
self._task_id, message, update_type, metadata
227-
)
225+
await self._task_manager.add_task_update(self._task_id, message, update_type, metadata)
228226
except Exception as e:
229227
# Best-effort: don't fail the agent if notification logging fails
230228
logger.warning(f"Failed to emit task notification: {e}")

redis_sre_agent/core/redis.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,7 @@ def get_vectorizer() -> OpenAITextVectorizer:
235235
redis_url=redis_url,
236236
ttl=settings.embeddings_cache_ttl,
237237
)
238-
logger.debug(
239-
f"Vectorizer created with embeddings cache (ttl={settings.embeddings_cache_ttl}s)"
240-
)
238+
logger.debug(f"Vectorizer created with embeddings cache (ttl={settings.embeddings_cache_ttl}s)")
241239

242240
return OpenAITextVectorizer(
243241
model=settings.embedding_model,

redis_sre_agent/tools/manager.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,9 @@ async def _load_mcp_providers(self) -> None:
239239
# Build set of excluded capabilities for fast lookup
240240
excluded_caps = set(self.exclude_mcp_categories or [])
241241
if excluded_caps:
242-
logger.info(f"MCP tools with these categories will be excluded: {[c.value for c in excluded_caps]}")
242+
logger.info(
243+
f"MCP tools with these categories will be excluded: {[c.value for c in excluded_caps]}"
244+
)
243245

244246
for server_name, server_config in settings.mcp_servers.items():
245247
try:

tests/unit/agent/test_chat_agent.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from redis_sre_agent.core.progress import (
1313
CallbackEmitter,
1414
NullEmitter,
15-
ProgressEmitter,
1615
)
1716

1817

@@ -121,6 +120,7 @@ def test_get_chat_agent_without_instance(self):
121120

122121
# Clear cache
123122
from redis_sre_agent.agent import chat_agent
123+
124124
chat_agent._chat_agents.clear()
125125

126126
agent = get_chat_agent()
@@ -137,6 +137,7 @@ def test_get_chat_agent_caches_by_instance_name(self):
137137

138138
# Clear cache
139139
from redis_sre_agent.agent import chat_agent
140+
140141
chat_agent._chat_agents.clear()
141142

142143
instance1 = RedisInstance(

tests/unit/agent/test_envelope_summarization.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
"""Tests for envelope summarization and expand_evidence tool in the reasoning phase."""
22

3-
import pytest
43
from unittest.mock import AsyncMock, MagicMock, patch
54

5+
import pytest
6+
67
from redis_sre_agent.agent.langgraph_agent import SRELangGraphAgent
78

89

@@ -95,9 +96,7 @@ async def test_mixed_envelopes_partial_summarization(self, agent):
9596
mock_response.content = '[{"summary": "Large content summarized"}]'
9697
agent.mini_llm.ainvoke = AsyncMock(return_value=mock_response)
9798

98-
result = await agent._summarize_envelopes_for_reasoning(
99-
[small_envelope, large_envelope]
100-
)
99+
result = await agent._summarize_envelopes_for_reasoning([small_envelope, large_envelope])
101100

102101
assert len(result) == 2
103102
# Small envelope unchanged
@@ -109,8 +108,13 @@ async def test_mixed_envelopes_partial_summarization(self, agent):
109108
async def test_order_preserved(self, agent):
110109
"""Test that envelope order is preserved after summarization."""
111110
envelopes = [
112-
{"tool_key": f"tool_{i}", "name": f"t{i}", "args": {}, "status": "success",
113-
"data": {"id": i, "content": "x" * (100 if i % 2 == 0 else 1000)}}
111+
{
112+
"tool_key": f"tool_{i}",
113+
"name": f"t{i}",
114+
"args": {},
115+
"status": "success",
116+
"data": {"id": i, "content": "x" * (100 if i % 2 == 0 else 1000)},
117+
}
114118
for i in range(5)
115119
]
116120

0 commit comments

Comments
 (0)