Skip to content

Commit 274a440

Browse files
authored
fix(llmobs): do no annotate self for automatic annotations with decorators (#15349)
## Description Makes sure `self` is not decorated when using LLMObs decorators. Closes #14849 ## Testing Unit tests added ## Risks Should not be a breaking change as although we run evaluations on input values, they are not used as far as I know as part of monitors or sampling rates for evaluations. If anything, it might help with evaluations on spans that had `self` annotated previously as a result of using decorators without manual annotation override.
1 parent 906b99a commit 274a440

File tree

3 files changed

+35
-4
lines changed

3 files changed

+35
-4
lines changed

ddtrace/llmobs/decorators.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import sys
77
from typing import Callable
88
from typing import Optional
9+
from typing import OrderedDict
910

1011
from ddtrace.internal.logger import get_logger
1112
from ddtrace.llmobs import LLMObs
@@ -28,6 +29,10 @@ def _get_llmobs_span_options(name, model_name, func):
2829
return traced_model_name, span_name
2930

3031

32+
def _get_span_inputs(args: OrderedDict) -> dict:
33+
return {arg: value for arg, value in args.items() if arg != "self"}
34+
35+
3136
async def yield_from_async_gen(func, span, args, kwargs):
3237
try:
3338
gen = func(*args, **kwargs)
@@ -176,7 +181,7 @@ def generator_wrapper(*args, **kwargs):
176181
func_signature = signature(func)
177182
bound_args = func_signature.bind_partial(*args, **kwargs)
178183
if _automatic_io_annotation and bound_args.arguments:
179-
LLMObs.annotate(span=span, input_data=dict(bound_args.arguments))
184+
LLMObs.annotate(span=span, input_data=_get_span_inputs(bound_args.arguments))
180185
return yield_from_async_gen(func, span, args, kwargs)
181186

182187
@wraps(func)
@@ -192,7 +197,7 @@ async def wrapper(*args, **kwargs):
192197
func_signature = signature(func)
193198
bound_args = func_signature.bind_partial(*args, **kwargs)
194199
if _automatic_io_annotation and bound_args.arguments:
195-
LLMObs.annotate(span=span, input_data=dict(bound_args.arguments))
200+
LLMObs.annotate(span=span, input_data=_get_span_inputs(bound_args.arguments))
196201
resp = await func(*args, **kwargs)
197202
if (
198203
_automatic_io_annotation
@@ -217,7 +222,7 @@ def generator_wrapper(*args, **kwargs):
217222
func_signature = signature(func)
218223
bound_args = func_signature.bind_partial(*args, **kwargs)
219224
if _automatic_io_annotation and bound_args.arguments:
220-
LLMObs.annotate(span=span, input_data=dict(bound_args.arguments))
225+
LLMObs.annotate(span=span, input_data=_get_span_inputs(bound_args.arguments))
221226
try:
222227
yield from func(*args, **kwargs)
223228
except (StopIteration, GeneratorExit):
@@ -242,7 +247,7 @@ def wrapper(*args, **kwargs):
242247
func_signature = signature(func)
243248
bound_args = func_signature.bind_partial(*args, **kwargs)
244249
if _automatic_io_annotation and bound_args.arguments:
245-
LLMObs.annotate(span=span, input_data=dict(bound_args.arguments))
250+
LLMObs.annotate(span=span, input_data=_get_span_inputs(bound_args.arguments))
246251
resp = func(*args, **kwargs)
247252
if (
248253
_automatic_io_annotation
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
fixes:
3+
- |
4+
LLM Observability: Resolves an issue where ``self`` was being annotated as an input parameter using LLM Observability function decorators.

tests/llmobs/test_llmobs_decorators.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import json
2+
13
import mock
24
import pytest
35

@@ -828,3 +830,23 @@ def get_next_element(alist):
828830
error_message=span.get_tag("error.message"),
829831
error_stack=span.get_tag("error.stack"),
830832
)
833+
834+
835+
@pytest.mark.parametrize(
836+
"decorator",
837+
[task, tool, workflow, agent],
838+
ids=["task", "tool", "workflow", "agent"],
839+
)
840+
def test_generator_for_class_does_not_annotate_self(llmobs, llmobs_events, decorator):
841+
class TestClass:
842+
@decorator
843+
def add(self, a: int, b: int) -> int:
844+
return a + b
845+
846+
test_class = TestClass()
847+
test_class.add(1, 2)
848+
849+
assert len(llmobs_events) == 1
850+
851+
input_value = json.loads(llmobs_events[0]["meta"]["input"]["value"])
852+
assert input_value == {"a": 1, "b": 2}

0 commit comments

Comments
 (0)