Prysm AI — Observability SDK for LLM applications. One-line integration for OpenAI, Anthropic, and any OpenAI-compatible provider.
Project description
Prysm AI — Python SDK
Observability for LLM applications. One line of code.
Prysm wraps your existing OpenAI client and routes every request through the Prysm proxy, capturing latency, token usage, cost, errors, and full request/response payloads — with zero changes to your application logic.
Installation
pip install prysmai
Quick Start
import openai
from prysmai import monitor
# Your existing OpenAI client
client = openai.OpenAI(api_key="sk-...")
# Wrap it with Prysm — that's it
monitored = monitor(client, prysm_key="sk-prysm-...")
# Every call is now tracked
response = monitored.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Explain quantum computing"}],
)
print(response.choices[0].message.content)
Your dashboard at app.prysmai.io now shows the request with full metrics: latency, tokens, cost, model, and the complete request/response.
How It Works
The SDK creates a new OpenAI client that points at the Prysm proxy instead of the OpenAI API directly. The proxy:
- Authenticates the request using your
sk-prysm-*API key - Forwards the request to OpenAI (or any configured provider) using your project's stored credentials
- Captures the full request, response, timing, token counts, and cost
- Returns the response to your application — unchanged
Your application code stays exactly the same. The only difference is the client instance.
Your App → Prysm Proxy → OpenAI API
↓
Metrics stored
(latency, tokens,
cost, errors)
API Reference
monitor(client, prysm_key, base_url, timeout)
The primary entry point. Wraps an existing OpenAI client.
| Parameter | Type | Default | Description |
|---|---|---|---|
client |
openai.OpenAI or openai.AsyncOpenAI |
required | Your existing OpenAI client |
prysm_key |
str |
PRYSM_API_KEY env var |
Your Prysm API key (sk-prysm-...) |
base_url |
str |
https://prysmai.io/api/v1 |
Prysm proxy URL |
timeout |
float |
120.0 |
Request timeout in seconds |
Returns: A new OpenAI client of the same type (sync or async) routed through Prysm.
# Sync
monitored = monitor(openai.OpenAI(api_key="sk-..."), prysm_key="sk-prysm-...")
# Async
monitored = monitor(openai.AsyncOpenAI(api_key="sk-..."), prysm_key="sk-prysm-...")
PrysmClient(prysm_key, base_url, timeout)
Lower-level client for more control.
from prysmai import PrysmClient
prysm = PrysmClient(prysm_key="sk-prysm-...")
# Create sync client
client = prysm.openai()
# Create async client
async_client = prysm.async_openai()
prysm_context — Request Metadata
Attach metadata (user ID, session ID, custom tags) to every request for filtering and grouping in your dashboard.
from prysmai import prysm_context
# Set globally
prysm_context.set(user_id="user_123", session_id="sess_abc")
# Or use scoped context
with prysm_context(user_id="user_456", metadata={"env": "production"}):
response = monitored.chat.completions.create(...)
# This request is tagged with user_456
# Outside the block, context reverts to user_123
| Method | Description |
|---|---|
prysm_context.set(user_id, session_id, metadata) |
Set global context for all subsequent requests |
prysm_context.get() |
Get the current context object |
prysm_context.clear() |
Reset context to defaults |
prysm_context(user_id, session_id, metadata) |
Use as a context manager for scoped metadata |
Environment Variables
The SDK reads these environment variables as fallbacks:
| Variable | Description |
|---|---|
PRYSM_API_KEY |
Your Prysm API key (used if prysm_key is not passed) |
PRYSM_BASE_URL |
Custom proxy URL (used if base_url is not passed) |
export PRYSM_API_KEY="sk-prysm-your-key-here"
from prysmai import monitor
import openai
# No need to pass prysm_key — reads from env
monitored = monitor(openai.OpenAI(api_key="sk-..."))
Streaming
Streaming works exactly as you'd expect — no changes needed:
stream = monitored.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Write a haiku about AI"}],
stream=True,
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")
The proxy captures Time to First Token (TTFT), total latency, and full streamed content.
Async Support
Full async support with the same API:
import asyncio
import openai
from prysmai import monitor
async def main():
client = openai.AsyncOpenAI(api_key="sk-...")
monitored = monitor(client, prysm_key="sk-prysm-...")
response = await monitored.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Hello async!"}],
)
print(response.choices[0].message.content)
asyncio.run(main())
Self-Hosted Proxy
If you're running the Prysm proxy on your own infrastructure:
monitored = monitor(
client,
prysm_key="sk-prysm-...",
base_url="http://localhost:3000/api/v1", # Your self-hosted proxy
)
What Gets Captured
Every request through the SDK is logged with:
| Metric | Description |
|---|---|
| Model | Which model was called (gpt-4o, gpt-4o-mini, etc.) |
| Latency | Total request duration in milliseconds |
| TTFT | Time to first token (streaming requests) |
| Prompt tokens | Input token count |
| Completion tokens | Output token count |
| Cost | Calculated cost based on model pricing |
| Status | Success, error, or timeout |
| Request body | Full messages array and parameters |
| Response body | Complete model response |
| User ID | From prysm_context (if set) |
| Session ID | From prysm_context (if set) |
| Custom metadata | Any key-value pairs from prysm_context |
Error Handling
The SDK preserves OpenAI's error types. If the upstream API returns an error, you get the same exception you'd get without Prysm:
try:
response = monitored.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "test"}],
)
except openai.AuthenticationError:
print("Invalid API key")
except openai.RateLimitError:
print("Rate limited")
except openai.APIError as e:
print(f"API error: {e}")
Prysm-specific errors (invalid Prysm key, proxy unreachable) also surface as standard OpenAI exceptions so your existing error handling works unchanged.
Requirements
- Python 3.9+
openai >= 1.0.0httpx >= 0.24.0
Development
git clone https://github.com/osasisorae/prysmai-python.git
cd prysmai-python
# Install with dev dependencies
pip install -e ".[dev]"
# Run tests
pytest tests/ -v
Test Coverage
The SDK includes 41 tests covering:
- Client initialization and validation
- Environment variable fallbacks
- Sync and async client creation
monitor()function behavior- Context management (global, scoped, nested)
- Header injection via custom transports
- Full integration tests with mock HTTP server
- Error propagation (401, 500)
License
MIT — see LICENSE for details.
Built by Prysm AI — See inside your AI.
Project details
Release history Release notifications | RSS feed
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 prysmai-0.1.3.tar.gz.
File metadata
- Download URL: prysmai-0.1.3.tar.gz
- Upload date:
- Size: 10.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.0rc1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d7f0f9d1da558042894abe47647e0ef1d5b055481246f1083b03a26beaecb28d
|
|
| MD5 |
100f0669f6013a8d5e9001899b99fbbd
|
|
| BLAKE2b-256 |
f1b9c9284276b006ce63174d25d3311ba539c5de1c422610dd4c451665abfa1f
|
File details
Details for the file prysmai-0.1.3-py3-none-any.whl.
File metadata
- Download URL: prysmai-0.1.3-py3-none-any.whl
- Upload date:
- Size: 8.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.0rc1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
89ab71fbbe6bea4d8026be03aa4e2b81533c8cfe509440d11d4b67e0d1e55b77
|
|
| MD5 |
fcabd4c16a68e5d8dafdec9268f1bf80
|
|
| BLAKE2b-256 |
48cf19ab5188aad98fbd4f55b371addf89db24041e088db3077f3086f0b667c6
|