Skip to content

Commit e1b6ee1

Browse files
committed
Move listener hooks to enable instead of on init
1 parent 04ee68f commit e1b6ee1

File tree

3 files changed

+41
-4
lines changed

3 files changed

+41
-4
lines changed

ddtrace/llmobs/_llmobs.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,6 @@ def __init__(self, tracer=None):
109109
self._annotations = []
110110
self._annotation_context_lock = forksafe.RLock()
111111

112-
# Register hooks for span events
113-
core.on("trace.span_start", self._do_annotations)
114-
core.on("trace.span_finish", self._on_span_finish)
115-
116112
def _on_span_finish(self, span):
117113
if self.enabled and span.span_type == SpanTypes.LLM:
118114
self._submit_llmobs_span(span)
@@ -270,6 +266,10 @@ def _start_service(self) -> None:
270266
log.debug("Error starting evaluator runner")
271267

272268
def _stop_service(self) -> None:
269+
# Remove listener hooks for span events
270+
core.reset_listeners("trace.span_start")
271+
core.reset_listeners("trace.span_finish")
272+
273273
try:
274274
self._evaluator_runner.stop()
275275
# flush remaining evaluation spans & evaluations
@@ -364,6 +364,10 @@ def enable(
364364
cls.enabled = True
365365
cls._instance.start()
366366

367+
# Register hooks for span events
368+
core.on("trace.span_start", cls._instance._do_annotations)
369+
core.on("trace.span_finish", cls._instance._on_span_finish)
370+
367371
atexit.register(cls.disable)
368372
telemetry_writer.product_activated(TELEMETRY_APM_PRODUCT.LLMOBS, True)
369373

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
fixes:
3+
- |
4+
LLM Observability: Resolves an issue where enabling LLM Observability in agentless mode would result in traces also being sent to the agent proxy endpoint.

tests/llmobs/test_llmobs_service.py

+29
Original file line numberDiff line numberDiff line change
@@ -1319,6 +1319,35 @@ def test_activate_distributed_headers_activates_context(llmobs, mock_llmobs_logs
13191319
mock_activate.assert_called_once_with(dummy_context)
13201320

13211321

1322+
def test_listener_hooks_enqueue_correct_writer(run_python_code_in_subprocess):
1323+
"""
1324+
Regression test that ensures that listener hooks enqueue span events to the correct writer,
1325+
not the default writer created at startup.
1326+
"""
1327+
env = os.environ.copy()
1328+
pypath = [os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))]
1329+
if "PYTHONPATH" in env:
1330+
pypath.append(env["PYTHONPATH"])
1331+
env.update(
1332+
{"DD_API_KEY": "foobar.baz", "DD_SITE": "datad0g.com", "PYTHONPATH": ":".join(pypath), "DD_TRACE_ENABLED": "0"}
1333+
)
1334+
1335+
out, err, status, pid = run_python_code_in_subprocess(
1336+
"""
1337+
from ddtrace.llmobs import LLMObs
1338+
1339+
LLMObs.enable(ml_app="repro-issue", agentless_enabled=True)
1340+
with LLMObs.agent("dummy"):
1341+
pass
1342+
""",
1343+
env=env,
1344+
)
1345+
assert status == 0, err
1346+
assert out == b""
1347+
assert err == b"failed to send traces to intake at https://llmobs-intake.datad0g.com/api/v2/llmobs: HTTP error status 403, reason Forbidden\n" # noqa: E501
1348+
assert b"failed to send, dropping 1 traces to intake at http://localhost:8126/evp_proxy/v2/api/v2/llmobs after 5 retries" not in err # noqa: E501
1349+
1350+
13221351
def test_llmobs_fork_recreates_and_restarts_span_writer():
13231352
"""Test that forking a process correctly recreates and restarts the LLMObsSpanWriter."""
13241353
with mock.patch("ddtrace.internal.writer.HTTPWriter._send_payload"):

0 commit comments

Comments
 (0)