Skip to content

Commit 5d6bdc1

Browse files
committed
add unit tests
1 parent 0c109e6 commit 5d6bdc1

File tree

2 files changed

+112
-3
lines changed

2 files changed

+112
-3
lines changed

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,8 +328,6 @@ def patched_extract_tool_calls(message: dict[str, Any], capture_content: bool) -
328328
bedrock_utils.ConverseStreamWrapper._process_event = patched_process_event
329329
bedrock_utils.extract_tool_calls = patched_extract_tool_calls
330330

331-
# bedrock-runtime is handled by upstream
332-
333331

334332
def _apply_botocore_dynamodb_patch() -> None:
335333
"""Botocore instrumentation patch for DynamoDB

aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/test_instrumentation_patch.py

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
apply_instrumentation_patches,
1616
)
1717
from opentelemetry.instrumentation.botocore import BotocoreInstrumentor
18-
from opentelemetry.instrumentation.botocore.extensions import _KNOWN_EXTENSIONS
18+
from opentelemetry.instrumentation.botocore.extensions import _KNOWN_EXTENSIONS, bedrock_utils
1919
from opentelemetry.propagate import get_global_textmap
2020
from opentelemetry.semconv.trace import SpanAttributes
2121
from opentelemetry.trace.span import Span
@@ -84,6 +84,7 @@ def _run_patch_behaviour_tests(self):
8484
self._test_unpatched_botocore_propagator()
8585
self._test_unpatched_gevent_instrumentation()
8686
self._test_unpatched_starlette_instrumentation()
87+
self._test_unpatched_bedrock_runtime_instrumentation()
8788

8889
# Apply patches
8990
apply_instrumentation_patches()
@@ -219,6 +220,9 @@ def _test_patched_botocore_instrumentation(self):
219220
# Bedrock Agent Operation
220221
self._test_patched_bedrock_agent_instrumentation()
221222

223+
# Bedrock Runtime
224+
self._test_patched_bedrock_runtime_instrumentation()
225+
222226
# Bedrock Agent Runtime
223227
self.assertTrue("bedrock-agent-runtime" in _KNOWN_EXTENSIONS)
224228
bedrock_agent_runtime_attributes: Dict[str, str] = _do_extract_attributes_bedrock("bedrock-agent-runtime")
@@ -470,6 +474,113 @@ def _test_patched_bedrock_instrumentation(self):
470474
self.assertEqual(len(bedrock_sucess_attributes), 1)
471475
self.assertEqual(bedrock_sucess_attributes["aws.bedrock.guardrail.id"], _BEDROCK_GUARDRAIL_ID)
472476

477+
def _test_unpatched_bedrock_runtime_instrumentation(self):
478+
"""Test unpatched bedrock-runtime where input values remain as numbers"""
479+
480+
mock_stream = MagicMock()
481+
mock_span = MagicMock()
482+
mock_stream_error_callback = MagicMock()
483+
484+
wrapper = bedrock_utils.ConverseStreamWrapper(mock_stream, mock_span, mock_stream_error_callback)
485+
wrapper._record_message = True
486+
wrapper._message = {"role": "assistant", "content": []}
487+
488+
start_event = {
489+
"contentBlockStart": {
490+
"start": {
491+
"toolUse": {
492+
"toolUseId": "random_id",
493+
"name": "example",
494+
"input": '{"input": 999999999999999999}',
495+
}
496+
},
497+
"contentBlockIndex": 0,
498+
}
499+
}
500+
wrapper._process_event(start_event)
501+
502+
# Validate that _content_block contains toolUse input that has been JSON decoded
503+
self.assertIn("toolUse", wrapper._content_block)
504+
self.assertIn("input", wrapper._content_block["toolUse"])
505+
self.assertIn("input", wrapper._content_block["toolUse"]["input"])
506+
# Validate that input values are numbers (unpatched behavior)
507+
self.assertIsInstance(wrapper._content_block["toolUse"]["input"]["input"], int)
508+
self.assertEqual(wrapper._content_block["toolUse"]["input"]["input"], 999999999999999999)
509+
510+
stop_event = {"contentBlockStop": {"contentBlockIndex": 0}}
511+
wrapper._process_event(stop_event)
512+
513+
expected_tool_use = {
514+
"toolUseId": "random_id",
515+
"name": "example",
516+
"input": {"input": 999999999999999999},
517+
}
518+
self.assertEqual(len(wrapper._message["content"]), 1)
519+
self.assertEqual(wrapper._message["content"][0]["toolUse"], expected_tool_use)
520+
521+
def _test_patched_bedrock_runtime_instrumentation(self):
522+
"""Test patched bedrock-runtime"""
523+
524+
# Create mock arguments for ConverseStreamWrapper
525+
mock_stream = MagicMock()
526+
mock_span = MagicMock()
527+
mock_stream_error_callback = MagicMock()
528+
529+
# Create real ConverseStreamWrapper with mocked arguments
530+
wrapper = bedrock_utils.ConverseStreamWrapper(mock_stream, mock_span, mock_stream_error_callback)
531+
wrapper._record_message = True
532+
wrapper._message = {"role": "assistant", "content": []}
533+
534+
# Test contentBlockStart
535+
start_event = {
536+
"contentBlockStart": {
537+
"start": {
538+
"toolUse": {
539+
"toolUseId": "random_id",
540+
"name": "example",
541+
"input": '{"input": 999999999999999999}',
542+
}
543+
},
544+
"contentBlockIndex": 0,
545+
}
546+
}
547+
548+
wrapper._process_event(start_event)
549+
550+
# Validate that _content_block contains toolUse input as literal string (patched behavior)
551+
self.assertIn("toolUse", wrapper._content_block)
552+
self.assertIn("input", wrapper._content_block["toolUse"])
553+
# Validate that input is a string containing the literal JSON (not decoded)
554+
self.assertIsInstance(wrapper._content_block["toolUse"]["input"], str)
555+
self.assertEqual(wrapper._content_block["toolUse"]["input"], '{"input": 999999999999999999}')
556+
557+
# Test contentBlockDelta events
558+
delta_events = [
559+
{"contentBlockDelta": {"delta": {"toolUse": {"input": '{"in'}}, "contentBlockIndex": 0}},
560+
{"contentBlockDelta": {"delta": {"toolUse": {"input": 'put": 9'}}, "contentBlockIndex": 0}},
561+
{"contentBlockDelta": {"delta": {"toolUse": {"input": "99"}}, "contentBlockIndex": 0}},
562+
{"contentBlockDelta": {"delta": {"toolUse": {"input": "99"}}, "contentBlockIndex": 0}},
563+
]
564+
565+
for delta_event in delta_events:
566+
wrapper._process_event(delta_event)
567+
568+
# Verify accumulated input buffer
569+
self.assertEqual(wrapper._tool_json_input_buf, '{"input": 99999')
570+
571+
# Test contentBlockStop
572+
stop_event = {"contentBlockStop": {"contentBlockIndex": 0}}
573+
wrapper._process_event(stop_event)
574+
575+
# Verify final content_block toolUse value (input becomes the accumulated JSON string)
576+
expected_tool_use = {
577+
"toolUseId": "random_id",
578+
"name": "example",
579+
"input": '{"input": 99999',
580+
}
581+
self.assertEqual(len(wrapper._message["content"]), 1)
582+
self.assertEqual(wrapper._message["content"][0]["toolUse"], expected_tool_use)
583+
473584
def _test_patched_bedrock_agent_instrumentation(self):
474585
"""For bedrock-agent service, both extract_attributes and on_success provides attributes,
475586
the attributes depend on the API being invoked."""

0 commit comments

Comments
 (0)