Skip to content

Commit cf5382f

Browse files
committed
Guess some attributes from input/response
1 parent c234dd4 commit cf5382f

File tree

2 files changed

+134
-10
lines changed

2 files changed

+134
-10
lines changed

sentry_sdk/consts.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,12 @@ class SPANDATA:
460460
Example: 0.7
461461
"""
462462

463+
GEN_AI_REQUEST_TOP_K = "gen_ai.request.top_k"
464+
"""
465+
For an AI model call, the top_k parameter. Top_k essentially controls how random the output will be.
466+
Example: 35
467+
"""
468+
463469
GEN_AI_REQUEST_TOP_P = "gen_ai.request.top_p"
464470
"""
465471
The top_p parameter used to control diversity via nucleus sampling.

sentry_sdk/tracing_utils.py

Lines changed: 128 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -830,21 +830,31 @@ def _sample_rand_range(parent_sampled, sample_rate):
830830
return sample_rate, 1.0
831831

832832

833-
def _get_span_name(template, name):
834-
# type: (Union[str, "SpanTemplate"], str) -> str
833+
def _get_span_name(template, name=None, kwargs=None):
834+
# type: (Union[str, "SpanTemplate"], Optional[str], Optional[dict[str, Any]]) -> str
835835
"""
836836
Get the name of the span based on the template and the name.
837837
"""
838838
span_name = name
839839

840840
if template == SpanTemplate.AI_CHAT:
841-
span_name = "chat [MODEL]"
841+
model = None
842+
if kwargs and "model" in kwargs and isinstance(kwargs["model"], str):
843+
model = kwargs["model"]
844+
elif (
845+
kwargs and "model_name" in kwargs and isinstance(kwargs["model_name"], str)
846+
):
847+
model = kwargs["model_name"]
848+
849+
span_name = f"chat {model}" if model else "chat"
850+
842851
elif template == SpanTemplate.AI_AGENT:
843852
span_name = f"invoke_agent {name}"
853+
844854
elif template == SpanTemplate.AI_TOOL:
845855
span_name = f"execute_tool {name}"
846856

847-
return span_name
857+
return span_name or ""
848858

849859

850860
def _get_span_op(template):
@@ -856,18 +866,119 @@ def _get_span_op(template):
856866

857867
if template == SpanTemplate.AI_CHAT:
858868
op = OP.GEN_AI_CHAT
869+
859870
elif template == SpanTemplate.AI_AGENT:
860871
op = OP.GEN_AI_INVOKE_AGENT
872+
861873
elif template == SpanTemplate.AI_TOOL:
862874
op = OP.GEN_AI_EXECUTE_TOOL
863875

864876
return op
865877

866878

879+
def _get_input_attributes(template, kwargs):
880+
# type: (Union[str, "SpanTemplate"], dict[str, Any]) -> dict[str, Any]
881+
"""
882+
Get input attributes for the given span template.
883+
"""
884+
attributes = {} # type: dict[str, Any]
885+
886+
for key, value in list(kwargs.items()):
887+
if template in [
888+
SpanTemplate.AI_AGENT,
889+
SpanTemplate.AI_TOOL,
890+
SpanTemplate.AI_CHAT,
891+
]:
892+
if key == "model" and isinstance(value, str):
893+
attributes[SPANDATA.GEN_AI_REQUEST_MODEL] = value
894+
elif key == "model_name" and isinstance(value, str):
895+
attributes[SPANDATA.GEN_AI_REQUEST_MODEL] = value
896+
897+
elif key == "agent" and isinstance(value, str):
898+
attributes[SPANDATA.GEN_AI_AGENT_NAME] = value
899+
elif key == "agent_name" and isinstance(value, str):
900+
attributes[SPANDATA.GEN_AI_AGENT_NAME] = value
901+
902+
elif key == "prompt" and isinstance(value, str):
903+
attributes.setdefault(SPANDATA.GEN_AI_REQUEST_MESSAGES, []).append(
904+
{"role": "user", "content": value}
905+
)
906+
elif key == "system_prompt" and isinstance(value, str):
907+
attributes.setdefault(SPANDATA.GEN_AI_REQUEST_MESSAGES, []).append(
908+
{"role": "system", "content": value}
909+
)
910+
911+
elif key == "max_tokens" and isinstance(value, int):
912+
attributes.setdefault(SPANDATA.GEN_AI_REQUEST_MAX_TOKENS, []).append(
913+
value
914+
)
915+
elif key == "frequency_penalty" and isinstance(value, float):
916+
attributes.setdefault(
917+
SPANDATA.GEN_AI_REQUEST_FREQUENCY_PENALTY, []
918+
).append(value)
919+
elif key == "presence_penalty" and isinstance(value, float):
920+
attributes.setdefault(
921+
SPANDATA.GEN_AI_REQUEST_PRESENCE_PENALTY, []
922+
).append(value)
923+
elif key == "temperature" and isinstance(value, float):
924+
attributes.setdefault(SPANDATA.GEN_AI_REQUEST_TEMPERATURE, []).append(
925+
value
926+
)
927+
elif key == "top_p" and isinstance(value, float):
928+
attributes.setdefault(SPANDATA.GEN_AI_REQUEST_TOP_P, []).append(value)
929+
elif key == "top_k" and isinstance(value, int):
930+
attributes.setdefault(SPANDATA.GEN_AI_REQUEST_TOP_K, []).append(value)
931+
932+
return attributes
933+
934+
935+
def _get_output_attributes(template, result):
936+
# type: (Union[str, "SpanTemplate"], Any) -> dict[str, Any]
937+
"""
938+
Get output attributes for the given span template.
939+
"""
940+
attributes = {} # type: dict[str, Any]
941+
942+
if template in [SpanTemplate.AI_AGENT, SpanTemplate.AI_TOOL, SpanTemplate.AI_CHAT]:
943+
if hasattr(result, "usage"):
944+
if hasattr(result.usage, "input_tokens") and isinstance(
945+
result.usage.input_tokens, int
946+
):
947+
attributes[SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] = (
948+
result.usage.input_tokens
949+
)
950+
elif hasattr(result.usage, "output_tokens") and isinstance(
951+
result.usage.output_tokens, int
952+
):
953+
attributes[SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS] = (
954+
result.usage.output_tokens
955+
)
956+
elif hasattr(result.usage, "total_tokens") and isinstance(
957+
result.usage.total_tokens, int
958+
):
959+
attributes[SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS] = (
960+
result.usage.total_tokens
961+
)
962+
963+
elif hasattr(result, "input_tokens") and isinstance(result.input_tokens, int):
964+
attributes[SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] = result.input_tokens
965+
elif hasattr(result, "output_tokens") and isinstance(result.output_tokens, int):
966+
attributes[SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS] = result.output_tokens
967+
elif hasattr(result, "total_tokens") and isinstance(result.total_tokens, int):
968+
attributes[SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS] = result.total_tokens
969+
970+
elif hasattr(result, "model") and isinstance(result.model, str):
971+
attributes[SPANDATA.GEN_AI_RESPONSE_MODEL] = result.model
972+
elif hasattr(result, "model_name") and isinstance(result.model_name, str):
973+
attributes[SPANDATA.GEN_AI_RESPONSE_MODEL] = result.model_name
974+
975+
return attributes
976+
977+
867978
def _set_input_attributes(span, template, args, kwargs):
868979
# type: (Span, Union[str, "SpanTemplate"], tuple[Any, ...], dict[str, Any]) -> None
869980
"""
870-
Set span input attributes based on the given template.
981+
Set span input attributes based on the given span template.
871982
872983
:param span: The span to set attributes on.
873984
:param template: The template to use to set attributes on the span.
@@ -876,8 +987,6 @@ def _set_input_attributes(span, template, args, kwargs):
876987
"""
877988
attributes = {} # type: dict[str, Any]
878989

879-
# TODO: Implement actual input parameters for those templates :)
880-
881990
if template == SpanTemplate.AI_AGENT:
882991
attributes = {
883992
SPANDATA.GEN_AI_OPERATION_NAME: "invoke_agent",
@@ -893,22 +1002,25 @@ def _set_input_attributes(span, template, args, kwargs):
8931002
SPANDATA.GEN_AI_TOOL_NAME: span.description,
8941003
}
8951004

1005+
attributes.update(_get_input_attributes(template, kwargs))
8961006
span.set_data(attributes)
8971007

8981008

8991009
def _set_output_attributes(span, template, result):
9001010
# type: (Span, Union[str, "SpanTemplate"], Any) -> None
9011011
"""
902-
Set span output attributes based on the given template.
1012+
Set span output attributes based on the given span template.
9031013
9041014
:param span: The span to set attributes on.
9051015
:param template: The template to use to set attributes on the span.
9061016
:param result: The result of the wrapped function.
9071017
"""
9081018
attributes = {} # type: dict[str, Any]
9091019

910-
# TODO: Implement :)
1020+
if template == SpanTemplate.AI_AGENT and isinstance(result, str):
1021+
attributes[SPANDATA.GEN_AI_TOOL_OUTPUT] = result
9111022

1023+
attributes.update(_get_output_attributes(template, result))
9121024
span.set_data(attributes)
9131025

9141026

@@ -948,10 +1060,16 @@ async def async_wrapper(*args, **kwargs):
9481060

9491061
with start_span_func(
9501062
op=_get_span_op(template),
951-
name=_get_span_name(template, name or qualname_from_function(f)),
1063+
name=_get_span_name(
1064+
template, name or qualname_from_function(f), kwargs
1065+
),
9521066
) as span:
9531067
_set_input_attributes(span, template, args, kwargs)
9541068

1069+
docstring = getattr(f, "__doc__", None)
1070+
if template == SpanTemplate.AI_TOOL and docstring is not None:
1071+
span.set_data(SPANDATA.GEN_AI_TOOL_DESCRIPTION, docstring)
1072+
9551073
result = await f(*args, **kwargs)
9561074

9571075
_set_output_attributes(span, template, result)

0 commit comments

Comments
 (0)