-
Notifications
You must be signed in to change notification settings - Fork 424
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(llmobs): llmobs-specific context manager #10767
base: main
Are you sure you want to change the base?
Changes from 7 commits
b34e3bd
c99c0bc
191a0d0
b0ede81
bd9b539
dd0a23a
0e41826
ec5779f
304bbe7
80eced2
2f04735
8565dd8
76f00e3
88fb4ae
173271f
5feb4ba
e5be797
c37d1b2
35aae06
8f0a797
1490117
2fbba44
a879fd3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import contextvars | ||
from typing import Optional | ||
from typing import Union | ||
|
||
from ddtrace._trace.context import Context | ||
from ddtrace._trace.provider import DefaultContextProvider | ||
from ddtrace._trace.span import Span | ||
from ddtrace.ext import SpanTypes | ||
|
||
|
||
ContextTypeValue = Optional[Union[Context, Span]] | ||
|
||
|
||
_DD_LLMOBS_CONTEXTVAR: contextvars.ContextVar[ContextTypeValue] = contextvars.ContextVar( | ||
"datadog_llmobs_contextvar", | ||
default=None, | ||
) | ||
|
||
|
||
class LLMObsContextProvider(DefaultContextProvider): | ||
"""Context provider that retrieves contexts from a context variable. | ||
It is suitable for synchronous programming and for asynchronous executors | ||
that support contextvars. | ||
""" | ||
|
||
def __init__(self): | ||
# type: () -> None | ||
Yun-Kim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
super(DefaultContextProvider, self).__init__() | ||
_DD_LLMOBS_CONTEXTVAR.set(None) | ||
|
||
def _has_active_context(self): | ||
# type: () -> bool | ||
Yun-Kim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"""Returns whether there is an active context in the current execution.""" | ||
ctx = _DD_LLMOBS_CONTEXTVAR.get() | ||
return ctx is not None | ||
|
||
def _update_active(self, span: Span) -> Optional[Span]: | ||
"""Updates the active LLMObs span. | ||
The active span is updated to be the span's closest unfinished LLMObs ancestor span. | ||
""" | ||
if not span.finished: | ||
return span | ||
new_active: Optional[Span] = span | ||
while new_active and new_active.finished: | ||
new_active = new_active._parent | ||
if new_active and not new_active.finished and new_active.span_type == SpanTypes.LLM: | ||
break | ||
self.activate(new_active) | ||
return new_active | ||
|
||
def activate(self, ctx: ContextTypeValue) -> None: | ||
"""Makes the given context active in the current execution.""" | ||
_DD_LLMOBS_CONTEXTVAR.set(ctx) | ||
super(DefaultContextProvider, self).activate(ctx) | ||
|
||
def active(self) -> ContextTypeValue: | ||
"""Returns the active span or context for the current execution.""" | ||
item = _DD_LLMOBS_CONTEXTVAR.get() | ||
if isinstance(item, Span): | ||
return self._update_active(item) | ||
return item |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
|
||
from ddtrace._trace.span import Span | ||
from ddtrace.internal.logger import get_logger | ||
from ddtrace.llmobs import LLMObs | ||
from ddtrace.llmobs._constants import INPUT_MESSAGES | ||
from ddtrace.llmobs._constants import INPUT_TOKENS_METRIC_KEY | ||
from ddtrace.llmobs._constants import METADATA | ||
|
@@ -13,12 +14,9 @@ | |
from ddtrace.llmobs._constants import MODEL_PROVIDER | ||
from ddtrace.llmobs._constants import OUTPUT_MESSAGES | ||
from ddtrace.llmobs._constants import OUTPUT_TOKENS_METRIC_KEY | ||
from ddtrace.llmobs._constants import PARENT_ID_KEY | ||
from ddtrace.llmobs._constants import PROPAGATED_PARENT_ID_KEY | ||
from ddtrace.llmobs._constants import SPAN_KIND | ||
from ddtrace.llmobs._constants import TOTAL_TOKENS_METRIC_KEY | ||
from ddtrace.llmobs._integrations import BaseLLMIntegration | ||
from ddtrace.llmobs._utils import _get_llmobs_parent_id | ||
from ddtrace.llmobs._utils import safe_json | ||
|
||
|
||
|
@@ -32,9 +30,7 @@ def _llmobs_set_tags( | |
self, span: Span, args: List[Any], kwargs: Dict[str, Any], response: Optional[Any] = None, operation: str = "" | ||
) -> None: | ||
"""Extract prompt/response tags from a completion and set them as temporary "_ml_obs.*" tags.""" | ||
if span.get_tag(PROPAGATED_PARENT_ID_KEY) is None: | ||
parent_id = _get_llmobs_parent_id(span) or "undefined" | ||
span.set_tag(PARENT_ID_KEY, parent_id) | ||
LLMObs._instance._activate_llmobs_span(span) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: this is an existing issue and we'll need to fix it after this PR. But because of the way the bedrock integration uses shared tracing handlers, we don't actually get to reuse the |
||
parameters = {} | ||
if span.get_tag("bedrock.request.temperature"): | ||
parameters["temperature"] = float(span.get_tag("bedrock.request.temperature") or 0.0) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explore using signals instead of importing!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following up on a separate PR.