holmesgpt 0.13.2__py3-none-any.whl → 0.16.2a0__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.
Files changed (134) hide show
  1. holmes/__init__.py +1 -1
  2. holmes/clients/robusta_client.py +17 -4
  3. holmes/common/env_vars.py +40 -1
  4. holmes/config.py +114 -144
  5. holmes/core/conversations.py +53 -14
  6. holmes/core/feedback.py +191 -0
  7. holmes/core/investigation.py +18 -22
  8. holmes/core/llm.py +489 -88
  9. holmes/core/models.py +103 -1
  10. holmes/core/openai_formatting.py +13 -0
  11. holmes/core/prompt.py +1 -1
  12. holmes/core/safeguards.py +4 -4
  13. holmes/core/supabase_dal.py +293 -100
  14. holmes/core/tool_calling_llm.py +423 -323
  15. holmes/core/tools.py +311 -33
  16. holmes/core/tools_utils/token_counting.py +14 -0
  17. holmes/core/tools_utils/tool_context_window_limiter.py +57 -0
  18. holmes/core/tools_utils/tool_executor.py +13 -8
  19. holmes/core/toolset_manager.py +155 -4
  20. holmes/core/tracing.py +6 -1
  21. holmes/core/transformers/__init__.py +23 -0
  22. holmes/core/transformers/base.py +62 -0
  23. holmes/core/transformers/llm_summarize.py +174 -0
  24. holmes/core/transformers/registry.py +122 -0
  25. holmes/core/transformers/transformer.py +31 -0
  26. holmes/core/truncation/compaction.py +59 -0
  27. holmes/core/truncation/dal_truncation_utils.py +23 -0
  28. holmes/core/truncation/input_context_window_limiter.py +218 -0
  29. holmes/interactive.py +177 -24
  30. holmes/main.py +7 -4
  31. holmes/plugins/prompts/_fetch_logs.jinja2 +26 -1
  32. holmes/plugins/prompts/_general_instructions.jinja2 +1 -2
  33. holmes/plugins/prompts/_runbook_instructions.jinja2 +23 -12
  34. holmes/plugins/prompts/conversation_history_compaction.jinja2 +88 -0
  35. holmes/plugins/prompts/generic_ask.jinja2 +2 -4
  36. holmes/plugins/prompts/generic_ask_conversation.jinja2 +2 -1
  37. holmes/plugins/prompts/generic_ask_for_issue_conversation.jinja2 +2 -1
  38. holmes/plugins/prompts/generic_investigation.jinja2 +2 -1
  39. holmes/plugins/prompts/investigation_procedure.jinja2 +48 -0
  40. holmes/plugins/prompts/kubernetes_workload_ask.jinja2 +2 -1
  41. holmes/plugins/prompts/kubernetes_workload_chat.jinja2 +2 -1
  42. holmes/plugins/runbooks/__init__.py +117 -18
  43. holmes/plugins/runbooks/catalog.json +2 -0
  44. holmes/plugins/toolsets/__init__.py +21 -8
  45. holmes/plugins/toolsets/aks-node-health.yaml +46 -0
  46. holmes/plugins/toolsets/aks.yaml +64 -0
  47. holmes/plugins/toolsets/atlas_mongodb/mongodb_atlas.py +26 -36
  48. holmes/plugins/toolsets/azure_sql/azure_sql_toolset.py +0 -1
  49. holmes/plugins/toolsets/azure_sql/tools/analyze_connection_failures.py +10 -7
  50. holmes/plugins/toolsets/azure_sql/tools/analyze_database_connections.py +9 -6
  51. holmes/plugins/toolsets/azure_sql/tools/analyze_database_health_status.py +8 -6
  52. holmes/plugins/toolsets/azure_sql/tools/analyze_database_performance.py +8 -6
  53. holmes/plugins/toolsets/azure_sql/tools/analyze_database_storage.py +9 -6
  54. holmes/plugins/toolsets/azure_sql/tools/get_active_alerts.py +9 -7
  55. holmes/plugins/toolsets/azure_sql/tools/get_slow_queries.py +9 -6
  56. holmes/plugins/toolsets/azure_sql/tools/get_top_cpu_queries.py +9 -6
  57. holmes/plugins/toolsets/azure_sql/tools/get_top_data_io_queries.py +9 -6
  58. holmes/plugins/toolsets/azure_sql/tools/get_top_log_io_queries.py +9 -6
  59. holmes/plugins/toolsets/bash/bash_toolset.py +10 -13
  60. holmes/plugins/toolsets/bash/common/bash.py +7 -7
  61. holmes/plugins/toolsets/cilium.yaml +284 -0
  62. holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py +5 -3
  63. holmes/plugins/toolsets/datadog/datadog_api.py +490 -24
  64. holmes/plugins/toolsets/datadog/datadog_logs_instructions.jinja2 +21 -10
  65. holmes/plugins/toolsets/datadog/toolset_datadog_general.py +349 -216
  66. holmes/plugins/toolsets/datadog/toolset_datadog_logs.py +190 -19
  67. holmes/plugins/toolsets/datadog/toolset_datadog_metrics.py +101 -44
  68. holmes/plugins/toolsets/datadog/toolset_datadog_rds.py +13 -16
  69. holmes/plugins/toolsets/datadog/toolset_datadog_traces.py +25 -31
  70. holmes/plugins/toolsets/git.py +51 -46
  71. holmes/plugins/toolsets/grafana/common.py +15 -3
  72. holmes/plugins/toolsets/grafana/grafana_api.py +46 -24
  73. holmes/plugins/toolsets/grafana/grafana_tempo_api.py +454 -0
  74. holmes/plugins/toolsets/grafana/loki/instructions.jinja2 +9 -0
  75. holmes/plugins/toolsets/grafana/loki/toolset_grafana_loki.py +117 -0
  76. holmes/plugins/toolsets/grafana/toolset_grafana.py +211 -91
  77. holmes/plugins/toolsets/grafana/toolset_grafana_dashboard.jinja2 +27 -0
  78. holmes/plugins/toolsets/grafana/toolset_grafana_tempo.jinja2 +246 -11
  79. holmes/plugins/toolsets/grafana/toolset_grafana_tempo.py +653 -293
  80. holmes/plugins/toolsets/grafana/trace_parser.py +1 -1
  81. holmes/plugins/toolsets/internet/internet.py +6 -7
  82. holmes/plugins/toolsets/internet/notion.py +5 -6
  83. holmes/plugins/toolsets/investigator/core_investigation.py +42 -34
  84. holmes/plugins/toolsets/kafka.py +25 -36
  85. holmes/plugins/toolsets/kubernetes.yaml +58 -84
  86. holmes/plugins/toolsets/kubernetes_logs.py +6 -6
  87. holmes/plugins/toolsets/kubernetes_logs.yaml +32 -0
  88. holmes/plugins/toolsets/logging_utils/logging_api.py +80 -4
  89. holmes/plugins/toolsets/mcp/toolset_mcp.py +181 -55
  90. holmes/plugins/toolsets/newrelic/__init__.py +0 -0
  91. holmes/plugins/toolsets/newrelic/new_relic_api.py +125 -0
  92. holmes/plugins/toolsets/newrelic/newrelic.jinja2 +41 -0
  93. holmes/plugins/toolsets/newrelic/newrelic.py +163 -0
  94. holmes/plugins/toolsets/opensearch/opensearch.py +10 -17
  95. holmes/plugins/toolsets/opensearch/opensearch_logs.py +7 -7
  96. holmes/plugins/toolsets/opensearch/opensearch_ppl_query_docs.jinja2 +1616 -0
  97. holmes/plugins/toolsets/opensearch/opensearch_query_assist.py +78 -0
  98. holmes/plugins/toolsets/opensearch/opensearch_query_assist_instructions.jinja2 +223 -0
  99. holmes/plugins/toolsets/opensearch/opensearch_traces.py +13 -16
  100. holmes/plugins/toolsets/openshift.yaml +283 -0
  101. holmes/plugins/toolsets/prometheus/prometheus.py +915 -390
  102. holmes/plugins/toolsets/prometheus/prometheus_instructions.jinja2 +43 -2
  103. holmes/plugins/toolsets/prometheus/utils.py +28 -0
  104. holmes/plugins/toolsets/rabbitmq/toolset_rabbitmq.py +9 -10
  105. holmes/plugins/toolsets/robusta/robusta.py +236 -65
  106. holmes/plugins/toolsets/robusta/robusta_instructions.jinja2 +26 -9
  107. holmes/plugins/toolsets/runbook/runbook_fetcher.py +137 -26
  108. holmes/plugins/toolsets/service_discovery.py +1 -1
  109. holmes/plugins/toolsets/servicenow_tables/instructions.jinja2 +83 -0
  110. holmes/plugins/toolsets/servicenow_tables/servicenow_tables.py +426 -0
  111. holmes/plugins/toolsets/utils.py +88 -0
  112. holmes/utils/config_utils.py +91 -0
  113. holmes/utils/default_toolset_installation_guide.jinja2 +1 -22
  114. holmes/utils/env.py +7 -0
  115. holmes/utils/global_instructions.py +75 -10
  116. holmes/utils/holmes_status.py +2 -1
  117. holmes/utils/holmes_sync_toolsets.py +0 -2
  118. holmes/utils/krr_utils.py +188 -0
  119. holmes/utils/sentry_helper.py +41 -0
  120. holmes/utils/stream.py +61 -7
  121. holmes/version.py +34 -14
  122. holmesgpt-0.16.2a0.dist-info/LICENSE +178 -0
  123. {holmesgpt-0.13.2.dist-info → holmesgpt-0.16.2a0.dist-info}/METADATA +29 -27
  124. {holmesgpt-0.13.2.dist-info → holmesgpt-0.16.2a0.dist-info}/RECORD +126 -102
  125. holmes/core/performance_timing.py +0 -72
  126. holmes/plugins/toolsets/grafana/tempo_api.py +0 -124
  127. holmes/plugins/toolsets/grafana/toolset_grafana_loki.py +0 -110
  128. holmes/plugins/toolsets/newrelic.py +0 -231
  129. holmes/plugins/toolsets/servicenow/install.md +0 -37
  130. holmes/plugins/toolsets/servicenow/instructions.jinja2 +0 -3
  131. holmes/plugins/toolsets/servicenow/servicenow.py +0 -219
  132. holmesgpt-0.13.2.dist-info/LICENSE.txt +0 -21
  133. {holmesgpt-0.13.2.dist-info → holmesgpt-0.16.2a0.dist-info}/WHEEL +0 -0
  134. {holmesgpt-0.13.2.dist-info → holmesgpt-0.16.2a0.dist-info}/entry_points.txt +0 -0
holmes/core/models.py CHANGED
@@ -1,15 +1,94 @@
1
+ import json
1
2
  from holmes.core.investigation_structured_output import InputSectionsDataType
2
- from holmes.core.tool_calling_llm import ToolCallResult
3
3
  from typing import Optional, List, Dict, Any, Union
4
4
  from pydantic import BaseModel, model_validator, Field
5
5
  from enum import Enum
6
6
 
7
+ from holmes.core.tools import StructuredToolResult, StructuredToolResultStatus
8
+
9
+
10
+ class TruncationMetadata(BaseModel):
11
+ tool_call_id: str
12
+ start_index: int
13
+ end_index: int
14
+ tool_name: str
15
+ original_token_count: int
16
+
17
+
18
+ class TruncationResult(BaseModel):
19
+ truncated_messages: list[dict]
20
+ truncations: list[TruncationMetadata]
21
+
22
+
23
+ class ToolCallResult(BaseModel):
24
+ tool_call_id: str
25
+ tool_name: str
26
+ description: str
27
+ result: StructuredToolResult
28
+ size: Optional[int] = None
29
+
30
+ def as_tool_call_message(self):
31
+ return {
32
+ "tool_call_id": self.tool_call_id,
33
+ "role": "tool",
34
+ "name": self.tool_name,
35
+ "content": format_tool_result_data(self.result),
36
+ }
37
+
38
+ def as_tool_result_response(self):
39
+ result_dump = self.result.model_dump()
40
+ result_dump["data"] = self.result.get_stringified_data()
41
+
42
+ return {
43
+ "tool_call_id": self.tool_call_id,
44
+ "tool_name": self.tool_name,
45
+ "description": self.description,
46
+ "role": "tool",
47
+ "result": result_dump,
48
+ }
49
+
50
+ def as_streaming_tool_result_response(self):
51
+ result_dump = self.result.model_dump()
52
+ result_dump["data"] = self.result.get_stringified_data()
53
+
54
+ return {
55
+ "tool_call_id": self.tool_call_id,
56
+ "role": "tool",
57
+ "description": self.description,
58
+ "name": self.tool_name,
59
+ "result": result_dump,
60
+ }
61
+
62
+
63
+ def format_tool_result_data(tool_result: StructuredToolResult) -> str:
64
+ tool_response = tool_result.data
65
+ if isinstance(tool_result.data, str):
66
+ tool_response = tool_result.data
67
+ else:
68
+ try:
69
+ if isinstance(tool_result.data, BaseModel):
70
+ tool_response = tool_result.data.model_dump_json(indent=2)
71
+ else:
72
+ tool_response = json.dumps(tool_result.data, indent=2)
73
+ except Exception:
74
+ tool_response = str(tool_result.data)
75
+ if tool_result.status == StructuredToolResultStatus.ERROR:
76
+ tool_response = f"{tool_result.error or 'Tool execution failed'}:\n\n{tool_result.data or ''}".strip()
77
+
78
+ if tool_result.params:
79
+ tool_response = (
80
+ f"Params used for the tool call: {json.dumps(tool_result.params)}. The tool call output follows on the next line.\n"
81
+ + tool_response
82
+ )
83
+ return tool_response
84
+
7
85
 
8
86
  class InvestigationResult(BaseModel):
9
87
  analysis: Optional[str] = None
10
88
  sections: Optional[Dict[str, Union[str, None]]] = None
11
89
  tool_calls: List[ToolCallResult] = []
12
90
  instructions: List[str] = []
91
+ metadata: Optional[Dict[Any, Any]] = None
13
92
 
14
93
 
15
94
  class InvestigateRequest(BaseModel):
@@ -86,10 +165,31 @@ class ConversationRequest(BaseModel):
86
165
  include_tool_call_results: bool = False
87
166
 
88
167
 
168
+ class PendingToolApproval(BaseModel):
169
+ """Represents a tool call that requires user approval."""
170
+
171
+ tool_call_id: str
172
+ tool_name: str
173
+ description: str
174
+ params: Dict[str, Any]
175
+
176
+
177
+ class ToolApprovalDecision(BaseModel):
178
+ """Represents a user's decision on a tool approval."""
179
+
180
+ tool_call_id: str
181
+ approved: bool
182
+
183
+
89
184
  class ChatRequestBaseModel(BaseModel):
90
185
  conversation_history: Optional[list[dict]] = None
91
186
  model: Optional[str] = None
92
187
  stream: bool = Field(default=False)
188
+ enable_tool_approval: Optional[bool] = (
189
+ False # Optional boolean for backwards compatibility
190
+ )
191
+ tool_decisions: Optional[List[ToolApprovalDecision]] = None
192
+ additional_system_prompt: Optional[str] = None
93
193
 
94
194
  # In our setup with litellm, the first message in conversation_history
95
195
  # should follow the structure [{"role": "system", "content": ...}],
@@ -145,6 +245,8 @@ class ChatResponse(BaseModel):
145
245
  conversation_history: list[dict]
146
246
  tool_calls: Optional[List[ToolCallResult]] = []
147
247
  follow_up_actions: Optional[List[FollowUpAction]] = []
248
+ pending_approvals: Optional[List[PendingToolApproval]] = None
249
+ metadata: Optional[Dict[Any, Any]] = None
148
250
 
149
251
 
150
252
  class WorkloadHealthInvestigationResult(BaseModel):
@@ -80,6 +80,19 @@ def format_tool_to_open_ai_standard(
80
80
  )
81
81
  if param_attributes.description is not None:
82
82
  tool_properties[param_name]["description"] = param_attributes.description
83
+ # Add enum constraint if specified
84
+ if hasattr(param_attributes, "enum") and param_attributes.enum:
85
+ enum_values = list(
86
+ param_attributes.enum
87
+ ) # Create a copy to avoid modifying original
88
+ # In strict mode, optional parameters need None in their enum to match the type allowing null
89
+ if (
90
+ strict_mode
91
+ and not param_attributes.required
92
+ and None not in enum_values
93
+ ):
94
+ enum_values.append(None)
95
+ tool_properties[param_name]["enum"] = enum_values
83
96
 
84
97
  result: dict[str, Any] = {
85
98
  "type": "function",
holmes/core/prompt.py CHANGED
@@ -57,7 +57,7 @@ def build_initial_ask_messages(
57
57
  system_prompt_template = "builtin://generic_ask.jinja2"
58
58
  template_context = {
59
59
  "toolsets": tool_executor.toolsets,
60
- "runbooks": runbooks or {},
60
+ "runbooks_enabled": True if runbooks else False,
61
61
  "system_prompt_additions": system_prompt_additions or "",
62
62
  }
63
63
  system_prompt_rendered = load_and_render_prompt(
holmes/core/safeguards.py CHANGED
@@ -5,7 +5,7 @@ from pydantic import ValidationError
5
5
 
6
6
  from holmes.common.env_vars import TOOL_CALL_SAFEGUARDS_ENABLED
7
7
  from holmes.plugins.toolsets.logging_utils.logging_api import POD_LOGGING_TOOL_NAME
8
- from holmes.core.tools import StructuredToolResult, ToolResultStatus
8
+ from holmes.core.tools import StructuredToolResult, StructuredToolResultStatus
9
9
  from holmes.plugins.toolsets.logging_utils.logging_api import FetchPodLogsParams
10
10
 
11
11
 
@@ -39,7 +39,7 @@ def _has_previous_unfiltered_pod_logs_call(
39
39
  result = tool_call.get("result", {})
40
40
  if (
41
41
  tool_call.get("tool_name") == POD_LOGGING_TOOL_NAME
42
- and result.get("status") == ToolResultStatus.NO_DATA
42
+ and result.get("status") == StructuredToolResultStatus.NO_DATA
43
43
  and result.get("params")
44
44
  ):
45
45
  params = FetchPodLogsParams(**result.get("params"))
@@ -94,7 +94,7 @@ def prevent_overly_repeated_tool_call(
94
94
  For example if Holmes checks if a resource is deployed, runs a command to deploy it and then checks again if it has deployed properly.
95
95
  """
96
96
  return StructuredToolResult(
97
- status=ToolResultStatus.ERROR,
97
+ status=StructuredToolResultStatus.ERROR,
98
98
  error=(
99
99
  "Refusing to run this tool call because it has already been called during this session with the exact same parameters.\n"
100
100
  "Move on with your investigation to a different tool or change the parameter values."
@@ -106,7 +106,7 @@ def prevent_overly_repeated_tool_call(
106
106
  tool_name=tool_name, tool_params=tool_params, tool_calls=tool_calls
107
107
  ):
108
108
  return StructuredToolResult(
109
- status=ToolResultStatus.ERROR,
109
+ status=StructuredToolResultStatus.ERROR,
110
110
  error=(
111
111
  f"Refusing to run this tool call because the exact same {POD_LOGGING_TOOL_NAME} tool call without filter has already run and returned no data.\n"
112
112
  "This tool call would also have returned no data.\n"