Skip to main content

Debug, trace, and understand your AI agents

Project description

SideSeat Python SDK

AI Development Workbench — Debug, trace, and understand your AI agents.

PyPI Python 3.10+ License: MIT

Table of Contents

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

Supported providers: OpenAI, Amazon Bedrock, Anthropic, Google Gemini

Quick Start

Requirements: Python 3.10+, Node.js 18+ (for the server)

1. Start the server

npx sideseat

2. Install and initialize

pip install sideseat
# or
uv add sideseat

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)

Vercel AI SDK:

npm install ai @ai-sdk/amazon-bedrock @sideseat/sdk
import { generateText } from "ai";
import { bedrock } from "@ai-sdk/amazon-bedrock";
import { init } from "@sideseat/sdk";

init();

const { text } = await generateText({
  model: bedrock("anthropic.claude-sonnet-4-5-20250929-v1:0"),
  prompt: "Analyze this dataset...",
  experimental_telemetry: { isEnabled: true },
});

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]"          # + OpenAI / OpenAI Agents

# Extras for provider instrumentation:
pip install "sideseat[anthropic]"       # + Anthropic
pip install "sideseat[aws]"             # + Amazon Bedrock
pip install "sideseat[google-genai]"    # + Google Gemini
pip install "sideseat[vertex-ai]"       # + Google Vertex AI (native SDK)

pip install "sideseat[all]"             # All frameworks + providers

Strands Agents, Google ADK, and Microsoft Agent Framework require only the core SDK.

Framework Examples

SideSeat auto-detects installed frameworks in this order: Strands, LangChain, CrewAI, AutoGen, OpenAI Agents, Google ADK, PydanticAI. When multiple frameworks are installed, use the framework parameter to select one explicitly. LangGraph is detected as LangChain — use framework=Frameworks.LangGraph to select it 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)

Amazon Bedrock (Converse)

import boto3
from sideseat import SideSeat, Frameworks

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", session_id="session-abc", user_id="user-123"):
    messages = []

    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"])

    messages.append({"role": "user", "content": [{"text": "What about Germany?"}]})
    response = bedrock.converse(modelId=model_id, messages=messages)
    print(response["output"]["message"]["content"][0]["text"])

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)

Provider Examples

Use SideSeat directly with cloud provider SDKs, without an agent framework. Install the matching extra for instrumentation (e.g., pip install "sideseat[openai]").

Amazon Bedrock

pip install "sideseat[aws]"
from sideseat import SideSeat, Frameworks
import 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",
    messages=[{"role": "user", "content": [{"text": "What is 2+2?"}]}],
)

print(response["output"]["message"]["content"][0]["text"])

Anthropic

pip install "sideseat[anthropic]"
from sideseat import SideSeat, Frameworks
import anthropic

SideSeat(framework=Frameworks.Anthropic)

client = anthropic.Anthropic()
message = client.messages.create(
    model="claude-sonnet-4-5-20250929",
    max_tokens=1024,
    messages=[{"role": "user", "content": "What is 2+2?"}],
)

print(message.content[0].text)

OpenAI

pip install "sideseat[openai]"

Chat Completions API:

from sideseat import SideSeat, Frameworks
from openai import OpenAI

client = SideSeat(framework=Frameworks.OpenAI)
openai = OpenAI()

with client.trace("geography-chat", session_id="sess-abc", user_id="user-123"):
    messages = []

    messages.append({"role": "user", "content": "What is the capital of France?"})
    response = openai.chat.completions.create(model="gpt-5-mini", messages=messages)
    messages.append({"role": "assistant", "content": response.choices[0].message.content})

    messages.append({"role": "user", "content": "What about Germany?"})
    response = openai.chat.completions.create(model="gpt-5-mini", messages=messages)
    print(response.choices[0].message.content)

Responses API:

from sideseat import SideSeat, Frameworks
from openai import OpenAI

SideSeat(framework=Frameworks.OpenAI)
openai = OpenAI()

response = openai.responses.create(
    model="gpt-5-mini",
    instructions="Answer in one sentence.",
    input="What is the speed of light?",
    max_output_tokens=1024,
)

print(response.output_text)

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.Strands,
    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 | list Auto-detected Framework/providers 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, **kwargs) ContextManager[Span] Create a custom span
trace(name, **kwargs) ContextManager[Span] Create a root span (trace group)
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

Providers (via Frameworks)

Frameworks.Bedrock    # Amazon Bedrock (patches botocore)
Frameworks.OpenAI     # OpenAI (instruments openai SDK)
Frameworks.Anthropic  # Anthropic (instruments anthropic SDK)
Frameworks.GoogleGenAI # Google Gemini (instruments google-genai SDK)

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

MIT

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

sideseat-1.0.8.tar.gz (114.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sideseat-1.0.8-py3-none-any.whl (37.7 kB view details)

Uploaded Python 3

File details

Details for the file sideseat-1.0.8.tar.gz.

File metadata

  • Download URL: sideseat-1.0.8.tar.gz
  • Upload date:
  • Size: 114.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.22

File hashes

Hashes for sideseat-1.0.8.tar.gz
Algorithm Hash digest
SHA256 ea02a8a4ab4aa1d8f6dc79a37e096868343b44274dc3443d8780e98e13fe844a
MD5 6f8fa11ba1df11167b463481d35724eb
BLAKE2b-256 8764d29cd1fba5e4508c687483a26c33c1ed9c2d586948470b00ce3a63e72df4

See more details on using hashes here.

File details

Details for the file sideseat-1.0.8-py3-none-any.whl.

File metadata

  • Download URL: sideseat-1.0.8-py3-none-any.whl
  • Upload date:
  • Size: 37.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.22

File hashes

Hashes for sideseat-1.0.8-py3-none-any.whl
Algorithm Hash digest
SHA256 de9864c17bbfa73a67b13e23bb38c1117a83b631823e857e768107f73a1d3811
MD5 10562f3772967137b2d2dead989ef053
BLAKE2b-256 8006e2975e4a41ace670af6fe05f6662399eb7e358a652a49877c29d1f22fb09

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page