holmesgpt 0.13.1__py3-none-any.whl → 0.13.3__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.

Potentially problematic release.


This version of holmesgpt might be problematic. Click here for more details.

Files changed (76) hide show
  1. holmes/__init__.py +1 -1
  2. holmes/common/env_vars.py +7 -0
  3. holmes/config.py +3 -1
  4. holmes/core/conversations.py +0 -11
  5. holmes/core/investigation.py +0 -6
  6. holmes/core/llm.py +60 -1
  7. holmes/core/prompt.py +0 -2
  8. holmes/core/supabase_dal.py +2 -2
  9. holmes/core/todo_tasks_formatter.py +51 -0
  10. holmes/core/tool_calling_llm.py +166 -91
  11. holmes/core/tools.py +20 -4
  12. holmes/interactive.py +63 -2
  13. holmes/main.py +0 -1
  14. holmes/plugins/prompts/_general_instructions.jinja2 +3 -1
  15. holmes/plugins/prompts/investigation_procedure.jinja2 +3 -13
  16. holmes/plugins/toolsets/__init__.py +5 -1
  17. holmes/plugins/toolsets/argocd.yaml +1 -1
  18. holmes/plugins/toolsets/atlas_mongodb/mongodb_atlas.py +18 -6
  19. holmes/plugins/toolsets/aws.yaml +9 -5
  20. holmes/plugins/toolsets/azure_sql/tools/analyze_connection_failures.py +3 -1
  21. holmes/plugins/toolsets/azure_sql/tools/analyze_database_connections.py +3 -1
  22. holmes/plugins/toolsets/azure_sql/tools/analyze_database_health_status.py +3 -1
  23. holmes/plugins/toolsets/azure_sql/tools/analyze_database_performance.py +3 -1
  24. holmes/plugins/toolsets/azure_sql/tools/analyze_database_storage.py +3 -1
  25. holmes/plugins/toolsets/azure_sql/tools/get_active_alerts.py +3 -1
  26. holmes/plugins/toolsets/azure_sql/tools/get_slow_queries.py +3 -1
  27. holmes/plugins/toolsets/azure_sql/tools/get_top_cpu_queries.py +3 -1
  28. holmes/plugins/toolsets/azure_sql/tools/get_top_data_io_queries.py +3 -1
  29. holmes/plugins/toolsets/azure_sql/tools/get_top_log_io_queries.py +3 -1
  30. holmes/plugins/toolsets/bash/bash_toolset.py +31 -20
  31. holmes/plugins/toolsets/confluence.yaml +1 -1
  32. holmes/plugins/toolsets/coralogix/api.py +3 -1
  33. holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py +4 -4
  34. holmes/plugins/toolsets/coralogix/utils.py +41 -14
  35. holmes/plugins/toolsets/datadog/datadog_api.py +45 -2
  36. holmes/plugins/toolsets/datadog/datadog_general_instructions.jinja2 +208 -0
  37. holmes/plugins/toolsets/datadog/datadog_logs_instructions.jinja2 +43 -0
  38. holmes/plugins/toolsets/datadog/datadog_metrics_instructions.jinja2 +12 -9
  39. holmes/plugins/toolsets/datadog/toolset_datadog_general.py +722 -0
  40. holmes/plugins/toolsets/datadog/toolset_datadog_logs.py +17 -6
  41. holmes/plugins/toolsets/datadog/toolset_datadog_metrics.py +15 -7
  42. holmes/plugins/toolsets/datadog/toolset_datadog_rds.py +6 -2
  43. holmes/plugins/toolsets/datadog/toolset_datadog_traces.py +9 -3
  44. holmes/plugins/toolsets/docker.yaml +1 -1
  45. holmes/plugins/toolsets/git.py +15 -5
  46. holmes/plugins/toolsets/grafana/toolset_grafana.py +25 -4
  47. holmes/plugins/toolsets/grafana/toolset_grafana_loki.py +4 -4
  48. holmes/plugins/toolsets/grafana/toolset_grafana_tempo.jinja2 +5 -3
  49. holmes/plugins/toolsets/grafana/toolset_grafana_tempo.py +299 -32
  50. holmes/plugins/toolsets/helm.yaml +1 -1
  51. holmes/plugins/toolsets/internet/internet.py +4 -2
  52. holmes/plugins/toolsets/internet/notion.py +4 -2
  53. holmes/plugins/toolsets/investigator/core_investigation.py +5 -17
  54. holmes/plugins/toolsets/investigator/investigator_instructions.jinja2 +1 -5
  55. holmes/plugins/toolsets/kafka.py +19 -7
  56. holmes/plugins/toolsets/kubernetes.yaml +5 -5
  57. holmes/plugins/toolsets/kubernetes_logs.py +4 -4
  58. holmes/plugins/toolsets/kubernetes_logs.yaml +1 -1
  59. holmes/plugins/toolsets/logging_utils/logging_api.py +15 -2
  60. holmes/plugins/toolsets/mcp/toolset_mcp.py +3 -1
  61. holmes/plugins/toolsets/newrelic.py +8 -4
  62. holmes/plugins/toolsets/opensearch/opensearch.py +13 -5
  63. holmes/plugins/toolsets/opensearch/opensearch_logs.py +4 -4
  64. holmes/plugins/toolsets/opensearch/opensearch_traces.py +9 -6
  65. holmes/plugins/toolsets/prometheus/prometheus.py +193 -82
  66. holmes/plugins/toolsets/rabbitmq/toolset_rabbitmq.py +7 -3
  67. holmes/plugins/toolsets/robusta/robusta.py +10 -4
  68. holmes/plugins/toolsets/runbook/runbook_fetcher.py +4 -2
  69. holmes/plugins/toolsets/servicenow/servicenow.py +9 -3
  70. holmes/plugins/toolsets/slab.yaml +1 -1
  71. {holmesgpt-0.13.1.dist-info → holmesgpt-0.13.3.dist-info}/METADATA +3 -2
  72. {holmesgpt-0.13.1.dist-info → holmesgpt-0.13.3.dist-info}/RECORD +75 -72
  73. holmes/core/todo_manager.py +0 -88
  74. {holmesgpt-0.13.1.dist-info → holmesgpt-0.13.3.dist-info}/LICENSE.txt +0 -0
  75. {holmesgpt-0.13.1.dist-info → holmesgpt-0.13.3.dist-info}/WHEEL +0 -0
  76. {holmesgpt-0.13.1.dist-info → holmesgpt-0.13.3.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, params: Dict, tool_number: Optional[int] = None
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(self, params: Dict) -> StructuredToolResult:
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(self, params) -> StructuredToolResult:
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
@@ -308,7 +308,6 @@ def ask(
308
308
  prompt, # type: ignore
309
309
  include_file,
310
310
  ai.tool_executor,
311
- ai.investigation_id,
312
311
  config.get_runbook_catalog(),
313
312
  system_prompt_additions,
314
313
  )
@@ -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. If tasks depend on one another, do them one after the other.
20
- - You MAY execute multiple INDEPENDENT tasks simultaneously
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://docs.robusta.dev/master/configuration/holmesgpt/custom_toolsets.html"
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://docs.robusta.dev/master/configuration/holmesgpt/toolsets/argocd.html"
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(self, params: Any) -> StructuredToolResult:
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(self, params: Any) -> StructuredToolResult:
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(self, params: Any) -> StructuredToolResult:
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(self, params: Any) -> StructuredToolResult:
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(self, params: Any) -> StructuredToolResult:
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(self, params: Any) -> StructuredToolResult:
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
 
@@ -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://docs.robusta.dev/master/configuration/holmesgpt/toolsets/aws.html#security"
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://docs.robusta.dev/master/configuration/holmesgpt/toolsets/aws.html#rds"
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 a RDS instance"
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(self, params: Dict) -> StructuredToolResult:
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(self, params: Dict) -> StructuredToolResult:
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(self, params: Dict) -> StructuredToolResult:
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(self, params: Dict) -> StructuredToolResult:
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(self, params: Dict) -> StructuredToolResult:
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(self, params: Dict) -> StructuredToolResult:
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(self, params: Dict) -> StructuredToolResult:
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(self, params: Dict) -> StructuredToolResult:
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(self, params: Dict) -> StructuredToolResult:
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(self, params: Dict) -> StructuredToolResult:
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 BASH_TOOL_UNSAFE_ALLOW_ALL
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(self, params: Dict[str, Any]) -> StructuredToolResult:
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(self, params: Dict[str, Any]) -> StructuredToolResult:
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
- try:
183
- command_to_execute = make_command_safe(command_str, self.toolset.config)
184
-
185
- except (argparse.ArgumentError, ValueError) as e:
186
- with sentry_sdk.configure_scope() as scope:
187
- scope.set_extra("command", command_str)
188
- scope.set_extra("error", str(e))
189
- scope.set_extra("unsafe_allow_all", BASH_TOOL_UNSAFE_ALLOW_ALL)
190
- sentry_sdk.capture_exception(e)
191
-
192
- if not BASH_TOOL_UNSAFE_ALLOW_ALL:
193
- logging.info(f"Refusing LLM tool call {command_str}")
194
- return StructuredToolResult(
195
- status=ToolResultStatus.ERROR,
196
- error=f"Refusing to execute bash command. Only some commands are supported and this is likely because requested command is unsupported. Error: {str(e)}",
197
- params=params,
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://docs.robusta.dev/master/configuration/holmesgpt/toolsets/confluence.html"
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(raw_logs=response.text.strip())
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://docs.robusta.dev/master/configuration/holmesgpt/toolsets/coralogix_logs.html",
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(