holmesgpt 0.11.5__py3-none-any.whl → 0.12.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of holmesgpt might be problematic. Click here for more details.
- holmes/__init__.py +1 -1
- holmes/common/env_vars.py +8 -4
- holmes/config.py +52 -13
- holmes/core/investigation_structured_output.py +7 -0
- holmes/core/llm.py +14 -4
- holmes/core/models.py +24 -0
- holmes/core/tool_calling_llm.py +48 -6
- holmes/core/tools.py +7 -4
- holmes/core/toolset_manager.py +24 -5
- holmes/core/tracing.py +224 -0
- holmes/interactive.py +761 -44
- holmes/main.py +59 -127
- holmes/plugins/prompts/_fetch_logs.jinja2 +4 -0
- holmes/plugins/prompts/kubernetes_workload_ask.jinja2 +2 -10
- holmes/plugins/toolsets/__init__.py +10 -2
- holmes/plugins/toolsets/azure_sql/apis/azure_sql_api.py +2 -1
- holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py +3 -0
- holmes/plugins/toolsets/datadog/datadog_api.py +161 -0
- holmes/plugins/toolsets/datadog/datadog_metrics_instructions.jinja2 +26 -0
- holmes/plugins/toolsets/datadog/datadog_traces_formatter.py +310 -0
- holmes/plugins/toolsets/datadog/instructions_datadog_traces.jinja2 +51 -0
- holmes/plugins/toolsets/datadog/toolset_datadog_logs.py +267 -0
- holmes/plugins/toolsets/datadog/toolset_datadog_metrics.py +488 -0
- holmes/plugins/toolsets/datadog/toolset_datadog_traces.py +689 -0
- holmes/plugins/toolsets/grafana/toolset_grafana_loki.py +3 -0
- holmes/plugins/toolsets/internet/internet.py +1 -1
- holmes/plugins/toolsets/logging_utils/logging_api.py +9 -3
- holmes/plugins/toolsets/opensearch/opensearch_logs.py +3 -0
- holmes/plugins/toolsets/utils.py +6 -2
- holmes/utils/cache.py +4 -4
- holmes/utils/console/consts.py +2 -0
- holmes/utils/console/logging.py +95 -0
- holmes/utils/console/result.py +37 -0
- {holmesgpt-0.11.5.dist-info → holmesgpt-0.12.0.dist-info}/METADATA +3 -4
- {holmesgpt-0.11.5.dist-info → holmesgpt-0.12.0.dist-info}/RECORD +38 -29
- {holmesgpt-0.11.5.dist-info → holmesgpt-0.12.0.dist-info}/WHEEL +1 -1
- holmes/__init__.py.bak +0 -76
- holmes/plugins/toolsets/datadog.py +0 -153
- {holmesgpt-0.11.5.dist-info → holmesgpt-0.12.0.dist-info}/LICENSE.txt +0 -0
- {holmesgpt-0.11.5.dist-info → holmesgpt-0.12.0.dist-info}/entry_points.txt +0 -0
holmes/core/tracing.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Optional, Any, Union
|
|
4
|
+
from enum import Enum
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
import braintrust
|
|
8
|
+
from braintrust import Span, SpanTypeAttribute
|
|
9
|
+
|
|
10
|
+
BRAINTRUST_AVAILABLE = True
|
|
11
|
+
except ImportError:
|
|
12
|
+
BRAINTRUST_AVAILABLE = False
|
|
13
|
+
# Type aliases for when braintrust is not available
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from braintrust import Span, SpanTypeAttribute
|
|
18
|
+
else:
|
|
19
|
+
Span = Any
|
|
20
|
+
SpanTypeAttribute = Any
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _is_noop_span(span) -> bool:
|
|
24
|
+
"""Check if a span is a Braintrust NoopSpan (inactive span)."""
|
|
25
|
+
return span is None or str(type(span)).endswith("_NoopSpan'>")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SpanType(Enum):
|
|
29
|
+
"""Standard span types for tracing categorization."""
|
|
30
|
+
|
|
31
|
+
LLM = "llm"
|
|
32
|
+
TOOL = "tool"
|
|
33
|
+
TASK = "task"
|
|
34
|
+
SCORE = "score"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class DummySpan:
|
|
38
|
+
"""A no-op span implementation for when tracing is disabled."""
|
|
39
|
+
|
|
40
|
+
def start_span(self, name: str, span_type: Optional[SpanType] = None, **kwargs):
|
|
41
|
+
return DummySpan()
|
|
42
|
+
|
|
43
|
+
def log(self, *args, **kwargs):
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
def end(self):
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
def __enter__(self):
|
|
50
|
+
return self
|
|
51
|
+
|
|
52
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class DummyTracer:
|
|
57
|
+
"""A no-op tracer implementation for when tracing is disabled."""
|
|
58
|
+
|
|
59
|
+
def start_experiment(self, experiment_name=None, metadata=None):
|
|
60
|
+
"""No-op experiment creation."""
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
def start_trace(self, name: str, span_type=None):
|
|
64
|
+
"""No-op trace creation."""
|
|
65
|
+
return DummySpan()
|
|
66
|
+
|
|
67
|
+
def get_trace_url(self):
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
def wrap_llm(self, llm_module):
|
|
71
|
+
"""No-op LLM wrapping for dummy tracer."""
|
|
72
|
+
return llm_module
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class BraintrustTracer:
|
|
76
|
+
"""Braintrust implementation of tracing."""
|
|
77
|
+
|
|
78
|
+
def __init__(self, project: str = "HolmesGPT-CLI"):
|
|
79
|
+
if not BRAINTRUST_AVAILABLE:
|
|
80
|
+
raise ImportError("braintrust package is required for BraintrustTracer")
|
|
81
|
+
|
|
82
|
+
self.project = project
|
|
83
|
+
|
|
84
|
+
def start_experiment(
|
|
85
|
+
self, experiment_name: Optional[str] = None, metadata: Optional[dict] = None
|
|
86
|
+
):
|
|
87
|
+
"""Create and start a new Braintrust experiment.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
experiment_name: Name for the experiment, auto-generated if None
|
|
91
|
+
metadata: Metadata to attach to experiment
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Braintrust experiment object
|
|
95
|
+
"""
|
|
96
|
+
if not os.environ.get("BRAINTRUST_API_KEY"):
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
return braintrust.init(
|
|
100
|
+
project=self.project,
|
|
101
|
+
experiment=experiment_name,
|
|
102
|
+
metadata=metadata or {},
|
|
103
|
+
update=True,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def start_trace(
|
|
107
|
+
self, name: str, span_type: Optional[SpanType] = None
|
|
108
|
+
) -> Union[Span, DummySpan]:
|
|
109
|
+
"""Start a trace span in current Braintrust context.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
name: Span name
|
|
113
|
+
span_type: Type of span for categorization
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Span that can be used as context manager
|
|
117
|
+
"""
|
|
118
|
+
if not os.environ.get("BRAINTRUST_API_KEY"):
|
|
119
|
+
return DummySpan()
|
|
120
|
+
|
|
121
|
+
# Add span type to kwargs if provided
|
|
122
|
+
kwargs = {}
|
|
123
|
+
if span_type:
|
|
124
|
+
kwargs["type"] = getattr(SpanTypeAttribute, span_type.name)
|
|
125
|
+
|
|
126
|
+
# Use current Braintrust context (experiment or parent span)
|
|
127
|
+
current_span = braintrust.current_span()
|
|
128
|
+
if not _is_noop_span(current_span):
|
|
129
|
+
return current_span.start_span(name=name, **kwargs)
|
|
130
|
+
|
|
131
|
+
# Fallback to current experiment
|
|
132
|
+
current_experiment = braintrust.current_experiment()
|
|
133
|
+
if current_experiment:
|
|
134
|
+
return current_experiment.start_span(name=name, **kwargs)
|
|
135
|
+
|
|
136
|
+
return DummySpan()
|
|
137
|
+
|
|
138
|
+
def get_trace_url(self) -> Optional[str]:
|
|
139
|
+
"""Get URL to view the trace in Braintrust."""
|
|
140
|
+
logging.info("Getting trace URL for Braintrust")
|
|
141
|
+
if not os.environ.get("BRAINTRUST_API_KEY"):
|
|
142
|
+
logging.warning("BRAINTRUST_API_KEY not set, cannot get trace URL")
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
# Get current experiment from Braintrust context
|
|
146
|
+
current_experiment = braintrust.current_experiment()
|
|
147
|
+
if not current_experiment:
|
|
148
|
+
logging.warning("No current experiment found in Braintrust context")
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
experiment_name = getattr(current_experiment, "name", None)
|
|
152
|
+
if not experiment_name:
|
|
153
|
+
logging.warning("No experiment name found in current Braintrust context")
|
|
154
|
+
return None
|
|
155
|
+
|
|
156
|
+
current_span = braintrust.current_span()
|
|
157
|
+
if not _is_noop_span(current_span):
|
|
158
|
+
span_id = getattr(current_span, "span_id", None)
|
|
159
|
+
id_attr = getattr(current_span, "id", None)
|
|
160
|
+
if span_id and id_attr:
|
|
161
|
+
return f"https://www.braintrust.dev/app/robustadev/p/{self.project}/experiments/{experiment_name}?c=&tg=false&r={id_attr}&s={span_id}"
|
|
162
|
+
else:
|
|
163
|
+
logging.warning("No active span found in Braintrust context")
|
|
164
|
+
|
|
165
|
+
return f"https://www.braintrust.dev/app/robustadev/p/{self.project}/experiments/{experiment_name}"
|
|
166
|
+
|
|
167
|
+
def wrap_llm(self, llm_module):
|
|
168
|
+
"""Wrap LiteLLM with Braintrust tracing if in active context, otherwise return unwrapped."""
|
|
169
|
+
if not BRAINTRUST_AVAILABLE or not os.environ.get("BRAINTRUST_API_KEY"):
|
|
170
|
+
return llm_module
|
|
171
|
+
|
|
172
|
+
from braintrust.oai import ChatCompletionWrapper
|
|
173
|
+
|
|
174
|
+
class WrappedLiteLLM:
|
|
175
|
+
def __init__(self, original_module):
|
|
176
|
+
self._original_module = original_module
|
|
177
|
+
self._chat_wrapper = ChatCompletionWrapper(
|
|
178
|
+
create_fn=original_module.completion,
|
|
179
|
+
acreate_fn=None,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def completion(self, **kwargs):
|
|
183
|
+
return self._chat_wrapper.create(**kwargs)
|
|
184
|
+
|
|
185
|
+
def __getattr__(self, name):
|
|
186
|
+
return getattr(self._original_module, name)
|
|
187
|
+
|
|
188
|
+
return WrappedLiteLLM(llm_module)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class TracingFactory:
|
|
192
|
+
"""Factory for creating tracer instances."""
|
|
193
|
+
|
|
194
|
+
@staticmethod
|
|
195
|
+
def create_tracer(trace_type: Optional[str], project: str):
|
|
196
|
+
"""Create a tracer instance based on the trace type.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
trace_type: Type of tracing ('braintrust', etc.)
|
|
200
|
+
project: Project name for tracing
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
Tracer instance if tracing enabled, DummySpan if disabled
|
|
204
|
+
"""
|
|
205
|
+
if not trace_type:
|
|
206
|
+
return DummyTracer()
|
|
207
|
+
|
|
208
|
+
if trace_type.lower() == "braintrust":
|
|
209
|
+
if not BRAINTRUST_AVAILABLE:
|
|
210
|
+
logging.warning(
|
|
211
|
+
"Braintrust tracing requested but braintrust package not available"
|
|
212
|
+
)
|
|
213
|
+
return DummyTracer()
|
|
214
|
+
|
|
215
|
+
if not os.environ.get("BRAINTRUST_API_KEY"):
|
|
216
|
+
logging.warning(
|
|
217
|
+
"Braintrust tracing requested but BRAINTRUST_API_KEY not set"
|
|
218
|
+
)
|
|
219
|
+
return DummyTracer()
|
|
220
|
+
|
|
221
|
+
return BraintrustTracer(project=project)
|
|
222
|
+
|
|
223
|
+
logging.warning(f"Unknown trace type: {trace_type}")
|
|
224
|
+
return DummyTracer()
|