-
Notifications
You must be signed in to change notification settings - Fork 809
Description
Describe your environment
- OS: macOS / Linux (Docker)
- Python Version: 3.12
- Package Version: opentelemetry-instrumentation-botocore==0.54b1
- AWS SDK: boto3 with Bedrock Runtime
- Framework: LangChain with ChatBedrock
What happened?
When using OpenTelemetry instrumentation with AWS Bedrock's tool calling (streaming mode), the application crashes with a TypeError in bedrock_utils.py at the _decode_tool_use function.
The error occurs because tool_use["input"] is already a dict, but the code attempts to call json.loads() on it, which only accepts strings.
Steps to Reproduce
from langchain_aws import ChatBedrock
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
@tool
def get_current_time() -> str:
"""Get the current time."""
from datetime import datetime
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
llm = ChatBedrock(
model_id="eu.anthropic.claude-3-7-sonnet-20250219-v1:0",
region_name="eu-central-1",
model_kwargs={"temperature": 0.1, "max_tokens": 2048}
)
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant with access to tools."),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
])
agent = create_tool_calling_agent(llm, [get_current_time], prompt)
agent_executor = AgentExecutor(agent=agent, tools=[get_current_time])
response = agent_executor.invoke({"input": "What time is it now?"})Run with OpenTelemetry:
opentelemetry-instrument python script.pyExpected Result
The agent should successfully invoke the tool and return the result without errors.
Actual Result
TypeError: the JSON object must be str, bytes or bytearray, not dict
Full Traceback:
Traceback (most recent call last):
File "/path/to/script.py", line 36, in <module>
response = agent_executor.invoke({"input": "What time is it now?"})
File "langchain/chains/base.py", line 165, in invoke
self._call(inputs, run_manager=run_manager)
File "langchain/agents/agent.py", line 1625, in _call
next_step_output = self._take_next_step(
File "langchain/agents/agent.py", line 1325, in _take_next_step
list(
File "langchain/agents/agent.py", line 1352, in _iter_next_step
output = self._action_agent.plan(
File "langchain/agents/agent.py", line 573, in plan
for chunk in self.runnable.stream(inputs, config={"callbacks": callbacks}):
File "langchain_core/runnables/base.py", line 3650, in stream
yield from self.transform(iter([input]), config, **kwargs)
File "langchain_core/runnables/base.py", line 3636, in transform
yield from self._transform_stream_with_config(
File "langchain_core/runnables/base.py", line 2372, in _transform_stream_with_config
chunk: Output = context.run(next, iterator)
File "langchain_core/runnables/base.py", line 3595, in _transform
yield from final_pipeline
File "langchain_core/runnables/base.py", line 1571, in transform
for ichunk in input:
File "langchain_core/runnables/base.py", line 5927, in transform
yield from self.bound.transform(
File "langchain_core/runnables/base.py", line 1589, in transform
yield from self.stream(final, config, **kwargs)
File "langchain_core/language_models/chat_models.py", line 522, in stream
for chunk in self._stream(input_messages, stop=stop, **kwargs):
File "langchain_aws/chat_models/bedrock.py", line 869, in _stream
for chunk in self._prepare_input_and_invoke_stream(
File "langchain_aws/llms/bedrock.py", line 1165, in _prepare_input_and_invoke_stream
for chunk in LLMInputOutputAdapter.prepare_output_stream(
File "langchain_aws/llms/bedrock.py", line 509, in prepare_output_stream
for event in stream:
File "opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py", line 170, in __iter__
self._process_event(event)
File "opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py", line 192, in _process_event
self._process_anthropic_claude_chunk(chunk)
File "opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py", line 332, in _process_anthropic_claude_chunk
_decode_tool_use(self._content_block)
File "opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py", line 42, in _decode_tool_use
tool_use["input"] = json.loads(tool_use["input"])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: the JSON object must be str, bytes or bytearray, not dictRoot Cause
In bedrock_utils.py, the _decode_tool_use function assumes tool_use["input"] is always a string:
def _decode_tool_use(tool_use):
# input get sent encoded in json
if "input" in tool_use:
try:
tool_use["input"] = json.loads(tool_use["input"])
except json.JSONDecodeError:
pass
return tool_useProblem: The function catches json.JSONDecodeError but not TypeError. When tool_use["input"] is already a dict (which happens with Bedrock's streaming tool calls), json.loads() raises TypeError, not JSONDecodeError.
Additional context
- Works without OpenTelemetry instrumentation
- Only fails when OpenTelemetry is enabled via
opentelemetry-instrument - Workaround: Disable streaming in ChatBedrock with
disable_streaming="tool_calling" - Similar fixes were done in PR opentelemetry-instrumentation-botocore: fix handling of tool input chunked json in bedrock converse_stream #3544 and botocore: add dict type check before parsing tool_use from Bedrock messages #3548 for other Bedrock scenarios
Would you like to implement a fix?
Yes
Tip
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.