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.9+ 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

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

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.6.tar.gz (88.1 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.6-py3-none-any.whl (18.6 kB view details)

Uploaded Python 3

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

Hashes for sideseat-1.0.6.tar.gz
Algorithm Hash digest
SHA256 24e669e72cc13a9a752b7751c21c27973e6ab1eebaed8a5c4e9f148c0ddfc90f
MD5 8971ea13e81a1318a82f9f5ec971bd97
BLAKE2b-256 974fbb27a7be8590e0eae190a0594dd76163bbc2ce401fb7bd73d5dc879cacb0

See more details on using hashes here.

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

Hashes for sideseat-1.0.6-py3-none-any.whl
Algorithm Hash digest
SHA256 b8f149a91eece96d573646d68497850e5b8ada4322a60169580ba4036bc0e991
MD5 fa66094ce6fdee7e69cc3c3824984b67
BLAKE2b-256 46f720343fdf2e057711d2695edd381c0f51c0faa2e2ec567b22ae12f3315ec1

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