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.
- holmes/__init__.py +1 -1
- holmes/clients/robusta_client.py +17 -4
- holmes/common/env_vars.py +40 -1
- holmes/config.py +114 -144
- holmes/core/conversations.py +53 -14
- holmes/core/feedback.py +191 -0
- holmes/core/investigation.py +18 -22
- holmes/core/llm.py +489 -88
- holmes/core/models.py +103 -1
- holmes/core/openai_formatting.py +13 -0
- holmes/core/prompt.py +1 -1
- holmes/core/safeguards.py +4 -4
- holmes/core/supabase_dal.py +293 -100
- holmes/core/tool_calling_llm.py +423 -323
- holmes/core/tools.py +311 -33
- holmes/core/tools_utils/token_counting.py +14 -0
- holmes/core/tools_utils/tool_context_window_limiter.py +57 -0
- holmes/core/tools_utils/tool_executor.py +13 -8
- holmes/core/toolset_manager.py +155 -4
- holmes/core/tracing.py +6 -1
- holmes/core/transformers/__init__.py +23 -0
- holmes/core/transformers/base.py +62 -0
- holmes/core/transformers/llm_summarize.py +174 -0
- holmes/core/transformers/registry.py +122 -0
- holmes/core/transformers/transformer.py +31 -0
- holmes/core/truncation/compaction.py +59 -0
- holmes/core/truncation/dal_truncation_utils.py +23 -0
- holmes/core/truncation/input_context_window_limiter.py +218 -0
- holmes/interactive.py +177 -24
- holmes/main.py +7 -4
- holmes/plugins/prompts/_fetch_logs.jinja2 +26 -1
- holmes/plugins/prompts/_general_instructions.jinja2 +1 -2
- holmes/plugins/prompts/_runbook_instructions.jinja2 +23 -12
- holmes/plugins/prompts/conversation_history_compaction.jinja2 +88 -0
- holmes/plugins/prompts/generic_ask.jinja2 +2 -4
- holmes/plugins/prompts/generic_ask_conversation.jinja2 +2 -1
- holmes/plugins/prompts/generic_ask_for_issue_conversation.jinja2 +2 -1
- holmes/plugins/prompts/generic_investigation.jinja2 +2 -1
- holmes/plugins/prompts/investigation_procedure.jinja2 +48 -0
- holmes/plugins/prompts/kubernetes_workload_ask.jinja2 +2 -1
- holmes/plugins/prompts/kubernetes_workload_chat.jinja2 +2 -1
- holmes/plugins/runbooks/__init__.py +117 -18
- holmes/plugins/runbooks/catalog.json +2 -0
- holmes/plugins/toolsets/__init__.py +21 -8
- holmes/plugins/toolsets/aks-node-health.yaml +46 -0
- holmes/plugins/toolsets/aks.yaml +64 -0
- holmes/plugins/toolsets/atlas_mongodb/mongodb_atlas.py +26 -36
- holmes/plugins/toolsets/azure_sql/azure_sql_toolset.py +0 -1
- holmes/plugins/toolsets/azure_sql/tools/analyze_connection_failures.py +10 -7
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_connections.py +9 -6
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_health_status.py +8 -6
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_performance.py +8 -6
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_storage.py +9 -6
- holmes/plugins/toolsets/azure_sql/tools/get_active_alerts.py +9 -7
- holmes/plugins/toolsets/azure_sql/tools/get_slow_queries.py +9 -6
- holmes/plugins/toolsets/azure_sql/tools/get_top_cpu_queries.py +9 -6
- holmes/plugins/toolsets/azure_sql/tools/get_top_data_io_queries.py +9 -6
- holmes/plugins/toolsets/azure_sql/tools/get_top_log_io_queries.py +9 -6
- holmes/plugins/toolsets/bash/bash_toolset.py +10 -13
- holmes/plugins/toolsets/bash/common/bash.py +7 -7
- holmes/plugins/toolsets/cilium.yaml +284 -0
- holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py +5 -3
- holmes/plugins/toolsets/datadog/datadog_api.py +490 -24
- holmes/plugins/toolsets/datadog/datadog_logs_instructions.jinja2 +21 -10
- holmes/plugins/toolsets/datadog/toolset_datadog_general.py +349 -216
- holmes/plugins/toolsets/datadog/toolset_datadog_logs.py +190 -19
- holmes/plugins/toolsets/datadog/toolset_datadog_metrics.py +101 -44
- holmes/plugins/toolsets/datadog/toolset_datadog_rds.py +13 -16
- holmes/plugins/toolsets/datadog/toolset_datadog_traces.py +25 -31
- holmes/plugins/toolsets/git.py +51 -46
- holmes/plugins/toolsets/grafana/common.py +15 -3
- holmes/plugins/toolsets/grafana/grafana_api.py +46 -24
- holmes/plugins/toolsets/grafana/grafana_tempo_api.py +454 -0
- holmes/plugins/toolsets/grafana/loki/instructions.jinja2 +9 -0
- holmes/plugins/toolsets/grafana/loki/toolset_grafana_loki.py +117 -0
- holmes/plugins/toolsets/grafana/toolset_grafana.py +211 -91
- holmes/plugins/toolsets/grafana/toolset_grafana_dashboard.jinja2 +27 -0
- holmes/plugins/toolsets/grafana/toolset_grafana_tempo.jinja2 +246 -11
- holmes/plugins/toolsets/grafana/toolset_grafana_tempo.py +653 -293
- holmes/plugins/toolsets/grafana/trace_parser.py +1 -1
- holmes/plugins/toolsets/internet/internet.py +6 -7
- holmes/plugins/toolsets/internet/notion.py +5 -6
- holmes/plugins/toolsets/investigator/core_investigation.py +42 -34
- holmes/plugins/toolsets/kafka.py +25 -36
- holmes/plugins/toolsets/kubernetes.yaml +58 -84
- holmes/plugins/toolsets/kubernetes_logs.py +6 -6
- holmes/plugins/toolsets/kubernetes_logs.yaml +32 -0
- holmes/plugins/toolsets/logging_utils/logging_api.py +80 -4
- holmes/plugins/toolsets/mcp/toolset_mcp.py +181 -55
- holmes/plugins/toolsets/newrelic/__init__.py +0 -0
- holmes/plugins/toolsets/newrelic/new_relic_api.py +125 -0
- holmes/plugins/toolsets/newrelic/newrelic.jinja2 +41 -0
- holmes/plugins/toolsets/newrelic/newrelic.py +163 -0
- holmes/plugins/toolsets/opensearch/opensearch.py +10 -17
- holmes/plugins/toolsets/opensearch/opensearch_logs.py +7 -7
- holmes/plugins/toolsets/opensearch/opensearch_ppl_query_docs.jinja2 +1616 -0
- holmes/plugins/toolsets/opensearch/opensearch_query_assist.py +78 -0
- holmes/plugins/toolsets/opensearch/opensearch_query_assist_instructions.jinja2 +223 -0
- holmes/plugins/toolsets/opensearch/opensearch_traces.py +13 -16
- holmes/plugins/toolsets/openshift.yaml +283 -0
- holmes/plugins/toolsets/prometheus/prometheus.py +915 -390
- holmes/plugins/toolsets/prometheus/prometheus_instructions.jinja2 +43 -2
- holmes/plugins/toolsets/prometheus/utils.py +28 -0
- holmes/plugins/toolsets/rabbitmq/toolset_rabbitmq.py +9 -10
- holmes/plugins/toolsets/robusta/robusta.py +236 -65
- holmes/plugins/toolsets/robusta/robusta_instructions.jinja2 +26 -9
- holmes/plugins/toolsets/runbook/runbook_fetcher.py +137 -26
- 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/default_toolset_installation_guide.jinja2 +1 -22
- holmes/utils/env.py +7 -0
- holmes/utils/global_instructions.py +75 -10
- holmes/utils/holmes_status.py +2 -1
- holmes/utils/holmes_sync_toolsets.py +0 -2
- holmes/utils/krr_utils.py +188 -0
- holmes/utils/sentry_helper.py +41 -0
- holmes/utils/stream.py +61 -7
- holmes/version.py +34 -14
- holmesgpt-0.16.2a0.dist-info/LICENSE +178 -0
- {holmesgpt-0.13.2.dist-info → holmesgpt-0.16.2a0.dist-info}/METADATA +29 -27
- {holmesgpt-0.13.2.dist-info → holmesgpt-0.16.2a0.dist-info}/RECORD +126 -102
- holmes/core/performance_timing.py +0 -72
- 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/servicenow/install.md +0 -37
- holmes/plugins/toolsets/servicenow/instructions.jinja2 +0 -3
- holmes/plugins/toolsets/servicenow/servicenow.py +0 -219
- holmesgpt-0.13.2.dist-info/LICENSE.txt +0 -21
- {holmesgpt-0.13.2.dist-info → holmesgpt-0.16.2a0.dist-info}/WHEEL +0 -0
- {holmesgpt-0.13.2.dist-info → holmesgpt-0.16.2a0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
import os
|
|
3
|
+
import json
|
|
4
|
+
from holmes.core.tools import (
|
|
5
|
+
Tool,
|
|
6
|
+
ToolInvokeContext,
|
|
7
|
+
ToolParameter,
|
|
8
|
+
)
|
|
9
|
+
from holmes.plugins.toolsets.consts import (
|
|
10
|
+
STANDARD_END_DATETIME_TOOL_PARAM_DESCRIPTION,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from holmes.plugins.toolsets.grafana.common import get_base_url
|
|
14
|
+
from holmes.plugins.toolsets.grafana.toolset_grafana import BaseGrafanaToolset
|
|
15
|
+
from holmes.plugins.toolsets.utils import (
|
|
16
|
+
process_timestamps_to_rfc3339,
|
|
17
|
+
standard_start_datetime_tool_param_description,
|
|
18
|
+
)
|
|
19
|
+
from holmes.plugins.toolsets.logging_utils.logging_api import (
|
|
20
|
+
DEFAULT_TIME_SPAN_SECONDS,
|
|
21
|
+
DEFAULT_LOG_LIMIT,
|
|
22
|
+
)
|
|
23
|
+
from holmes.plugins.toolsets.grafana.loki_api import (
|
|
24
|
+
execute_loki_query,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
from holmes.plugins.toolsets.utils import toolset_name_for_one_liner
|
|
28
|
+
from holmes.core.tools import StructuredToolResult, StructuredToolResultStatus
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class GrafanaLokiToolset(BaseGrafanaToolset):
|
|
32
|
+
def __init__(self):
|
|
33
|
+
super().__init__(
|
|
34
|
+
name="grafana/loki",
|
|
35
|
+
description="Runs loki log queries using Grafana Loki or Loki directly.",
|
|
36
|
+
icon_url="https://grafana.com/media/docs/loki/logo-grafana-loki.png",
|
|
37
|
+
docs_url="https://holmesgpt.dev/data-sources/builtin-toolsets/grafanaloki/",
|
|
38
|
+
tools=[],
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
self.tools = [LokiQuery(toolset=self)]
|
|
42
|
+
instructions_filepath = os.path.abspath(
|
|
43
|
+
os.path.join(os.path.dirname(__file__), "instructions.jinja2")
|
|
44
|
+
)
|
|
45
|
+
self._load_llm_instructions(jinja_template=f"file://{instructions_filepath}")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class LokiQuery(Tool):
|
|
49
|
+
toolset: GrafanaLokiToolset
|
|
50
|
+
name: str = "grafana_loki_query"
|
|
51
|
+
description: str = "Run a query against Grafana Loki using LogQL query language."
|
|
52
|
+
parameters: Dict[str, ToolParameter] = {
|
|
53
|
+
"query": ToolParameter(
|
|
54
|
+
description="LogQL query string.",
|
|
55
|
+
type="string",
|
|
56
|
+
required=True,
|
|
57
|
+
),
|
|
58
|
+
"start": ToolParameter(
|
|
59
|
+
description=standard_start_datetime_tool_param_description(
|
|
60
|
+
DEFAULT_TIME_SPAN_SECONDS
|
|
61
|
+
),
|
|
62
|
+
type="string",
|
|
63
|
+
required=False,
|
|
64
|
+
),
|
|
65
|
+
"end": ToolParameter(
|
|
66
|
+
description=STANDARD_END_DATETIME_TOOL_PARAM_DESCRIPTION,
|
|
67
|
+
type="string",
|
|
68
|
+
required=False,
|
|
69
|
+
),
|
|
70
|
+
"limit": ToolParameter(
|
|
71
|
+
description="Maximum number of entries to return (default: 100)",
|
|
72
|
+
type="integer",
|
|
73
|
+
required=False,
|
|
74
|
+
),
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
def get_parameterized_one_liner(self, params) -> str:
|
|
78
|
+
return f"{toolset_name_for_one_liner(self.toolset.name)}: loki query {params}"
|
|
79
|
+
|
|
80
|
+
def _invoke(self, params: dict, context: ToolInvokeContext) -> StructuredToolResult:
|
|
81
|
+
(start, end) = process_timestamps_to_rfc3339(
|
|
82
|
+
start_timestamp=params.get("start"),
|
|
83
|
+
end_timestamp=params.get("end"),
|
|
84
|
+
default_time_span_seconds=DEFAULT_TIME_SPAN_SECONDS,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
config = self.toolset._grafana_config
|
|
88
|
+
try:
|
|
89
|
+
data = execute_loki_query(
|
|
90
|
+
base_url=get_base_url(config),
|
|
91
|
+
api_key=config.api_key,
|
|
92
|
+
headers=config.headers,
|
|
93
|
+
query=params.get(
|
|
94
|
+
"query", '{query="no_query_fallback"}'
|
|
95
|
+
), # make sure a string returns. fall back to query that will return nothing.
|
|
96
|
+
start=start,
|
|
97
|
+
end=end,
|
|
98
|
+
limit=params.get("limit") or DEFAULT_LOG_LIMIT,
|
|
99
|
+
)
|
|
100
|
+
if data:
|
|
101
|
+
return StructuredToolResult(
|
|
102
|
+
status=StructuredToolResultStatus.SUCCESS,
|
|
103
|
+
data=json.dumps(data),
|
|
104
|
+
params=params,
|
|
105
|
+
)
|
|
106
|
+
else:
|
|
107
|
+
return StructuredToolResult(
|
|
108
|
+
status=StructuredToolResultStatus.NO_DATA,
|
|
109
|
+
params=params,
|
|
110
|
+
)
|
|
111
|
+
except Exception as e:
|
|
112
|
+
return StructuredToolResult(
|
|
113
|
+
status=StructuredToolResultStatus.ERROR,
|
|
114
|
+
params=params,
|
|
115
|
+
error=str(e),
|
|
116
|
+
url=f"{get_base_url(config)}/loki/api/v1/query_range",
|
|
117
|
+
)
|
|
@@ -1,127 +1,247 @@
|
|
|
1
|
-
|
|
2
|
-
from
|
|
1
|
+
import os
|
|
2
|
+
from typing import ClassVar, Dict, Optional, Type, cast
|
|
3
|
+
from urllib.parse import urljoin
|
|
4
|
+
from abc import ABC
|
|
3
5
|
from holmes.core.tools import (
|
|
4
6
|
StructuredToolResult,
|
|
5
7
|
Tool,
|
|
8
|
+
ToolInvokeContext,
|
|
6
9
|
ToolParameter,
|
|
7
|
-
|
|
10
|
+
StructuredToolResultStatus,
|
|
8
11
|
)
|
|
9
12
|
from holmes.plugins.toolsets.grafana.base_grafana_toolset import BaseGrafanaToolset
|
|
10
13
|
import requests # type: ignore
|
|
11
|
-
import logging
|
|
12
14
|
|
|
15
|
+
from holmes.plugins.toolsets.grafana.common import (
|
|
16
|
+
get_base_url,
|
|
17
|
+
GrafanaConfig,
|
|
18
|
+
build_headers,
|
|
19
|
+
)
|
|
13
20
|
from holmes.plugins.toolsets.utils import toolset_name_for_one_liner
|
|
14
21
|
|
|
15
22
|
|
|
16
|
-
class
|
|
17
|
-
|
|
23
|
+
class GrafanaDashboardConfig(GrafanaConfig):
|
|
24
|
+
"""Configuration specific to Grafana Dashboard toolset with api/health as default healthcheck"""
|
|
25
|
+
|
|
26
|
+
healthcheck: Optional[str] = "api/health"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class GrafanaToolset(BaseGrafanaToolset):
|
|
30
|
+
config_class: ClassVar[Type[GrafanaDashboardConfig]] = GrafanaDashboardConfig
|
|
31
|
+
|
|
32
|
+
def __init__(self):
|
|
33
|
+
super().__init__(
|
|
34
|
+
name="grafana/dashboards",
|
|
35
|
+
description="Provides tools for interacting with Grafana dashboards",
|
|
36
|
+
icon_url="https://w7.pngwing.com/pngs/434/923/png-transparent-grafana-hd-logo-thumbnail.png",
|
|
37
|
+
docs_url="https://holmesgpt.dev/data-sources/builtin-toolsets/grafanadashboards/",
|
|
38
|
+
tools=[
|
|
39
|
+
SearchDashboards(self),
|
|
40
|
+
GetDashboardByUID(self),
|
|
41
|
+
GetHomeDashboard(self),
|
|
42
|
+
GetDashboardTags(self),
|
|
43
|
+
],
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
self._load_llm_instructions_from_file(
|
|
47
|
+
os.path.dirname(__file__), "toolset_grafana_dashboard.jinja2"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def grafana_config(self) -> GrafanaDashboardConfig:
|
|
52
|
+
return cast(GrafanaDashboardConfig, self._grafana_config)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class BaseGrafanaTool(Tool, ABC):
|
|
56
|
+
"""Base class for Grafana tools with common HTTP request functionality."""
|
|
57
|
+
|
|
58
|
+
def __init__(self, toolset: GrafanaToolset, *args, **kwargs):
|
|
59
|
+
super().__init__(*args, **kwargs)
|
|
60
|
+
self._toolset = toolset
|
|
61
|
+
|
|
62
|
+
def _make_grafana_request(
|
|
63
|
+
self,
|
|
64
|
+
endpoint: str,
|
|
65
|
+
params: dict,
|
|
66
|
+
query_params: Optional[Dict] = None,
|
|
67
|
+
timeout: int = 30,
|
|
68
|
+
) -> StructuredToolResult:
|
|
69
|
+
"""Make a GET request to Grafana API and return structured result.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
endpoint: API endpoint path (e.g., "/api/search")
|
|
73
|
+
params: Original parameters passed to the tool
|
|
74
|
+
query_params: Optional query parameters for the request
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
StructuredToolResult with the API response data
|
|
78
|
+
"""
|
|
79
|
+
url = urljoin(get_base_url(self._toolset.grafana_config), endpoint)
|
|
80
|
+
headers = build_headers(
|
|
81
|
+
api_key=self._toolset.grafana_config.api_key,
|
|
82
|
+
additional_headers=self._toolset.grafana_config.headers,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
response = requests.get(
|
|
86
|
+
url, headers=headers, params=query_params, timeout=timeout
|
|
87
|
+
)
|
|
88
|
+
response.raise_for_status()
|
|
89
|
+
data = response.json()
|
|
90
|
+
|
|
91
|
+
return StructuredToolResult(
|
|
92
|
+
status=StructuredToolResultStatus.SUCCESS,
|
|
93
|
+
data=data,
|
|
94
|
+
url=url,
|
|
95
|
+
params=params,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class SearchDashboards(BaseGrafanaTool):
|
|
100
|
+
def __init__(self, toolset: GrafanaToolset):
|
|
18
101
|
super().__init__(
|
|
19
|
-
|
|
20
|
-
|
|
102
|
+
toolset=toolset,
|
|
103
|
+
name="grafana_search_dashboards",
|
|
104
|
+
description="Search for Grafana dashboards and folders using the /api/search endpoint",
|
|
21
105
|
parameters={
|
|
22
|
-
"
|
|
23
|
-
description="
|
|
106
|
+
"query": ToolParameter(
|
|
107
|
+
description="Search text to filter dashboards",
|
|
24
108
|
type="string",
|
|
25
109
|
required=False,
|
|
26
110
|
),
|
|
27
|
-
"
|
|
28
|
-
description="
|
|
111
|
+
"tag": ToolParameter(
|
|
112
|
+
description="Search dashboards by tag",
|
|
29
113
|
type="string",
|
|
30
114
|
required=False,
|
|
31
115
|
),
|
|
32
|
-
"
|
|
33
|
-
description="
|
|
116
|
+
"type": ToolParameter(
|
|
117
|
+
description="Filter by type: 'dash-folder' or 'dash-db'",
|
|
34
118
|
type="string",
|
|
35
119
|
required=False,
|
|
36
120
|
),
|
|
37
|
-
"
|
|
38
|
-
description="
|
|
121
|
+
"dashboardIds": ToolParameter(
|
|
122
|
+
description="List of dashboard IDs to filter (comma-separated)",
|
|
39
123
|
type="string",
|
|
40
124
|
required=False,
|
|
41
125
|
),
|
|
126
|
+
"dashboardUIDs": ToolParameter(
|
|
127
|
+
description="List of dashboard UIDs to search for (comma-separated)",
|
|
128
|
+
type="string",
|
|
129
|
+
required=False,
|
|
130
|
+
),
|
|
131
|
+
"folderUIDs": ToolParameter(
|
|
132
|
+
description="List of folder UIDs to search within (comma-separated)",
|
|
133
|
+
type="string",
|
|
134
|
+
required=False,
|
|
135
|
+
),
|
|
136
|
+
"starred": ToolParameter(
|
|
137
|
+
description="Return only starred dashboards",
|
|
138
|
+
type="boolean",
|
|
139
|
+
required=False,
|
|
140
|
+
),
|
|
141
|
+
"limit": ToolParameter(
|
|
142
|
+
description="Maximum results (default 1000, max 5000)",
|
|
143
|
+
type="integer",
|
|
144
|
+
required=False,
|
|
145
|
+
),
|
|
146
|
+
"page": ToolParameter(
|
|
147
|
+
description="Page number for pagination",
|
|
148
|
+
type="integer",
|
|
149
|
+
required=False,
|
|
150
|
+
),
|
|
42
151
|
},
|
|
43
152
|
)
|
|
44
|
-
self._toolset = toolset
|
|
45
153
|
|
|
46
|
-
def _invoke(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
154
|
+
def _invoke(self, params: dict, context: ToolInvokeContext) -> StructuredToolResult:
|
|
155
|
+
query_params = {}
|
|
156
|
+
if params.get("query"):
|
|
157
|
+
query_params["query"] = params["query"]
|
|
158
|
+
if params.get("tag"):
|
|
159
|
+
query_params["tag"] = params["tag"]
|
|
160
|
+
if params.get("type"):
|
|
161
|
+
query_params["type"] = params["type"]
|
|
162
|
+
if params.get("dashboardIds"):
|
|
163
|
+
# Check if dashboardIds also needs to be passed as multiple params
|
|
164
|
+
dashboard_ids = params["dashboardIds"].split(",")
|
|
165
|
+
query_params["dashboardIds"] = [
|
|
166
|
+
dashboard_id.strip()
|
|
167
|
+
for dashboard_id in dashboard_ids
|
|
168
|
+
if dashboard_id.strip()
|
|
169
|
+
]
|
|
170
|
+
if params.get("dashboardUIDs"):
|
|
171
|
+
# Handle dashboardUIDs as a list - split comma-separated values
|
|
172
|
+
dashboard_uids = params["dashboardUIDs"].split(",")
|
|
173
|
+
query_params["dashboardUIDs"] = [
|
|
174
|
+
uid.strip() for uid in dashboard_uids if uid.strip()
|
|
175
|
+
]
|
|
176
|
+
if params.get("folderUIDs"):
|
|
177
|
+
# Check if folderUIDs also needs to be passed as multiple params
|
|
178
|
+
folder_uids = params["folderUIDs"].split(",")
|
|
179
|
+
query_params["folderUIDs"] = [
|
|
180
|
+
uid.strip() for uid in folder_uids if uid.strip()
|
|
181
|
+
]
|
|
182
|
+
if params.get("starred") is not None:
|
|
183
|
+
query_params["starred"] = str(params["starred"]).lower()
|
|
184
|
+
if params.get("limit"):
|
|
185
|
+
query_params["limit"] = params["limit"]
|
|
186
|
+
if params.get("page"):
|
|
187
|
+
query_params["page"] = params["page"]
|
|
188
|
+
|
|
189
|
+
return self._make_grafana_request("/api/search", params, query_params)
|
|
190
|
+
|
|
191
|
+
def get_parameterized_one_liner(self, params: Dict) -> str:
|
|
192
|
+
return f"{toolset_name_for_one_liner(self._toolset.name)}: Search Dashboards"
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class GetDashboardByUID(BaseGrafanaTool):
|
|
196
|
+
def __init__(self, toolset: GrafanaToolset):
|
|
197
|
+
super().__init__(
|
|
198
|
+
toolset=toolset,
|
|
199
|
+
name="grafana_get_dashboard_by_uid",
|
|
200
|
+
description="Get a dashboard by its UID using the /api/dashboards/uid/:uid endpoint",
|
|
201
|
+
parameters={
|
|
202
|
+
"uid": ToolParameter(
|
|
203
|
+
description="The unique identifier of the dashboard",
|
|
204
|
+
type="string",
|
|
205
|
+
required=True,
|
|
206
|
+
),
|
|
207
|
+
},
|
|
51
208
|
)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
response.raise_for_status()
|
|
57
|
-
dashboards = response.json()
|
|
58
|
-
formatted_dashboards: List[str] = []
|
|
59
|
-
base_url = (
|
|
60
|
-
self._toolset._grafana_config.external_url
|
|
61
|
-
or self._toolset._grafana_config.url
|
|
62
|
-
)
|
|
63
|
-
for dash in dashboards:
|
|
64
|
-
dashboard_url = urljoin(
|
|
65
|
-
base_url,
|
|
66
|
-
f"/d/{dash['uid']}/{dash['uri'].split('/')[-1]}",
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
params_dict = {
|
|
70
|
-
"var-cluster": params.get("cluster_name", ""),
|
|
71
|
-
"var-namespace": params.get("namespace", ""),
|
|
72
|
-
"var-pod": params.get("pod_name", ""),
|
|
73
|
-
"var-node": params.get("node_name", ""),
|
|
74
|
-
"var-datasource": self._toolset._grafana_config.grafana_datasource_uid,
|
|
75
|
-
"refresh": "5s",
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
# If filtering for nodes, ensure only node-related dashboards are included
|
|
79
|
-
if params.get("node_name") and "node" not in dash["title"].lower():
|
|
80
|
-
continue
|
|
81
|
-
|
|
82
|
-
# we add all params since if the dashboard isnt configured for a param it will ignore it if it is added
|
|
83
|
-
query_string = urlencode({k: v for k, v in params_dict.items() if v})
|
|
84
|
-
dashboard_url = (
|
|
85
|
-
f"{dashboard_url}?{query_string}" if query_string else dashboard_url
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
formatted_dashboards.append(
|
|
89
|
-
f"Title: {dash['title']}\nURL: {dashboard_url}\n"
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
return StructuredToolResult(
|
|
93
|
-
status=ToolResultStatus.SUCCESS
|
|
94
|
-
if formatted_dashboards
|
|
95
|
-
else ToolResultStatus.NO_DATA,
|
|
96
|
-
data="\n".join(formatted_dashboards)
|
|
97
|
-
if formatted_dashboards
|
|
98
|
-
else "No dashboards found.",
|
|
99
|
-
url=url,
|
|
100
|
-
params=params,
|
|
101
|
-
)
|
|
102
|
-
except requests.RequestException as e:
|
|
103
|
-
logging.error(f"Error fetching dashboards: {str(e)}")
|
|
104
|
-
return StructuredToolResult(
|
|
105
|
-
status=ToolResultStatus.ERROR,
|
|
106
|
-
error=f"Error fetching dashboards: {str(e)}",
|
|
107
|
-
url=url,
|
|
108
|
-
params=params,
|
|
109
|
-
)
|
|
209
|
+
|
|
210
|
+
def _invoke(self, params: dict, context: ToolInvokeContext) -> StructuredToolResult:
|
|
211
|
+
uid = params["uid"]
|
|
212
|
+
return self._make_grafana_request(f"/api/dashboards/uid/{uid}", params)
|
|
110
213
|
|
|
111
214
|
def get_parameterized_one_liner(self, params: Dict) -> str:
|
|
112
|
-
return (
|
|
113
|
-
|
|
215
|
+
return f"{toolset_name_for_one_liner(self._toolset.name)}: Get Dashboard {params.get('uid', '')}"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class GetHomeDashboard(BaseGrafanaTool):
|
|
219
|
+
def __init__(self, toolset: GrafanaToolset):
|
|
220
|
+
super().__init__(
|
|
221
|
+
toolset=toolset,
|
|
222
|
+
name="grafana_get_home_dashboard",
|
|
223
|
+
description="Get the home dashboard using the /api/dashboards/home endpoint",
|
|
224
|
+
parameters={},
|
|
114
225
|
)
|
|
115
226
|
|
|
227
|
+
def _invoke(self, params: dict, context: ToolInvokeContext) -> StructuredToolResult:
|
|
228
|
+
return self._make_grafana_request("/api/dashboards/home", params)
|
|
116
229
|
|
|
117
|
-
|
|
118
|
-
|
|
230
|
+
def get_parameterized_one_liner(self, params: Dict) -> str:
|
|
231
|
+
return f"{toolset_name_for_one_liner(self._toolset.name)}: Get Home Dashboard"
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class GetDashboardTags(BaseGrafanaTool):
|
|
235
|
+
def __init__(self, toolset: GrafanaToolset):
|
|
119
236
|
super().__init__(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
tools=[
|
|
125
|
-
ListAndBuildGrafanaDashboardURLs(self),
|
|
126
|
-
],
|
|
237
|
+
toolset=toolset,
|
|
238
|
+
name="grafana_get_dashboard_tags",
|
|
239
|
+
description="Get all tags used across dashboards using the /api/dashboards/tags endpoint",
|
|
240
|
+
parameters={},
|
|
127
241
|
)
|
|
242
|
+
|
|
243
|
+
def _invoke(self, params: dict, context: ToolInvokeContext) -> StructuredToolResult:
|
|
244
|
+
return self._make_grafana_request("/api/dashboards/tags", params)
|
|
245
|
+
|
|
246
|
+
def get_parameterized_one_liner(self, params: Dict) -> str:
|
|
247
|
+
return f"{toolset_name_for_one_liner(self._toolset.name)}: Get Dashboard Tags"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
## **Instruction: Prometheus Dashboard Query Execution**
|
|
4
|
+
|
|
5
|
+
**When running Prometheus queries from Grafana dashboards:**
|
|
6
|
+
|
|
7
|
+
### **Key Rules:**
|
|
8
|
+
1. **Always run as range queries** (not instant queries) to match dashboard behavior
|
|
9
|
+
2. **Use dashboard's time range** from JSON: `"time": {"from": "now-1h", "to": "now"}`
|
|
10
|
+
3. **Substitute variables:**
|
|
11
|
+
- `$__rate_interval` → `1m` (1-hour range) or `5m` (3+ hour range)
|
|
12
|
+
- `$cluster` → omit if empty
|
|
13
|
+
4. **Copy query exactly** including `ceil()`, `sort_desc()`, etc.
|
|
14
|
+
|
|
15
|
+
### **Example:**
|
|
16
|
+
```
|
|
17
|
+
Dashboard: "now-1h" + query with [$__rate_interval]
|
|
18
|
+
↓
|
|
19
|
+
Execute: start="-3600" + query with [1m]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### **Ignore:**
|
|
23
|
+
- `"refresh": "10s"` (UI setting, not query-related)
|
|
24
|
+
|
|
25
|
+
**This ensures you see the same time series data and historical spikes that users see on their dashboards.**
|
|
26
|
+
|
|
27
|
+
---
|