Skip to content

Commit 0c109e6

Browse files
committed
add BedrockRuntime fix
1 parent 07d9d0d commit 0c109e6

File tree

1 file changed

+84
-1
lines changed

1 file changed

+84
-1
lines changed

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/patches/_botocore_patches.py

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# SPDX-License-Identifier: Apache-2.0
33
# Modifications Copyright The OpenTelemetry Authors. Licensed under the Apache License 2.0 License.
44
import importlib
5+
import json
6+
from typing import Any, Dict, Sequence
57

68
from botocore.exceptions import ClientError
79

@@ -32,7 +34,7 @@
3234
_determine_call_context,
3335
_safe_invoke,
3436
)
35-
from opentelemetry.instrumentation.botocore.extensions import _KNOWN_EXTENSIONS, _find_extension
37+
from opentelemetry.instrumentation.botocore.extensions import _KNOWN_EXTENSIONS, _find_extension, bedrock_utils
3638
from opentelemetry.instrumentation.botocore.extensions.dynamodb import _DynamoDbExtension
3739
from opentelemetry.instrumentation.botocore.extensions.lmbd import _LambdaExtension
3840
from opentelemetry.instrumentation.botocore.extensions.sns import _SnsExtension
@@ -245,6 +247,87 @@ def _apply_botocore_bedrock_patch() -> None:
245247
_KNOWN_EXTENSIONS["bedrock"] = _lazy_load(".", "_BedrockExtension")
246248
_KNOWN_EXTENSIONS["bedrock-agent"] = _lazy_load(".", "_BedrockAgentExtension")
247249
_KNOWN_EXTENSIONS["bedrock-agent-runtime"] = _lazy_load(".", "_BedrockAgentRuntimeExtension")
250+
251+
# TODO: The following code is to patch bedrock-runtime bugs that are fixed in
252+
# opentelemetry-instrumentation-botocore==0.56b0 in these PRs:
253+
# https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3548
254+
# https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3544
255+
# Remove this code once we've bumped opentelemetry-instrumentation-botocore dependency to 0.56b0
256+
257+
old_init = bedrock_utils.ConverseStreamWrapper.__init__
258+
old_process_event = bedrock_utils.ConverseStreamWrapper._process_event
259+
260+
def patched_init(self, *args, **kwargs):
261+
old_init(self, *args, **kwargs)
262+
self._tool_json_input_buf = ""
263+
264+
def patched_process_event(self, event):
265+
if "contentBlockStart" in event:
266+
start = event["contentBlockStart"].get("start", {})
267+
if "toolUse" in start:
268+
self._content_block = {"toolUse": start["toolUse"]}
269+
return
270+
271+
if "contentBlockDelta" in event:
272+
if self._record_message:
273+
delta = event["contentBlockDelta"].get("delta", {})
274+
if "text" in delta:
275+
self._content_block.setdefault("text", "")
276+
self._content_block["text"] += delta["text"]
277+
elif "toolUse" in delta:
278+
if (input_buf := delta["toolUse"].get("input")) is not None:
279+
self._tool_json_input_buf += input_buf
280+
return
281+
282+
if "contentBlockStop" in event:
283+
if self._record_message:
284+
if self._tool_json_input_buf:
285+
try:
286+
self._content_block["toolUse"]["input"] = json.loads(self._tool_json_input_buf)
287+
except json.JSONDecodeError:
288+
self._content_block["toolUse"]["input"] = self._tool_json_input_buf
289+
self._message["content"].append(self._content_block)
290+
self._content_block = {}
291+
self._tool_json_input_buf = ""
292+
return
293+
294+
old_process_event(self, event)
295+
296+
def patched_extract_tool_calls(message: dict[str, Any], capture_content: bool) -> Sequence[Dict[str, Any]] | None:
297+
content = message.get("content")
298+
if not content:
299+
return None
300+
301+
tool_uses = [item["toolUse"] for item in content if "toolUse" in item]
302+
if not tool_uses:
303+
tool_uses = [item for item in content if isinstance(item, dict) and item.get("type") == "tool_use"]
304+
tool_id_key = "id"
305+
else:
306+
tool_id_key = "toolUseId"
307+
308+
if not tool_uses:
309+
return None
310+
311+
tool_calls = []
312+
for tool_use in tool_uses:
313+
tool_call = {"type": "function"}
314+
if call_id := tool_use.get(tool_id_key):
315+
tool_call["id"] = call_id
316+
317+
if function_name := tool_use.get("name"):
318+
tool_call["function"] = {"name": function_name}
319+
320+
if (function_input := tool_use.get("input")) and capture_content:
321+
tool_call.setdefault("function", {})
322+
tool_call["function"]["arguments"] = function_input
323+
324+
tool_calls.append(tool_call)
325+
return tool_calls
326+
327+
bedrock_utils.ConverseStreamWrapper.__init__ = patched_init
328+
bedrock_utils.ConverseStreamWrapper._process_event = patched_process_event
329+
bedrock_utils.extract_tool_calls = patched_extract_tool_calls
330+
248331
# bedrock-runtime is handled by upstream
249332

250333

0 commit comments

Comments
 (0)