Skip to content

Anthropic

SideSeat instruments the Anthropic Python SDK to capture model information, token usage, messages, and costs from the Messages API.

  • SideSeat running locally (sideseat)
  • Python SDK installed with the Anthropic extra (pip install "sideseat[anthropic]")
  • Anthropic API key configured (ANTHROPIC_API_KEY)

SideSeat instruments the Anthropic SDK automatically. Initialize SideSeat with the Anthropic framework, then use the SDK as usual:

from sideseat import SideSeat, Frameworks
import anthropic
SideSeat(framework=Frameworks.Anthropic)
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-sonnet-4-5-20250929",
system="Answer in one sentence.",
max_tokens=1024,
messages=[{"role": "user", "content": "What is the speed of light?"}],
)
print(message.content[0].text)

By default, each Anthropic API call produces its own independent trace. Use client.trace() to group related calls under a single root span:

client = SideSeat(framework=Frameworks.Anthropic)
anthropic_client = anthropic.Anthropic()
with client.trace("geography-chat"):
messages = []
# Turn 1
messages.append({"role": "user", "content": "What is the capital of France?"})
response = anthropic_client.messages.create(
model="claude-sonnet-4-5-20250929", max_tokens=1024, messages=messages,
)
messages.append({"role": "assistant", "content": response.content[0].text})
# Turn 2 — follows up on the previous answer
messages.append({"role": "user", "content": "What about Germany?"})
response = anthropic_client.messages.create(
model="claude-sonnet-4-5-20250929", max_tokens=1024, messages=messages,
)
messages.append({"role": "assistant", "content": response.content[0].text})
# Turn 3
messages.append({"role": "user", "content": "Which city has a larger population?"})
response = anthropic_client.messages.create(
model="claude-sonnet-4-5-20250929", max_tokens=1024, messages=messages,
)

This produces the following span hierarchy:

geography-chat (root span)
├── Messages claude-sonnet-4-5... (turn 1)
├── Messages claude-sonnet-4-5... (turn 2)
└── Messages 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.

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, Frameworks
import anthropic
client = SideSeat(framework=Frameworks.Anthropic)
anthropic_client = anthropic.Anthropic()
session_id = "sess-abc"
user_id = "user-123"
# Trace 1: Trip planning
with client.trace("trip-planning", session_id=session_id, user_id=user_id):
messages = []
messages.append({"role": "user", "content": "Plan a 5-day trip to Japan."})
response = anthropic_client.messages.create(
model="claude-sonnet-4-5-20250929", max_tokens=1024, messages=messages,
)
messages.append({"role": "assistant", "content": response.content[0].text})
messages.append({"role": "user", "content": "Tell me more about Kyoto."})
response = anthropic_client.messages.create(
model="claude-sonnet-4-5-20250929", max_tokens=1024, 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": "What are the must-try dishes in Tokyo?"})
response = anthropic_client.messages.create(
model="claude-sonnet-4-5-20250929", max_tokens=1024, messages=messages,
)
messages.append({"role": "assistant", "content": response.content[0].text})
messages.append({"role": "user", "content": "What about street food in Osaka?"})
response = anthropic_client.messages.create(
model="claude-sonnet-4-5-20250929", max_tokens=1024, 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)
├── Messages claude-sonnet-4-5... (turn 1)
└── Messages claude-sonnet-4-5... (turn 2)
Trace 2: food-recommendations (session_id=sess-abc, user_id=user-123)
├── Messages claude-sonnet-4-5... (turn 1)
└── Messages 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 responses are fully captured, including token counts aggregated from stream events:

from sideseat import SideSeat, Frameworks
import anthropic
client = SideSeat(framework=Frameworks.Anthropic)
anthropic_client = anthropic.Anthropic()
with anthropic_client.messages.stream(
model="claude-sonnet-4-5-20250929",
system="Answer in one sentence.",
max_tokens=1024,
messages=[{"role": "user", "content": "What is the boiling point of water?"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)

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, Frameworks
import anthropic
client = SideSeat(framework=Frameworks.Anthropic)
anthropic_client = anthropic.Anthropic()
message = anthropic_client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=16000,
thinking={"type": "enabled", "budget_tokens": 10000},
messages=[{"role": "user", "content": "What is 27 * 453?"}],
)
for block in message.content:
if block.type == "thinking":
print(f"Thinking: {block.thinking}")
elif block.type == "text":
print(f"Answer: {block.text}")

Tool definitions, tool use requests, and tool results are all captured:

from sideseat import SideSeat, Frameworks
import anthropic
client = SideSeat(framework=Frameworks.Anthropic)
anthropic_client = anthropic.Anthropic()
tools = [{
"name": "get_weather",
"description": "Get the current weather for a location.",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"}
},
"required": ["location"],
},
}]
# Step 1: model requests a tool call
messages = [{"role": "user", "content": "What's the weather in Paris?"}]
message = anthropic_client.messages.create(
model="claude-sonnet-4-5-20250929",
system="Use tools when available.",
max_tokens=1024,
tools=tools,
messages=messages,
)
messages.append({"role": "assistant", "content": message.content})
# Step 2: return the tool result
tool_use = next(b for b in message.content if b.type == "tool_use")
messages.append({
"role": "user",
"content": [{"type": "tool_result", "tool_use_id": tool_use.id, "content": "Sunny, 22C"}],
})
# Step 3: model produces the final answer
message = anthropic_client.messages.create(
model="claude-sonnet-4-5-20250929",
system="Use tools when available.",
max_tokens=1024,
tools=tools,
messages=messages,
)

SideSeat captures all three steps as separate spans, each with full message details.

Image inputs are captured (image data base64-encoded):

import base64
import anthropic
with open("image.jpg", "rb") as f:
image_data = base64.b64encode(f.read()).decode()
message = anthropic.Anthropic().messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=1024,
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "What's in this image?"},
{"type": "image", "source": {
"type": "base64",
"media_type": "image/jpeg",
"data": image_data,
}},
],
}],
)

Anthropic uses an API key:

Terminal window
export ANTHROPIC_API_KEY=sk-ant-...

The Anthropic SDK reads ANTHROPIC_API_KEY from the environment automatically. You can also pass it directly: anthropic.Anthropic(api_key="sk-ant-...").

  • PydanticAI — use SideSeat with PydanticAI on Anthropic
  • Python SDK — SDK configuration and API reference
  • Overview — get started with SideSeat