holmesgpt 0.13.1__py3-none-any.whl → 0.13.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- holmes/__init__.py +1 -1
- holmes/common/env_vars.py +7 -0
- holmes/config.py +3 -1
- holmes/core/conversations.py +0 -11
- holmes/core/investigation.py +0 -6
- holmes/core/llm.py +60 -1
- holmes/core/prompt.py +0 -2
- holmes/core/supabase_dal.py +2 -2
- holmes/core/todo_tasks_formatter.py +51 -0
- holmes/core/tool_calling_llm.py +166 -91
- holmes/core/tools.py +20 -4
- holmes/interactive.py +63 -2
- holmes/main.py +0 -1
- holmes/plugins/prompts/_general_instructions.jinja2 +3 -1
- holmes/plugins/prompts/investigation_procedure.jinja2 +3 -13
- holmes/plugins/toolsets/__init__.py +5 -1
- holmes/plugins/toolsets/argocd.yaml +1 -1
- holmes/plugins/toolsets/atlas_mongodb/mongodb_atlas.py +18 -6
- holmes/plugins/toolsets/aws.yaml +9 -5
- holmes/plugins/toolsets/azure_sql/tools/analyze_connection_failures.py +3 -1
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_connections.py +3 -1
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_health_status.py +3 -1
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_performance.py +3 -1
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_storage.py +3 -1
- holmes/plugins/toolsets/azure_sql/tools/get_active_alerts.py +3 -1
- holmes/plugins/toolsets/azure_sql/tools/get_slow_queries.py +3 -1
- holmes/plugins/toolsets/azure_sql/tools/get_top_cpu_queries.py +3 -1
- holmes/plugins/toolsets/azure_sql/tools/get_top_data_io_queries.py +3 -1
- holmes/plugins/toolsets/azure_sql/tools/get_top_log_io_queries.py +3 -1
- holmes/plugins/toolsets/bash/bash_toolset.py +31 -20
- holmes/plugins/toolsets/confluence.yaml +1 -1
- holmes/plugins/toolsets/coralogix/api.py +3 -1
- holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py +4 -4
- holmes/plugins/toolsets/coralogix/utils.py +41 -14
- holmes/plugins/toolsets/datadog/datadog_api.py +45 -2
- holmes/plugins/toolsets/datadog/datadog_general_instructions.jinja2 +208 -0
- holmes/plugins/toolsets/datadog/datadog_logs_instructions.jinja2 +43 -0
- holmes/plugins/toolsets/datadog/datadog_metrics_instructions.jinja2 +12 -9
- holmes/plugins/toolsets/datadog/toolset_datadog_general.py +722 -0
- holmes/plugins/toolsets/datadog/toolset_datadog_logs.py +17 -6
- holmes/plugins/toolsets/datadog/toolset_datadog_metrics.py +15 -7
- holmes/plugins/toolsets/datadog/toolset_datadog_rds.py +6 -2
- holmes/plugins/toolsets/datadog/toolset_datadog_traces.py +9 -3
- holmes/plugins/toolsets/docker.yaml +1 -1
- holmes/plugins/toolsets/git.py +15 -5
- holmes/plugins/toolsets/grafana/toolset_grafana.py +25 -4
- holmes/plugins/toolsets/grafana/toolset_grafana_loki.py +4 -4
- holmes/plugins/toolsets/grafana/toolset_grafana_tempo.jinja2 +5 -3
- holmes/plugins/toolsets/grafana/toolset_grafana_tempo.py +299 -32
- holmes/plugins/toolsets/helm.yaml +1 -1
- holmes/plugins/toolsets/internet/internet.py +4 -2
- holmes/plugins/toolsets/internet/notion.py +4 -2
- holmes/plugins/toolsets/investigator/core_investigation.py +5 -17
- holmes/plugins/toolsets/investigator/investigator_instructions.jinja2 +1 -5
- holmes/plugins/toolsets/kafka.py +19 -7
- holmes/plugins/toolsets/kubernetes.yaml +5 -5
- holmes/plugins/toolsets/kubernetes_logs.py +4 -4
- holmes/plugins/toolsets/kubernetes_logs.yaml +1 -1
- holmes/plugins/toolsets/logging_utils/logging_api.py +15 -2
- holmes/plugins/toolsets/mcp/toolset_mcp.py +3 -1
- holmes/plugins/toolsets/newrelic.py +8 -4
- holmes/plugins/toolsets/opensearch/opensearch.py +13 -5
- holmes/plugins/toolsets/opensearch/opensearch_logs.py +4 -4
- holmes/plugins/toolsets/opensearch/opensearch_traces.py +9 -6
- holmes/plugins/toolsets/prometheus/prometheus.py +193 -82
- holmes/plugins/toolsets/rabbitmq/toolset_rabbitmq.py +7 -3
- holmes/plugins/toolsets/robusta/robusta.py +10 -4
- holmes/plugins/toolsets/runbook/runbook_fetcher.py +4 -2
- holmes/plugins/toolsets/servicenow/servicenow.py +9 -3
- holmes/plugins/toolsets/slab.yaml +1 -1
- {holmesgpt-0.13.1.dist-info → holmesgpt-0.13.2.dist-info}/METADATA +3 -2
- {holmesgpt-0.13.1.dist-info → holmesgpt-0.13.2.dist-info}/RECORD +75 -72
- holmes/core/todo_manager.py +0 -88
- {holmesgpt-0.13.1.dist-info → holmesgpt-0.13.2.dist-info}/LICENSE.txt +0 -0
- {holmesgpt-0.13.1.dist-info → holmesgpt-0.13.2.dist-info}/WHEEL +0 -0
- {holmesgpt-0.13.1.dist-info → holmesgpt-0.13.2.dist-info}/entry_points.txt +0 -0
holmes/core/tools.py
CHANGED
|
@@ -24,12 +24,15 @@ class ToolResultStatus(str, Enum):
|
|
|
24
24
|
SUCCESS = "success"
|
|
25
25
|
ERROR = "error"
|
|
26
26
|
NO_DATA = "no_data"
|
|
27
|
+
APPROVAL_REQUIRED = "approval_required"
|
|
27
28
|
|
|
28
29
|
def to_color(self) -> str:
|
|
29
30
|
if self == ToolResultStatus.SUCCESS:
|
|
30
31
|
return "green"
|
|
31
32
|
elif self == ToolResultStatus.ERROR:
|
|
32
33
|
return "red"
|
|
34
|
+
elif self == ToolResultStatus.APPROVAL_REQUIRED:
|
|
35
|
+
return "yellow"
|
|
33
36
|
else:
|
|
34
37
|
return "white"
|
|
35
38
|
|
|
@@ -38,6 +41,8 @@ class ToolResultStatus(str, Enum):
|
|
|
38
41
|
return "✔"
|
|
39
42
|
elif self == ToolResultStatus.ERROR:
|
|
40
43
|
return "❌"
|
|
44
|
+
elif self == ToolResultStatus.APPROVAL_REQUIRED:
|
|
45
|
+
return "⚠️"
|
|
41
46
|
else:
|
|
42
47
|
return "⚪️"
|
|
43
48
|
|
|
@@ -148,14 +153,17 @@ class Tool(ABC, BaseModel):
|
|
|
148
153
|
)
|
|
149
154
|
|
|
150
155
|
def invoke(
|
|
151
|
-
self,
|
|
156
|
+
self,
|
|
157
|
+
params: Dict,
|
|
158
|
+
tool_number: Optional[int] = None,
|
|
159
|
+
user_approved: bool = False,
|
|
152
160
|
) -> StructuredToolResult:
|
|
153
161
|
tool_number_str = f"#{tool_number} " if tool_number else ""
|
|
154
162
|
logging.info(
|
|
155
163
|
f"Running tool {tool_number_str}[bold]{self.name}[/bold]: {self.get_parameterized_one_liner(params)}"
|
|
156
164
|
)
|
|
157
165
|
start_time = time.time()
|
|
158
|
-
result = self._invoke(params)
|
|
166
|
+
result = self._invoke(params=params, user_approved=user_approved)
|
|
159
167
|
result.icon_url = self.icon_url
|
|
160
168
|
elapsed = time.time() - start_time
|
|
161
169
|
output_str = (
|
|
@@ -171,7 +179,13 @@ class Tool(ABC, BaseModel):
|
|
|
171
179
|
return result
|
|
172
180
|
|
|
173
181
|
@abstractmethod
|
|
174
|
-
def _invoke(
|
|
182
|
+
def _invoke(
|
|
183
|
+
self, params: dict, user_approved: bool = False
|
|
184
|
+
) -> StructuredToolResult:
|
|
185
|
+
"""
|
|
186
|
+
params: the tool params
|
|
187
|
+
user_approved: whether the tool call is approved by the user. Can be used to confidently execute unsafe actions.
|
|
188
|
+
"""
|
|
175
189
|
pass
|
|
176
190
|
|
|
177
191
|
@abstractmethod
|
|
@@ -223,7 +237,9 @@ class YAMLTool(Tool, BaseModel):
|
|
|
223
237
|
return ToolResultStatus.NO_DATA
|
|
224
238
|
return ToolResultStatus.SUCCESS
|
|
225
239
|
|
|
226
|
-
def _invoke(
|
|
240
|
+
def _invoke(
|
|
241
|
+
self, params: dict, user_approved: bool = False
|
|
242
|
+
) -> StructuredToolResult:
|
|
227
243
|
if self.command is not None:
|
|
228
244
|
raw_output, return_code, invocation = self.__invoke_command(params)
|
|
229
245
|
else:
|
holmes/interactive.py
CHANGED
|
@@ -27,10 +27,11 @@ from pygments.lexers import guess_lexer
|
|
|
27
27
|
from rich.console import Console
|
|
28
28
|
from rich.markdown import Markdown, Panel
|
|
29
29
|
|
|
30
|
+
from holmes.common.env_vars import ENABLE_CLI_TOOL_APPROVAL
|
|
30
31
|
from holmes.core.config import config_path_dir
|
|
31
32
|
from holmes.core.prompt import build_initial_ask_messages
|
|
32
33
|
from holmes.core.tool_calling_llm import ToolCallingLLM, ToolCallResult
|
|
33
|
-
from holmes.core.tools import pretty_print_toolset_status
|
|
34
|
+
from holmes.core.tools import StructuredToolResult, pretty_print_toolset_status
|
|
34
35
|
from holmes.core.tracing import DummyTracer
|
|
35
36
|
from holmes.utils.colors import (
|
|
36
37
|
AI_COLOR,
|
|
@@ -584,6 +585,53 @@ def prompt_for_llm_sharing(
|
|
|
584
585
|
return None
|
|
585
586
|
|
|
586
587
|
|
|
588
|
+
def handle_tool_approval(
|
|
589
|
+
command: Optional[str],
|
|
590
|
+
error_message: Optional[str],
|
|
591
|
+
style: Style,
|
|
592
|
+
console: Console,
|
|
593
|
+
) -> tuple[bool, Optional[str]]:
|
|
594
|
+
"""
|
|
595
|
+
Handle user approval for potentially sensitive commands.
|
|
596
|
+
|
|
597
|
+
Args:
|
|
598
|
+
command: The command that needs approval
|
|
599
|
+
error_message: The error message explaining why approval is needed
|
|
600
|
+
session: PromptSession for user input
|
|
601
|
+
style: Style for prompts
|
|
602
|
+
console: Rich console for output
|
|
603
|
+
|
|
604
|
+
Returns:
|
|
605
|
+
Tuple of (approved: bool, feedback: Optional[str])
|
|
606
|
+
- approved: True if user approves, False if denied
|
|
607
|
+
- feedback: User's optional feedback message when denying
|
|
608
|
+
"""
|
|
609
|
+
console.print("\n[bold yellow]⚠️ Command Approval Required[/bold yellow]")
|
|
610
|
+
console.print(f"[yellow]Command:[/yellow] {command or 'unknown'}")
|
|
611
|
+
console.print(f"[yellow]Reason:[/yellow] {error_message or 'unknown'}")
|
|
612
|
+
console.print()
|
|
613
|
+
|
|
614
|
+
# Create a temporary session without history for approval prompts
|
|
615
|
+
temp_session = PromptSession(history=InMemoryHistory()) # type: ignore
|
|
616
|
+
|
|
617
|
+
approval_prompt = temp_session.prompt(
|
|
618
|
+
[("class:prompt", "Do you want to approve and execute this command? (y/N): ")],
|
|
619
|
+
style=style,
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
if approval_prompt.lower().startswith("y"):
|
|
623
|
+
return True, None
|
|
624
|
+
else:
|
|
625
|
+
# Ask for optional feedback when denying
|
|
626
|
+
feedback_prompt = temp_session.prompt(
|
|
627
|
+
[("class:prompt", "Optional feedback for the AI (press Enter to skip): ")],
|
|
628
|
+
style=style,
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
feedback = feedback_prompt.strip() if feedback_prompt.strip() else None
|
|
632
|
+
return False, feedback
|
|
633
|
+
|
|
634
|
+
|
|
587
635
|
def handle_run_command(
|
|
588
636
|
bash_command: str, session: PromptSession, style: Style, console: Console
|
|
589
637
|
) -> Optional[str]:
|
|
@@ -811,6 +859,20 @@ def run_interactive_loop(
|
|
|
811
859
|
}
|
|
812
860
|
)
|
|
813
861
|
|
|
862
|
+
# Set up approval callback for potentially sensitive commands
|
|
863
|
+
def approval_handler(
|
|
864
|
+
tool_call_result: StructuredToolResult,
|
|
865
|
+
) -> tuple[bool, Optional[str]]:
|
|
866
|
+
return handle_tool_approval(
|
|
867
|
+
command=tool_call_result.invocation,
|
|
868
|
+
error_message=tool_call_result.error,
|
|
869
|
+
style=style,
|
|
870
|
+
console=console,
|
|
871
|
+
)
|
|
872
|
+
|
|
873
|
+
if ENABLE_CLI_TOOL_APPROVAL:
|
|
874
|
+
ai.approval_callback = approval_handler
|
|
875
|
+
|
|
814
876
|
# Create merged completer with slash commands, conditional executables, show command, and smart paths
|
|
815
877
|
slash_completer = SlashCommandCompleter()
|
|
816
878
|
executable_completer = ConditionalExecutableCompleter()
|
|
@@ -1002,7 +1064,6 @@ def run_interactive_loop(
|
|
|
1002
1064
|
user_input,
|
|
1003
1065
|
include_files,
|
|
1004
1066
|
ai.tool_executor,
|
|
1005
|
-
ai.investigation_id,
|
|
1006
1067
|
runbooks,
|
|
1007
1068
|
system_prompt_additions,
|
|
1008
1069
|
)
|
holmes/main.py
CHANGED
|
@@ -62,10 +62,12 @@
|
|
|
62
62
|
* Follow ALL tasks in your plan - don't skip any tasks
|
|
63
63
|
* Use task management to ensure you don't miss important investigation steps
|
|
64
64
|
* If you discover additional steps during investigation, add them to your task list using TodoWrite
|
|
65
|
+
* When calling TodoWrite, you may ALSO call other tools in parallel to speed things up for your users and make them happy!
|
|
66
|
+
* On the first TodoWrite call, mark at least one task as in_progress, and start working on it in parallel.
|
|
67
|
+
* When calling TodoWrite for the first time, mark the tasks you started working on with ‘in_progress’ status.
|
|
65
68
|
|
|
66
69
|
# Tool/function calls
|
|
67
70
|
|
|
68
71
|
You are able to make tool calls / function calls. Recognise when a tool has already been called and reuse its result.
|
|
69
72
|
If a tool call returns nothing, modify the parameters as required instead of repeating the tool call.
|
|
70
73
|
When searching for resources in specific namespaces, test a cluster level tool to find the resource(s) and identify what namespace they are part of.
|
|
71
|
-
You are limited in use to a maximum of 5 tool calls for each specific tool. Therefore make sure are smart about what tools you call and how you call them.
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
{% if investigation_id %}
|
|
2
|
-
# Investigation ID for this session
|
|
3
|
-
Investigation id: {{ investigation_id }}
|
|
4
|
-
{% endif %}
|
|
5
|
-
|
|
6
1
|
CLARIFICATION REQUIREMENT: Before starting ANY investigation, if the user's question is ambiguous or lacks critical details, you MUST ask for clarification first. Do NOT create TodoWrite tasks for unclear questions.
|
|
7
2
|
Only proceed with TodoWrite and investigation AFTER you have clear, specific requirements.
|
|
8
3
|
|
|
@@ -16,8 +11,8 @@ MANDATORY Task Status Updates:
|
|
|
16
11
|
- When completing a task: Call TodoWrite changing that task's status to "completed"
|
|
17
12
|
|
|
18
13
|
PARALLEL EXECUTION RULES:
|
|
19
|
-
- When possible, work on multiple tasks at a time.
|
|
20
|
-
- You
|
|
14
|
+
- When possible, work on multiple tasks at a time. Only when tasks depend on one another, do them one after the other.
|
|
15
|
+
- You SHOULD execute multiple INDEPENDENT tasks simultaneously
|
|
21
16
|
- Mark multiple tasks as "in_progress" if they don't depend on each other
|
|
22
17
|
- Wait for dependent tasks to complete before starting tasks that need their results
|
|
23
18
|
- Always use a single TodoWrite call to update multiple task statuses
|
|
@@ -84,17 +79,12 @@ If you see ANY `[ ] pending` or `[~] in_progress` tasks, DO NOT provide final an
|
|
|
84
79
|
{"id": "3", "content": "Check resources", "status": "pending"}
|
|
85
80
|
])
|
|
86
81
|
|
|
87
|
-
|
|
88
|
-
{% if todo_list %}
|
|
89
|
-
{{ todo_list }}
|
|
90
|
-
{% endif %}
|
|
91
|
-
|
|
92
82
|
# MANDATORY Multi-Phase Investigation Process
|
|
93
83
|
|
|
94
84
|
For ANY question requiring investigation, you MUST follow this structured approach:
|
|
95
85
|
|
|
96
86
|
## Phase 1: Initial Investigation
|
|
97
|
-
1. **IMMEDIATELY START with TodoWrite**: Create initial investigation task list
|
|
87
|
+
1. **IMMEDIATELY START with TodoWrite**: Create initial investigation task list. Already start working on tasks. Mark the tasks you're working on as in_progress.
|
|
98
88
|
2. **Execute ALL tasks systematically**: Mark each task in_progress → completed
|
|
99
89
|
3. **Complete EVERY task** in the current list before proceeding
|
|
100
90
|
|
|
@@ -26,6 +26,9 @@ from holmes.plugins.toolsets.datadog.toolset_datadog_traces import (
|
|
|
26
26
|
from holmes.plugins.toolsets.datadog.toolset_datadog_rds import (
|
|
27
27
|
DatadogRDSToolset,
|
|
28
28
|
)
|
|
29
|
+
from holmes.plugins.toolsets.datadog.toolset_datadog_general import (
|
|
30
|
+
DatadogGeneralToolset,
|
|
31
|
+
)
|
|
29
32
|
from holmes.plugins.toolsets.git import GitToolset
|
|
30
33
|
from holmes.plugins.toolsets.grafana.toolset_grafana import GrafanaToolset
|
|
31
34
|
from holmes.plugins.toolsets.grafana.toolset_grafana_loki import GrafanaLokiToolset
|
|
@@ -82,6 +85,7 @@ def load_python_toolsets(dal: Optional[SupabaseDal]) -> List[Toolset]:
|
|
|
82
85
|
NotionToolset(),
|
|
83
86
|
KafkaToolset(),
|
|
84
87
|
DatadogLogsToolset(),
|
|
88
|
+
DatadogGeneralToolset(),
|
|
85
89
|
DatadogMetricsToolset(),
|
|
86
90
|
DatadogTracesToolset(),
|
|
87
91
|
DatadogRDSToolset(),
|
|
@@ -155,7 +159,7 @@ def load_toolsets_from_config(
|
|
|
155
159
|
|
|
156
160
|
loaded_toolsets: list[Toolset] = []
|
|
157
161
|
if is_old_toolset_config(toolsets):
|
|
158
|
-
message = "Old toolset config format detected, please update to the new format: https://
|
|
162
|
+
message = "Old toolset config format detected, please update to the new format: https://holmesgpt.dev/data-sources/custom-toolsets/"
|
|
159
163
|
logging.warning(message)
|
|
160
164
|
raise ValueError(message)
|
|
161
165
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
toolsets:
|
|
2
2
|
argocd/core:
|
|
3
3
|
description: "Set of tools to get argocd metadata like list of apps, repositories, projects, etc."
|
|
4
|
-
docs_url: "https://
|
|
4
|
+
docs_url: "https://holmesgpt.dev/data-sources/builtin-toolsets/argocd/"
|
|
5
5
|
icon_url: "https://argo-cd.readthedocs.io/en/stable/assets/logo.png"
|
|
6
6
|
llm_instructions: |
|
|
7
7
|
You have access to a set of ArgoCD tools for debugging Kubernetes application deployments.
|
|
@@ -118,7 +118,9 @@ class ReturnProjectAlerts(MongoDBAtlasBaseTool):
|
|
|
118
118
|
project_id = self.toolset.config.get("project_id", "")
|
|
119
119
|
return f"{toolset_name_for_one_liner(self.toolset.name)}: Get Project Alerts ({project_id})"
|
|
120
120
|
|
|
121
|
-
def _invoke(
|
|
121
|
+
def _invoke(
|
|
122
|
+
self, params: dict, user_approved: bool = False
|
|
123
|
+
) -> StructuredToolResult:
|
|
122
124
|
try:
|
|
123
125
|
url = "https://cloud.mongodb.com/api/atlas/v2/groups/{project_id}/alerts".format(
|
|
124
126
|
project_id=self.toolset.config.get("project_id")
|
|
@@ -143,7 +145,9 @@ class ReturnProjectProcesses(MongoDBAtlasBaseTool):
|
|
|
143
145
|
project_id = self.toolset.config.get("project_id", "")
|
|
144
146
|
return f"{toolset_name_for_one_liner(self.toolset.name)}: Get Project Processes ({project_id})"
|
|
145
147
|
|
|
146
|
-
def _invoke(
|
|
148
|
+
def _invoke(
|
|
149
|
+
self, params: dict, user_approved: bool = False
|
|
150
|
+
) -> StructuredToolResult:
|
|
147
151
|
try:
|
|
148
152
|
url = "https://cloud.mongodb.com/api/atlas/v2/groups/{project_id}/processes".format(
|
|
149
153
|
project_id=self.toolset.config.get("project_id")
|
|
@@ -176,7 +180,9 @@ class ReturnProjectSlowQueries(MongoDBAtlasBaseTool):
|
|
|
176
180
|
process_id = params.get("process_id", "")
|
|
177
181
|
return f"{toolset_name_for_one_liner(self.toolset.name)}: Get Slow Queries ({process_id})"
|
|
178
182
|
|
|
179
|
-
def _invoke(
|
|
183
|
+
def _invoke(
|
|
184
|
+
self, params: dict, user_approved: bool = False
|
|
185
|
+
) -> StructuredToolResult:
|
|
180
186
|
try:
|
|
181
187
|
url = self.url.format(
|
|
182
188
|
project_id=self.toolset.config.get("project_id"),
|
|
@@ -203,7 +209,9 @@ class ReturnEventsFromProject(MongoDBAtlasBaseTool):
|
|
|
203
209
|
project_id = self.toolset.config.get("project_id", "")
|
|
204
210
|
return f"{toolset_name_for_one_liner(self.toolset.name)}: Get Project Events ({project_id})"
|
|
205
211
|
|
|
206
|
-
def _invoke(
|
|
212
|
+
def _invoke(
|
|
213
|
+
self, params: dict, user_approved: bool = False
|
|
214
|
+
) -> StructuredToolResult:
|
|
207
215
|
params.update({"itemsPerPage": 500})
|
|
208
216
|
try:
|
|
209
217
|
now_utc = datetime.now(timezone.utc)
|
|
@@ -260,7 +268,9 @@ class ReturnLogsForProcessInProject(MongoDBAtlasBaseTool):
|
|
|
260
268
|
hostname = params.get("hostName", "")
|
|
261
269
|
return f"{toolset_name_for_one_liner(self.toolset.name)}: Get Host Logs ({hostname})"
|
|
262
270
|
|
|
263
|
-
def _invoke(
|
|
271
|
+
def _invoke(
|
|
272
|
+
self, params: dict, user_approved: bool = False
|
|
273
|
+
) -> StructuredToolResult:
|
|
264
274
|
one_hour_ago = datetime.now(timezone.utc) - timedelta(hours=1)
|
|
265
275
|
try:
|
|
266
276
|
url = self.url.format(
|
|
@@ -312,7 +322,9 @@ class ReturnEventTypeFromProject(MongoDBAtlasBaseTool):
|
|
|
312
322
|
event_type = params.get("eventType", "")
|
|
313
323
|
return f"{toolset_name_for_one_liner(self.toolset.name)}: Get Event Details ({event_type})"
|
|
314
324
|
|
|
315
|
-
def _invoke(
|
|
325
|
+
def _invoke(
|
|
326
|
+
self, params: dict, user_approved: bool = False
|
|
327
|
+
) -> StructuredToolResult:
|
|
316
328
|
try:
|
|
317
329
|
url = self.url.format(projectId=self.toolset.config.get("project_id"))
|
|
318
330
|
|
holmes/plugins/toolsets/aws.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
toolsets:
|
|
2
2
|
aws/security:
|
|
3
3
|
description: "Set of tools to audit AWS security"
|
|
4
|
-
docs_url: "https://
|
|
4
|
+
docs_url: "https://holmesgpt.dev/data-sources/builtin-toolsets/aws/"
|
|
5
5
|
icon_url: "https://upload.wikimedia.org/wikipedia/commons/9/93/Amazon_Web_Services_Logo.svg"
|
|
6
6
|
tags:
|
|
7
7
|
- cli
|
|
@@ -42,8 +42,12 @@ toolsets:
|
|
|
42
42
|
|
|
43
43
|
aws/rds:
|
|
44
44
|
description: "Read access to Amazon RDS resources"
|
|
45
|
-
docs_url: "https://
|
|
45
|
+
docs_url: "https://holmesgpt.dev/data-sources/builtin-toolsets/aws/"
|
|
46
46
|
icon_url: "https://upload.wikimedia.org/wikipedia/commons/9/93/Amazon_Web_Services_Logo.svg"
|
|
47
|
+
llm_instructions: |
|
|
48
|
+
You have access to information on RDS resources.
|
|
49
|
+
Use this toolset to get extra information on RDS resources on AWS.
|
|
50
|
+
When investigating RDS resources ALWAYS fetch additional information if possible.
|
|
47
51
|
tags:
|
|
48
52
|
- cli
|
|
49
53
|
prerequisites:
|
|
@@ -56,8 +60,8 @@ toolsets:
|
|
|
56
60
|
command: "aws rds describe-events"
|
|
57
61
|
|
|
58
62
|
- name: "aws_rds_describe_instance"
|
|
59
|
-
description: "Get the configuration of a RDS instance"
|
|
60
|
-
user_description: "Get the configuration of
|
|
63
|
+
description: "Get the configuration of a RDS instance, its status and availability stats. Runs 'aws rds describe-db-instances' for a specific instance."
|
|
64
|
+
user_description: "Get the configuration of an RDS instance"
|
|
61
65
|
command: "aws rds describe-db-instances --db-instance-identifier '{{ db_instance_identifier }}'"
|
|
62
66
|
|
|
63
67
|
- name: "aws_rds_describe_instances"
|
|
@@ -66,7 +70,7 @@ toolsets:
|
|
|
66
70
|
command: "aws rds describe-db-instances"
|
|
67
71
|
|
|
68
72
|
- name: "aws_rds_describe_logs"
|
|
69
|
-
description: "Describe all available logs for an AWS RDS instance."
|
|
73
|
+
description: "Describe all available logs for an AWS RDS instance. Runs 'aws rds describe-db-log-files' for a specific instance."
|
|
70
74
|
user_description: "list available RDS logs (e.g. slow query logs)"
|
|
71
75
|
command: "aws rds describe-db-log-files --db-instance-identifier '{{ db_instance_identifier }}'"
|
|
72
76
|
|
|
@@ -213,7 +213,9 @@ class AnalyzeConnectionFailures(BaseAzureSQLTool):
|
|
|
213
213
|
|
|
214
214
|
return "\n".join(report_sections)
|
|
215
215
|
|
|
216
|
-
def _invoke(
|
|
216
|
+
def _invoke(
|
|
217
|
+
self, params: dict, user_approved: bool = False
|
|
218
|
+
) -> StructuredToolResult:
|
|
217
219
|
try:
|
|
218
220
|
# Get configuration
|
|
219
221
|
db_config = self.toolset.database_config()
|
|
@@ -151,7 +151,9 @@ class AnalyzeDatabaseConnections(BaseAzureSQLTool):
|
|
|
151
151
|
|
|
152
152
|
return "\n".join(report_sections)
|
|
153
153
|
|
|
154
|
-
def _invoke(
|
|
154
|
+
def _invoke(
|
|
155
|
+
self, params: dict, user_approved: bool = False
|
|
156
|
+
) -> StructuredToolResult:
|
|
155
157
|
try:
|
|
156
158
|
hours_back = params.get("hours_back", 2)
|
|
157
159
|
|
|
@@ -131,7 +131,9 @@ class AnalyzeDatabaseHealthStatus(BaseAzureSQLTool):
|
|
|
131
131
|
|
|
132
132
|
return "\n".join(report_sections)
|
|
133
133
|
|
|
134
|
-
def _invoke(
|
|
134
|
+
def _invoke(
|
|
135
|
+
self, params: dict, user_approved: bool = False
|
|
136
|
+
) -> StructuredToolResult:
|
|
135
137
|
try:
|
|
136
138
|
db_config = self.toolset.database_config()
|
|
137
139
|
client = self.toolset.api_client()
|
|
@@ -192,7 +192,9 @@ class AnalyzeDatabasePerformance(BaseAzureSQLTool):
|
|
|
192
192
|
|
|
193
193
|
return "\n".join(report_sections)
|
|
194
194
|
|
|
195
|
-
def _invoke(
|
|
195
|
+
def _invoke(
|
|
196
|
+
self, params: dict, user_approved: bool = False
|
|
197
|
+
) -> StructuredToolResult:
|
|
196
198
|
try:
|
|
197
199
|
db_config = self.toolset.database_config()
|
|
198
200
|
client = self.toolset.api_client()
|
|
@@ -249,7 +249,9 @@ class AnalyzeDatabaseStorage(BaseAzureSQLTool):
|
|
|
249
249
|
|
|
250
250
|
return "\n".join(report_sections)
|
|
251
251
|
|
|
252
|
-
def _invoke(
|
|
252
|
+
def _invoke(
|
|
253
|
+
self, params: dict, user_approved: bool = False
|
|
254
|
+
) -> StructuredToolResult:
|
|
253
255
|
try:
|
|
254
256
|
hours_back = params.get("hours_back", 24)
|
|
255
257
|
top_tables = params.get("top_tables", 20)
|
|
@@ -147,7 +147,9 @@ class GetActiveAlerts(BaseAzureSQLTool):
|
|
|
147
147
|
|
|
148
148
|
return "\n".join(report_sections)
|
|
149
149
|
|
|
150
|
-
def _invoke(
|
|
150
|
+
def _invoke(
|
|
151
|
+
self, params: dict, user_approved: bool = False
|
|
152
|
+
) -> StructuredToolResult:
|
|
151
153
|
try:
|
|
152
154
|
db_config = self.toolset.database_config()
|
|
153
155
|
api_client = self.toolset.api_client()
|
|
@@ -99,7 +99,9 @@ class GetSlowQueries(BaseAzureSQLTool):
|
|
|
99
99
|
|
|
100
100
|
return "\n".join(report_sections)
|
|
101
101
|
|
|
102
|
-
def _invoke(
|
|
102
|
+
def _invoke(
|
|
103
|
+
self, params: dict, user_approved: bool = False
|
|
104
|
+
) -> StructuredToolResult:
|
|
103
105
|
try:
|
|
104
106
|
top_count = params.get("top_count", 15)
|
|
105
107
|
hours_back = params.get("hours_back", 2)
|
|
@@ -97,7 +97,9 @@ class GetTopCPUQueries(BaseAzureSQLTool):
|
|
|
97
97
|
|
|
98
98
|
return "\n".join(report_sections)
|
|
99
99
|
|
|
100
|
-
def _invoke(
|
|
100
|
+
def _invoke(
|
|
101
|
+
self, params: dict, user_approved: bool = False
|
|
102
|
+
) -> StructuredToolResult:
|
|
101
103
|
try:
|
|
102
104
|
top_count = params.get("top_count", 15)
|
|
103
105
|
hours_back = params.get("hours_back", 2)
|
|
@@ -115,7 +115,9 @@ class GetTopDataIOQueries(BaseAzureSQLTool):
|
|
|
115
115
|
|
|
116
116
|
return "\n".join(report_sections)
|
|
117
117
|
|
|
118
|
-
def _invoke(
|
|
118
|
+
def _invoke(
|
|
119
|
+
self, params: dict, user_approved: bool = False
|
|
120
|
+
) -> StructuredToolResult:
|
|
119
121
|
try:
|
|
120
122
|
top_count = params.get("top_count", 15)
|
|
121
123
|
hours_back = params.get("hours_back", 2)
|
|
@@ -107,7 +107,9 @@ class GetTopLogIOQueries(BaseAzureSQLTool):
|
|
|
107
107
|
|
|
108
108
|
return "\n".join(report_sections)
|
|
109
109
|
|
|
110
|
-
def _invoke(
|
|
110
|
+
def _invoke(
|
|
111
|
+
self, params: dict, user_approved: bool = False
|
|
112
|
+
) -> StructuredToolResult:
|
|
111
113
|
try:
|
|
112
114
|
top_count = params.get("top_count", 15)
|
|
113
115
|
hours_back = params.get("hours_back", 2)
|
|
@@ -9,7 +9,9 @@ from typing import Dict, Any, Optional
|
|
|
9
9
|
import sentry_sdk
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
from holmes.common.env_vars import
|
|
12
|
+
from holmes.common.env_vars import (
|
|
13
|
+
BASH_TOOL_UNSAFE_ALLOW_ALL,
|
|
14
|
+
)
|
|
13
15
|
from holmes.core.tools import (
|
|
14
16
|
CallablePrerequisite,
|
|
15
17
|
StructuredToolResult,
|
|
@@ -80,7 +82,9 @@ class KubectlRunImageCommand(BaseBashTool):
|
|
|
80
82
|
command_str = get_param_or_raise(params, "command")
|
|
81
83
|
return f"kubectl run {pod_name} --image={image} --namespace={namespace} --rm --attach --restart=Never -i -- {command_str}"
|
|
82
84
|
|
|
83
|
-
def _invoke(
|
|
85
|
+
def _invoke(
|
|
86
|
+
self, params: dict, user_approved: bool = False
|
|
87
|
+
) -> StructuredToolResult:
|
|
84
88
|
timeout = params.get("timeout", 60)
|
|
85
89
|
|
|
86
90
|
image = get_param_or_raise(params, "image")
|
|
@@ -160,7 +164,9 @@ class RunBashCommand(BaseBashTool):
|
|
|
160
164
|
toolset=toolset,
|
|
161
165
|
)
|
|
162
166
|
|
|
163
|
-
def _invoke(
|
|
167
|
+
def _invoke(
|
|
168
|
+
self, params: dict, user_approved: bool = False
|
|
169
|
+
) -> StructuredToolResult:
|
|
164
170
|
command_str = params.get("command")
|
|
165
171
|
timeout = params.get("timeout", 60)
|
|
166
172
|
|
|
@@ -179,23 +185,28 @@ class RunBashCommand(BaseBashTool):
|
|
|
179
185
|
)
|
|
180
186
|
|
|
181
187
|
command_to_execute = command_str
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
188
|
+
|
|
189
|
+
# Only run the safety check if user has NOT approved the command
|
|
190
|
+
if not user_approved:
|
|
191
|
+
try:
|
|
192
|
+
command_to_execute = make_command_safe(command_str, self.toolset.config)
|
|
193
|
+
|
|
194
|
+
except (argparse.ArgumentError, ValueError) as e:
|
|
195
|
+
with sentry_sdk.configure_scope() as scope:
|
|
196
|
+
scope.set_extra("command", command_str)
|
|
197
|
+
scope.set_extra("error", str(e))
|
|
198
|
+
scope.set_extra("unsafe_allow_all", BASH_TOOL_UNSAFE_ALLOW_ALL)
|
|
199
|
+
sentry_sdk.capture_exception(e)
|
|
200
|
+
|
|
201
|
+
if not BASH_TOOL_UNSAFE_ALLOW_ALL:
|
|
202
|
+
logging.info(f"Refusing LLM tool call {command_str}")
|
|
203
|
+
|
|
204
|
+
return StructuredToolResult(
|
|
205
|
+
status=ToolResultStatus.APPROVAL_REQUIRED,
|
|
206
|
+
error=f"Refusing to execute bash command. {str(e)}",
|
|
207
|
+
params=params,
|
|
208
|
+
invocation=command_str,
|
|
209
|
+
)
|
|
199
210
|
|
|
200
211
|
return execute_bash_command(
|
|
201
212
|
cmd=command_to_execute, timeout=timeout, params=params
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
toolsets:
|
|
2
2
|
confluence:
|
|
3
3
|
description: "Fetch confluence pages"
|
|
4
|
-
docs_url: "https://
|
|
4
|
+
docs_url: "https://holmesgpt.dev/data-sources/builtin-toolsets/confluence/"
|
|
5
5
|
icon_url: "https://platform.robusta.dev/demos/confluence.svg"
|
|
6
6
|
tags:
|
|
7
7
|
- core
|
|
@@ -106,7 +106,9 @@ def query_logs_for_tier(
|
|
|
106
106
|
)
|
|
107
107
|
http_status = response.status_code
|
|
108
108
|
if http_status == 200:
|
|
109
|
-
logs = parse_logs(
|
|
109
|
+
logs = parse_logs(
|
|
110
|
+
raw_logs=response.text.strip(), labels_config=config.labels
|
|
111
|
+
)
|
|
110
112
|
return CoralogixQueryResult(logs=logs, http_status=http_status, error=None)
|
|
111
113
|
else:
|
|
112
114
|
return CoralogixQueryResult(
|
|
@@ -38,14 +38,14 @@ class CoralogixLogsToolset(BasePodLoggingToolset):
|
|
|
38
38
|
super().__init__(
|
|
39
39
|
name="coralogix/logs",
|
|
40
40
|
description="Toolset for interacting with Coralogix to fetch logs",
|
|
41
|
-
docs_url="https://
|
|
41
|
+
docs_url="https://holmesgpt.dev/data-sources/builtin-toolsets/coralogix-logs/",
|
|
42
42
|
icon_url="https://avatars.githubusercontent.com/u/35295744?s=200&v=4",
|
|
43
43
|
prerequisites=[CallablePrerequisite(callable=self.prerequisites_callable)],
|
|
44
|
-
tools=[
|
|
45
|
-
PodLoggingTool(self),
|
|
46
|
-
],
|
|
44
|
+
tools=[], # Initialize with empty tools first
|
|
47
45
|
tags=[ToolsetTag.CORE],
|
|
48
46
|
)
|
|
47
|
+
# Now that parent is initialized and self.name exists, create the tool
|
|
48
|
+
self.tools = [PodLoggingTool(self)]
|
|
49
49
|
|
|
50
50
|
def get_example_config(self):
|
|
51
51
|
example_config = CoralogixConfig(
|