Amazon Bedrock
SideSeat instruments the boto3 Bedrock Runtime client to capture model information, token usage, messages, and costs from both the Converse and InvokeModel APIs.
Prerequisites
Section titled “Prerequisites”- SideSeat running locally (
sideseat) - Python SDK installed (
pip install sideseatoruv add sideseat) - AWS credentials configured for Amazon Bedrock
Converse API
Section titled “Converse API”SideSeat patches the boto3 Converse API automatically. Initialize SideSeat with the Bedrock framework, then use boto3 as usual:
from sideseat import SideSeat, Frameworksimport boto3
SideSeat(framework=Frameworks.Bedrock)
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
response = bedrock.converse( modelId="us.anthropic.claude-sonnet-4-5-20250929-v1:0", system=[{"text": "Answer in one sentence."}], messages=[{"role": "user", "content": [{"text": "What is the speed of light?"}]}], inferenceConfig={"maxTokens": 128},)
print(response["output"]["message"]["content"][0]["text"])Traces
Section titled “Traces”By default, each Bedrock API call produces its own independent trace. Use client.trace() to group related calls under a single root span:
client = SideSeat(framework=Frameworks.Bedrock)bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")model_id = "us.anthropic.claude-sonnet-4-5-20250929-v1:0"
with client.trace("geography-chat"): messages = []
# Turn 1 messages.append({"role": "user", "content": [{"text": "What is the capital of France?"}]}) response = bedrock.converse(modelId=model_id, messages=messages) messages.append(response["output"]["message"])
# Turn 2 — follows up on the previous answer messages.append({"role": "user", "content": [{"text": "What about Germany?"}]}) response = bedrock.converse(modelId=model_id, messages=messages) messages.append(response["output"]["message"])
# Turn 3 messages.append({"role": "user", "content": [{"text": "Which city has a larger population?"}]}) response = bedrock.converse(modelId=model_id, messages=messages)This produces the following span hierarchy:
geography-chat (root span) ├── chat claude-sonnet-4-5... (turn 1) ├── chat claude-sonnet-4-5... (turn 2) └── chat claude-sonnet-4-5... (turn 3)All three calls appear as child spans in the SideSeat UI, with the full multi-turn conversation visible in the trace detail view.
Sessions
Section titled “Sessions”Pass session_id and user_id to client.trace() to group independent traces into a session. The SideSeat sessions view groups all traces that share the same session_id.
Each client.trace() produces its own trace with its own trace ID, but they are linked by the shared session:
from sideseat import SideSeat, Frameworksimport boto3
client = SideSeat(framework=Frameworks.Bedrock)bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")model_id = "us.anthropic.claude-sonnet-4-5-20250929-v1:0"
session_id = "sess-abc"user_id = "user-123"
# Trace 1: Trip planningwith client.trace("trip-planning", session_id=session_id, user_id=user_id): messages = [] messages.append({"role": "user", "content": [{"text": "Plan a 5-day trip to Japan."}]}) response = bedrock.converse(modelId=model_id, messages=messages) messages.append(response["output"]["message"])
messages.append({"role": "user", "content": [{"text": "Tell me more about Kyoto."}]}) response = bedrock.converse(modelId=model_id, messages=messages)
# Trace 2: Food recommendations (fresh conversation, same session)with client.trace("food-recommendations", session_id=session_id, user_id=user_id): messages = [] messages.append({"role": "user", "content": [{"text": "What are the must-try dishes in Tokyo?"}]}) response = bedrock.converse(modelId=model_id, messages=messages) messages.append(response["output"]["message"])
messages.append({"role": "user", "content": [{"text": "What about street food in Osaka?"}]}) response = bedrock.converse(modelId=model_id, messages=messages)This produces two independent traces, each with their own span hierarchy:
Trace 1: trip-planning (session_id=sess-abc, user_id=user-123) ├── chat claude-sonnet-4-5... (turn 1) └── chat claude-sonnet-4-5... (turn 2)
Trace 2: food-recommendations (session_id=sess-abc, user_id=user-123) ├── chat claude-sonnet-4-5... (turn 1) └── chat claude-sonnet-4-5... (turn 2)Each trace starts a fresh conversation with its own message history. The SideSeat sessions view groups them by session_id.
Streaming
Section titled “Streaming”converse_stream responses are fully captured, including token counts aggregated from stream metadata:
from sideseat import SideSeat, Frameworksimport boto3
client = SideSeat(framework=Frameworks.Bedrock)bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
response = bedrock.converse_stream( modelId="us.anthropic.claude-sonnet-4-5-20250929-v1:0", system=[{"text": "Answer in one sentence."}], messages=[{"role": "user", "content": [{"text": "What is the boiling point of water?"}]}], inferenceConfig={"maxTokens": 128},)
for event in response["stream"]: if "contentBlockDelta" in event: print(event["contentBlockDelta"]["delta"].get("text", ""), end="", flush=True)Extended Thinking
Section titled “Extended Thinking”Claude’s extended thinking is captured for both sync and streaming calls. Thinking blocks appear in the message thread alongside the final response.
from sideseat import SideSeat, Frameworksimport boto3
client = SideSeat(framework=Frameworks.Bedrock)bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
response = bedrock.converse( modelId="us.anthropic.claude-sonnet-4-5-20250929-v1:0", system=[{"text": "You are a math tutor. Show your work."}], messages=[{"role": "user", "content": [{"text": "What is 27 * 453?"}]}], inferenceConfig={"maxTokens": 8192}, additionalModelRequestFields={ "thinking": {"type": "enabled", "budget_tokens": 1024} },)
for block in response["output"]["message"]["content"]: if "reasoningContent" in block: print(f"Thinking: {block['reasoningContent']['reasoningText']['text']}") elif "text" in block: print(f"Answer: {block['text']}")Tool Use
Section titled “Tool Use”Tool definitions, tool use requests, and tool results are all captured:
from sideseat import SideSeat, Frameworksimport boto3
client = SideSeat(framework=Frameworks.Bedrock)bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")model_id = "us.anthropic.claude-sonnet-4-5-20250929-v1:0"
tool_config = { "tools": [{ "toolSpec": { "name": "get_weather", "description": "Get the current weather for a location.", "inputSchema": { "json": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"], } }, } }]}
# Step 1: model requests a tool callmessages = [{"role": "user", "content": [{"text": "What's the weather in Paris?"}]}]response = bedrock.converse( modelId=model_id, system=[{"text": "Use tools when available."}], messages=messages, toolConfig=tool_config,)assistant_msg = response["output"]["message"]messages.append(assistant_msg)
# Step 2: return the tool resulttool_use = next(b["toolUse"] for b in assistant_msg["content"] if "toolUse" in b)messages.append({ "role": "user", "content": [{"toolResult": { "toolUseId": tool_use["toolUseId"], "content": [{"text": "Sunny, 22C"}], }}],})
# Step 3: model produces the final answerresponse = bedrock.converse( modelId=model_id, system=[{"text": "Use tools when available."}], messages=messages, toolConfig=tool_config,)SideSeat captures all three steps as separate spans, each with full message details.
InvokeModel API
Section titled “InvokeModel API”SideSeat also instruments invoke_model and invoke_model_with_response_stream for direct Claude Messages API access:
from sideseat import SideSeat, Frameworksimport boto3import json
SideSeat(framework=Frameworks.Bedrock)bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
body = json.dumps({ "anthropic_version": "bedrock-2023-05-31", "max_tokens": 128, "system": "Answer in one sentence.", "messages": [{"role": "user", "content": "What is the speed of light?"}],})
response = bedrock.invoke_model( modelId="us.anthropic.claude-sonnet-4-5-20250929-v1:0", body=body, contentType="application/json",)
result = json.loads(response["body"].read())print(result["content"][0]["text"])Authentication
Section titled “Authentication”Amazon Bedrock uses the standard AWS credential chain:
# Environment variablesexport AWS_ACCESS_KEY_ID=xxxexport AWS_SECRET_ACCESS_KEY=xxxexport AWS_REGION=us-east-1
# Or AWS profileexport AWS_PROFILE=my-profileNext Steps
Section titled “Next Steps”- Strands (Python) — use SideSeat with the Strands agent framework on Bedrock
- Python SDK — SDK configuration and API reference
- Overview — get started with SideSeat