Skip to content

Commit b3b1f6d

Browse files
committed
Update to record function details only on the span.
1 parent 25362ac commit b3b1f6d

File tree

4 files changed

+48
-133
lines changed

4 files changed

+48
-133
lines changed

instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/custom_semconv.py

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,3 @@
1616
# Semantic Convention still being defined in:
1717
# https://github.com/open-telemetry/semantic-conventions/pull/2125
1818
GCP_GENAI_OPERATION_CONFIG = "gcp.gen_ai.operation.config"
19-
20-
21-
# Semantic Convention to be defined.
22-
# https://github.com/open-telemetry/semantic-conventions/issues/2183
23-
TOOL_CALL_POSITIONAL_ARG_COUNT = "gen_ai.tool.positional_args.count"
24-
25-
26-
# Semantic Convention to be defined.
27-
# https://github.com/open-telemetry/semantic-conventions/issues/2183
28-
TOOL_CALL_KEYWORD_ARG_COUNT = "gen_ai.tool.keyword_args.count"
29-
30-
31-
# Semantic Convention to be defined.
32-
# https://github.com/open-telemetry/semantic-conventions/issues/2185
33-
FUNCTION_TOOL_CALL_START_EVENT_NAME = "function_call.start"
34-
FUNCTION_TOOL_CALL_START_EVENT_ATTRS_POSITIONAL_ARGS_COUNT = (
35-
"positional_argument_count"
36-
)
37-
FUNCTION_TOOL_CALL_START_EVENT_ATTRS_KEYWORD_ARGS_COUNT = (
38-
"keyword_argument_count"
39-
)
40-
FUNCTION_TOOL_CALL_START_EVENT_BODY_POSITIONAL_ARGS = "positional_arguments"
41-
FUNCTION_TOOL_CALL_START_EVENT_BODY_KEYWORD_ARGS = "keyword_arguments"
42-
43-
44-
# Semantic Convention to be defined.
45-
# https://github.com/open-telemetry/semantic-conventions/issues/2185
46-
FUNCTION_TOOL_CALL_END_EVENT_NAME = "function_call.end"
47-
FUNCTION_TOOL_CALL_END_EVENT_BODY_RESULT = "result"
48-
49-
50-
# Semantic Convention to be defined.
51-
# https://github.com/open-telemetry/semantic-conventions/issues/2184
52-
CODE_MODULE = "code.module"

instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/otel_wrapper.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@
2020
from opentelemetry.semconv._incubating.metrics import gen_ai_metrics
2121
from opentelemetry.semconv.schemas import Schemas
2222

23-
from .custom_semconv import (
24-
FUNCTION_TOOL_CALL_END_EVENT_NAME,
25-
FUNCTION_TOOL_CALL_START_EVENT_NAME,
26-
)
2723
from .version import __version__ as _LIBRARY_VERSION
2824

2925
_logger = logging.getLogger(__name__)
@@ -91,16 +87,6 @@ def log_response_content(self, attributes, body):
9187
event_name = "gen_ai.choice"
9288
self._log_event(event_name, attributes, body)
9389

94-
def log_function_call_start(self, attributes, body):
95-
_logger.debug("Recording function call start.")
96-
event_name = FUNCTION_TOOL_CALL_START_EVENT_NAME
97-
self._log_event(event_name, attributes, body)
98-
99-
def log_function_call_end(self, attributes, body):
100-
_logger.debug("Recording function call end.")
101-
event_name = FUNCTION_TOOL_CALL_END_EVENT_NAME
102-
self._log_event(event_name, attributes, body)
103-
10490
def _log_event(self, event_name, attributes, body):
10591
event = Event(event_name, body=body, attributes=attributes)
10692
self._event_logger.emit(event)

instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/tool_call_wrapper.py

Lines changed: 22 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,6 @@
2727
code_attributes,
2828
)
2929

30-
from .custom_semconv import (
31-
CODE_MODULE,
32-
FUNCTION_TOOL_CALL_END_EVENT_BODY_RESULT,
33-
FUNCTION_TOOL_CALL_START_EVENT_ATTRS_KEYWORD_ARGS_COUNT,
34-
FUNCTION_TOOL_CALL_START_EVENT_ATTRS_POSITIONAL_ARGS_COUNT,
35-
FUNCTION_TOOL_CALL_START_EVENT_BODY_KEYWORD_ARGS,
36-
FUNCTION_TOOL_CALL_START_EVENT_BODY_POSITIONAL_ARGS,
37-
TOOL_CALL_KEYWORD_ARG_COUNT,
38-
TOOL_CALL_POSITIONAL_ARG_COUNT,
39-
)
4030
from .flags import is_content_recording_enabled
4131
from .otel_wrapper import OTelWrapper
4232

@@ -74,95 +64,48 @@ def _create_function_span_attributes(
7464
if extra_span_attributes:
7565
result.update(extra_span_attributes)
7666
result[code_attributes.CODE_FUNCTION_NAME] = wrapped_function.__name__
77-
result[CODE_MODULE] = wrapped_function.__module__
78-
result[TOOL_CALL_POSITIONAL_ARG_COUNT] = len(function_args)
79-
result[TOOL_CALL_KEYWORD_ARG_COUNT] = len(function_kwargs)
67+
result["code.module"] = wrapped_function.__module__
68+
result["code.args.positional.count"] = len(function_args)
69+
result["code.args.keyword.count"] = len(function_kwargs)
8070
return result
8171

8272

83-
def _record_function_call_span_attributes(
73+
def _record_function_call_arguments(
8474
otel_wrapper, wrapped_function, function_args, function_kwargs
8575
):
8676
"""Records the details about a function invocation as span attributes."""
87-
if not is_content_recording_enabled():
88-
return
77+
include_values = is_content_recording_enabled()
8978
span = trace.get_current_span()
9079
signature = inspect.signature(wrapped_function)
9180
params = list(signature.parameters.values())
9281
for index, entry in enumerate(function_args):
9382
param_name = f"args[{index}]"
9483
if index < len(params):
9584
param_name = params[index].name
96-
attribute_name = f"code.function.params.{param_name}"
97-
span.set_attribute(attribute_name, _to_otel_value(entry))
85+
attribute_prefix = f"code.function.parameters.{param_name}"
86+
type_attribute = f"{attribute_prefix}.type"
87+
span.set_attribute(type_attribute, type(entry).__name__)
88+
if include_values:
89+
value_attribute = f"{attribute_prefix}.value"
90+
span.set_attribute(value_attribute, _to_otel_value(entry))
9891
for key, value in function_kwargs.items():
99-
attribute_name = f"code.function.params.{key}"
100-
span.set_attribute(attribute_name, _to_otel_value(value))
92+
attribute_prefix = f"code.function.parameters.{key}"
93+
type_attribute = f"{attribute_prefix}.type"
94+
span.set_attribute(type_attribute, type(value).__name__)
95+
if include_values:
96+
value_attribute = f"{attribute_prefix}.value"
97+
span.set_attribute(value_attribute, _to_otel_value(value))
10198

10299

103-
def _record_function_call_event(
104-
otel_wrapper, wrapped_function, function_args, function_kwargs
105-
):
106-
"""Records the details about a function invocation as a log event."""
107-
attributes = {
108-
code_attributes.CODE_FUNCTION_NAME: wrapped_function.__name__,
109-
CODE_MODULE: wrapped_function.__module__,
110-
FUNCTION_TOOL_CALL_START_EVENT_ATTRS_POSITIONAL_ARGS_COUNT: len(
111-
function_args
112-
),
113-
FUNCTION_TOOL_CALL_START_EVENT_ATTRS_KEYWORD_ARGS_COUNT: len(
114-
function_kwargs
115-
),
116-
}
117-
body = {}
118-
if is_content_recording_enabled():
119-
body[FUNCTION_TOOL_CALL_START_EVENT_BODY_POSITIONAL_ARGS] = (
120-
_to_otel_value(function_args)
121-
)
122-
body[FUNCTION_TOOL_CALL_START_EVENT_BODY_KEYWORD_ARGS] = (
123-
_to_otel_value(function_kwargs)
124-
)
125-
otel_wrapper.log_function_call_start(attributes, body)
126-
127-
128-
def _record_function_call_arguments(
129-
otel_wrapper, wrapped_function, function_args, function_kwargs
130-
):
131-
_record_function_call_span_attributes(
132-
otel_wrapper, wrapped_function, function_args, function_kwargs
133-
)
134-
_record_function_call_event(
135-
otel_wrapper, wrapped_function, function_args, function_kwargs
136-
)
137-
138-
139-
def _record_function_call_result_event(otel_wrapper, wrapped_function, result):
140-
"""Records the details about a function result as a log event."""
141-
attributes = {
142-
code_attributes.CODE_FUNCTION_NAME: wrapped_function.__name__,
143-
CODE_MODULE: wrapped_function.__module__,
144-
}
145-
body = {}
146-
if is_content_recording_enabled():
147-
body[FUNCTION_TOOL_CALL_END_EVENT_BODY_RESULT] = _to_otel_value(result)
148-
otel_wrapper.log_function_call_end(attributes, body)
149-
150-
151-
def _record_function_call_result_span_attributes(
100+
def _record_function_call_result(
152101
otel_wrapper, wrapped_function, result
153102
):
154103
"""Records the details about a function result as span attributes."""
155-
if not is_content_recording_enabled():
156-
return
104+
include_values = is_content_recording_enabled()
157105
span = trace.get_current_span()
158-
span.set_attribute("code.function.return_value", _to_otel_value(result))
159-
160-
161-
def _record_function_call_result(otel_wrapper, wrapped_function, result):
162-
_record_function_call_result_event(otel_wrapper, wrapped_function, result)
163-
_record_function_call_result_span_attributes(
164-
otel_wrapper, wrapped_function, result
165-
)
106+
span.set_attribute("code.function.return.type", type(result).__name__)
107+
if include_values:
108+
span.set_attribute("code.function.return.value", _to_otel_value(result))
166109

167110

168111
def _wrap_sync_tool_function(

instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/test_tool_call_instrumentation.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,17 @@ def somefunction(someparam, otherparam=2):
111111
self.otel.assert_has_span_named("tool_call somefunction")
112112
generated_span = self.otel.get_span_named("tool_call somefunction")
113113
self.assertEqual(
114-
generated_span.attributes["code.function.params.someparam"], "123"
114+
generated_span.attributes["code.function.parameters.someparam.type"], "int"
115115
)
116116
self.assertEqual(
117-
generated_span.attributes["code.function.params.otherparam"],
117+
generated_span.attributes["code.function.parameters.otherparam.type"],
118+
"str",
119+
)
120+
self.assertEqual(
121+
generated_span.attributes["code.function.parameters.someparam.value"], "123"
122+
)
123+
self.assertEqual(
124+
generated_span.attributes["code.function.parameters.otherparam.value"],
118125
"'abc'",
119126
)
120127

@@ -146,11 +153,18 @@ def somefunction(someparam, otherparam=2):
146153
wrapped_somefunction(123, otherparam="abc")
147154
self.otel.assert_has_span_named("tool_call somefunction")
148155
generated_span = self.otel.get_span_named("tool_call somefunction")
156+
self.assertEqual(
157+
generated_span.attributes["code.function.parameters.someparam.type"], "int"
158+
)
159+
self.assertEqual(
160+
generated_span.attributes["code.function.parameters.otherparam.type"],
161+
"str",
162+
)
149163
self.assertNotIn(
150-
"code.function.params.someparam", generated_span.attributes
164+
"code.function.parameters.someparam.value", generated_span.attributes
151165
)
152166
self.assertNotIn(
153-
"code.function.params.otherparam", generated_span.attributes
167+
"code.function.parameters.otherparam.value", generated_span.attributes
154168
)
155169

156170
def test_tool_calls_record_return_values_on_span_if_enabled(self):
@@ -182,7 +196,10 @@ def somefunction(x, y=2):
182196
self.otel.assert_has_span_named("tool_call somefunction")
183197
generated_span = self.otel.get_span_named("tool_call somefunction")
184198
self.assertEqual(
185-
generated_span.attributes["code.function.return_value"], "125"
199+
generated_span.attributes["code.function.return.type"], "int"
200+
)
201+
self.assertEqual(
202+
generated_span.attributes["code.function.return.value"], "125"
186203
)
187204

188205
def test_tool_calls_do_not_record_return_values_if_not_enabled(self):
@@ -213,6 +230,9 @@ def somefunction(x, y=2):
213230
wrapped_somefunction(123)
214231
self.otel.assert_has_span_named("tool_call somefunction")
215232
generated_span = self.otel.get_span_named("tool_call somefunction")
233+
self.assertEqual(
234+
generated_span.attributes["code.function.return.type"], "int"
235+
)
216236
self.assertNotIn(
217-
"code.function.return_value", generated_span.attributes
237+
"code.function.return.value", generated_span.attributes
218238
)

0 commit comments

Comments
 (0)