Skip to content

Commit 647757d

Browse files
Kyle-Verhoogncybul
authored andcommitted
chore(llmobs): [MLOB-1944] generalize helper for extracting token metrics (#12223)
[applying #12026 to 3.x-staging] This PR generalizes the helper method used to extract token metrics from an APM span to be attached to an LLMObs span. Before, Anthropic, Bedrock, and Open AI had specific methods on each of their integration classes to accomplish this. Now, there is one get_llmobs_metrics_tags utils function adapted from the google-specific get_llmobs_metrics_tags_google function that gets reused across these integrations as well as Vertex AI and Gemini. The Langchain integration was excluded from this change since its logic for extracting token metrics varies significantly compared to the other integrations. ## Checklist - [x] PR author has checked that all the criteria below are met - The PR description includes an overview of the change - The PR description articulates the motivation for the change - The change includes tests OR the PR description describes a testing strategy - The PR description notes risks associated with the change, if any - Newly-added code is easy to change - The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) - The change includes or references documentation updates if necessary - Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Reviewer has checked that all the criteria below are met - Title is accurate - All changes are related to the pull request's stated goal - Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - Testing strategy adequately addresses listed risks - Newly-added code is easy to change - Release note makes sense to a user of the library - If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting) --------- Co-authored-by: Nicole Cybul <[email protected]>
1 parent 91ef4ee commit 647757d

File tree

6 files changed

+32
-50
lines changed

6 files changed

+32
-50
lines changed

ddtrace/llmobs/_integrations/anthropic.py

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,14 @@
77

88
from ddtrace.internal.logger import get_logger
99
from ddtrace.llmobs._constants import INPUT_MESSAGES
10-
from ddtrace.llmobs._constants import INPUT_TOKENS_METRIC_KEY
1110
from ddtrace.llmobs._constants import METADATA
1211
from ddtrace.llmobs._constants import METRICS
1312
from ddtrace.llmobs._constants import MODEL_NAME
1413
from ddtrace.llmobs._constants import MODEL_PROVIDER
1514
from ddtrace.llmobs._constants import OUTPUT_MESSAGES
16-
from ddtrace.llmobs._constants import OUTPUT_TOKENS_METRIC_KEY
1715
from ddtrace.llmobs._constants import SPAN_KIND
18-
from ddtrace.llmobs._constants import TOTAL_TOKENS_METRIC_KEY
1916
from ddtrace.llmobs._integrations.base import BaseLLMIntegration
17+
from ddtrace.llmobs._integrations.utils import get_llmobs_metrics_tags
2018
from ddtrace.llmobs._utils import _get_attr
2119
from ddtrace.trace import Span
2220

@@ -77,7 +75,7 @@ def _llmobs_set_tags(
7775
INPUT_MESSAGES: input_messages,
7876
METADATA: parameters,
7977
OUTPUT_MESSAGES: output_messages,
80-
METRICS: self._get_llmobs_metrics_tags(span),
78+
METRICS: get_llmobs_metrics_tags("anthropic", span),
8179
}
8280
)
8381

@@ -188,18 +186,3 @@ def record_usage(self, span: Span, usage: Dict[str, Any]) -> None:
188186
span.set_metric("anthropic.response.usage.output_tokens", output_tokens)
189187
if input_tokens is not None and output_tokens is not None:
190188
span.set_metric("anthropic.response.usage.total_tokens", input_tokens + output_tokens)
191-
192-
@staticmethod
193-
def _get_llmobs_metrics_tags(span):
194-
usage = {}
195-
input_tokens = span.get_metric("anthropic.response.usage.input_tokens")
196-
output_tokens = span.get_metric("anthropic.response.usage.output_tokens")
197-
total_tokens = span.get_metric("anthropic.response.usage.total_tokens")
198-
199-
if input_tokens is not None:
200-
usage[INPUT_TOKENS_METRIC_KEY] = input_tokens
201-
if output_tokens is not None:
202-
usage[OUTPUT_TOKENS_METRIC_KEY] = output_tokens
203-
if total_tokens is not None:
204-
usage[TOTAL_TOKENS_METRIC_KEY] = total_tokens
205-
return usage

ddtrace/llmobs/_integrations/bedrock.py

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,16 @@
55

66
from ddtrace.internal.logger import get_logger
77
from ddtrace.llmobs._constants import INPUT_MESSAGES
8-
from ddtrace.llmobs._constants import INPUT_TOKENS_METRIC_KEY
98
from ddtrace.llmobs._constants import METADATA
109
from ddtrace.llmobs._constants import METRICS
1110
from ddtrace.llmobs._constants import MODEL_NAME
1211
from ddtrace.llmobs._constants import MODEL_PROVIDER
1312
from ddtrace.llmobs._constants import OUTPUT_MESSAGES
14-
from ddtrace.llmobs._constants import OUTPUT_TOKENS_METRIC_KEY
1513
from ddtrace.llmobs._constants import PARENT_ID_KEY
1614
from ddtrace.llmobs._constants import PROPAGATED_PARENT_ID_KEY
1715
from ddtrace.llmobs._constants import SPAN_KIND
18-
from ddtrace.llmobs._constants import TOTAL_TOKENS_METRIC_KEY
1916
from ddtrace.llmobs._integrations import BaseLLMIntegration
17+
from ddtrace.llmobs._integrations.utils import get_llmobs_metrics_tags
2018
from ddtrace.llmobs._utils import _get_llmobs_parent_id
2119
from ddtrace.trace import Span
2220

@@ -57,22 +55,11 @@ def _llmobs_set_tags(
5755
MODEL_PROVIDER: span.get_tag("bedrock.request.model_provider") or "",
5856
INPUT_MESSAGES: input_messages,
5957
METADATA: parameters,
60-
METRICS: self._llmobs_metrics(span, response),
58+
METRICS: get_llmobs_metrics_tags("bedrock", span),
6159
OUTPUT_MESSAGES: output_messages,
6260
}
6361
)
6462

65-
@staticmethod
66-
def _llmobs_metrics(span: Span, response: Optional[Dict[str, Any]]) -> Dict[str, Any]:
67-
metrics = {}
68-
if response and response.get("text"):
69-
prompt_tokens = int(span.get_tag("bedrock.usage.prompt_tokens") or 0)
70-
completion_tokens = int(span.get_tag("bedrock.usage.completion_tokens") or 0)
71-
metrics[INPUT_TOKENS_METRIC_KEY] = prompt_tokens
72-
metrics[OUTPUT_TOKENS_METRIC_KEY] = completion_tokens
73-
metrics[TOTAL_TOKENS_METRIC_KEY] = prompt_tokens + completion_tokens
74-
return metrics
75-
7663
@staticmethod
7764
def _extract_input_message(prompt):
7865
"""Extract input messages from the stored prompt.

ddtrace/llmobs/_integrations/gemini.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from ddtrace.llmobs._constants import SPAN_KIND
1515
from ddtrace.llmobs._integrations.base import BaseLLMIntegration
1616
from ddtrace.llmobs._integrations.utils import extract_message_from_part_google
17-
from ddtrace.llmobs._integrations.utils import get_llmobs_metrics_tags_google
17+
from ddtrace.llmobs._integrations.utils import get_llmobs_metrics_tags
1818
from ddtrace.llmobs._integrations.utils import get_system_instructions_from_google_model
1919
from ddtrace.llmobs._integrations.utils import llmobs_get_metadata_google
2020
from ddtrace.llmobs._utils import _get_attr
@@ -59,7 +59,7 @@ def _llmobs_set_tags(
5959
METADATA: metadata,
6060
INPUT_MESSAGES: input_messages,
6161
OUTPUT_MESSAGES: output_messages,
62-
METRICS: get_llmobs_metrics_tags_google("google_generativeai", span),
62+
METRICS: get_llmobs_metrics_tags("google_generativeai", span),
6363
}
6464
)
6565

ddtrace/llmobs/_integrations/openai.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from ddtrace.llmobs._constants import SPAN_KIND
2121
from ddtrace.llmobs._constants import TOTAL_TOKENS_METRIC_KEY
2222
from ddtrace.llmobs._integrations.base import BaseLLMIntegration
23+
from ddtrace.llmobs._integrations.utils import get_llmobs_metrics_tags
2324
from ddtrace.llmobs._utils import _get_attr
2425
from ddtrace.llmobs.utils import Document
2526
from ddtrace.trace import Pin
@@ -234,12 +235,4 @@ def _extract_llmobs_metrics_tags(span: Span, resp: Any) -> Dict[str, Any]:
234235
OUTPUT_TOKENS_METRIC_KEY: completion_tokens,
235236
TOTAL_TOKENS_METRIC_KEY: prompt_tokens + completion_tokens,
236237
}
237-
prompt_tokens = span.get_metric("openai.response.usage.prompt_tokens")
238-
completion_tokens = span.get_metric("openai.response.usage.completion_tokens")
239-
if prompt_tokens is None or completion_tokens is None:
240-
return {}
241-
return {
242-
INPUT_TOKENS_METRIC_KEY: prompt_tokens,
243-
OUTPUT_TOKENS_METRIC_KEY: completion_tokens,
244-
TOTAL_TOKENS_METRIC_KEY: prompt_tokens + completion_tokens,
245-
}
238+
return get_llmobs_metrics_tags("openai", span)

ddtrace/llmobs/_integrations/utils.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,29 @@ def extract_message_from_part_google(part, role=None):
118118
return message
119119

120120

121-
def get_llmobs_metrics_tags_google(integration_name, span):
121+
def get_llmobs_metrics_tags(integration_name, span):
122122
usage = {}
123-
input_tokens = span.get_metric("%s.response.usage.prompt_tokens" % integration_name)
124-
output_tokens = span.get_metric("%s.response.usage.completion_tokens" % integration_name)
123+
124+
# bedrock integration tags usage under meta instead of metrics
125+
if integration_name == "bedrock":
126+
input_tokens = int(span.get_tag("bedrock.usage.prompt_tokens") or 0)
127+
output_tokens = int(span.get_tag("bedrock.usage.completion_tokens") or 0)
128+
total_tokens = input_tokens + output_tokens
129+
if input_tokens:
130+
usage[INPUT_TOKENS_METRIC_KEY] = input_tokens
131+
if output_tokens:
132+
usage[OUTPUT_TOKENS_METRIC_KEY] = output_tokens
133+
if total_tokens:
134+
usage[TOTAL_TOKENS_METRIC_KEY] = total_tokens
135+
return usage
136+
137+
# check for both prompt / completion or input / output tokens
138+
input_tokens = span.get_metric("%s.response.usage.prompt_tokens" % integration_name) or span.get_metric(
139+
"%s.response.usage.input_tokens" % integration_name
140+
)
141+
output_tokens = span.get_metric("%s.response.usage.completion_tokens" % integration_name) or span.get_metric(
142+
"%s.response.usage.output_tokens" % integration_name
143+
)
125144
total_tokens = span.get_metric("%s.response.usage.total_tokens" % integration_name)
126145

127146
if input_tokens is not None:

ddtrace/llmobs/_integrations/vertexai.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from ddtrace.llmobs._constants import SPAN_KIND
1616
from ddtrace.llmobs._integrations.base import BaseLLMIntegration
1717
from ddtrace.llmobs._integrations.utils import extract_message_from_part_google
18-
from ddtrace.llmobs._integrations.utils import get_llmobs_metrics_tags_google
18+
from ddtrace.llmobs._integrations.utils import get_llmobs_metrics_tags
1919
from ddtrace.llmobs._integrations.utils import get_system_instructions_from_google_model
2020
from ddtrace.llmobs._integrations.utils import llmobs_get_metadata_google
2121
from ddtrace.llmobs._utils import _get_attr
@@ -65,7 +65,7 @@ def _llmobs_set_tags(
6565
METADATA: metadata,
6666
INPUT_MESSAGES: input_messages,
6767
OUTPUT_MESSAGES: output_messages,
68-
METRICS: get_llmobs_metrics_tags_google("vertexai", span),
68+
METRICS: get_llmobs_metrics_tags("vertexai", span),
6969
}
7070
)
7171

0 commit comments

Comments
 (0)