Skip to content

Commit a428585

Browse files
authored
GenAI Utils | Update ToolCall Type (#4218)
1 parent 195bcdb commit a428585

File tree

15 files changed

+316
-83
lines changed

15 files changed

+316
-83
lines changed

instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/utils.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
MessagePart,
3939
Reasoning,
4040
Text,
41-
ToolCall,
41+
ToolCallRequest,
4242
ToolCallResponse,
4343
)
4444

@@ -114,7 +114,7 @@ def _convert_dict_block_to_part(
114114

115115
if block_type == "tool_use":
116116
inp = block.get("input")
117-
return ToolCall(
117+
return ToolCallRequest(
118118
arguments=inp if isinstance(inp, dict) else None,
119119
name=str(block.get("name", "")),
120120
id=str(block.get("id", "")),
@@ -144,7 +144,9 @@ def _convert_content_block_to_part(
144144
return Text(content=block.text)
145145

146146
if isinstance(block, (ToolUseBlock, ServerToolUseBlock)):
147-
return ToolCall(arguments=block.input, name=block.name, id=block.id)
147+
return ToolCallRequest(
148+
arguments=block.input, name=block.name, id=block.id
149+
)
148150

149151
if isinstance(block, (ThinkingBlock, RedactedThinkingBlock)):
150152
content = (
@@ -229,7 +231,7 @@ def stream_block_state_to_part(state: StreamBlockState) -> MessagePart | None:
229231
arguments = json.loads(state.input_json)
230232
except ValueError:
231233
arguments = state.input_json
232-
return ToolCall(
234+
return ToolCallRequest(
233235
arguments=arguments,
234236
name=state.tool_name,
235237
id=state.tool_id,

instrumentation-genai/opentelemetry-instrumentation-google-genai/pyproject.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ classifiers = [
3939
"Programming Language :: Python :: 3.14",
4040
]
4141
dependencies = [
42-
"opentelemetry-api ~=1.37",
43-
"opentelemetry-instrumentation >=0.58b0, <2",
44-
"opentelemetry-semantic-conventions >=0.58b0, <2",
45-
"opentelemetry-util-genai >= 0.3b0, <0.4b0",
42+
"opentelemetry-api ~=1.39",
43+
"opentelemetry-instrumentation >=0.60b0, <2",
44+
"opentelemetry-semantic-conventions >=0.60b0, <2",
45+
"opentelemetry-util-genai >= 0.4b0.dev, <0.5b0",
4646
]
4747

4848
[project.optional-dependencies]

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

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,20 @@
1515
from __future__ import annotations
1616

1717
import logging
18-
from dataclasses import dataclass
1918
from enum import Enum
20-
from typing import Literal
2119

2220
from google.genai import types as genai_types
2321

2422
from opentelemetry.util.genai.types import (
23+
Blob,
2524
FinishReason,
2625
InputMessage,
2726
MessagePart,
2827
OutputMessage,
2928
Text,
30-
ToolCall,
29+
ToolCallRequest,
3130
ToolCallResponse,
31+
Uri,
3232
)
3333

3434

@@ -39,23 +39,6 @@ class Role(str, Enum):
3939
TOOL = "tool"
4040

4141

42-
@dataclass
43-
class BlobPart:
44-
data: bytes
45-
mime_type: str
46-
type: Literal["blob"] = "blob"
47-
48-
49-
@dataclass
50-
class FileDataPart:
51-
mime_type: str
52-
uri: str
53-
type: Literal["file_data"] = "file_data"
54-
55-
class Config:
56-
extra = "allow"
57-
58-
5942
_logger = logging.getLogger(__name__)
6043

6144

@@ -121,16 +104,26 @@ def tool_call_id(name: str | None) -> str:
121104
if (text := part.text) is not None:
122105
return Text(content=text)
123106

124-
if data := part.inline_data:
125-
return BlobPart(mime_type=data.mime_type or "", data=data.data or b"")
107+
if inline_data := part.inline_data:
108+
mime_type = inline_data.mime_type or ""
109+
modality = mime_type.split("/")[0] if mime_type else ""
110+
return Blob(
111+
mime_type=mime_type,
112+
modality=modality,
113+
content=inline_data.data or b"",
114+
)
126115

127-
if data := part.file_data:
128-
return FileDataPart(
129-
mime_type=data.mime_type or "", uri=data.file_uri or ""
116+
if file_data := part.file_data:
117+
mime_type = file_data.mime_type or ""
118+
modality = mime_type.split("/")[0] if mime_type else ""
119+
return Uri(
120+
mime_type=mime_type,
121+
modality=modality,
122+
uri=file_data.file_uri or "",
130123
)
131124

132125
if call := part.function_call:
133-
return ToolCall(
126+
return ToolCallRequest(
134127
id=call.id or tool_call_id(call.name),
135128
name=call.name or "",
136129
arguments=call.args,

instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/requirements.oldest.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ pytest-vcr==1.0.2
2121

2222
google-auth==2.15.0
2323
google-genai==1.32.0
24-
opentelemetry-api==1.37.0
25-
opentelemetry-sdk==1.37.0
26-
opentelemetry-semantic-conventions==0.58b0
27-
opentelemetry-instrumentation==0.58b0
28-
opentelemetry-util-genai[upload]==0.3b0
24+
opentelemetry-api==1.39.0
25+
opentelemetry-sdk==1.39.0
26+
opentelemetry-semantic-conventions==0.60b0
27+
opentelemetry-instrumentation==0.60b0
28+
-e util/opentelemetry-util-genai[upload]
2929

3030
fsspec==2025.9.0
3131

instrumentation-genai/opentelemetry-instrumentation-langchain/src/opentelemetry/instrumentation/langchain/callback_handler.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from __future__ import annotations
1616

17-
from typing import Any, Optional
17+
from typing import Any, Optional, cast
1818
from uuid import UUID
1919

2020
from langchain_core.callbacks import BaseCallbackHandler
@@ -29,6 +29,7 @@
2929
Error,
3030
InputMessage,
3131
LLMInvocation,
32+
MessagePart,
3233
OutputMessage,
3334
Text,
3435
)
@@ -133,7 +134,11 @@ def on_chat_model_start(
133134
Text(content=text_value, type="text")
134135
)
135136

136-
input_messages.append(InputMessage(parts=parts, role=role))
137+
input_messages.append(
138+
InputMessage(
139+
parts=cast(list[MessagePart], parts), role=role
140+
)
141+
)
137142

138143
llm_invocation = LLMInvocation(
139144
request_model=request_model,
@@ -206,7 +211,7 @@ def on_llm_end(
206211
role = chat_generation.message.type
207212
output_message = OutputMessage(
208213
role=role,
209-
parts=parts,
214+
parts=cast(list[MessagePart], parts),
210215
finish_reason=finish_reason,
211216
)
212217
output_messages.append(output_message)

instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/patch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
LLMInvocation,
4040
OutputMessage,
4141
Text,
42-
ToolCall,
42+
ToolCallRequest,
4343
)
4444

4545
from .instruments import Instruments
@@ -914,7 +914,7 @@ def _set_output_messages(self):
914914
arguments = json.loads(arguments_str)
915915
except json.JSONDecodeError:
916916
arguments = arguments_str
917-
tool_call_part = ToolCall(
917+
tool_call_part = ToolCallRequest(
918918
name=tool_call.function_name,
919919
id=tool_call.tool_call_id,
920920
arguments=arguments,

instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/utils.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
LLMInvocation,
4242
OutputMessage,
4343
Text,
44-
ToolCall,
44+
ToolCallRequest,
4545
ToolCallResponse,
4646
)
4747

@@ -452,7 +452,7 @@ def _prepare_input_messages(messages) -> List[InputMessage]:
452452
return chat_messages
453453

454454

455-
def extract_tool_calls_new(tool_calls) -> list[ToolCall]:
455+
def extract_tool_calls_new(tool_calls) -> list[ToolCallRequest]:
456456
parts = []
457457
for tool_call in tool_calls:
458458
call_id = get_property_value(tool_call, "id")
@@ -470,7 +470,9 @@ def extract_tool_calls_new(tool_calls) -> list[ToolCall]:
470470
arguments = arguments_str
471471

472472
# TODO: support custom
473-
parts.append(ToolCall(id=call_id, name=func_name, arguments=arguments))
473+
parts.append(
474+
ToolCallRequest(id=call_id, name=func_name, arguments=arguments)
475+
)
474476
return parts
475477

476478

instrumentation-genai/opentelemetry-instrumentation-vertexai/pyproject.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ classifiers = [
2626
"Programming Language :: Python :: 3.14",
2727
]
2828
dependencies = [
29-
"opentelemetry-api ~= 1.37",
30-
"opentelemetry-instrumentation ~= 0.58b0",
31-
"opentelemetry-semantic-conventions ~= 0.58b0",
32-
"opentelemetry-util-genai >= 0.2b0, <0.4b0",
29+
"opentelemetry-api ~= 1.39",
30+
"opentelemetry-instrumentation ~= 0.60b0",
31+
"opentelemetry-semantic-conventions ~= 0.60b0",
32+
"opentelemetry-util-genai >= 0.4b0.dev, <0.5b0",
3333
]
3434

3535
[project.optional-dependencies]

instrumentation-genai/opentelemetry-instrumentation-vertexai/src/opentelemetry/instrumentation/vertexai/utils.py

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,14 @@
5252
)
5353
from opentelemetry.semconv.attributes import server_attributes
5454
from opentelemetry.util.genai.types import (
55+
Blob,
5556
ContentCapturingMode,
5657
FinishReason,
5758
MessagePart,
5859
Text,
59-
ToolCall,
60+
ToolCallRequest,
6061
ToolCallResponse,
62+
Uri,
6163
)
6264
from opentelemetry.util.genai.utils import get_content_capturing_mode
6365
from opentelemetry.util.types import AnyValue, AttributeValue
@@ -308,21 +310,15 @@ def request_to_events(
308310
yield user_event(role=content.role, content=request_content)
309311

310312

311-
@dataclass
312-
class BlobPart:
313-
data: bytes
314-
mime_type: str
315-
type: Literal["blob"] = "blob"
316-
317-
318-
@dataclass
319-
class FileDataPart:
320-
mime_type: str
321-
uri: str
322-
type: Literal["file_data"] = "file_data"
323-
324-
class Config:
325-
extra = "allow"
313+
def _modality_from_mime_type(mime_type: str) -> str:
314+
"""Infer modality from MIME type prefix."""
315+
if mime_type.startswith("image/"):
316+
return "image"
317+
if mime_type.startswith("video/"):
318+
return "video"
319+
if mime_type.startswith("audio/"):
320+
return "audio"
321+
return mime_type
326322

327323

328324
def convert_content_to_message_parts(
@@ -341,7 +337,7 @@ def convert_content_to_message_parts(
341337
elif "function_call" in part:
342338
part = part.function_call
343339
parts.append(
344-
ToolCall(
340+
ToolCallRequest(
345341
id=f"{part.name}_{idx}",
346342
name=part.name,
347343
arguments=json_format.MessageToDict(
@@ -353,14 +349,22 @@ def convert_content_to_message_parts(
353349
parts.append(Text(content=part.text))
354350
elif "inline_data" in part:
355351
part = part.inline_data
352+
mime_type = part.mime_type or ""
356353
parts.append(
357-
BlobPart(mime_type=part.mime_type or "", data=part.data or b"")
354+
Blob(
355+
mime_type=mime_type,
356+
modality=_modality_from_mime_type(mime_type),
357+
content=part.data or b"",
358+
)
358359
)
359360
elif "file_data" in part:
360361
part = part.file_data
362+
mime_type = part.mime_type or ""
361363
parts.append(
362-
FileDataPart(
363-
mime_type=part.mime_type or "", uri=part.file_uri or ""
364+
Uri(
365+
mime_type=mime_type,
366+
modality=_modality_from_mime_type(mime_type),
367+
uri=part.file_uri or "",
364368
)
365369
)
366370
else:

instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/requirements.oldest.txt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,12 @@ grpcio>=1.75.1 ; python_version >= "3.14"
6565
shapely==2.0.6 ; python_version < "3.10"
6666
shapely==2.1.2 ; python_version >= "3.10"
6767
# when updating, also update in pyproject.toml
68-
opentelemetry-api==1.37
69-
opentelemetry-sdk==1.37
70-
opentelemetry-semantic-conventions==0.58b0
71-
opentelemetry-instrumentation==0.58b0
72-
opentelemetry-util-genai[upload]==0.2b0
68+
opentelemetry-api==1.39
69+
opentelemetry-sdk==1.39
70+
opentelemetry-semantic-conventions==0.60b0
71+
opentelemetry-instrumentation==0.60b0
72+
# opentelemetry-util-genai[upload]==0.2b0
73+
-e util/opentelemetry-util-genai[upload]
7374
fsspec==2025.9.0
7475

7576
-e instrumentation-genai/opentelemetry-instrumentation-vertexai[instruments]

0 commit comments

Comments
 (0)