mseep-agentops 0.4.18__py3-none-any.whl → 0.4.22__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.
- agentops/__init__.py +0 -0
- agentops/client/api/base.py +28 -30
- agentops/client/api/versions/v3.py +29 -25
- agentops/client/api/versions/v4.py +87 -46
- agentops/client/client.py +98 -29
- agentops/client/http/README.md +87 -0
- agentops/client/http/http_client.py +126 -172
- agentops/config.py +8 -2
- agentops/instrumentation/OpenTelemetry.md +133 -0
- agentops/instrumentation/README.md +167 -0
- agentops/instrumentation/__init__.py +13 -1
- agentops/instrumentation/agentic/ag2/__init__.py +18 -0
- agentops/instrumentation/agentic/ag2/instrumentor.py +922 -0
- agentops/instrumentation/agentic/agno/__init__.py +19 -0
- agentops/instrumentation/agentic/agno/attributes/__init__.py +20 -0
- agentops/instrumentation/agentic/agno/attributes/agent.py +250 -0
- agentops/instrumentation/agentic/agno/attributes/metrics.py +214 -0
- agentops/instrumentation/agentic/agno/attributes/storage.py +158 -0
- agentops/instrumentation/agentic/agno/attributes/team.py +195 -0
- agentops/instrumentation/agentic/agno/attributes/tool.py +210 -0
- agentops/instrumentation/agentic/agno/attributes/workflow.py +254 -0
- agentops/instrumentation/agentic/agno/instrumentor.py +1313 -0
- agentops/instrumentation/agentic/crewai/LICENSE +201 -0
- agentops/instrumentation/agentic/crewai/NOTICE.md +10 -0
- agentops/instrumentation/agentic/crewai/__init__.py +6 -0
- agentops/instrumentation/agentic/crewai/crewai_span_attributes.py +335 -0
- agentops/instrumentation/agentic/crewai/instrumentation.py +535 -0
- agentops/instrumentation/agentic/crewai/version.py +1 -0
- agentops/instrumentation/agentic/google_adk/__init__.py +19 -0
- agentops/instrumentation/agentic/google_adk/instrumentor.py +68 -0
- agentops/instrumentation/agentic/google_adk/patch.py +767 -0
- agentops/instrumentation/agentic/haystack/__init__.py +1 -0
- agentops/instrumentation/agentic/haystack/instrumentor.py +186 -0
- agentops/instrumentation/agentic/langgraph/__init__.py +3 -0
- agentops/instrumentation/agentic/langgraph/attributes.py +54 -0
- agentops/instrumentation/agentic/langgraph/instrumentation.py +598 -0
- agentops/instrumentation/agentic/langgraph/version.py +1 -0
- agentops/instrumentation/agentic/openai_agents/README.md +156 -0
- agentops/instrumentation/agentic/openai_agents/SPANS.md +145 -0
- agentops/instrumentation/agentic/openai_agents/TRACING_API.md +144 -0
- agentops/instrumentation/agentic/openai_agents/__init__.py +30 -0
- agentops/instrumentation/agentic/openai_agents/attributes/common.py +549 -0
- agentops/instrumentation/agentic/openai_agents/attributes/completion.py +172 -0
- agentops/instrumentation/agentic/openai_agents/attributes/model.py +58 -0
- agentops/instrumentation/agentic/openai_agents/attributes/tokens.py +275 -0
- agentops/instrumentation/agentic/openai_agents/exporter.py +469 -0
- agentops/instrumentation/agentic/openai_agents/instrumentor.py +107 -0
- agentops/instrumentation/agentic/openai_agents/processor.py +58 -0
- agentops/instrumentation/agentic/smolagents/README.md +88 -0
- agentops/instrumentation/agentic/smolagents/__init__.py +12 -0
- agentops/instrumentation/agentic/smolagents/attributes/agent.py +354 -0
- agentops/instrumentation/agentic/smolagents/attributes/model.py +205 -0
- agentops/instrumentation/agentic/smolagents/instrumentor.py +286 -0
- agentops/instrumentation/agentic/smolagents/stream_wrapper.py +258 -0
- agentops/instrumentation/agentic/xpander/__init__.py +15 -0
- agentops/instrumentation/agentic/xpander/context.py +112 -0
- agentops/instrumentation/agentic/xpander/instrumentor.py +877 -0
- agentops/instrumentation/agentic/xpander/trace_probe.py +86 -0
- agentops/instrumentation/agentic/xpander/version.py +3 -0
- agentops/instrumentation/common/README.md +65 -0
- agentops/instrumentation/common/attributes.py +1 -2
- agentops/instrumentation/providers/anthropic/__init__.py +24 -0
- agentops/instrumentation/providers/anthropic/attributes/__init__.py +23 -0
- agentops/instrumentation/providers/anthropic/attributes/common.py +64 -0
- agentops/instrumentation/providers/anthropic/attributes/message.py +541 -0
- agentops/instrumentation/providers/anthropic/attributes/tools.py +231 -0
- agentops/instrumentation/providers/anthropic/event_handler_wrapper.py +90 -0
- agentops/instrumentation/providers/anthropic/instrumentor.py +146 -0
- agentops/instrumentation/providers/anthropic/stream_wrapper.py +436 -0
- agentops/instrumentation/providers/google_genai/README.md +33 -0
- agentops/instrumentation/providers/google_genai/__init__.py +24 -0
- agentops/instrumentation/providers/google_genai/attributes/__init__.py +25 -0
- agentops/instrumentation/providers/google_genai/attributes/chat.py +125 -0
- agentops/instrumentation/providers/google_genai/attributes/common.py +88 -0
- agentops/instrumentation/providers/google_genai/attributes/model.py +284 -0
- agentops/instrumentation/providers/google_genai/instrumentor.py +170 -0
- agentops/instrumentation/providers/google_genai/stream_wrapper.py +238 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/__init__.py +28 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/attributes/__init__.py +27 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/attributes/attributes.py +277 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/attributes/common.py +104 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/instrumentor.py +162 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/stream_wrapper.py +302 -0
- agentops/instrumentation/providers/mem0/__init__.py +45 -0
- agentops/instrumentation/providers/mem0/common.py +377 -0
- agentops/instrumentation/providers/mem0/instrumentor.py +270 -0
- agentops/instrumentation/providers/mem0/memory.py +430 -0
- agentops/instrumentation/providers/openai/__init__.py +21 -0
- agentops/instrumentation/providers/openai/attributes/__init__.py +7 -0
- agentops/instrumentation/providers/openai/attributes/common.py +55 -0
- agentops/instrumentation/providers/openai/attributes/response.py +607 -0
- agentops/instrumentation/providers/openai/config.py +36 -0
- agentops/instrumentation/providers/openai/instrumentor.py +312 -0
- agentops/instrumentation/providers/openai/stream_wrapper.py +941 -0
- agentops/instrumentation/providers/openai/utils.py +44 -0
- agentops/instrumentation/providers/openai/v0.py +176 -0
- agentops/instrumentation/providers/openai/v0_wrappers.py +483 -0
- agentops/instrumentation/providers/openai/wrappers/__init__.py +30 -0
- agentops/instrumentation/providers/openai/wrappers/assistant.py +277 -0
- agentops/instrumentation/providers/openai/wrappers/chat.py +259 -0
- agentops/instrumentation/providers/openai/wrappers/completion.py +109 -0
- agentops/instrumentation/providers/openai/wrappers/embeddings.py +94 -0
- agentops/instrumentation/providers/openai/wrappers/image_gen.py +75 -0
- agentops/instrumentation/providers/openai/wrappers/responses.py +191 -0
- agentops/instrumentation/providers/openai/wrappers/shared.py +81 -0
- agentops/instrumentation/utilities/concurrent_futures/__init__.py +10 -0
- agentops/instrumentation/utilities/concurrent_futures/instrumentation.py +206 -0
- agentops/integration/callbacks/dspy/__init__.py +11 -0
- agentops/integration/callbacks/dspy/callback.py +471 -0
- agentops/integration/callbacks/langchain/README.md +59 -0
- agentops/integration/callbacks/langchain/__init__.py +15 -0
- agentops/integration/callbacks/langchain/callback.py +791 -0
- agentops/integration/callbacks/langchain/utils.py +54 -0
- agentops/legacy/crewai.md +121 -0
- agentops/logging/instrument_logging.py +4 -0
- agentops/sdk/README.md +220 -0
- agentops/sdk/core.py +75 -32
- agentops/sdk/descriptors/classproperty.py +28 -0
- agentops/sdk/exporters.py +152 -33
- agentops/semconv/README.md +125 -0
- agentops/semconv/span_kinds.py +0 -2
- agentops/validation.py +102 -63
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.22.dist-info}/METADATA +30 -40
- mseep_agentops-0.4.22.dist-info/RECORD +178 -0
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.22.dist-info}/WHEEL +1 -2
- mseep_agentops-0.4.18.dist-info/RECORD +0 -94
- mseep_agentops-0.4.18.dist-info/top_level.txt +0 -2
- tests/conftest.py +0 -10
- tests/unit/client/__init__.py +0 -1
- tests/unit/client/test_http_adapter.py +0 -221
- tests/unit/client/test_http_client.py +0 -206
- tests/unit/conftest.py +0 -54
- tests/unit/sdk/__init__.py +0 -1
- tests/unit/sdk/instrumentation_tester.py +0 -207
- tests/unit/sdk/test_attributes.py +0 -392
- tests/unit/sdk/test_concurrent_instrumentation.py +0 -468
- tests/unit/sdk/test_decorators.py +0 -763
- tests/unit/sdk/test_exporters.py +0 -241
- tests/unit/sdk/test_factory.py +0 -1188
- tests/unit/sdk/test_internal_span_processor.py +0 -397
- tests/unit/sdk/test_resource_attributes.py +0 -35
- tests/unit/test_config.py +0 -82
- tests/unit/test_context_manager.py +0 -777
- tests/unit/test_events.py +0 -27
- tests/unit/test_host_env.py +0 -54
- tests/unit/test_init_py.py +0 -501
- tests/unit/test_serialization.py +0 -433
- tests/unit/test_session.py +0 -676
- tests/unit/test_user_agent.py +0 -34
- tests/unit/test_validation.py +0 -405
- {tests → agentops/instrumentation/agentic/openai_agents/attributes}/__init__.py +0 -0
- /tests/unit/__init__.py → /agentops/instrumentation/providers/openai/attributes/tools.py +0 -0
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.22.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,471 @@
|
|
1
|
+
from typing import Any, List
|
2
|
+
|
3
|
+
from opentelemetry.context import attach, detach
|
4
|
+
from opentelemetry.trace import set_span_in_context
|
5
|
+
from opentelemetry.sdk.trace import Span as SDKSpan
|
6
|
+
|
7
|
+
from agentops.logging import logger
|
8
|
+
from agentops.sdk.core import tracer
|
9
|
+
from agentops.semconv import AgentOpsSpanKindValues, SpanAttributes
|
10
|
+
|
11
|
+
from dspy.utils.callback import BaseCallback
|
12
|
+
import dspy
|
13
|
+
|
14
|
+
DSPY_INPUT = "dspy.input.{key}"
|
15
|
+
DSPY_OUTPUT = "dspy.output.{key}"
|
16
|
+
DSPY_ATTRIBUTE = "dspy.attribute.{key}"
|
17
|
+
DSPY_EVALUATE = "evaluate"
|
18
|
+
|
19
|
+
|
20
|
+
class DSPyCallbackHandler(BaseCallback):
|
21
|
+
"""
|
22
|
+
AgentOps callback handler for DSPy.
|
23
|
+
"""
|
24
|
+
|
25
|
+
def __init__(
|
26
|
+
self,
|
27
|
+
api_key: str | None = None,
|
28
|
+
tags: List[str] | None = None,
|
29
|
+
cache: bool = True,
|
30
|
+
auto_session: bool = True,
|
31
|
+
):
|
32
|
+
self.active_spans: dict[str, SDKSpan] = {}
|
33
|
+
self.api_key = api_key
|
34
|
+
self.tags = tags or []
|
35
|
+
self.session_span = None
|
36
|
+
self.session_token = None
|
37
|
+
self.context_tokens = {}
|
38
|
+
self.token_counts = {}
|
39
|
+
|
40
|
+
if auto_session:
|
41
|
+
self._initialize_agentops()
|
42
|
+
|
43
|
+
# Configure caching
|
44
|
+
dspy.configure_cache(
|
45
|
+
enable_disk_cache=cache,
|
46
|
+
enable_memory_cache=cache,
|
47
|
+
)
|
48
|
+
|
49
|
+
# not entirely sure if this works
|
50
|
+
def _initialize_agentops(self):
|
51
|
+
"""Initialize AgentOps"""
|
52
|
+
import agentops
|
53
|
+
|
54
|
+
if not tracer.initialized:
|
55
|
+
init_kwargs = {"auto_start_session": False, "instrument_llm_calls": True, "api_key": None}
|
56
|
+
|
57
|
+
if self.api_key:
|
58
|
+
init_kwargs["api_key"] = self.api_key
|
59
|
+
|
60
|
+
agentops.init(**init_kwargs)
|
61
|
+
logger.debug("AgentOps initialized from DSPy callback handler")
|
62
|
+
|
63
|
+
if not tracer.initialized:
|
64
|
+
logger.warning("AgentOps not initialized, session span will not be created")
|
65
|
+
return
|
66
|
+
|
67
|
+
otel_tracer = tracer.get_tracer()
|
68
|
+
|
69
|
+
span_name = f"session.{AgentOpsSpanKindValues.SESSION.value}"
|
70
|
+
|
71
|
+
attributes = {
|
72
|
+
SpanAttributes.AGENTOPS_SPAN_KIND: AgentOpsSpanKindValues.SESSION.value,
|
73
|
+
"session.tags": self.tags,
|
74
|
+
"agentops.operation.name": "session",
|
75
|
+
"span.kind": AgentOpsSpanKindValues.SESSION.value,
|
76
|
+
}
|
77
|
+
|
78
|
+
# Create a root session span
|
79
|
+
self.session_span = otel_tracer.start_span(span_name, attributes=attributes)
|
80
|
+
|
81
|
+
# Attach session span to current context
|
82
|
+
self.session_token = attach(set_span_in_context(self.session_span))
|
83
|
+
|
84
|
+
logger.debug("Created trace as root span for DSPy")
|
85
|
+
|
86
|
+
def _create_span(
|
87
|
+
self,
|
88
|
+
operation_name: str,
|
89
|
+
span_kind: str,
|
90
|
+
run_id: Any = None,
|
91
|
+
attributes: dict[str, Any] | None = None,
|
92
|
+
parent_run_id: str | None = None,
|
93
|
+
inputs: dict[str, Any] | None = None,
|
94
|
+
):
|
95
|
+
"""
|
96
|
+
Create a span for the operation.
|
97
|
+
|
98
|
+
Args:
|
99
|
+
operation_name: Name of the operation
|
100
|
+
span_kind: Type of span
|
101
|
+
run_id: Unique identifier for the operation
|
102
|
+
attributes: Additional attributes for the span
|
103
|
+
parent_run_id: The run_id of the parent span if this is a child span
|
104
|
+
inputs: The DSPy input dictionary
|
105
|
+
|
106
|
+
Returns:
|
107
|
+
The created span
|
108
|
+
"""
|
109
|
+
if not tracer.initialized:
|
110
|
+
logger.warning("AgentOps not initialized, spans will not be created")
|
111
|
+
return # No valid context for non-recording span
|
112
|
+
|
113
|
+
otel_tracer = tracer.get_tracer()
|
114
|
+
|
115
|
+
span_name = f"{operation_name}.{span_kind}"
|
116
|
+
|
117
|
+
if attributes is None:
|
118
|
+
logger.warning(f"No attributes recorded on span {run_id}")
|
119
|
+
attributes = {}
|
120
|
+
|
121
|
+
if inputs is None:
|
122
|
+
logger.warning(f"No inputs recorded on span {run_id}")
|
123
|
+
inputs = {}
|
124
|
+
|
125
|
+
inputs = {DSPY_INPUT.format(key=key): value for key, value in inputs.items()}
|
126
|
+
|
127
|
+
attributes = {**attributes, **inputs}
|
128
|
+
attributes[SpanAttributes.AGENTOPS_SPAN_KIND] = span_kind
|
129
|
+
attributes["agentops.operation.name"] = operation_name
|
130
|
+
|
131
|
+
if run_id is None:
|
132
|
+
run_id = id(attributes)
|
133
|
+
|
134
|
+
# parent_span = None
|
135
|
+
if parent_run_id is not None and parent_run_id in self.active_spans:
|
136
|
+
# Get parent span from active spans
|
137
|
+
parent_span = self.active_spans[parent_run_id]
|
138
|
+
# Create context with parent span
|
139
|
+
parent_ctx = set_span_in_context(parent_span)
|
140
|
+
# Start span with parent context
|
141
|
+
span = otel_tracer.start_span(span_name, context=parent_ctx, attributes=attributes)
|
142
|
+
logger.debug(f"Start span: {span_name} with parent: {parent_run_id}")
|
143
|
+
else:
|
144
|
+
if not self.session_span:
|
145
|
+
logger.warning(f"Root session span not set. Starting {span_name} as root span.")
|
146
|
+
self.session_span = otel_tracer.start_span(span_name, attributes=attributes)
|
147
|
+
parent_ctx = set_span_in_context(self.session_span)
|
148
|
+
# Start span with session as parent context
|
149
|
+
span = otel_tracer.start_span(span_name, context=parent_ctx, attributes=attributes)
|
150
|
+
logger.debug(f"Started span: {span_name} with session as parent")
|
151
|
+
|
152
|
+
if isinstance(span, SDKSpan):
|
153
|
+
self.active_spans[run_id] = span
|
154
|
+
else:
|
155
|
+
logger.warning(f"Span type warning: generated {type(span)}")
|
156
|
+
|
157
|
+
# Store token to detach later
|
158
|
+
token = attach(set_span_in_context(span))
|
159
|
+
self.context_tokens[run_id] = token
|
160
|
+
|
161
|
+
return span
|
162
|
+
|
163
|
+
def _end_span(
|
164
|
+
self,
|
165
|
+
run_id: str,
|
166
|
+
outputs: Any | None,
|
167
|
+
exception: Exception | None = None,
|
168
|
+
):
|
169
|
+
"""
|
170
|
+
End the span associated with the run_id.
|
171
|
+
|
172
|
+
Args:
|
173
|
+
run_id: Unique identifier for the operation
|
174
|
+
outputs: The DSPy output
|
175
|
+
exception: The DSPy exception
|
176
|
+
"""
|
177
|
+
if run_id not in self.active_spans:
|
178
|
+
logger.warning(f"No span found for call {run_id}")
|
179
|
+
return
|
180
|
+
|
181
|
+
span: SDKSpan = self.active_spans.pop(run_id)
|
182
|
+
token = self.context_tokens.pop(run_id, None)
|
183
|
+
|
184
|
+
if exception:
|
185
|
+
logger.warning(f"Exception {str(exception)}")
|
186
|
+
span.add_event(
|
187
|
+
name="exception",
|
188
|
+
attributes={"exception.type": exception.__class__.__name__, "exception.message": str(exception)},
|
189
|
+
)
|
190
|
+
|
191
|
+
if isinstance(outputs, dict):
|
192
|
+
outputs = {DSPY_OUTPUT.format(key=key): value for key, value in outputs.items()}
|
193
|
+
span.set_attributes(outputs)
|
194
|
+
|
195
|
+
if token is not None:
|
196
|
+
detach(token)
|
197
|
+
|
198
|
+
try:
|
199
|
+
span.end()
|
200
|
+
logger.debug(f"Ended span: {span.update_name('test')}") # ugh
|
201
|
+
except Exception as e:
|
202
|
+
logger.warning(f"Error ending span: {e}")
|
203
|
+
|
204
|
+
# Clean up token counts if present
|
205
|
+
if run_id in self.token_counts:
|
206
|
+
del self.token_counts[run_id]
|
207
|
+
|
208
|
+
# does this type check break on things?
|
209
|
+
def _get_span_kind(self, instance: dspy.Module) -> str:
|
210
|
+
if isinstance(instance, (dspy.ReAct, dspy.ProgramOfThought)):
|
211
|
+
return AgentOpsSpanKindValues.AGENT.value
|
212
|
+
elif isinstance(instance, (dspy.ChainOfThought, dspy.MultiChainComparison, dspy.BestOfN, dspy.Refine)):
|
213
|
+
return AgentOpsSpanKindValues.WORKFLOW.value
|
214
|
+
elif isinstance(instance, dspy.Predict):
|
215
|
+
return AgentOpsSpanKindValues.CHAIN.value
|
216
|
+
elif isinstance(instance, dspy.LM):
|
217
|
+
return AgentOpsSpanKindValues.LLM.value
|
218
|
+
else:
|
219
|
+
logger.warning(f"Instance's span type not found: {instance}")
|
220
|
+
return AgentOpsSpanKindValues.UNKNOWN.value
|
221
|
+
|
222
|
+
def on_module_start(
|
223
|
+
self,
|
224
|
+
call_id: str,
|
225
|
+
instance: Any,
|
226
|
+
inputs: dict[str, Any],
|
227
|
+
):
|
228
|
+
"""A handler triggered when forward() method of a module (subclass of dspy.Module) is called.
|
229
|
+
|
230
|
+
Args:
|
231
|
+
call_id: A unique identifier for the call. Can be used to connect start/end handlers.
|
232
|
+
instance: The Module instance.
|
233
|
+
inputs: The inputs to the module's forward() method. Each arguments is stored as
|
234
|
+
a key-value pair in a dictionary.
|
235
|
+
"""
|
236
|
+
span_kind = self._get_span_kind(instance)
|
237
|
+
attributes = {"instance": instance.__dict__}
|
238
|
+
|
239
|
+
self._create_span(
|
240
|
+
operation_name=f"{instance.__class__.__name__}",
|
241
|
+
span_kind=span_kind,
|
242
|
+
run_id=call_id,
|
243
|
+
inputs=inputs,
|
244
|
+
attributes=attributes,
|
245
|
+
)
|
246
|
+
|
247
|
+
def on_module_end(
|
248
|
+
self,
|
249
|
+
call_id: str,
|
250
|
+
outputs: Any | None,
|
251
|
+
exception: Exception | None = None,
|
252
|
+
):
|
253
|
+
# not collecting?
|
254
|
+
# why was it collecting on the other one?
|
255
|
+
"""A handler triggered after forward() method of a module (subclass of dspy.Module) is executed.
|
256
|
+
|
257
|
+
Args:
|
258
|
+
call_id: A unique identifier for the call. Can be used to connect start/end handlers.
|
259
|
+
outputs: The outputs of the module's forward() method. If the method is interrupted by
|
260
|
+
an exception, this will be None.
|
261
|
+
exception: If an exception is raised during the execution, it will be stored here.
|
262
|
+
"""
|
263
|
+
if isinstance(outputs, dspy.Prediction):
|
264
|
+
outputs = outputs.toDict()
|
265
|
+
|
266
|
+
self._end_span(call_id, outputs, exception)
|
267
|
+
|
268
|
+
def on_lm_start(
|
269
|
+
self,
|
270
|
+
call_id: str,
|
271
|
+
instance: Any,
|
272
|
+
inputs: dict[str, Any],
|
273
|
+
):
|
274
|
+
"""A handler triggered when __call__ method of dspy.LM instance is called.
|
275
|
+
|
276
|
+
Args:
|
277
|
+
call_id: A unique identifier for the call. Can be used to connect start/end handlers.
|
278
|
+
instance: The LM instance.
|
279
|
+
inputs: The inputs to the LM's __call__ method. Each arguments is stored as
|
280
|
+
a key-value pair in a dictionary.
|
281
|
+
"""
|
282
|
+
span_kind = self._get_span_kind(instance)
|
283
|
+
attributes = {"instance": instance.__dict__}
|
284
|
+
|
285
|
+
self._create_span(
|
286
|
+
operation_name=f"{instance.__class__.__name__}",
|
287
|
+
span_kind=span_kind,
|
288
|
+
run_id=call_id,
|
289
|
+
inputs=inputs,
|
290
|
+
attributes=attributes,
|
291
|
+
)
|
292
|
+
|
293
|
+
def on_lm_end(
|
294
|
+
self,
|
295
|
+
call_id: str,
|
296
|
+
outputs: dict[str, Any] | None,
|
297
|
+
exception: Exception | None = None,
|
298
|
+
):
|
299
|
+
"""A handler triggered after __call__ method of dspy.LM instance is executed.
|
300
|
+
|
301
|
+
Args:
|
302
|
+
call_id: A unique identifier for the call. Can be used to connect start/end handlers.
|
303
|
+
outputs: The outputs of the LM's __call__ method. If the method is interrupted by
|
304
|
+
an exception, this will be None.
|
305
|
+
exception: If an exception is raised during the execution, it will be stored here.
|
306
|
+
"""
|
307
|
+
self._end_span(call_id, outputs, exception)
|
308
|
+
|
309
|
+
def on_adapter_format_start(
|
310
|
+
self,
|
311
|
+
call_id: str,
|
312
|
+
instance: Any,
|
313
|
+
inputs: dict[str, Any],
|
314
|
+
):
|
315
|
+
"""A handler triggered when format() method of an adapter (subclass of dspy.Adapter) is called.
|
316
|
+
|
317
|
+
Args:
|
318
|
+
call_id: A unique identifier for the call. Can be used to connect start/end handlers.
|
319
|
+
instance: The Adapter instance.
|
320
|
+
inputs: The inputs to the Adapter's format() method. Each arguments is stored as
|
321
|
+
a key-value pair in a dictionary.
|
322
|
+
"""
|
323
|
+
span_kind = AgentOpsSpanKindValues.OPERATION.value
|
324
|
+
attributes = {"instance": instance.__dict__}
|
325
|
+
|
326
|
+
self._create_span(
|
327
|
+
operation_name=f"{instance.__class__.__name__}",
|
328
|
+
span_kind=span_kind,
|
329
|
+
run_id=call_id,
|
330
|
+
inputs=inputs,
|
331
|
+
attributes=attributes,
|
332
|
+
)
|
333
|
+
|
334
|
+
def on_adapter_format_end(
|
335
|
+
self,
|
336
|
+
call_id: str,
|
337
|
+
outputs: dict[str, Any] | None,
|
338
|
+
exception: Exception | None = None,
|
339
|
+
):
|
340
|
+
"""A handler triggered after format() method of an adapter (subclass of dspy.Adapter) is called..
|
341
|
+
|
342
|
+
Args:
|
343
|
+
call_id: A unique identifier for the call. Can be used to connect start/end handlers.
|
344
|
+
outputs: The outputs of the Adapter's format() method. If the method is interrupted
|
345
|
+
by an exception, this will be None.
|
346
|
+
exception: If an exception is raised during the execution, it will be stored here.
|
347
|
+
"""
|
348
|
+
self._end_span(call_id, outputs, exception)
|
349
|
+
|
350
|
+
def on_adapter_parse_start(
|
351
|
+
self,
|
352
|
+
call_id: str,
|
353
|
+
instance: Any,
|
354
|
+
inputs: dict[str, Any],
|
355
|
+
):
|
356
|
+
"""A handler triggered when parse() method of an adapter (subclass of dspy.Adapter) is called.
|
357
|
+
|
358
|
+
Args:
|
359
|
+
call_id: A unique identifier for the call. Can be used to connect start/end handlers.
|
360
|
+
instance: The Adapter instance.
|
361
|
+
inputs: The inputs to the Adapter's parse() method. Each arguments is stored as
|
362
|
+
a key-value pair in a dictionary.
|
363
|
+
"""
|
364
|
+
span_kind = AgentOpsSpanKindValues.OPERATION.value
|
365
|
+
attributes = {"instance": instance.__dict__}
|
366
|
+
|
367
|
+
self._create_span(
|
368
|
+
operation_name=f"{instance.__class__.__name__}",
|
369
|
+
span_kind=span_kind,
|
370
|
+
run_id=call_id,
|
371
|
+
inputs=inputs,
|
372
|
+
attributes=attributes,
|
373
|
+
)
|
374
|
+
|
375
|
+
def on_adapter_parse_end(
|
376
|
+
self,
|
377
|
+
call_id: str,
|
378
|
+
outputs: dict[str, Any] | None,
|
379
|
+
exception: Exception | None = None,
|
380
|
+
):
|
381
|
+
"""A handler triggered after parse() method of an adapter (subclass of dspy.Adapter) is called.
|
382
|
+
|
383
|
+
Args:
|
384
|
+
call_id: A unique identifier for the call. Can be used to connect start/end handlers.
|
385
|
+
outputs: The outputs of the Adapter's parse() method. If the method is interrupted
|
386
|
+
by an exception, this will be None.
|
387
|
+
exception: If an exception is raised during the execution, it will be stored here.
|
388
|
+
"""
|
389
|
+
self._end_span(call_id, outputs, exception)
|
390
|
+
|
391
|
+
def on_tool_start(
|
392
|
+
self,
|
393
|
+
call_id: str,
|
394
|
+
instance: Any,
|
395
|
+
inputs: dict[str, Any],
|
396
|
+
):
|
397
|
+
"""A handler triggered when a tool is called.
|
398
|
+
|
399
|
+
Args:
|
400
|
+
call_id: A unique identifier for the call. Can be used to connect start/end handlers.
|
401
|
+
instance: The Tool instance.
|
402
|
+
inputs: The inputs to the Tool's __call__ method. Each arguments is stored as
|
403
|
+
a key-value pair in a dictionary.
|
404
|
+
"""
|
405
|
+
span_kind = AgentOpsSpanKindValues.TOOL.value
|
406
|
+
attributes = {"instance": instance.__dict__}
|
407
|
+
|
408
|
+
self._create_span(
|
409
|
+
operation_name=f"{instance.__class__.__name__}",
|
410
|
+
span_kind=span_kind,
|
411
|
+
run_id=call_id,
|
412
|
+
inputs=inputs,
|
413
|
+
attributes=attributes,
|
414
|
+
)
|
415
|
+
|
416
|
+
def on_tool_end(
|
417
|
+
self,
|
418
|
+
call_id: str,
|
419
|
+
outputs: dict[str, Any] | None,
|
420
|
+
exception: Exception | None = None,
|
421
|
+
):
|
422
|
+
"""A handler triggered after a tool is executed.
|
423
|
+
|
424
|
+
Args:
|
425
|
+
call_id: A unique identifier for the call. Can be used to connect start/end handlers.
|
426
|
+
outputs: The outputs of the Tool's __call__ method. If the method is interrupted by
|
427
|
+
an exception, this will be None.
|
428
|
+
exception: If an exception is raised during the execution, it will be stored here.
|
429
|
+
"""
|
430
|
+
self._end_span(call_id, outputs, exception)
|
431
|
+
|
432
|
+
def on_evaluate_start(
|
433
|
+
self,
|
434
|
+
call_id: str,
|
435
|
+
instance: Any,
|
436
|
+
inputs: dict[str, Any],
|
437
|
+
):
|
438
|
+
"""A handler triggered when evaluation is started.
|
439
|
+
|
440
|
+
Args:
|
441
|
+
call_id: A unique identifier for the call. Can be used to connect start/end handlers.
|
442
|
+
instance: The Evaluate instance.
|
443
|
+
inputs: The inputs to the Evaluate's __call__ method. Each arguments is stored as
|
444
|
+
a key-value pair in a dictionary.
|
445
|
+
"""
|
446
|
+
span_kind = DSPY_EVALUATE
|
447
|
+
attributes = {"instance": instance.__dict__}
|
448
|
+
|
449
|
+
self._create_span(
|
450
|
+
operation_name=f"{instance.__class__.__name__}",
|
451
|
+
span_kind=span_kind,
|
452
|
+
run_id=call_id,
|
453
|
+
inputs=inputs,
|
454
|
+
attributes=attributes,
|
455
|
+
)
|
456
|
+
|
457
|
+
def on_evaluate_end(
|
458
|
+
self,
|
459
|
+
call_id: str,
|
460
|
+
outputs: Any | None,
|
461
|
+
exception: Exception | None = None,
|
462
|
+
):
|
463
|
+
"""A handler triggered after evaluation is executed.
|
464
|
+
|
465
|
+
Args:
|
466
|
+
call_id: A unique identifier for the call. Can be used to connect start/end handlers.
|
467
|
+
outputs: The outputs of the Evaluate's __call__ method. If the method is interrupted by
|
468
|
+
an exception, this will be None.
|
469
|
+
exception: If an exception is raised during the execution, it will be stored here.
|
470
|
+
"""
|
471
|
+
self._end_span(call_id, outputs, exception)
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# AgentOps LangChain Callback Handler
|
2
|
+
|
3
|
+
This callback handler enables seamless integration between LangChain and AgentOps for tracing and monitoring LLM applications.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- **Complete Coverage**: Supports all LangChain callback methods
|
8
|
+
- **Session Tracking**: Creates a session span that serves as the root for all operations
|
9
|
+
- **Proper Hierarchy**: Maintains parent-child relationships between operations
|
10
|
+
- **Complete Instrumentation**: Tracks LLMs, chains, tools, and agent actions
|
11
|
+
- **Error Tracking**: Records errors from LLMs, chains, and tools
|
12
|
+
- **Streaming Support**: Handles token streaming for real-time insights
|
13
|
+
- **Attribute Capture**: Records inputs, outputs, and metadata for all operations
|
14
|
+
- **Error Resilience**: Handles errors gracefully to ensure spans are always properly closed
|
15
|
+
|
16
|
+
## Supported Callbacks
|
17
|
+
|
18
|
+
The handler implements all LangChain callback methods:
|
19
|
+
|
20
|
+
| Method | Description | Span Kind | Attributes |
|
21
|
+
|--------|-------------|-----------|------------|
|
22
|
+
| `on_llm_start` | Start of an LLM call | `llm` | Model, prompts, parameters |
|
23
|
+
| `on_llm_end` | End of an LLM call | `llm` | Completions, token usage |
|
24
|
+
| `on_llm_new_token` | Streaming token received | N/A | Token count, last token |
|
25
|
+
| `on_llm_error` | LLM call error | `llm` | Error details |
|
26
|
+
| `on_chat_model_start` | Start of a chat model call | `llm` | Model, messages, parameters |
|
27
|
+
| `on_chain_start` | Start of a chain | `task` | Chain type, inputs |
|
28
|
+
| `on_chain_end` | End of a chain | `task` | Outputs |
|
29
|
+
| `on_chain_error` | Chain execution error | `task` | Error details |
|
30
|
+
| `on_tool_start` | Start of a tool call | `tool` | Tool name, input |
|
31
|
+
| `on_tool_end` | End of a tool call | `tool` | Output |
|
32
|
+
| `on_tool_error` | Tool execution error | `tool` | Error details |
|
33
|
+
| `on_agent_action` | Agent taking an action | `agent` | Tool, input, log |
|
34
|
+
| `on_agent_finish` | Agent completing a task | `agent` | Output, log |
|
35
|
+
| `on_text` | Arbitrary text event | `text` | Text content |
|
36
|
+
|
37
|
+
All spans have appropriate attributes such as:
|
38
|
+
- Model information for LLM spans
|
39
|
+
- Input/output for all operations
|
40
|
+
- Tool names and types
|
41
|
+
- Chain types and configurations
|
42
|
+
- Error details for failed operations
|
43
|
+
|
44
|
+
## Troubleshooting
|
45
|
+
|
46
|
+
If you're not seeing data in AgentOps:
|
47
|
+
|
48
|
+
1. Check that your API key is correctly configured
|
49
|
+
2. Ensure you're passing the handler to all relevant components
|
50
|
+
3. Verify that all operations are properly ending/closing
|
51
|
+
|
52
|
+
## How It Works
|
53
|
+
|
54
|
+
The callback handler:
|
55
|
+
1. Creates a session span when initialized
|
56
|
+
2. Intercepts LangChain callbacks for various operations
|
57
|
+
3. Creates appropriate spans with meaningful attributes
|
58
|
+
4. Maintains proper parent-child relationships
|
59
|
+
5. Automatically cleans up and ends spans when operations complete
|
@@ -0,0 +1,15 @@
|
|
1
|
+
"""
|
2
|
+
LangChain integration for AgentOps.
|
3
|
+
|
4
|
+
This module provides the AgentOps LangChain integration, including callbacks and utilities.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from agentops.integration.callbacks.langchain.callback import (
|
8
|
+
LangchainCallbackHandler,
|
9
|
+
AsyncLangchainCallbackHandler,
|
10
|
+
)
|
11
|
+
|
12
|
+
__all__ = [
|
13
|
+
"LangchainCallbackHandler",
|
14
|
+
"AsyncLangchainCallbackHandler",
|
15
|
+
]
|