Skip to content

Commit 47a283d

Browse files
authored
live stream handling and better agent response along with better memory management (#9)
1 parent 4cbcb17 commit 47a283d

8 files changed

Lines changed: 123 additions & 18 deletions

File tree

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,25 @@ devops-agent/
106106
└── docs/ # Documentation
107107
```
108108

109+
## Common issues and fixes
110+
- if you see any error like `INFO Error checking if content_hash ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73 exists: Unexpected Response: 400 (Bad Request)
111+
Raw response content:
112+
b'{"status":{"error":"Bad request: Index required but not found for \\"content_hash\\" of one of the following types: [keyword]. Help: Create an index for this key or use a
113+
different filter."},"time":2 ...' `
114+
```text
115+
curl --request PUT \
116+
--url https://9df18135-290c-45b3-8158-f73b103dc352.eu-west-2-0.aws.cloud.qdrant.io:6333/collections/devops-memory/index \
117+
--header 'Authorization: Bearer YOUR_API_KEY' \
118+
--header 'Content-Type: application/json' \
119+
--data '{
120+
"field_name": "content_hash",
121+
"field_schema": {
122+
"type": "keyword",
123+
"on_disk": true
124+
}
125+
}'
126+
```
127+
109128
## Contributing
110129

111130
Contributions are welcome! Please feel free to submit a Pull Request.

devops_agent/core/devops_agent.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
import os
3+
from textwrap import dedent
34

45
from agno.agent import Agent
56
from agno.knowledge import Knowledge
@@ -48,13 +49,20 @@ def execute_devops_agent(provider: str, user_query: str = None) -> Agent:
4849
devops_assist = Agent(
4950
name="DevOps Agent",
5051
model=model,
51-
description="You help answer questions about the devops domain.",
52+
description="You help answer questions about the devops domain like kubernetes troubleshooting, docker troubleshooting etc.",
5253
instructions=devops_prompt,
53-
# knowledge=knowledge,
54+
additional_input=dedent("""\
55+
Instruction: You should always answer scenarios like below (few examples as below).
56+
- Debug high memory usage in Kubernetes pods causing frequent OOMKills and restarts
57+
- Analyze distributed tracing data to identify performance bottleneck in microservices architecture
58+
- Troubleshoot intermittent 504 gateway timeout errors in production load balancer
59+
- Investigate CI/CD pipeline failures and implement automated debugging workflows
60+
- Root cause analysis for database deadlocks causing application timeouts
61+
- Debug DNS resolution issues affecting service discovery in Kubernetes cluster
62+
- Analyze logs to identify security breach and implement containment procedures
63+
- Troubleshoot GitOps deployment failures and implement automated rollback procedures
64+
"""),
5465
stream_intermediate_steps=True,
55-
# add_knowledge_to_context=True,
56-
# add_datetime_to_context=True,
57-
# add_session_summary_to_context=True,
5866
markdown=True,
5967
)
6068

devops_agent/core/kubernetes_agent.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
import os
3+
from textwrap import dedent
34

45
from agno.agent import Agent
56
from agno.knowledge import Knowledge
@@ -51,13 +52,20 @@ def execute_k8s_agent(provider: str, user_query: str = None) -> Agent:
5152
k8s_assist = Agent(
5253
name="Kubernetes Agent",
5354
model=model,
54-
description="You help answer questions about the kubernetes domain of any infrastructure like Azure(AKS), AWS(EKS), and GCP(GKS)",
55+
description="You help answer questions about the application with kubernetes design and implementation domain of any infrastructure like Azure(AKS), AWS(EKS), and GCP(GKS)",
5556
instructions=k8s_prompt,
56-
# knowledge=knowledge,
57+
additional_input=dedent("""\
58+
Instruction: You should always answer scenarios like below (few examples as below).
59+
- Design a multi-cluster Kubernetes platform with GitOps for a financial services company
60+
- Implement progressive delivery with Argo Rollouts and service mesh traffic splitting
61+
- Create a secure multi-tenant Kubernetes platform with namespace isolation and RBAC
62+
- Design disaster recovery for stateful applications across multiple Kubernetes clusters
63+
- Optimize Kubernetes costs while maintaining performance and availability SLAs
64+
- Implement observability stack with Prometheus, Grafana, and OpenTelemetry for microservices
65+
- Create CI/CD pipeline with GitOps for container applications with security scanning
66+
- Design Kubernetes operator for custom application lifecycle management
67+
"""),
5768
stream_intermediate_steps=True,
58-
# add_knowledge_to_context=True,
59-
# add_datetime_to_context=True,
60-
# add_session_summary_to_context=True,
6169
markdown=True,
6270
)
6371

devops_agent/core/terraform_agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
console = Console()
1616

17-
def execute_terraform_agent(provider: str, user_query: str = None) -> Agent:
17+
def execute_terraform_agent(provider: str) -> Agent:
1818

1919
console.print(Panel.fit(
2020
"[bold cyan]Terraform Agent Invoking...[/bold cyan]",

devops_agent/prompts/devops.poml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<poml>
2-
<task>You are a DevOps troubleshooter specializing in rapid incident response, advanced debugging, and modern observability practices. Your purpose is to provide expert troubleshooting with comprehensive knowledge of modern observability tools, debugging methodologies, and incident response practices.</task>
2+
<task>You are a DevOps troubleshooter specializing in rapid incident response, advanced debugging, and modern observability practices. Your purpose is to provide expert troubleshooting with comprehensive knowledge of modern observability tools, debugging methodologies, and incident response practices. Think through the user ask, plan properly and then only answer the user.</task>
33

44
<stepwise-instructions>
55
<list listStyle="decimal">

devops_agent/prompts/kubernetes.poml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<poml>
2-
<task>You are a Kubernetes architect specializing in cloud-native infrastructure, modern GitOps workflows, and enterprise container orchestration at scale. Your purpose is to provide expert Kubernetes architecture with comprehensive knowledge of container orchestration, cloud-native technologies, and modern GitOps practices across all major providers.</task>
2+
<task>You are a Kubernetes architect specializing in cloud-native infrastructure, modern GitOps workflows, and enterprise container orchestration at scale. Your purpose is to provide expert Kubernetes architecture with comprehensive knowledge of container orchestration, cloud-native technologies, and modern GitOps practices across all major providers. Think through the user ask, plan properly and then only answer the user.</task>
33

44
<stepwise-instructions>
55
<list listStyle="decimal">

devops_agent/prompts/terraform.poml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<poml>
2-
<task>You are a Terraform/OpenTofu specialist focused on advanced infrastructure automation, state management, and modern IaC practices. Your purpose is to provide expert Infrastructure as Code guidance with comprehensive knowledge of Terraform, OpenTofu, and modern IaC ecosystems for enterprise-scale infrastructure automation.</task>
2+
<task>You are a Terraform/OpenTofu specialist focused on advanced infrastructure automation, state management, and modern IaC practices. Your purpose is to provide expert Infrastructure as Code guidance with comprehensive knowledge of Terraform, OpenTofu, and modern IaC ecosystems for enterprise-scale infrastructure automation. Think through the user ask, plan properly and then only answer the user.</task>
33

44
<stepwise-instructions>
55
<list listStyle="decimal">

devops_agent/utils/stream_handler.py

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ def __init__(
3131
# Content trackers
3232
self.response_content = ""
3333
self.reasoning_content = ""
34+
self.reasoning_steps = []
35+
self.processed_reasoning_steps = set() # Track processed reasoning steps
3436
self.input_content = ""
3537

3638
# Tool call trackers
@@ -62,6 +64,48 @@ def _create_panel(
6264
padding=(1, 2),
6365
)
6466

67+
def _add_reasoning_step(self, step: Any):
68+
"""Add a reasoning step, avoiding duplicates"""
69+
if step is None:
70+
return
71+
72+
# Create a unique identifier for the reasoning step
73+
# Use multiple attributes to create a more robust ID
74+
step_id = None
75+
76+
# Try to create ID from step attributes
77+
if hasattr(step, 'title') and hasattr(step, 'reasoning'):
78+
title = getattr(step, 'title', '')
79+
reasoning = getattr(step, 'reasoning', '')
80+
# Use a hash of title + reasoning content (first 100 chars to avoid huge IDs)
81+
step_id = hash(f"{title}:{reasoning[:100] if reasoning else ''}")
82+
else:
83+
# Fallback to hash of string representation
84+
step_id = hash(str(step)[:200])
85+
86+
# Only add if we haven't seen this step before
87+
if step_id not in self.processed_reasoning_steps:
88+
self.processed_reasoning_steps.add(step_id)
89+
self.reasoning_steps.append(step)
90+
91+
def _format_reasoning_step(self, step: Any) -> str:
92+
"""Format a reasoning step for display"""
93+
if isinstance(step, str):
94+
return step
95+
96+
# Try to extract content from ReasoningStep object
97+
content = getattr(step, 'content', None)
98+
if content:
99+
return str(content)
100+
101+
# Try to extract text or message
102+
text = getattr(step, 'text', None) or getattr(step, 'message', None)
103+
if text:
104+
return str(text)
105+
106+
# Fallback to string representation
107+
return str(step)
108+
65109
def _format_tool_call(self, tool: Any) -> str:
66110
"""Format a tool call for display"""
67111
if tool is None:
@@ -110,7 +154,18 @@ def _build_panels(self) -> List[Panel]:
110154
)
111155
panels.append(message_panel)
112156

113-
# Reasoning panel
157+
# Reasoning steps panels
158+
if self.reasoning_steps and self.show_reasoning:
159+
for i, step in enumerate(self.reasoning_steps, 1):
160+
reasoning_text = self._format_reasoning_step(step)
161+
reasoning_panel = self._create_panel(
162+
Text(reasoning_text),
163+
f"Reasoning Step {i}",
164+
border_style="green"
165+
)
166+
panels.append(reasoning_panel)
167+
168+
# Reasoning content panel (for string-based reasoning)
114169
if self.reasoning_content and self.show_reasoning:
115170
thinking_panel = self._create_panel(
116171
Text(self.reasoning_content),
@@ -240,16 +295,31 @@ def handle_stream(
240295
self.response_content += content
241296

242297
elif event_type == "TeamReasoningStep":
243-
# Reasoning content
298+
# Reasoning content - could be string or ReasoningStep object
244299
reasoning = getattr(event, 'content', '')
245300
if reasoning:
246-
self.reasoning_content += reasoning
301+
if isinstance(reasoning, str):
302+
self.reasoning_content += reasoning
303+
else:
304+
# It's a ReasoningStep object - deduplicate
305+
self._add_reasoning_step(reasoning)
247306

248307
elif event_type == "reasoning_content":
249308
# Alternative reasoning event
250309
reasoning = getattr(event, 'reasoning_content', '')
251310
if reasoning:
252-
self.reasoning_content += reasoning
311+
if isinstance(reasoning, str):
312+
self.reasoning_content += reasoning
313+
else:
314+
self._add_reasoning_step(reasoning)
315+
316+
# Handle reasoning_steps attribute - deduplicate each step
317+
if hasattr(event, 'reasoning_steps') and event.reasoning_steps:
318+
if isinstance(event.reasoning_steps, list):
319+
for step in event.reasoning_steps:
320+
self._add_reasoning_step(step)
321+
else:
322+
self._add_reasoning_step(event.reasoning_steps)
253323

254324
elif event_type == "TeamToolCallStarted":
255325
# Team tool call started

0 commit comments

Comments
 (0)