Debug, trace, and understand your AI agents
Project description
SideSeat Python SDK
AI Development Workbench — Debug, trace, and understand your AI agents.
Table of Contents
- What is SideSeat?
- Quick Start
- Installation
- Framework Examples
- Configuration
- Advanced Usage
- Data and Privacy
- Troubleshooting
- API Reference
What is SideSeat?
AI agents are hard to debug. Requests fly by, context builds up, and when something fails you're left guessing.
SideSeat captures every LLM call, tool call, and agent decision, then displays them in a web UI as they happen. Run it locally during development, or deploy to your private cloud for team visibility.
Built on OpenTelemetry — the open standard already supported by most AI frameworks.
Features:
- Zero config — Auto-detects and instruments your AI framework
- Real-time tracing — Watch LLM requests and tool calls as they happen
- Message threading — See full conversations, tool calls, and images
- Cost tracking — Automatic token counting and cost calculation
Supported frameworks: Strands Agents, LangGraph, LangChain, CrewAI, AutoGen, OpenAI Agents, Google ADK, PydanticAI
Quick Start
Requirements: Python 3.9+, Node.js 18+ (for the server)
1. Start the server
npx sideseat
2. Install and initialize
pip install strands-agents sideseat
# or
uv add strands-agents sideseat
from sideseat import SideSeat, Frameworks
from strands import Agent
SideSeat(framework=Frameworks.Strands)
agent = Agent()
response = agent("What is 2+2?")
print(response)
3. View traces
Open localhost:5388 and run your agent. Traces appear in real time.
Installation
pip install sideseat # Core SDK
# or
uv add sideseat # Core SDK
# Extras for framework instrumentation:
pip install "sideseat[langgraph]" # + LangGraph
pip install "sideseat[crewai]" # + CrewAI
pip install "sideseat[autogen]" # + AutoGen
pip install "sideseat[openai-agents]" # + OpenAI Agents
pip install "sideseat[all]" # All frameworks
Strands Agents and Google ADK require only the core SDK.
Framework Examples
SideSeat auto-detects installed frameworks in this order: Strands, LangChain/LangGraph, CrewAI, AutoGen, OpenAI Agents, Google ADK, PydanticAI. When multiple frameworks are installed, use the framework parameter to select one explicitly.
Strands Agents
from sideseat import SideSeat, Frameworks
from strands import Agent
SideSeat(framework=Frameworks.Strands)
agent = Agent()
response = agent("What is 2+2?")
print(response)
Google ADK
import asyncio
from sideseat import SideSeat, Frameworks
from google.adk.agents import Agent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types
SideSeat(framework=Frameworks.GoogleADK)
agent = Agent(
model="gemini-2.5-flash",
name="assistant",
instruction="You are a helpful assistant.",
)
async def main():
session_service = InMemorySessionService()
runner = Runner(agent=agent, app_name="my_app", session_service=session_service)
session = await session_service.create_session(app_name="my_app", user_id="user")
message = types.Content(role="user", parts=[types.Part(text="What is 2+2?")])
async for event in runner.run_async(
session_id=session.id, user_id="user", new_message=message
):
if event.content and event.content.parts:
for part in event.content.parts:
if hasattr(part, "text") and part.text:
print(part.text)
asyncio.run(main())
LangGraph
from sideseat import SideSeat, Frameworks
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
SideSeat(framework=Frameworks.LangGraph)
llm = ChatOpenAI(model="gpt-5-mini")
agent = create_react_agent(llm, tools=[])
result = agent.invoke({"messages": [("user", "What is 2+2?")]})
print(result["messages"][-1].content)
CrewAI
from sideseat import SideSeat, Frameworks
from crewai import Agent, Task, Crew
SideSeat(framework=Frameworks.CrewAI)
researcher = Agent(
role="Researcher",
goal="Find information",
backstory="Expert researcher",
)
task = Task(
description="Research AI trends",
expected_output="Summary of trends",
agent=researcher,
)
crew = Crew(agents=[researcher], tasks=[task])
result = crew.kickoff()
print(result)
AutoGen
from sideseat import SideSeat, Frameworks
from autogen import AssistantAgent, UserProxyAgent
SideSeat(framework=Frameworks.AutoGen)
llm_config = {"config_list": [{"model": "gpt-5-mini"}]}
assistant = AssistantAgent("assistant", llm_config=llm_config)
user = UserProxyAgent("user", human_input_mode="NEVER")
user.initiate_chat(assistant, message="Hello!")
OpenAI Agents
from sideseat import SideSeat, Frameworks
from agents import Agent, Runner
SideSeat(framework=Frameworks.OpenAIAgents)
agent = Agent(name="Assistant", instructions="You are helpful.")
result = Runner.run_sync(agent, "What is the capital of France?")
print(result.final_output)
LangChain
from sideseat import SideSeat, Frameworks
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
SideSeat(framework=Frameworks.LangChain)
llm = ChatOpenAI(model="gpt-5-mini")
response = llm.invoke([HumanMessage(content="Hello!")])
print(response.content)
PydanticAI
from sideseat import SideSeat, Frameworks
from pydantic_ai import Agent
SideSeat(framework=Frameworks.PydanticAI)
agent = Agent("openai:gpt-5-mini", system_prompt="Be concise.")
result = agent.run_sync("What is Python?")
print(result.data)
Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
SIDESEAT_ENDPOINT |
http://127.0.0.1:5388 |
Server URL |
SIDESEAT_PROJECT |
default |
Project identifier |
SIDESEAT_API_KEY |
— | Authentication key |
SIDESEAT_DISABLED |
false |
Disable all telemetry |
SIDESEAT_DEBUG |
false |
Enable verbose logging |
Constructor Parameters
SideSeat(
endpoint="http://localhost:5388",
project_id="my-project",
api_key="pk-...",
framework=Frameworks.LangGraph,
auto_instrument=True,
service_name="my-app",
service_version="1.0.0",
enable_traces=True,
enable_metrics=True,
enable_logs=False,
capture_content=True,
encode_binary=True,
disabled=False,
debug=False,
)
| Parameter | Type | Default | Description |
|---|---|---|---|
endpoint |
str |
http://127.0.0.1:5388 |
Server URL |
project_id |
str |
default |
Project identifier |
api_key |
str |
None |
Authentication key |
framework |
str |
Auto-detected | Framework to instrument |
auto_instrument |
bool |
True |
Enable framework instrumentation |
service_name |
str |
Framework name | Application name in traces |
service_version |
str |
Framework version | Application version |
enable_traces |
bool |
True |
Export trace spans |
enable_metrics |
bool |
True |
Export metrics |
enable_logs |
bool |
False |
Export logs |
capture_content |
bool |
True |
Capture LLM prompts and responses |
encode_binary |
bool |
True |
Base64 encode binary data |
disabled |
bool |
False |
Disable all telemetry |
debug |
bool |
False |
Enable verbose logging |
Resolution order: Constructor → SIDESEAT_* env → OTEL_* env → defaults
Advanced Usage
Context Manager
with SideSeat() as client:
run_my_agent()
# Traces flushed and connection closed automatically
Global Instance
import sideseat
sideseat.init(project_id="my-project") # Initialize once
client = sideseat.get_client() # Access anywhere
sideseat.shutdown() # Clean up
Custom Spans
client = SideSeat()
with client.span("process-request") as span:
span.set_attribute("user_id", "12345")
result = do_work()
# Exceptions recorded automatically with stack traces
Async Support
import asyncio
from sideseat import SideSeat
async def main():
with SideSeat():
result = await my_async_agent.run("Hello")
print(result)
asyncio.run(main())
Debug Exporters
client = SideSeat()
client.telemetry.setup_console_exporter() # Print to stdout
client.telemetry.setup_file_exporter("traces.jsonl") # Write to file
Disabled Mode
SideSeat(disabled=True) # Or set SIDESEAT_DISABLED=true
Existing OpenTelemetry Setup
If a TracerProvider already exists, SideSeat adds its exporter to the existing provider.
Unsupported Frameworks
SideSeat(auto_instrument=False)
# Use your framework's native OpenTelemetry instrumentation
Data and Privacy
What is collected:
- Trace spans with timing and hierarchy
- LLM prompts and responses (when
capture_content=True) - Token counts and model names
- Errors and stack traces
Where it goes:
All data is sent to your self-hosted server. Nothing leaves your infrastructure.
Resilience:
- Up to 2,048 spans buffered in memory
- Batched exports every 5 seconds
- 30-second timeout per export
- Server downtime does not affect your application
Troubleshooting
| Problem | Solution |
|---|---|
| Connection refused | Server not running. Run npx sideseat |
| No traces appear | Check endpoint with SIDESEAT_DEBUG=true |
| Wrong framework detected | Set framework=Frameworks.X explicitly |
| Duplicate traces | Initialize SideSeat() once per process |
| Import error for extras | Install extras: pip install "sideseat[langgraph]" |
API Reference
SideSeat
client = SideSeat(**kwargs)
Properties:
| Name | Type | Description |
|---|---|---|
config |
Config |
Immutable configuration |
telemetry |
TelemetryClient |
Access to debug exporters |
tracer_provider |
TracerProvider |
OpenTelemetry tracer provider |
is_disabled |
bool |
Whether telemetry is disabled |
Methods:
| Name | Returns | Description |
|---|---|---|
span(name) |
ContextManager[Span] |
Create a custom span |
get_tracer(name) |
Tracer |
Get an OpenTelemetry tracer |
force_flush(timeout_millis) |
bool |
Export pending spans immediately |
validate_connection(timeout) |
bool |
Test server connectivity |
shutdown(timeout_millis) |
None |
Flush pending spans and shut down |
Frameworks
Frameworks.Strands
Frameworks.LangGraph
Frameworks.LangChain
Frameworks.CrewAI
Frameworks.AutoGen
Frameworks.OpenAIAgents
Frameworks.GoogleADK
Frameworks.PydanticAI
Module Functions
| Function | Returns | Description |
|---|---|---|
init(**kwargs) |
SideSeat |
Create global instance |
get_client() |
SideSeat |
Get global instance |
shutdown() |
None |
Shut down global instance |
is_initialized() |
bool |
Check if initialized |
Utilities
| Function | Description |
|---|---|
encode_value(value) |
JSON-encode a value; base64 for binary |
span_to_dict(span) |
Convert span to dictionary |
JsonFileSpanExporter |
JSONL file exporter class |
Resources
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sideseat-1.0.6.tar.gz.
File metadata
- Download URL: sideseat-1.0.6.tar.gz
- Upload date:
- Size: 88.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.22
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
24e669e72cc13a9a752b7751c21c27973e6ab1eebaed8a5c4e9f148c0ddfc90f
|
|
| MD5 |
8971ea13e81a1318a82f9f5ec971bd97
|
|
| BLAKE2b-256 |
974fbb27a7be8590e0eae190a0594dd76163bbc2ce401fb7bd73d5dc879cacb0
|
File details
Details for the file sideseat-1.0.6-py3-none-any.whl.
File metadata
- Download URL: sideseat-1.0.6-py3-none-any.whl
- Upload date:
- Size: 18.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.22
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b8f149a91eece96d573646d68497850e5b8ada4322a60169580ba4036bc0e991
|
|
| MD5 |
fa66094ce6fdee7e69cc3c3824984b67
|
|
| BLAKE2b-256 |
46f720343fdf2e057711d2695edd381c0f51c0faa2e2ec567b22ae12f3315ec1
|