holmesgpt 0.13.2__py3-none-any.whl → 0.18.4__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 +3 -5
- holmes/clients/robusta_client.py +20 -6
- holmes/common/env_vars.py +58 -3
- holmes/common/openshift.py +1 -1
- holmes/config.py +123 -148
- holmes/core/conversations.py +71 -15
- holmes/core/feedback.py +191 -0
- holmes/core/investigation.py +31 -39
- holmes/core/investigation_structured_output.py +3 -3
- holmes/core/issue.py +1 -1
- holmes/core/llm.py +508 -88
- holmes/core/models.py +108 -4
- holmes/core/openai_formatting.py +14 -1
- holmes/core/prompt.py +48 -3
- holmes/core/runbooks.py +1 -0
- holmes/core/safeguards.py +8 -6
- holmes/core/supabase_dal.py +295 -100
- holmes/core/tool_calling_llm.py +489 -428
- holmes/core/tools.py +325 -56
- holmes/core/tools_utils/token_counting.py +21 -0
- holmes/core/tools_utils/tool_context_window_limiter.py +40 -0
- holmes/core/tools_utils/tool_executor.py +0 -13
- holmes/core/tools_utils/toolset_utils.py +1 -0
- holmes/core/toolset_manager.py +191 -5
- holmes/core/tracing.py +19 -3
- holmes/core/transformers/__init__.py +23 -0
- holmes/core/transformers/base.py +63 -0
- holmes/core/transformers/llm_summarize.py +175 -0
- holmes/core/transformers/registry.py +123 -0
- holmes/core/transformers/transformer.py +32 -0
- holmes/core/truncation/compaction.py +94 -0
- holmes/core/truncation/dal_truncation_utils.py +23 -0
- holmes/core/truncation/input_context_window_limiter.py +219 -0
- holmes/interactive.py +228 -31
- holmes/main.py +23 -40
- holmes/plugins/interfaces.py +2 -1
- holmes/plugins/prompts/__init__.py +2 -1
- holmes/plugins/prompts/_fetch_logs.jinja2 +31 -6
- holmes/plugins/prompts/_general_instructions.jinja2 +1 -2
- holmes/plugins/prompts/_runbook_instructions.jinja2 +24 -12
- holmes/plugins/prompts/base_user_prompt.jinja2 +7 -0
- holmes/plugins/prompts/conversation_history_compaction.jinja2 +89 -0
- holmes/plugins/prompts/generic_ask.jinja2 +0 -4
- holmes/plugins/prompts/generic_ask_conversation.jinja2 +0 -1
- holmes/plugins/prompts/generic_ask_for_issue_conversation.jinja2 +0 -1
- holmes/plugins/prompts/generic_investigation.jinja2 +0 -1
- holmes/plugins/prompts/investigation_procedure.jinja2 +50 -1
- holmes/plugins/prompts/kubernetes_workload_ask.jinja2 +0 -1
- holmes/plugins/prompts/kubernetes_workload_chat.jinja2 +0 -1
- holmes/plugins/runbooks/__init__.py +145 -17
- holmes/plugins/runbooks/catalog.json +2 -0
- holmes/plugins/sources/github/__init__.py +4 -2
- holmes/plugins/sources/prometheus/models.py +1 -0
- holmes/plugins/toolsets/__init__.py +44 -27
- holmes/plugins/toolsets/aks-node-health.yaml +46 -0
- holmes/plugins/toolsets/aks.yaml +64 -0
- holmes/plugins/toolsets/atlas_mongodb/mongodb_atlas.py +38 -47
- holmes/plugins/toolsets/azure_sql/apis/alert_monitoring_api.py +3 -2
- holmes/plugins/toolsets/azure_sql/apis/azure_sql_api.py +2 -1
- holmes/plugins/toolsets/azure_sql/apis/connection_failure_api.py +3 -2
- holmes/plugins/toolsets/azure_sql/apis/connection_monitoring_api.py +3 -1
- holmes/plugins/toolsets/azure_sql/apis/storage_analysis_api.py +3 -1
- holmes/plugins/toolsets/azure_sql/azure_sql_toolset.py +12 -13
- holmes/plugins/toolsets/azure_sql/tools/analyze_connection_failures.py +15 -12
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_connections.py +15 -12
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_health_status.py +11 -11
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_performance.py +11 -9
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_storage.py +15 -12
- holmes/plugins/toolsets/azure_sql/tools/get_active_alerts.py +15 -15
- holmes/plugins/toolsets/azure_sql/tools/get_slow_queries.py +11 -8
- holmes/plugins/toolsets/azure_sql/tools/get_top_cpu_queries.py +11 -8
- holmes/plugins/toolsets/azure_sql/tools/get_top_data_io_queries.py +11 -8
- holmes/plugins/toolsets/azure_sql/tools/get_top_log_io_queries.py +11 -8
- holmes/plugins/toolsets/azure_sql/utils.py +0 -32
- holmes/plugins/toolsets/bash/argocd/__init__.py +3 -3
- holmes/plugins/toolsets/bash/aws/__init__.py +4 -4
- holmes/plugins/toolsets/bash/azure/__init__.py +4 -4
- holmes/plugins/toolsets/bash/bash_toolset.py +11 -15
- holmes/plugins/toolsets/bash/common/bash.py +23 -13
- holmes/plugins/toolsets/bash/common/bash_command.py +1 -1
- holmes/plugins/toolsets/bash/common/stringify.py +1 -1
- holmes/plugins/toolsets/bash/kubectl/__init__.py +2 -1
- holmes/plugins/toolsets/bash/kubectl/constants.py +0 -1
- holmes/plugins/toolsets/bash/kubectl/kubectl_get.py +3 -4
- holmes/plugins/toolsets/bash/parse_command.py +12 -13
- holmes/plugins/toolsets/cilium.yaml +284 -0
- holmes/plugins/toolsets/connectivity_check.py +124 -0
- holmes/plugins/toolsets/coralogix/api.py +132 -119
- holmes/plugins/toolsets/coralogix/coralogix.jinja2 +14 -0
- holmes/plugins/toolsets/coralogix/toolset_coralogix.py +219 -0
- holmes/plugins/toolsets/coralogix/utils.py +15 -79
- holmes/plugins/toolsets/datadog/datadog_api.py +525 -26
- holmes/plugins/toolsets/datadog/datadog_logs_instructions.jinja2 +55 -11
- holmes/plugins/toolsets/datadog/datadog_metrics_instructions.jinja2 +3 -3
- holmes/plugins/toolsets/datadog/datadog_models.py +59 -0
- holmes/plugins/toolsets/datadog/datadog_url_utils.py +213 -0
- holmes/plugins/toolsets/datadog/instructions_datadog_traces.jinja2 +165 -28
- holmes/plugins/toolsets/datadog/toolset_datadog_general.py +417 -241
- holmes/plugins/toolsets/datadog/toolset_datadog_logs.py +234 -214
- holmes/plugins/toolsets/datadog/toolset_datadog_metrics.py +167 -79
- holmes/plugins/toolsets/datadog/toolset_datadog_traces.py +374 -363
- holmes/plugins/toolsets/elasticsearch/__init__.py +6 -0
- holmes/plugins/toolsets/elasticsearch/elasticsearch.py +834 -0
- holmes/plugins/toolsets/elasticsearch/opensearch_ppl_query_docs.jinja2 +1616 -0
- holmes/plugins/toolsets/elasticsearch/opensearch_query_assist.py +78 -0
- holmes/plugins/toolsets/elasticsearch/opensearch_query_assist_instructions.jinja2 +223 -0
- holmes/plugins/toolsets/git.py +54 -50
- holmes/plugins/toolsets/grafana/base_grafana_toolset.py +16 -4
- holmes/plugins/toolsets/grafana/common.py +13 -29
- holmes/plugins/toolsets/grafana/grafana_tempo_api.py +455 -0
- holmes/plugins/toolsets/grafana/loki/instructions.jinja2 +25 -0
- holmes/plugins/toolsets/grafana/loki/toolset_grafana_loki.py +191 -0
- holmes/plugins/toolsets/grafana/loki_api.py +4 -0
- holmes/plugins/toolsets/grafana/toolset_grafana.py +293 -89
- holmes/plugins/toolsets/grafana/toolset_grafana_dashboard.jinja2 +49 -0
- holmes/plugins/toolsets/grafana/toolset_grafana_tempo.jinja2 +246 -11
- holmes/plugins/toolsets/grafana/toolset_grafana_tempo.py +820 -292
- holmes/plugins/toolsets/grafana/trace_parser.py +4 -3
- holmes/plugins/toolsets/internet/internet.py +15 -16
- holmes/plugins/toolsets/internet/notion.py +9 -11
- holmes/plugins/toolsets/investigator/core_investigation.py +44 -36
- holmes/plugins/toolsets/investigator/model.py +3 -1
- holmes/plugins/toolsets/json_filter_mixin.py +134 -0
- holmes/plugins/toolsets/kafka.py +36 -42
- holmes/plugins/toolsets/kubernetes.yaml +317 -113
- holmes/plugins/toolsets/kubernetes_logs.py +9 -9
- holmes/plugins/toolsets/kubernetes_logs.yaml +32 -0
- holmes/plugins/toolsets/logging_utils/logging_api.py +94 -8
- holmes/plugins/toolsets/mcp/toolset_mcp.py +218 -64
- holmes/plugins/toolsets/newrelic/new_relic_api.py +165 -0
- holmes/plugins/toolsets/newrelic/newrelic.jinja2 +65 -0
- holmes/plugins/toolsets/newrelic/newrelic.py +320 -0
- holmes/plugins/toolsets/openshift.yaml +283 -0
- holmes/plugins/toolsets/prometheus/prometheus.py +1202 -421
- holmes/plugins/toolsets/prometheus/prometheus_instructions.jinja2 +54 -5
- holmes/plugins/toolsets/prometheus/utils.py +28 -0
- holmes/plugins/toolsets/rabbitmq/api.py +23 -4
- holmes/plugins/toolsets/rabbitmq/toolset_rabbitmq.py +13 -14
- holmes/plugins/toolsets/robusta/robusta.py +239 -68
- holmes/plugins/toolsets/robusta/robusta_instructions.jinja2 +26 -9
- holmes/plugins/toolsets/runbook/runbook_fetcher.py +157 -27
- holmes/plugins/toolsets/service_discovery.py +1 -1
- holmes/plugins/toolsets/servicenow_tables/instructions.jinja2 +83 -0
- holmes/plugins/toolsets/servicenow_tables/servicenow_tables.py +426 -0
- holmes/plugins/toolsets/utils.py +88 -0
- holmes/utils/config_utils.py +91 -0
- holmes/utils/connection_utils.py +31 -0
- holmes/utils/console/result.py +10 -0
- holmes/utils/default_toolset_installation_guide.jinja2 +1 -22
- holmes/utils/env.py +7 -0
- holmes/utils/file_utils.py +2 -1
- holmes/utils/global_instructions.py +60 -11
- holmes/utils/holmes_status.py +6 -4
- holmes/utils/holmes_sync_toolsets.py +0 -2
- holmes/utils/krr_utils.py +188 -0
- holmes/utils/log.py +15 -0
- holmes/utils/markdown_utils.py +2 -3
- holmes/utils/memory_limit.py +58 -0
- holmes/utils/sentry_helper.py +64 -0
- holmes/utils/stream.py +69 -8
- holmes/utils/tags.py +4 -3
- holmes/version.py +37 -15
- holmesgpt-0.18.4.dist-info/LICENSE +178 -0
- {holmesgpt-0.13.2.dist-info → holmesgpt-0.18.4.dist-info}/METADATA +35 -31
- holmesgpt-0.18.4.dist-info/RECORD +258 -0
- holmes/core/performance_timing.py +0 -72
- holmes/plugins/toolsets/aws.yaml +0 -80
- holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py +0 -112
- holmes/plugins/toolsets/datadog/datadog_traces_formatter.py +0 -310
- holmes/plugins/toolsets/datadog/toolset_datadog_rds.py +0 -739
- holmes/plugins/toolsets/grafana/grafana_api.py +0 -42
- holmes/plugins/toolsets/grafana/tempo_api.py +0 -124
- holmes/plugins/toolsets/grafana/toolset_grafana_loki.py +0 -110
- holmes/plugins/toolsets/newrelic.py +0 -231
- holmes/plugins/toolsets/opensearch/opensearch.py +0 -257
- holmes/plugins/toolsets/opensearch/opensearch_logs.py +0 -161
- holmes/plugins/toolsets/opensearch/opensearch_traces.py +0 -218
- holmes/plugins/toolsets/opensearch/opensearch_traces_instructions.jinja2 +0 -12
- holmes/plugins/toolsets/opensearch/opensearch_utils.py +0 -166
- holmes/plugins/toolsets/servicenow/install.md +0 -37
- holmes/plugins/toolsets/servicenow/instructions.jinja2 +0 -3
- holmes/plugins/toolsets/servicenow/servicenow.py +0 -219
- holmes/utils/keygen_utils.py +0 -6
- holmesgpt-0.13.2.dist-info/LICENSE.txt +0 -21
- holmesgpt-0.13.2.dist-info/RECORD +0 -234
- /holmes/plugins/toolsets/{opensearch → newrelic}/__init__.py +0 -0
- {holmesgpt-0.13.2.dist-info → holmesgpt-0.18.4.dist-info}/WHEEL +0 -0
- {holmesgpt-0.13.2.dist-info → holmesgpt-0.18.4.dist-info}/entry_points.txt +0 -0
|
@@ -1,63 +1,174 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import os
|
|
2
3
|
import textwrap
|
|
3
|
-
from
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Dict, List, Optional, Union, cast
|
|
4
6
|
|
|
7
|
+
from holmes.core.supabase_dal import SupabaseDal
|
|
5
8
|
from holmes.core.tools import (
|
|
6
9
|
StructuredToolResult,
|
|
10
|
+
StructuredToolResultStatus,
|
|
7
11
|
Tool,
|
|
12
|
+
ToolInvokeContext,
|
|
8
13
|
ToolParameter,
|
|
9
|
-
ToolResultStatus,
|
|
10
14
|
Toolset,
|
|
11
15
|
ToolsetTag,
|
|
12
16
|
)
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
from holmes.plugins.runbooks import (
|
|
18
|
+
DEFAULT_RUNBOOK_SEARCH_PATH,
|
|
19
|
+
get_runbook_by_path,
|
|
20
|
+
load_runbook_catalog,
|
|
21
|
+
)
|
|
15
22
|
from holmes.plugins.toolsets.utils import toolset_name_for_one_liner
|
|
16
23
|
|
|
17
24
|
|
|
18
|
-
# TODO(mainred): currently we support fetch runbooks hosted internally, in the future we may want to support fetching
|
|
19
|
-
# runbooks from external sources as well.
|
|
20
25
|
class RunbookFetcher(Tool):
|
|
21
26
|
toolset: "RunbookToolset"
|
|
27
|
+
available_runbooks: List[str] = []
|
|
28
|
+
additional_search_paths: Optional[List[str]] = None
|
|
29
|
+
_dal: Optional[SupabaseDal] = None
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
toolset: "RunbookToolset",
|
|
34
|
+
additional_search_paths: Optional[List[str]] = None,
|
|
35
|
+
dal: Optional[SupabaseDal] = None,
|
|
36
|
+
custom_catalog_paths: Optional[List[Union[str, Path]]] = None,
|
|
37
|
+
):
|
|
38
|
+
catalog = load_runbook_catalog(
|
|
39
|
+
dal=dal, custom_catalog_paths=custom_catalog_paths
|
|
40
|
+
)
|
|
41
|
+
available_runbooks = []
|
|
42
|
+
if catalog:
|
|
43
|
+
available_runbooks = catalog.list_available_runbooks()
|
|
44
|
+
|
|
45
|
+
if additional_search_paths:
|
|
46
|
+
for search_path in additional_search_paths:
|
|
47
|
+
if not os.path.isdir(search_path):
|
|
48
|
+
continue
|
|
49
|
+
|
|
50
|
+
for file in os.listdir(search_path):
|
|
51
|
+
if file.endswith(".md") and file not in available_runbooks:
|
|
52
|
+
available_runbooks.append(f"{file}")
|
|
53
|
+
|
|
54
|
+
runbook_list = ", ".join([f'"{rb}"' for rb in available_runbooks])
|
|
22
55
|
|
|
23
|
-
def __init__(self, toolset: "RunbookToolset"):
|
|
24
56
|
super().__init__(
|
|
25
57
|
name="fetch_runbook",
|
|
26
58
|
description="Get runbook content by runbook link. Use this to get troubleshooting steps for incidents",
|
|
27
59
|
parameters={
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
description="The link to the runbook",
|
|
60
|
+
"runbook_id": ToolParameter(
|
|
61
|
+
description=f"The runbook_id: either a UUID or a .md filename. Must be one of: {runbook_list}",
|
|
31
62
|
type="string",
|
|
32
63
|
required=True,
|
|
33
64
|
),
|
|
34
65
|
},
|
|
35
|
-
toolset=toolset, # type: ignore
|
|
66
|
+
toolset=toolset, # type: ignore[call-arg]
|
|
67
|
+
available_runbooks=available_runbooks, # type: ignore[call-arg]
|
|
68
|
+
additional_search_paths=additional_search_paths, # type: ignore[call-arg]
|
|
36
69
|
)
|
|
70
|
+
self._dal = dal
|
|
71
|
+
|
|
72
|
+
def _invoke(self, params: dict, context: ToolInvokeContext) -> StructuredToolResult:
|
|
73
|
+
runbook_id: str = params.get("runbook_id", "")
|
|
74
|
+
is_md_file: bool = True if runbook_id.endswith(".md") else False
|
|
75
|
+
|
|
76
|
+
# Validate link is not empty
|
|
77
|
+
if not runbook_id or not runbook_id.strip():
|
|
78
|
+
err_msg = (
|
|
79
|
+
"Runbook link cannot be empty. Please provide a valid runbook path."
|
|
80
|
+
)
|
|
81
|
+
logging.error(err_msg)
|
|
82
|
+
return StructuredToolResult(
|
|
83
|
+
status=StructuredToolResultStatus.ERROR,
|
|
84
|
+
error=err_msg,
|
|
85
|
+
params=params,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if is_md_file:
|
|
89
|
+
return self._get_md_runbook(runbook_id, params)
|
|
90
|
+
else:
|
|
91
|
+
return self._get_robusta_runbook(runbook_id, params)
|
|
37
92
|
|
|
38
|
-
def
|
|
39
|
-
self
|
|
40
|
-
|
|
41
|
-
|
|
93
|
+
def _get_robusta_runbook(self, link: str, params: dict) -> StructuredToolResult:
|
|
94
|
+
if self._dal and self._dal.enabled:
|
|
95
|
+
try:
|
|
96
|
+
runbook_content = self._dal.get_runbook_content(link)
|
|
97
|
+
if runbook_content:
|
|
98
|
+
return StructuredToolResult(
|
|
99
|
+
status=StructuredToolResultStatus.SUCCESS,
|
|
100
|
+
data=runbook_content.pretty(),
|
|
101
|
+
params=params,
|
|
102
|
+
)
|
|
103
|
+
else:
|
|
104
|
+
err_msg = f"Runbook with UUID '{link}' not found in remote storage."
|
|
105
|
+
logging.error(err_msg)
|
|
106
|
+
return StructuredToolResult(
|
|
107
|
+
status=StructuredToolResultStatus.ERROR,
|
|
108
|
+
error=err_msg,
|
|
109
|
+
params=params,
|
|
110
|
+
)
|
|
111
|
+
except Exception as e:
|
|
112
|
+
err_msg = f"Failed to fetch runbook with UUID '{link}': {str(e)}"
|
|
113
|
+
logging.error(err_msg)
|
|
114
|
+
return StructuredToolResult(
|
|
115
|
+
status=StructuredToolResultStatus.ERROR,
|
|
116
|
+
error=err_msg,
|
|
117
|
+
params=params,
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
err_msg = "Runbook link appears to be a UUID, but no remote data access layer (dal) is enabled."
|
|
121
|
+
logging.error(err_msg)
|
|
122
|
+
return StructuredToolResult(
|
|
123
|
+
status=StructuredToolResultStatus.ERROR,
|
|
124
|
+
error=err_msg,
|
|
125
|
+
params=params,
|
|
126
|
+
)
|
|
42
127
|
|
|
128
|
+
def _get_md_runbook(self, link: str, params: dict) -> StructuredToolResult:
|
|
43
129
|
search_paths = [DEFAULT_RUNBOOK_SEARCH_PATH]
|
|
44
|
-
if self.
|
|
45
|
-
search_paths.extend(self.
|
|
130
|
+
if self.additional_search_paths:
|
|
131
|
+
search_paths.extend(self.additional_search_paths)
|
|
132
|
+
# Validate link is in the available runbooks list OR is a valid path within allowed directories
|
|
133
|
+
if link not in self.available_runbooks:
|
|
134
|
+
# Check if the link would resolve to a valid path within allowed directories
|
|
135
|
+
# This prevents path traversal attacks like ../../secret.md
|
|
136
|
+
is_valid_path = False
|
|
137
|
+
for search_path in search_paths:
|
|
138
|
+
candidate_path = os.path.join(search_path, link)
|
|
139
|
+
# Canonicalize both paths to resolve any .. or . components
|
|
140
|
+
real_search_path = os.path.realpath(search_path)
|
|
141
|
+
real_candidate_path = os.path.realpath(candidate_path)
|
|
46
142
|
|
|
47
|
-
|
|
143
|
+
# Check if the resolved path is within the allowed directory
|
|
144
|
+
if (
|
|
145
|
+
real_candidate_path.startswith(real_search_path + os.sep)
|
|
146
|
+
or real_candidate_path == real_search_path
|
|
147
|
+
):
|
|
148
|
+
if os.path.isfile(real_candidate_path):
|
|
149
|
+
is_valid_path = True
|
|
150
|
+
break
|
|
48
151
|
|
|
152
|
+
if not is_valid_path:
|
|
153
|
+
err_msg = f"Invalid runbook link '{link}'. Must be one of: {', '.join(self.available_runbooks) if self.available_runbooks else 'No runbooks available'}"
|
|
154
|
+
logging.error(err_msg)
|
|
155
|
+
return StructuredToolResult(
|
|
156
|
+
status=StructuredToolResultStatus.ERROR,
|
|
157
|
+
error=err_msg,
|
|
158
|
+
params=params,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
runbook_path = get_runbook_by_path(link, search_paths)
|
|
49
162
|
if runbook_path is None:
|
|
50
163
|
err_msg = (
|
|
51
164
|
f"Runbook '{link}' not found in any of the search paths: {search_paths}"
|
|
52
165
|
)
|
|
53
166
|
logging.error(err_msg)
|
|
54
167
|
return StructuredToolResult(
|
|
55
|
-
status=
|
|
168
|
+
status=StructuredToolResultStatus.ERROR,
|
|
56
169
|
error=err_msg,
|
|
57
170
|
params=params,
|
|
58
171
|
)
|
|
59
|
-
|
|
60
|
-
# Read and return the runbook content
|
|
61
172
|
try:
|
|
62
173
|
with open(runbook_path, "r") as file:
|
|
63
174
|
content = file.read()
|
|
@@ -96,7 +207,7 @@ class RunbookFetcher(Tool):
|
|
|
96
207
|
</example>
|
|
97
208
|
""")
|
|
98
209
|
return StructuredToolResult(
|
|
99
|
-
status=
|
|
210
|
+
status=StructuredToolResultStatus.SUCCESS,
|
|
100
211
|
data=wrapped_content,
|
|
101
212
|
params=params,
|
|
102
213
|
)
|
|
@@ -104,29 +215,47 @@ class RunbookFetcher(Tool):
|
|
|
104
215
|
err_msg = f"Failed to read runbook {runbook_path}: {str(e)}"
|
|
105
216
|
logging.error(err_msg)
|
|
106
217
|
return StructuredToolResult(
|
|
107
|
-
status=
|
|
218
|
+
status=StructuredToolResultStatus.ERROR,
|
|
108
219
|
error=err_msg,
|
|
109
220
|
params=params,
|
|
110
221
|
)
|
|
111
222
|
|
|
112
223
|
def get_parameterized_one_liner(self, params) -> str:
|
|
113
|
-
path: str = params.get("
|
|
224
|
+
path: str = params.get("runbook_id", "")
|
|
114
225
|
return f"{toolset_name_for_one_liner(self.toolset.name)}: Fetch Runbook {path}"
|
|
115
226
|
|
|
116
227
|
|
|
117
228
|
class RunbookToolset(Toolset):
|
|
118
|
-
def __init__(
|
|
119
|
-
|
|
229
|
+
def __init__(
|
|
230
|
+
self,
|
|
231
|
+
dal: Optional[SupabaseDal],
|
|
232
|
+
additional_search_paths: Optional[List[str]] = None,
|
|
233
|
+
):
|
|
234
|
+
# Store additional search paths in config for RunbookFetcher to access
|
|
120
235
|
config = {}
|
|
121
236
|
if additional_search_paths:
|
|
122
237
|
config["additional_search_paths"] = additional_search_paths
|
|
123
238
|
|
|
239
|
+
# Compute custom catalog paths from additional search paths
|
|
240
|
+
custom_catalog_paths = None
|
|
241
|
+
if additional_search_paths:
|
|
242
|
+
custom_catalog_paths = [
|
|
243
|
+
os.path.join(search_path, "catalog.json")
|
|
244
|
+
for search_path in additional_search_paths
|
|
245
|
+
if os.path.isfile(os.path.join(search_path, "catalog.json"))
|
|
246
|
+
]
|
|
247
|
+
|
|
124
248
|
super().__init__(
|
|
125
249
|
name="runbook",
|
|
126
250
|
description="Fetch runbooks",
|
|
127
251
|
icon_url="https://platform.robusta.dev/demos/runbook.svg",
|
|
128
252
|
tools=[
|
|
129
|
-
RunbookFetcher(
|
|
253
|
+
RunbookFetcher(
|
|
254
|
+
self,
|
|
255
|
+
additional_search_paths,
|
|
256
|
+
dal,
|
|
257
|
+
cast(Optional[List[Union[str, Path]]], custom_catalog_paths),
|
|
258
|
+
),
|
|
130
259
|
],
|
|
131
260
|
docs_url="https://holmesgpt.dev/data-sources/",
|
|
132
261
|
tags=[
|
|
@@ -134,6 +263,7 @@ class RunbookToolset(Toolset):
|
|
|
134
263
|
],
|
|
135
264
|
is_default=True,
|
|
136
265
|
config=config,
|
|
266
|
+
enabled=True,
|
|
137
267
|
)
|
|
138
268
|
|
|
139
269
|
def get_example_config(self) -> Dict[str, Any]:
|
|
@@ -36,7 +36,7 @@ def find_service_url(label_selector):
|
|
|
36
36
|
port = svc.spec.ports[0].port
|
|
37
37
|
url = f"http://{name}.{namespace}.svc.{CLUSTER_DOMAIN}:{port}"
|
|
38
38
|
logging.info(
|
|
39
|
-
f"
|
|
39
|
+
f"Discovered service with label-selector: `{label_selector}` at url: `{url}`"
|
|
40
40
|
)
|
|
41
41
|
return url
|
|
42
42
|
except Exception:
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
## ServiceNow Tables Toolset
|
|
2
|
+
|
|
3
|
+
This toolset provides access to ServiceNow tables for retrieving records and data.
|
|
4
|
+
|
|
5
|
+
### Available Tools
|
|
6
|
+
|
|
7
|
+
1. **servicenow_get_records**: Retrieve multiple records from a ServiceNow table with filtering capabilities
|
|
8
|
+
2. **servicenow_get_record**: Retrieve a single record by its sys_id
|
|
9
|
+
|
|
10
|
+
**IMPORTANT**: For servicenow_get_record, you MUST have a valid sys_id value that was either:
|
|
11
|
+
- Provided by the user
|
|
12
|
+
- Obtained from a previous servicenow_get_records call
|
|
13
|
+
- Never guess or make up sys_id values - they are unique identifiers that must come from a legitimate source
|
|
14
|
+
|
|
15
|
+
### Query Syntax
|
|
16
|
+
|
|
17
|
+
The `sysparm_query` parameter supports a powerful query language:
|
|
18
|
+
- Use `^` for AND conditions
|
|
19
|
+
- Use `^OR` for OR conditions
|
|
20
|
+
- Common operators:
|
|
21
|
+
- `=` (equals) - Use for exact matches, REQUIRED for reference fields
|
|
22
|
+
- `!=` (not equals)
|
|
23
|
+
- `LIKE` (contains) - Use for partial string matches on text fields only
|
|
24
|
+
- `STARTSWITH`
|
|
25
|
+
- `ENDSWITH`
|
|
26
|
+
- `CONTAINS`
|
|
27
|
+
- `ISNOTEMPTY`
|
|
28
|
+
- `ISEMPTY`
|
|
29
|
+
- `<`, `<=`, `>`, `>=` (comparisons)
|
|
30
|
+
- `BETWEEN` (for date ranges)
|
|
31
|
+
|
|
32
|
+
Example: `active=true^priority=1^short_descriptionLIKEerror`
|
|
33
|
+
|
|
34
|
+
**CRITICAL**: Reference fields (fields that link to other records) MUST use the `=` operator with the exact sys_id value. Never use `LIKE` with reference fields.
|
|
35
|
+
- ✅ Correct: `cmdb_ci=b0ccabf1c0a80009001f14fd151d8df0`
|
|
36
|
+
- ❌ Wrong: `cmdb_ciLIKE827b692d0ad337021abb72d3737ff401`
|
|
37
|
+
|
|
38
|
+
**Date Queries - Best Practice**:
|
|
39
|
+
Use `>=` and `<=` operators for date ranges. When using date-only format (YYYY-MM-DD), ServiceNow includes the entire day.
|
|
40
|
+
- ✅ Recommended: `sys_created_on>=2024-01-01^sys_created_on<=2024-01-31` (includes all of Jan 31)
|
|
41
|
+
- With time: `sys_created_on>=2024-01-01 00:00:00^sys_created_on<=2024-01-31 23:59:59`
|
|
42
|
+
- Avoid BETWEEN operator as date handling can be inconsistent
|
|
43
|
+
|
|
44
|
+
### Understanding Field Types
|
|
45
|
+
|
|
46
|
+
In ServiceNow, fields can be of different types:
|
|
47
|
+
- **Text fields**: Store plain text (use LIKE, CONTAINS, STARTSWITH for partial matches)
|
|
48
|
+
- **Reference fields**: Store links to other records via sys_id (MUST use = for exact match)
|
|
49
|
+
- **Boolean fields**: Store true/false values
|
|
50
|
+
- **Date/Time fields**: Store timestamps (use comparison operators)
|
|
51
|
+
|
|
52
|
+
How to identify reference fields:
|
|
53
|
+
- When `sysparm_display_value=all`, reference fields return an object with {display_value, link, value}
|
|
54
|
+
- The 'value' property contains the sys_id
|
|
55
|
+
- Common reference fields: cmdb_ci, assigned_to, caller_id, company, location
|
|
56
|
+
|
|
57
|
+
### Useful Tables
|
|
58
|
+
|
|
59
|
+
- **cmdb_ci** - Configuration items (applications, servers, infrastructure). Search by name to get sys_id.
|
|
60
|
+
- **change_request** - Links to CIs via cmdb_ci field. Use CI's sys_id to find who requested changes, when, and completion status.
|
|
61
|
+
|
|
62
|
+
{# Additional tables can be added by user #}
|
|
63
|
+
|
|
64
|
+
### Discovering Tables
|
|
65
|
+
|
|
66
|
+
To discover available tables in ServiceNow, you can query the system table catalog:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
table_name: sys_db_object
|
|
70
|
+
sysparm_fields: name,label,description
|
|
71
|
+
sysparm_query: nameSTARTSWITHincident
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This will return tables with names starting with "incident". You can modify the query to search for other table patterns.
|
|
75
|
+
|
|
76
|
+
**Note**: ServiceNow contains many tables, so it's recommended to filter your search using `STARTSWITH`, `CONTAINS`, or `LIKE` operators to find relevant tables efficiently.
|
|
77
|
+
|
|
78
|
+
### Tips for Usage
|
|
79
|
+
|
|
80
|
+
1. Use `sysparm_view=mobile` to get a concise set of fields when exploring data
|
|
81
|
+
2. Use `sysparm_fields` to specify exactly which fields you need to reduce response size
|
|
82
|
+
3. The default limit is 100 records; increase it if you need more data
|
|
83
|
+
4. Display values are returned by default (human-readable format)
|