holmesgpt 0.16.2a0__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 +4 -3
- holmes/common/env_vars.py +18 -2
- holmes/common/openshift.py +1 -1
- holmes/config.py +11 -6
- holmes/core/conversations.py +30 -13
- holmes/core/investigation.py +21 -25
- holmes/core/investigation_structured_output.py +3 -3
- holmes/core/issue.py +1 -1
- holmes/core/llm.py +50 -31
- holmes/core/models.py +19 -17
- holmes/core/openai_formatting.py +1 -1
- holmes/core/prompt.py +47 -2
- holmes/core/runbooks.py +1 -0
- holmes/core/safeguards.py +4 -2
- holmes/core/supabase_dal.py +4 -2
- holmes/core/tool_calling_llm.py +102 -141
- holmes/core/tools.py +19 -28
- holmes/core/tools_utils/token_counting.py +9 -2
- holmes/core/tools_utils/tool_context_window_limiter.py +13 -30
- holmes/core/tools_utils/tool_executor.py +0 -18
- holmes/core/tools_utils/toolset_utils.py +1 -0
- holmes/core/toolset_manager.py +37 -2
- holmes/core/tracing.py +13 -2
- holmes/core/transformers/__init__.py +1 -1
- holmes/core/transformers/base.py +1 -0
- holmes/core/transformers/llm_summarize.py +3 -2
- holmes/core/transformers/registry.py +2 -1
- holmes/core/transformers/transformer.py +1 -0
- holmes/core/truncation/compaction.py +37 -2
- holmes/core/truncation/input_context_window_limiter.py +3 -2
- holmes/interactive.py +52 -8
- holmes/main.py +17 -37
- holmes/plugins/interfaces.py +2 -1
- holmes/plugins/prompts/__init__.py +2 -1
- holmes/plugins/prompts/_fetch_logs.jinja2 +5 -5
- holmes/plugins/prompts/_runbook_instructions.jinja2 +2 -1
- holmes/plugins/prompts/base_user_prompt.jinja2 +7 -0
- holmes/plugins/prompts/conversation_history_compaction.jinja2 +2 -1
- holmes/plugins/prompts/generic_ask.jinja2 +0 -2
- holmes/plugins/prompts/generic_ask_conversation.jinja2 +0 -2
- holmes/plugins/prompts/generic_ask_for_issue_conversation.jinja2 +0 -2
- holmes/plugins/prompts/generic_investigation.jinja2 +0 -2
- holmes/plugins/prompts/investigation_procedure.jinja2 +2 -1
- holmes/plugins/prompts/kubernetes_workload_ask.jinja2 +0 -2
- holmes/plugins/prompts/kubernetes_workload_chat.jinja2 +0 -2
- holmes/plugins/runbooks/__init__.py +32 -3
- holmes/plugins/sources/github/__init__.py +4 -2
- holmes/plugins/sources/prometheus/models.py +1 -0
- holmes/plugins/toolsets/__init__.py +30 -26
- holmes/plugins/toolsets/atlas_mongodb/mongodb_atlas.py +13 -12
- 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 -12
- holmes/plugins/toolsets/azure_sql/tools/analyze_connection_failures.py +7 -7
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_connections.py +7 -7
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_health_status.py +3 -5
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_performance.py +3 -3
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_storage.py +7 -7
- holmes/plugins/toolsets/azure_sql/tools/get_active_alerts.py +6 -8
- holmes/plugins/toolsets/azure_sql/tools/get_slow_queries.py +3 -3
- holmes/plugins/toolsets/azure_sql/tools/get_top_cpu_queries.py +3 -3
- holmes/plugins/toolsets/azure_sql/tools/get_top_data_io_queries.py +3 -3
- holmes/plugins/toolsets/azure_sql/tools/get_top_log_io_queries.py +3 -3
- 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 +2 -3
- holmes/plugins/toolsets/bash/common/bash.py +19 -9
- 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/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 +36 -3
- holmes/plugins/toolsets/datadog/datadog_logs_instructions.jinja2 +34 -1
- 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 +71 -28
- holmes/plugins/toolsets/datadog/toolset_datadog_logs.py +224 -375
- holmes/plugins/toolsets/datadog/toolset_datadog_metrics.py +67 -36
- holmes/plugins/toolsets/datadog/toolset_datadog_traces.py +360 -343
- holmes/plugins/toolsets/elasticsearch/__init__.py +6 -0
- holmes/plugins/toolsets/elasticsearch/elasticsearch.py +834 -0
- holmes/plugins/toolsets/git.py +7 -8
- holmes/plugins/toolsets/grafana/base_grafana_toolset.py +16 -4
- holmes/plugins/toolsets/grafana/common.py +2 -30
- holmes/plugins/toolsets/grafana/grafana_tempo_api.py +2 -1
- holmes/plugins/toolsets/grafana/loki/instructions.jinja2 +18 -2
- holmes/plugins/toolsets/grafana/loki/toolset_grafana_loki.py +92 -18
- holmes/plugins/toolsets/grafana/loki_api.py +4 -0
- holmes/plugins/toolsets/grafana/toolset_grafana.py +109 -25
- holmes/plugins/toolsets/grafana/toolset_grafana_dashboard.jinja2 +22 -0
- holmes/plugins/toolsets/grafana/toolset_grafana_tempo.py +201 -33
- holmes/plugins/toolsets/grafana/trace_parser.py +3 -2
- holmes/plugins/toolsets/internet/internet.py +10 -10
- holmes/plugins/toolsets/internet/notion.py +5 -6
- holmes/plugins/toolsets/investigator/core_investigation.py +3 -3
- holmes/plugins/toolsets/investigator/model.py +3 -1
- holmes/plugins/toolsets/json_filter_mixin.py +134 -0
- holmes/plugins/toolsets/kafka.py +12 -7
- holmes/plugins/toolsets/kubernetes.yaml +260 -30
- holmes/plugins/toolsets/kubernetes_logs.py +3 -3
- holmes/plugins/toolsets/logging_utils/logging_api.py +16 -6
- holmes/plugins/toolsets/mcp/toolset_mcp.py +88 -60
- holmes/plugins/toolsets/newrelic/new_relic_api.py +41 -1
- holmes/plugins/toolsets/newrelic/newrelic.jinja2 +24 -0
- holmes/plugins/toolsets/newrelic/newrelic.py +212 -55
- holmes/plugins/toolsets/prometheus/prometheus.py +358 -102
- holmes/plugins/toolsets/prometheus/prometheus_instructions.jinja2 +11 -3
- holmes/plugins/toolsets/rabbitmq/api.py +23 -4
- holmes/plugins/toolsets/rabbitmq/toolset_rabbitmq.py +5 -5
- holmes/plugins/toolsets/robusta/robusta.py +5 -5
- holmes/plugins/toolsets/runbook/runbook_fetcher.py +25 -6
- holmes/plugins/toolsets/servicenow_tables/servicenow_tables.py +1 -1
- holmes/plugins/toolsets/utils.py +1 -1
- holmes/utils/config_utils.py +1 -1
- holmes/utils/connection_utils.py +31 -0
- holmes/utils/console/result.py +10 -0
- holmes/utils/file_utils.py +2 -1
- holmes/utils/global_instructions.py +10 -26
- holmes/utils/holmes_status.py +4 -3
- 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 +23 -0
- holmes/utils/stream.py +12 -5
- holmes/utils/tags.py +4 -3
- holmes/version.py +3 -1
- {holmesgpt-0.16.2a0.dist-info → holmesgpt-0.18.4.dist-info}/METADATA +12 -10
- holmesgpt-0.18.4.dist-info/RECORD +258 -0
- holmes/plugins/toolsets/aws.yaml +0 -80
- holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py +0 -114
- holmes/plugins/toolsets/datadog/datadog_traces_formatter.py +0 -310
- holmes/plugins/toolsets/datadog/toolset_datadog_rds.py +0 -736
- holmes/plugins/toolsets/grafana/grafana_api.py +0 -64
- holmes/plugins/toolsets/opensearch/__init__.py +0 -0
- holmes/plugins/toolsets/opensearch/opensearch.py +0 -250
- holmes/plugins/toolsets/opensearch/opensearch_logs.py +0 -161
- holmes/plugins/toolsets/opensearch/opensearch_traces.py +0 -215
- holmes/plugins/toolsets/opensearch/opensearch_traces_instructions.jinja2 +0 -12
- holmes/plugins/toolsets/opensearch/opensearch_utils.py +0 -166
- holmes/utils/keygen_utils.py +0 -6
- holmesgpt-0.16.2a0.dist-info/RECORD +0 -258
- holmes/plugins/toolsets/{opensearch → elasticsearch}/opensearch_ppl_query_docs.jinja2 +0 -0
- holmes/plugins/toolsets/{opensearch → elasticsearch}/opensearch_query_assist.py +2 -2
- /holmes/plugins/toolsets/{opensearch → elasticsearch}/opensearch_query_assist_instructions.jinja2 +0 -0
- {holmesgpt-0.16.2a0.dist-info → holmesgpt-0.18.4.dist-info}/LICENSE +0 -0
- {holmesgpt-0.16.2a0.dist-info → holmesgpt-0.18.4.dist-info}/WHEEL +0 -0
- {holmesgpt-0.16.2a0.dist-info → holmesgpt-0.18.4.dist-info}/entry_points.txt +0 -0
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import requests # type: ignore
|
|
3
|
-
from typing import Tuple
|
|
4
|
-
import backoff
|
|
5
|
-
|
|
6
|
-
from holmes.plugins.toolsets.grafana.common import (
|
|
7
|
-
GrafanaConfig,
|
|
8
|
-
build_headers,
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@backoff.on_exception(
|
|
13
|
-
backoff.expo,
|
|
14
|
-
requests.exceptions.RequestException,
|
|
15
|
-
max_tries=2,
|
|
16
|
-
giveup=lambda e: isinstance(e, requests.exceptions.HTTPError)
|
|
17
|
-
and e.response.status_code < 500,
|
|
18
|
-
)
|
|
19
|
-
def _try_health_url(url: str, headers: dict) -> None:
|
|
20
|
-
response = requests.get(url, headers=headers, timeout=5)
|
|
21
|
-
response.raise_for_status()
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def grafana_health_check(config: GrafanaConfig) -> Tuple[bool, str]:
|
|
25
|
-
"""
|
|
26
|
-
Tests a healthcheck url for grafna loki.
|
|
27
|
-
1. When using grafana as proxy, grafana_datasource_uid is provided, use the data source health url (docs are added).
|
|
28
|
-
2. When using loki directly there are two cases.
|
|
29
|
-
a. Using loki cloud, health check is provided on the base url.
|
|
30
|
-
b. Using local loki, uses url/healthcheck default is url/ready
|
|
31
|
-
c. This function tries both direct loki cases for the user.
|
|
32
|
-
"""
|
|
33
|
-
health_urls = []
|
|
34
|
-
if config.grafana_datasource_uid:
|
|
35
|
-
# https://grafana.com/docs/grafana/latest/developers/http_api/data_source/#check-data-source-health
|
|
36
|
-
health_urls.append(
|
|
37
|
-
f"{config.url}/api/datasources/uid/{config.grafana_datasource_uid}/health"
|
|
38
|
-
)
|
|
39
|
-
else:
|
|
40
|
-
health_urls.append(f"{config.url}/{config.healthcheck}")
|
|
41
|
-
health_urls.append(config.url) # loki cloud uses no suffix.
|
|
42
|
-
g_headers = build_headers(api_key=config.api_key, additional_headers=config.headers)
|
|
43
|
-
|
|
44
|
-
error_msg = ""
|
|
45
|
-
for url in health_urls:
|
|
46
|
-
try:
|
|
47
|
-
_try_health_url(url, g_headers)
|
|
48
|
-
return True, ""
|
|
49
|
-
except Exception as e:
|
|
50
|
-
logging.debug(
|
|
51
|
-
f"Failed to fetch grafana health status at {url}", exc_info=True
|
|
52
|
-
)
|
|
53
|
-
error_msg += f"Failed to fetch grafana health status at {url}. {str(e)}\n"
|
|
54
|
-
|
|
55
|
-
# Add helpful hint if this looks like a common misconfiguration
|
|
56
|
-
if config.grafana_datasource_uid and ":3100" in config.url:
|
|
57
|
-
error_msg += (
|
|
58
|
-
"\n\nPossible configuration issue: grafana_datasource_uid is set but URL contains port 3100 "
|
|
59
|
-
"(typically used for direct Loki connections). Please verify:\n"
|
|
60
|
-
"- If connecting directly to Loki: remove grafana_datasource_uid from config\n"
|
|
61
|
-
"- If connecting via Grafana proxy: ensure URL points to Grafana (usually port 3000)"
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
return False, error_msg
|
|
File without changes
|
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from typing import Any, Dict, List, Optional, Tuple
|
|
3
|
-
|
|
4
|
-
from opensearchpy import OpenSearch
|
|
5
|
-
from pydantic import BaseModel, ConfigDict
|
|
6
|
-
|
|
7
|
-
from holmes.core.tools import (
|
|
8
|
-
CallablePrerequisite,
|
|
9
|
-
StructuredToolResult,
|
|
10
|
-
Tool,
|
|
11
|
-
ToolInvokeContext,
|
|
12
|
-
ToolParameter,
|
|
13
|
-
StructuredToolResultStatus,
|
|
14
|
-
Toolset,
|
|
15
|
-
ToolsetTag,
|
|
16
|
-
)
|
|
17
|
-
from holmes.plugins.toolsets.consts import TOOLSET_CONFIG_MISSING_ERROR
|
|
18
|
-
from holmes.plugins.toolsets.utils import toolset_name_for_one_liner
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class OpenSearchHttpAuth(BaseModel):
|
|
22
|
-
username: str
|
|
23
|
-
password: str
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class OpenSearchHost(BaseModel):
|
|
27
|
-
host: str
|
|
28
|
-
port: int = 9200
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class OpenSearchCluster(BaseModel):
|
|
32
|
-
hosts: list[OpenSearchHost]
|
|
33
|
-
headers: Optional[dict[str, Any]] = None
|
|
34
|
-
use_ssl: bool = True
|
|
35
|
-
ssl_assert_hostname: bool = False
|
|
36
|
-
verify_certs: bool = False
|
|
37
|
-
ssl_show_warn: bool = False
|
|
38
|
-
http_auth: Optional[OpenSearchHttpAuth] = None
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
class OpenSearchConfig(BaseModel):
|
|
42
|
-
opensearch_clusters: list[OpenSearchCluster]
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class OpenSearchClient:
|
|
46
|
-
def __init__(self, **kwargs):
|
|
47
|
-
# Handle http_auth explicitly
|
|
48
|
-
if "http_auth" in kwargs:
|
|
49
|
-
http_auth = kwargs.pop("http_auth")
|
|
50
|
-
if isinstance(http_auth, dict):
|
|
51
|
-
kwargs["http_auth"] = (
|
|
52
|
-
http_auth.get("username"),
|
|
53
|
-
http_auth.get("password"),
|
|
54
|
-
)
|
|
55
|
-
# Initialize OpenSearch client
|
|
56
|
-
self.hosts = [host.get("host") for host in kwargs.get("hosts", [])]
|
|
57
|
-
self.client = OpenSearch(**kwargs)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def get_client(clients: List[OpenSearchClient], host: Optional[str]):
|
|
61
|
-
if len(clients) == 1:
|
|
62
|
-
return clients[0]
|
|
63
|
-
|
|
64
|
-
if not host:
|
|
65
|
-
raise Exception("Missing host to resolve opensearch client")
|
|
66
|
-
|
|
67
|
-
for client in clients:
|
|
68
|
-
found = any(host in client.hosts for client in clients)
|
|
69
|
-
if found:
|
|
70
|
-
return client
|
|
71
|
-
|
|
72
|
-
raise Exception(
|
|
73
|
-
f"Failed to resolve opensearch client. Could not find a matching host: {host}"
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
class BaseOpenSearchTool(Tool):
|
|
78
|
-
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
79
|
-
toolset: "OpenSearchToolset"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
class ListShards(BaseOpenSearchTool):
|
|
83
|
-
def __init__(self, toolset: "OpenSearchToolset"):
|
|
84
|
-
super().__init__(
|
|
85
|
-
name="opensearch_list_shards",
|
|
86
|
-
description="List the shards within an opensearch cluster",
|
|
87
|
-
parameters={
|
|
88
|
-
"host": ToolParameter(
|
|
89
|
-
description="The cluster host",
|
|
90
|
-
type="string",
|
|
91
|
-
required=True,
|
|
92
|
-
)
|
|
93
|
-
},
|
|
94
|
-
toolset=toolset,
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
def _invoke(self, params: dict, context: ToolInvokeContext) -> StructuredToolResult:
|
|
98
|
-
client = get_client(self.toolset.clients, host=params.get("host", ""))
|
|
99
|
-
shards = client.client.cat.shards()
|
|
100
|
-
return StructuredToolResult(
|
|
101
|
-
status=StructuredToolResultStatus.SUCCESS,
|
|
102
|
-
data=str(shards),
|
|
103
|
-
params=params,
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
def get_parameterized_one_liner(self, params: Dict) -> str:
|
|
107
|
-
host = params.get("host", "")
|
|
108
|
-
return f"{toolset_name_for_one_liner(self.toolset.name)}: List Shards ({host})"
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
class GetClusterSettings(BaseOpenSearchTool):
|
|
112
|
-
def __init__(self, toolset: "OpenSearchToolset"):
|
|
113
|
-
super().__init__(
|
|
114
|
-
name="opensearch_get_cluster_settings",
|
|
115
|
-
description="Retrieve the cluster's settings",
|
|
116
|
-
parameters={
|
|
117
|
-
"host": ToolParameter(
|
|
118
|
-
description="The cluster host",
|
|
119
|
-
type="string",
|
|
120
|
-
required=True,
|
|
121
|
-
)
|
|
122
|
-
},
|
|
123
|
-
toolset=toolset,
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
def _invoke(self, params: dict, context: ToolInvokeContext) -> StructuredToolResult:
|
|
127
|
-
client = get_client(self.toolset.clients, host=params.get("host"))
|
|
128
|
-
response = client.client.cluster.get_settings(
|
|
129
|
-
include_defaults=True, flat_settings=True
|
|
130
|
-
)
|
|
131
|
-
return StructuredToolResult(
|
|
132
|
-
status=StructuredToolResultStatus.SUCCESS,
|
|
133
|
-
data=str(response),
|
|
134
|
-
params=params,
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
def get_parameterized_one_liner(self, params) -> str:
|
|
138
|
-
host = params.get("host", "")
|
|
139
|
-
return f"{toolset_name_for_one_liner(self.toolset.name)}: Get Cluster Settings ({host})"
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
class GetClusterHealth(BaseOpenSearchTool):
|
|
143
|
-
def __init__(self, toolset: "OpenSearchToolset"):
|
|
144
|
-
super().__init__(
|
|
145
|
-
name="opensearch_get_cluster_health",
|
|
146
|
-
description="Retrieve the cluster's health",
|
|
147
|
-
parameters={
|
|
148
|
-
"host": ToolParameter(
|
|
149
|
-
description="The cluster host",
|
|
150
|
-
type="string",
|
|
151
|
-
required=True,
|
|
152
|
-
)
|
|
153
|
-
},
|
|
154
|
-
toolset=toolset,
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
def _invoke(self, params: dict, context: ToolInvokeContext) -> StructuredToolResult:
|
|
158
|
-
client = get_client(self.toolset.clients, host=params.get("host", ""))
|
|
159
|
-
health = client.client.cluster.health()
|
|
160
|
-
return StructuredToolResult(
|
|
161
|
-
status=StructuredToolResultStatus.SUCCESS,
|
|
162
|
-
data=str(health),
|
|
163
|
-
params=params,
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
def get_parameterized_one_liner(self, params) -> str:
|
|
167
|
-
host = params.get("host", "")
|
|
168
|
-
return f"{toolset_name_for_one_liner(self.toolset.name)}: Check Cluster Health ({host})"
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
class ListOpenSearchHosts(BaseOpenSearchTool):
|
|
172
|
-
def __init__(self, toolset: "OpenSearchToolset"):
|
|
173
|
-
super().__init__(
|
|
174
|
-
name="opensearch_list_hosts",
|
|
175
|
-
description="List all OpenSearch hosts in the cluster",
|
|
176
|
-
parameters={},
|
|
177
|
-
toolset=toolset,
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
def _invoke(self, params: dict, context: ToolInvokeContext) -> StructuredToolResult:
|
|
181
|
-
hosts = [host for client in self.toolset.clients for host in client.hosts]
|
|
182
|
-
return StructuredToolResult(
|
|
183
|
-
status=StructuredToolResultStatus.SUCCESS,
|
|
184
|
-
data=str(hosts),
|
|
185
|
-
params=params,
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
def get_parameterized_one_liner(self, params: Dict) -> str:
|
|
189
|
-
return f"{toolset_name_for_one_liner(self.toolset.name)}: List OpenSearch Hosts"
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
class OpenSearchToolset(Toolset):
|
|
193
|
-
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
194
|
-
clients: List[OpenSearchClient] = []
|
|
195
|
-
|
|
196
|
-
def __init__(self):
|
|
197
|
-
super().__init__(
|
|
198
|
-
name="opensearch/status",
|
|
199
|
-
enabled=False,
|
|
200
|
-
description="Provide cluster metadata information like health, shards, settings.",
|
|
201
|
-
docs_url="https://holmesgpt.dev/data-sources/builtin-toolsets/opensearch-status/",
|
|
202
|
-
icon_url="https://opensearch.org/assets/brand/PNG/Mark/opensearch_mark_default.png",
|
|
203
|
-
prerequisites=[CallablePrerequisite(callable=self.prerequisites_callable)],
|
|
204
|
-
tools=[
|
|
205
|
-
ListShards(self),
|
|
206
|
-
GetClusterSettings(self),
|
|
207
|
-
GetClusterHealth(self),
|
|
208
|
-
ListOpenSearchHosts(self),
|
|
209
|
-
],
|
|
210
|
-
tags=[
|
|
211
|
-
ToolsetTag.CORE,
|
|
212
|
-
],
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
def prerequisites_callable(self, config: dict[str, Any]) -> Tuple[bool, str]:
|
|
216
|
-
if not config:
|
|
217
|
-
return False, TOOLSET_CONFIG_MISSING_ERROR
|
|
218
|
-
|
|
219
|
-
try:
|
|
220
|
-
os_config = OpenSearchConfig(**config)
|
|
221
|
-
errors = []
|
|
222
|
-
for cluster in os_config.opensearch_clusters:
|
|
223
|
-
try:
|
|
224
|
-
logging.info("Setting up OpenSearch client")
|
|
225
|
-
cluster_kwargs = cluster.model_dump()
|
|
226
|
-
client = OpenSearchClient(**cluster_kwargs)
|
|
227
|
-
if client.client.cluster.health(params={"timeout": 5}):
|
|
228
|
-
self.clients.append(client)
|
|
229
|
-
except Exception as e:
|
|
230
|
-
message = f"Failed to set up opensearch client {str(cluster.hosts)}. {str(e)}"
|
|
231
|
-
logging.exception(message)
|
|
232
|
-
errors.append(message)
|
|
233
|
-
|
|
234
|
-
return len(self.clients) > 0, "\n".join(errors)
|
|
235
|
-
except Exception as e:
|
|
236
|
-
logging.exception("Failed to set up OpenSearch toolset")
|
|
237
|
-
return False, str(e)
|
|
238
|
-
|
|
239
|
-
def get_example_config(self) -> Dict[str, Any]:
|
|
240
|
-
example_config = OpenSearchConfig(
|
|
241
|
-
opensearch_clusters=[
|
|
242
|
-
OpenSearchCluster(
|
|
243
|
-
hosts=[OpenSearchHost(host="YOUR OPENSEACH HOST")],
|
|
244
|
-
headers={"Authorization": "{{ env.OPENSEARCH_BEARER_TOKEN }}"},
|
|
245
|
-
use_ssl=True,
|
|
246
|
-
ssl_assert_hostname=False,
|
|
247
|
-
)
|
|
248
|
-
]
|
|
249
|
-
)
|
|
250
|
-
return example_config.model_dump()
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from typing import Any, Dict, Optional, Tuple, Set
|
|
3
|
-
|
|
4
|
-
import requests # type: ignore
|
|
5
|
-
from requests import RequestException # type: ignore
|
|
6
|
-
from urllib.parse import urljoin
|
|
7
|
-
|
|
8
|
-
from holmes.core.tools import (
|
|
9
|
-
CallablePrerequisite,
|
|
10
|
-
StructuredToolResult,
|
|
11
|
-
StructuredToolResultStatus,
|
|
12
|
-
ToolsetTag,
|
|
13
|
-
)
|
|
14
|
-
from holmes.plugins.toolsets.logging_utils.logging_api import (
|
|
15
|
-
BasePodLoggingToolset,
|
|
16
|
-
FetchPodLogsParams,
|
|
17
|
-
LoggingCapability,
|
|
18
|
-
PodLoggingTool,
|
|
19
|
-
process_time_parameters,
|
|
20
|
-
DEFAULT_TIME_SPAN_SECONDS,
|
|
21
|
-
)
|
|
22
|
-
from holmes.plugins.toolsets.opensearch.opensearch_utils import (
|
|
23
|
-
OpenSearchLoggingConfig,
|
|
24
|
-
add_auth_header,
|
|
25
|
-
build_query,
|
|
26
|
-
format_logs,
|
|
27
|
-
opensearch_health_check,
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
LOGS_FIELDS_CACHE_KEY = "cached_logs_fields"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class OpenSearchLogsToolset(BasePodLoggingToolset):
|
|
34
|
-
"""Implementation of the unified logging API for OpenSearch logs"""
|
|
35
|
-
|
|
36
|
-
@property
|
|
37
|
-
def supported_capabilities(self) -> Set[LoggingCapability]:
|
|
38
|
-
"""OpenSearch only supports phrase matching, not regex or exclude filters"""
|
|
39
|
-
return set() # No regex support, no exclude filter
|
|
40
|
-
|
|
41
|
-
def __init__(self):
|
|
42
|
-
super().__init__(
|
|
43
|
-
name="opensearch/logs",
|
|
44
|
-
description="OpenSearch integration to fetch logs",
|
|
45
|
-
docs_url="https://holmesgpt.dev/data-sources/builtin-toolsets/opensearch-logs/",
|
|
46
|
-
icon_url="https://opensearch.org/wp-content/uploads/2025/01/opensearch_mark_default.png",
|
|
47
|
-
prerequisites=[CallablePrerequisite(callable=self.prerequisites_callable)],
|
|
48
|
-
tools=[], # Initialize with empty tools first
|
|
49
|
-
tags=[
|
|
50
|
-
ToolsetTag.CORE,
|
|
51
|
-
],
|
|
52
|
-
)
|
|
53
|
-
# Now that parent is initialized and self.name exists, create the tool
|
|
54
|
-
self.tools = [PodLoggingTool(self)]
|
|
55
|
-
|
|
56
|
-
def get_example_config(self) -> Dict[str, Any]:
|
|
57
|
-
example_config = OpenSearchLoggingConfig(
|
|
58
|
-
opensearch_url="YOUR OPENSEARCH LOGS URL",
|
|
59
|
-
index_pattern="YOUR OPENSEARCH LOGS INDEX NAME",
|
|
60
|
-
opensearch_auth_header="YOUR OPENSEARCH LOGS AUTH HEADER (Optional)",
|
|
61
|
-
)
|
|
62
|
-
return example_config.model_dump()
|
|
63
|
-
|
|
64
|
-
def prerequisites_callable(self, config: dict[str, Any]) -> Tuple[bool, str]:
|
|
65
|
-
if not config:
|
|
66
|
-
return False, "Missing OpenSearch configuration. Check your config."
|
|
67
|
-
|
|
68
|
-
self.config = OpenSearchLoggingConfig(**config)
|
|
69
|
-
|
|
70
|
-
return opensearch_health_check(self.config)
|
|
71
|
-
|
|
72
|
-
@property
|
|
73
|
-
def opensearch_config(self) -> Optional[OpenSearchLoggingConfig]:
|
|
74
|
-
return self.config
|
|
75
|
-
|
|
76
|
-
def logger_name(self) -> str:
|
|
77
|
-
return "OpenSearch"
|
|
78
|
-
|
|
79
|
-
def fetch_pod_logs(self, params: FetchPodLogsParams) -> StructuredToolResult:
|
|
80
|
-
if not self.opensearch_config:
|
|
81
|
-
return StructuredToolResult(
|
|
82
|
-
status=StructuredToolResultStatus.ERROR,
|
|
83
|
-
error="Missing OpenSearch configuration",
|
|
84
|
-
params=params.model_dump(),
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
try:
|
|
88
|
-
start_time = None
|
|
89
|
-
end_time = None
|
|
90
|
-
if params.start_time or params.end_time:
|
|
91
|
-
start_time, end_time = process_time_parameters(
|
|
92
|
-
params.start_time, params.end_time, DEFAULT_TIME_SPAN_SECONDS
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
query = build_query(
|
|
96
|
-
config=self.opensearch_config,
|
|
97
|
-
namespace=params.namespace,
|
|
98
|
-
pod_name=params.pod_name,
|
|
99
|
-
start_time=start_time,
|
|
100
|
-
end_time=end_time,
|
|
101
|
-
match=params.filter,
|
|
102
|
-
limit=params.limit,
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
headers = {"Content-Type": "application/json"}
|
|
106
|
-
headers.update(
|
|
107
|
-
add_auth_header(self.opensearch_config.opensearch_auth_header)
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
url = urljoin(
|
|
111
|
-
self.opensearch_config.opensearch_url,
|
|
112
|
-
f"/{self.opensearch_config.index_pattern}/_search",
|
|
113
|
-
)
|
|
114
|
-
logs_response = requests.post(
|
|
115
|
-
url=url,
|
|
116
|
-
timeout=180,
|
|
117
|
-
verify=True,
|
|
118
|
-
json=query,
|
|
119
|
-
headers=headers,
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
if logs_response.status_code == 200:
|
|
123
|
-
response = logs_response.json()
|
|
124
|
-
logs = format_logs(
|
|
125
|
-
logs=response.get("hits", {}).get("hits", []),
|
|
126
|
-
config=self.opensearch_config,
|
|
127
|
-
)
|
|
128
|
-
return StructuredToolResult(
|
|
129
|
-
status=StructuredToolResultStatus.SUCCESS,
|
|
130
|
-
data=logs,
|
|
131
|
-
params=params.model_dump(),
|
|
132
|
-
)
|
|
133
|
-
else:
|
|
134
|
-
return StructuredToolResult(
|
|
135
|
-
status=StructuredToolResultStatus.ERROR,
|
|
136
|
-
return_code=logs_response.status_code,
|
|
137
|
-
error=logs_response.text,
|
|
138
|
-
params=params.model_dump(),
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
except requests.Timeout:
|
|
142
|
-
logging.warning("Timeout while fetching OpenSearch logs", exc_info=True)
|
|
143
|
-
return StructuredToolResult(
|
|
144
|
-
status=StructuredToolResultStatus.ERROR,
|
|
145
|
-
error="Request timed out while fetching OpenSearch logs",
|
|
146
|
-
params=params.model_dump(),
|
|
147
|
-
)
|
|
148
|
-
except RequestException as e:
|
|
149
|
-
logging.warning("Failed to fetch OpenSearch logs", exc_info=True)
|
|
150
|
-
return StructuredToolResult(
|
|
151
|
-
status=StructuredToolResultStatus.ERROR,
|
|
152
|
-
error=f"Network error while fetching OpenSearch logs: {str(e)}",
|
|
153
|
-
params=params.model_dump(),
|
|
154
|
-
)
|
|
155
|
-
except Exception as e:
|
|
156
|
-
logging.warning("Failed to process OpenSearch logs", exc_info=True)
|
|
157
|
-
return StructuredToolResult(
|
|
158
|
-
status=StructuredToolResultStatus.ERROR,
|
|
159
|
-
error=f"Unexpected error: {str(e)}",
|
|
160
|
-
params=params.model_dump(),
|
|
161
|
-
)
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import logging
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import requests # type: ignore
|
|
6
|
-
from cachetools import TTLCache # type: ignore
|
|
7
|
-
from holmes.core.tools import (
|
|
8
|
-
CallablePrerequisite,
|
|
9
|
-
Tool,
|
|
10
|
-
ToolInvokeContext,
|
|
11
|
-
ToolParameter,
|
|
12
|
-
ToolsetTag,
|
|
13
|
-
)
|
|
14
|
-
import json
|
|
15
|
-
from requests import RequestException
|
|
16
|
-
|
|
17
|
-
from holmes.plugins.toolsets.opensearch.opensearch_utils import (
|
|
18
|
-
BaseOpenSearchToolset,
|
|
19
|
-
add_auth_header,
|
|
20
|
-
get_search_url,
|
|
21
|
-
)
|
|
22
|
-
from holmes.core.tools import StructuredToolResult, StructuredToolResultStatus
|
|
23
|
-
from holmes.plugins.toolsets.utils import get_param_or_raise, toolset_name_for_one_liner
|
|
24
|
-
|
|
25
|
-
TRACES_FIELDS_CACHE_KEY = "cached_traces_fields"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class GetTracesFields(Tool):
|
|
29
|
-
def __init__(self, toolset: BaseOpenSearchToolset):
|
|
30
|
-
super().__init__(
|
|
31
|
-
name="get_traces_fields",
|
|
32
|
-
description="Get all the fields in the traces documents",
|
|
33
|
-
parameters={},
|
|
34
|
-
)
|
|
35
|
-
self._toolset = toolset
|
|
36
|
-
self._cache = None
|
|
37
|
-
|
|
38
|
-
def _invoke(self, params: dict, context: ToolInvokeContext) -> StructuredToolResult:
|
|
39
|
-
try:
|
|
40
|
-
if not self._cache and self._toolset.opensearch_config.fields_ttl_seconds:
|
|
41
|
-
self._cache = TTLCache(
|
|
42
|
-
maxsize=5, ttl=self._toolset.opensearch_config.fields_ttl_seconds
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
if self._cache:
|
|
46
|
-
cached_response = self._cache.get(TRACES_FIELDS_CACHE_KEY, None)
|
|
47
|
-
if cached_response:
|
|
48
|
-
logging.debug("traces fields returned from cache")
|
|
49
|
-
return StructuredToolResult(
|
|
50
|
-
status=StructuredToolResultStatus.SUCCESS,
|
|
51
|
-
data=cached_response,
|
|
52
|
-
params=params,
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
body = {
|
|
56
|
-
"size": 1,
|
|
57
|
-
"_source": False,
|
|
58
|
-
"script_fields": {
|
|
59
|
-
"all_fields": {
|
|
60
|
-
"script": {
|
|
61
|
-
"lang": "painless",
|
|
62
|
-
"source": "Map fields = new HashMap(); List stack = new ArrayList(); stack.add(['prefix': '', 'obj': params['_source']]); while (!stack.isEmpty()) { Map item = stack.remove(stack.size() - 1); String prefix = item.get('prefix'); Map obj = item.get('obj'); for (entry in obj.entrySet()) { String fullPath = prefix.isEmpty() ? entry.getKey() : prefix + '.' + entry.getKey(); fields.put(fullPath, true); if (entry.getValue() instanceof Map) { stack.add(['prefix': fullPath, 'obj': entry.getValue()]); } } } return fields.keySet();",
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
}
|
|
67
|
-
headers = {"Content-Type": "application/json"}
|
|
68
|
-
headers.update(
|
|
69
|
-
add_auth_header(self._toolset.opensearch_config.opensearch_auth_header)
|
|
70
|
-
)
|
|
71
|
-
logs_response = requests.get(
|
|
72
|
-
url=get_search_url(self._toolset.opensearch_config),
|
|
73
|
-
timeout=180,
|
|
74
|
-
verify=True,
|
|
75
|
-
data=json.dumps(body),
|
|
76
|
-
headers=headers,
|
|
77
|
-
)
|
|
78
|
-
logs_response.raise_for_status()
|
|
79
|
-
response = json.dumps(logs_response.json())
|
|
80
|
-
if self._cache:
|
|
81
|
-
self._cache[TRACES_FIELDS_CACHE_KEY] = response
|
|
82
|
-
return StructuredToolResult(
|
|
83
|
-
status=StructuredToolResultStatus.SUCCESS,
|
|
84
|
-
data=response,
|
|
85
|
-
params=params,
|
|
86
|
-
)
|
|
87
|
-
except requests.Timeout:
|
|
88
|
-
logging.warning(
|
|
89
|
-
"Timeout while fetching opensearch traces fields", exc_info=True
|
|
90
|
-
)
|
|
91
|
-
return StructuredToolResult(
|
|
92
|
-
status=StructuredToolResultStatus.ERROR,
|
|
93
|
-
error="Request timed out while fetching opensearch traces fields",
|
|
94
|
-
params=params,
|
|
95
|
-
)
|
|
96
|
-
except RequestException as e:
|
|
97
|
-
logging.warning("Failed to fetch opensearch traces fields", exc_info=True)
|
|
98
|
-
return StructuredToolResult(
|
|
99
|
-
status=StructuredToolResultStatus.ERROR,
|
|
100
|
-
error=f"Network error while opensearch traces fields: {str(e)}",
|
|
101
|
-
params=params,
|
|
102
|
-
)
|
|
103
|
-
except Exception as e:
|
|
104
|
-
logging.warning("Failed to process opensearch traces fields", exc_info=True)
|
|
105
|
-
return StructuredToolResult(
|
|
106
|
-
status=StructuredToolResultStatus.ERROR,
|
|
107
|
-
error=f"Unexpected error: {str(e)}",
|
|
108
|
-
params=params,
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
def get_parameterized_one_liner(self, params) -> str:
|
|
112
|
-
return f"{toolset_name_for_one_liner(self._toolset.name)}: List Trace Fields"
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
class TracesSearchQuery(Tool):
|
|
116
|
-
def __init__(self, toolset: "OpenSearchTracesToolset"):
|
|
117
|
-
super().__init__(
|
|
118
|
-
name="traces_in_range_search",
|
|
119
|
-
description="Get traces in a specified time range for an opensearch query",
|
|
120
|
-
parameters={
|
|
121
|
-
"query": ToolParameter(
|
|
122
|
-
description="An OpenSearch search query. It should be a stringified json, matching opensearch search syntax. "
|
|
123
|
-
"The query must contain a 'range' on the startTimeMillis field. From a given startTimeMillis, until a given startTimeMillis. This is time in milliseconds",
|
|
124
|
-
type="string",
|
|
125
|
-
),
|
|
126
|
-
},
|
|
127
|
-
)
|
|
128
|
-
self._toolset = toolset
|
|
129
|
-
self._cache = None
|
|
130
|
-
|
|
131
|
-
def _invoke(self, params: dict, context: ToolInvokeContext) -> StructuredToolResult:
|
|
132
|
-
err_msg = ""
|
|
133
|
-
try:
|
|
134
|
-
body = json.loads(get_param_or_raise(params, "query"))
|
|
135
|
-
full_query = body
|
|
136
|
-
full_query["size"] = int(
|
|
137
|
-
os.environ.get("OPENSEARCH_TRACES_SEARCH_SIZE", "5000")
|
|
138
|
-
)
|
|
139
|
-
logging.debug(f"opensearch traces search query: {full_query}")
|
|
140
|
-
headers = {"Content-Type": "application/json"}
|
|
141
|
-
headers.update(
|
|
142
|
-
add_auth_header(self._toolset.opensearch_config.opensearch_auth_header)
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
logs_response = requests.get(
|
|
146
|
-
url=get_search_url(self._toolset.opensearch_config),
|
|
147
|
-
timeout=180,
|
|
148
|
-
verify=True,
|
|
149
|
-
data=json.dumps(full_query),
|
|
150
|
-
headers=headers,
|
|
151
|
-
)
|
|
152
|
-
if logs_response.status_code > 300:
|
|
153
|
-
err_msg = logs_response.text
|
|
154
|
-
|
|
155
|
-
logs_response.raise_for_status()
|
|
156
|
-
return StructuredToolResult(
|
|
157
|
-
status=StructuredToolResultStatus.SUCCESS,
|
|
158
|
-
data=json.dumps(logs_response.json()),
|
|
159
|
-
params=params,
|
|
160
|
-
)
|
|
161
|
-
except requests.Timeout:
|
|
162
|
-
logging.warning(
|
|
163
|
-
"Timeout while fetching opensearch traces search", exc_info=True
|
|
164
|
-
)
|
|
165
|
-
return StructuredToolResult(
|
|
166
|
-
status=StructuredToolResultStatus.ERROR,
|
|
167
|
-
error=f"Request timed out while fetching opensearch traces search {err_msg}",
|
|
168
|
-
params=params,
|
|
169
|
-
)
|
|
170
|
-
except RequestException as e:
|
|
171
|
-
logging.warning("Failed to fetch opensearch traces search", exc_info=True)
|
|
172
|
-
return StructuredToolResult(
|
|
173
|
-
status=StructuredToolResultStatus.ERROR,
|
|
174
|
-
error=f"Network error while opensearch traces search {err_msg} : {str(e)}",
|
|
175
|
-
params=params,
|
|
176
|
-
)
|
|
177
|
-
except Exception as e:
|
|
178
|
-
logging.warning(
|
|
179
|
-
"Failed to process opensearch traces search ", exc_info=True
|
|
180
|
-
)
|
|
181
|
-
return StructuredToolResult(
|
|
182
|
-
status=StructuredToolResultStatus.ERROR,
|
|
183
|
-
error=f"Unexpected error {err_msg}: {str(e)}",
|
|
184
|
-
params=params,
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
def get_parameterized_one_liner(self, params) -> str:
|
|
188
|
-
query = params.get("query", "")
|
|
189
|
-
return (
|
|
190
|
-
f"{toolset_name_for_one_liner(self._toolset.name)}: Search Traces ({query})"
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
class OpenSearchTracesToolset(BaseOpenSearchToolset):
|
|
195
|
-
def __init__(self):
|
|
196
|
-
super().__init__(
|
|
197
|
-
name="opensearch/traces",
|
|
198
|
-
description="OpenSearch integration to fetch traces",
|
|
199
|
-
docs_url="https://holmesgpt.dev/data-sources/builtin-toolsets/opensearch-status/",
|
|
200
|
-
icon_url="https://opensearch.org/assets/brand/PNG/Mark/opensearch_mark_default.png",
|
|
201
|
-
prerequisites=[CallablePrerequisite(callable=self.prerequisites_callable)],
|
|
202
|
-
tools=[
|
|
203
|
-
GetTracesFields(toolset=self),
|
|
204
|
-
TracesSearchQuery(toolset=self),
|
|
205
|
-
],
|
|
206
|
-
tags=[
|
|
207
|
-
ToolsetTag.CORE,
|
|
208
|
-
],
|
|
209
|
-
)
|
|
210
|
-
template_file_path = os.path.abspath(
|
|
211
|
-
os.path.join(
|
|
212
|
-
os.path.dirname(__file__), "opensearch_traces_instructions.jinja2"
|
|
213
|
-
)
|
|
214
|
-
)
|
|
215
|
-
self._load_llm_instructions(jinja_template=f"file://{template_file_path}")
|