SwisperStudio SDK
Simple, high-performance integration for tracing Swisper LangGraph applications.
What changed in each release is in CHANGELOG.md.
Installation
From PyPI (Recommended)
pip install swisper-studio-sdk
The package is public on PyPI, so no authentication is needed. Pin the version in your project and check CHANGELOG.md before upgrading.
From Source (Development)
git clone https://github.com/Fintama/swisper_studio.git
cd swisper_studio/sdk
pip install -e .
Note: Source installation requires Fintama GitHub organization access.
Trace Inspection CLI (studio-trace)
The SDK ships a studio-trace CLI for inspecting traces from the command
line. Configure it with two env vars:
export STUDIO_API_TOKEN=<your-api-key>
# Pick the URL that matches WHERE the CLI is running:
export STUDIO_API_URL=http://localhost:8003 # from a bare host shell
# export STUDIO_API_URL=http://host.docker.internal:8003 # from inside a container
# that needs to reach the host
Heads up (changed in 0.8.2): the 0.8.1 release notes only listed
host.docker.internal:8003, which works from inside a container but fails from a bare host shell. Use the localhost form on the host.
Then:
studio-trace --version
studio-trace catalog --format human # discover all subcommands
studio-trace --help # top-level flags
studio-trace outline <trace_id> # first call on any trace
Tab completion (QW3, 0.8.2):
# Bash
eval "$(studio-trace completion bash)"
# Zsh
eval "$(studio-trace completion zsh)"
Quick Start (30 seconds)
1. Initialize at Startup (Redis Streams)
# In your main.py or startup code
from swisper_studio_sdk import safe_initialize, wrap_llm_adapter
# Async initialization (in lifespan or startup) - RECOMMENDED
status = await safe_initialize(
redis_url="redis://swisper_studio_redis:6379", # SwisperStudio Redis
project_id="your-project-id", # From SwisperStudio
enabled=True
)
if status["initialized"]:
logger.info("SwisperStudio tracing enabled")
# CRITICAL: Wrap LLM adapters to capture prompts/responses/tokens
wrap_llm_adapter()
else:
logger.info("Tracing disabled (Swisper continues normally)")
Note: safe_initialize() NEVER blocks or raises exceptions. If Redis is unavailable, Swisper continues normally with tracing disabled.
⚠️ IMPORTANT: You MUST call wrap_llm_adapter() after successful initialization to capture LLM telemetry (prompts, responses, token usage). Without this, LLM-calling nodes will show as SPAN instead of GENERATION.
2. ONE LINE CHANGE to Enable Graph Tracing
# Before:
from langgraph.graph import StateGraph
graph = StateGraph(GlobalSupervisorState)
# After (change ONE line):
from swisper_studio_sdk import create_traced_graph
graph = create_traced_graph(GlobalSupervisorState, trace_name="supervisor")
# All nodes added to this graph are automatically traced!
3. Add Nodes as Normal
# Add nodes - they're automatically traced!
graph.add_node("intent_classification", intent_classification_node)
graph.add_node("memory", memory_node)
graph.add_node("planner", planner_node)
graph.add_node("ui_node", ui_node)
# Compile and run as usual
app = graph.compile()
result = await app.ainvoke(initial_state)
# All executions are now traced to SwisperStudio! 🎉
Two-Part Setup Explained
SwisperStudio tracing requires TWO components:
| Component | Purpose | What it captures |
|---|---|---|
create_traced_graph() |
Wraps graph nodes | Execution flow, state, timing |
wrap_llm_adapter() |
Wraps LLM adapters | Prompts, responses, tokens |
Both are required for full observability:
# ✅ CORRECT - Full observability
await safe_initialize(...)
wrap_llm_adapter() # Captures LLM telemetry
graph = create_traced_graph(...) # Captures node execution
# ❌ WRONG - Missing LLM telemetry
await safe_initialize(...)
# wrap_llm_adapter() NOT called!
graph = create_traced_graph(...) # Nodes show as SPAN, not GENERATION
Features
Core Features:
- ✅ One-line integration -
create_traced_graph()instead ofStateGraph() - ✅ Auto-instrumentation - All nodes automatically traced
- ✅ State capture - Captures input/output state at each node
- ✅ Error tracking - Captures exceptions and error messages
- ✅ Nested observations - Supports parent-child relationships
- ✅ Zero boilerplate - No decorators needed on individual nodes
v0.5.x Features:
- ✅ Redis Streams - 50x faster than HTTP (500ms → 10ms)
- ✅ LLM Reasoning - Captures
<think>...</think>tags from DeepSeek R1, o1, etc. - ✅ Streaming Support - Captures full responses from streaming LLM calls
- ✅ Connection Status - Verifies SwisperStudio consumer is running
- ✅ Per-Node Config - Enable/disable reasoning per node
- ✅ Memory Safety - Auto-cleanup prevents memory leaks
- ✅ Full LLM Telemetry (v0.5.4) - Wraps LLMAdapterFactory directly for reliable capture
Advanced Usage
LLM Reasoning Capture
Control reasoning capture per node:
from swisper_studio_sdk import traced
# Enable reasoning with custom length limit
@traced("classify_intent", capture_reasoning=True, reasoning_max_length=20000)
async def classify_intent_node(state):
# Captures <think>...</think> tags (up to 20KB)
return state
# Disable reasoning for specific nodes
@traced("memory_node", capture_reasoning=False)
async def memory_node(state):
# No reasoning captured (faster, less data)
return state
# Use defaults (reasoning enabled, 50KB limit)
@traced("global_planner")
async def global_planner_node(state):
return state
What gets captured:
- ✅ LLM prompts (system + user messages)
- ✅ Reasoning process (
<think>...</think>tags) - ✅ Final responses (structured output or streaming)
- ✅ Token usage (prompt + completion)
Supported models:
- DeepSeek R1 (with reasoning)
- OpenAI o1/o3 (with reasoning)
- GPT-4, Claude, Llama (no reasoning, just prompts + responses)
Manual Tracing (Optional)
For fine-grained control, use @traced decorator:
from swisper_studio_sdk import traced
# Full control over observation
@traced(
name="intent_classification",
observation_type="GENERATION",
capture_reasoning=True,
reasoning_max_length=10000
)
async def intent_classification_node(state):
return state
Observation Types
AUTO- Auto-detect based on LLM data (default, recommended)SPAN- Generic execution spanGENERATION- LLM generationEVENT- Point-in-time eventTOOL- Tool callAGENT- Agent execution
Architecture
Redis Streams (v0.4.0)
Your App (Swisper) Redis Stream SwisperStudio
│ │ │
@traced decorator │ │
│ │ │
XADD event (1-2ms) ──────────→ │ │
│ │ │
Return immediately │ │
(zero latency!) │ │
│ Consumer reads batch │
│ ←─────────────────────────┤
│ │
│ Store in PostgreSQL │
│ ──────────────────────────→
Benefits:
- 50x faster than HTTP (500ms → 10ms overhead)
- No race conditions (ordered stream delivery)
- Reliable (persistent queue, automatic retry)
- Scalable (100k+ events/sec)
How It Works
create_traced_graph()monkey-patchesadd_node()to auto-wrap functions@traceddecorator publishes events to Redis Streams (1-2ms)- SwisperStudio consumer reads from stream and stores in database
- Zero user-facing latency (fire-and-forget pattern)
Configuration
Required Settings
# In your config.py or .env
SWISPER_STUDIO_REDIS_URL: str = "redis://redis:6379"
SWISPER_STUDIO_PROJECT_ID: str = "your-project-id"
SWISPER_STUDIO_STREAM_NAME: str = "observability:events"
Optional Settings
# Reasoning capture
SWISPER_STUDIO_CAPTURE_REASONING: bool = True
SWISPER_STUDIO_REASONING_MAX_LENGTH: int = 50000 # 50 KB
# Connection verification
SWISPER_STUDIO_VERIFY_CONSUMER: bool = True # Check consumer health
Requirements
- Python 3.11+
- LangGraph 1.x. The full dependency list, with version ranges, is in pyproject.toml.
Upgrading
CHANGELOG.md lists every release and calls out the changes that need action in the agent. If LLM nodes show up as SPAN with no token data after upgrading, see the first troubleshooting entry below.
Troubleshooting
LLM nodes showing as SPAN instead of GENERATION
Symptom: LLM-calling nodes (classify_intent, global_planner, etc.) show as SPAN with no token data.
Cause: wrap_llm_adapter() was not called after initialization.
Fix:
status = await safe_initialize(...)
if status["initialized"]:
wrap_llm_adapter() # Must be called!
Verify in logs:
✅ LLMAdapterFactory wrapped for LLM telemetry capture
✅ LLM adapter wrapped for prompt capture (2 adapter(s))
Traces not appearing in SwisperStudio
- Check Redis connectivity in initialization logs
- Verify project_id matches your SwisperStudio project
- Ensure SwisperStudio backend consumer is running
Token counts showing as 0
This is normal for streaming responses where the LLM doesn't return usage data.
Use get_structured_output() for accurate token counts.
License
Proprietary, for Fintama internal use only.
Metadata
Release files for swisper-studio-sdk 0.14.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| swisper_studio_sdk-0.14.3.tar.gz | 154.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| swisper_studio_sdk-0.14.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 300.4 kB
Release files / swisper_studio_sdk-0.14.3.tar.gz
| Download URL | swisper_studio_sdk-0.14.3.tar.gz |
|---|---|
| Size | 154.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0233792694064b376cfece420afa34b67df4beab81925005c679b5267b224b44
|
|
BLAKE2b-256 checksum How to use checksums |
b2c4a6b89c95968387fc4a254dc40f5cb982ccbb9f158d1107128e337ec66de5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / swisper_studio_sdk-0.14.3-py3-none-any.whl
| Download URL | swisper_studio_sdk-0.14.3-py3-none-any.whl |
|---|---|
| Size | 146.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1f775cfcb600f4f645eeaa50f9c764009856771f9c2defbb22ec00dbb26bed7c
|
|
BLAKE2b-256 checksum How to use checksums |
51ba89169008deaa6f8fee07bd2fa1027b39833f535c1b20b87856c0f90a77b5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|