Observability¶
Track token usage and export execution traces across your agent systems.
from promptise import build_agent
from promptise.config import HTTPServerSpec
# Simple: turn on observability with defaults
agent = await build_agent(
servers={"tools": HTTPServerSpec(url="http://localhost:8000/mcp")},
model="openai:gpt-5-mini",
observe=True,
)
result = await agent.ainvoke({"messages": [{"role": "user", "content": "Hello"}]})
# An interactive HTML report is generated automatically.
Concepts¶
Promptise observability is plug-and-play. Set observe=True and every LLM turn, tool call, token count, latency, retry, and error is captured automatically via a LangChain callback handler. Events are routed to one or more transporters -- HTML reports, structured logs, console output, Prometheus metrics, OpenTelemetry spans, or custom webhooks.
ObservabilityConfig¶
For full control, pass an ObservabilityConfig instead of True:
from promptise import build_agent
from promptise.observability_config import ObservabilityConfig, ObserveLevel, TransporterType
config = ObservabilityConfig(
level=ObserveLevel.FULL,
session_name="production-audit",
record_prompts=True,
transporters=[
TransporterType.HTML,
TransporterType.STRUCTURED_LOG,
TransporterType.CONSOLE,
],
output_dir="./reports",
log_file="./logs/agent.jsonl",
console_live=True,
)
agent = await build_agent(
servers={"tools": HTTPServerSpec(url="http://localhost:8000/mcp")},
model="openai:gpt-5-mini",
observe=config,
)
ObserveLevel¶
Controls the detail level of captured events:
| Level | Captures |
|---|---|
ObserveLevel.OFF |
Nothing (observability disabled) |
ObserveLevel.BASIC |
Tool calls, agent I/O, errors |
ObserveLevel.STANDARD |
Everything in BASIC + every LLM turn with token usage and latency |
ObserveLevel.FULL |
Everything in STANDARD + prompt/response content, streaming tokens |
TransporterType¶
Available backends for receiving observability events:
| Transporter | Description |
|---|---|
TransporterType.HTML |
Self-contained interactive HTML report (default) |
TransporterType.JSON |
JSON file export (full session dump + NDJSON streaming) |
TransporterType.STRUCTURED_LOG |
JSON log lines for ELK, Datadog, Splunk, CloudWatch |
TransporterType.CONSOLE |
Real-time Rich console output with color-coded events |
TransporterType.PROMETHEUS |
Prometheus metrics (counters, histograms) for Grafana |
TransporterType.OTLP |
OpenTelemetry span export via OTLP gRPC |
TransporterType.WEBHOOK |
HTTP POST each event to a configurable URL |
TransporterType.CALLBACK |
Invoke a user-provided Python callable for each event |
Transporter Classes¶
Each TransporterType maps to a concrete class in promptise.observability_transporters:
from promptise.observability_transporters import (
HTMLReportTransporter,
JSONFileTransporter,
StructuredLogTransporter,
ConsoleTransporter,
PrometheusTransporter,
OTLPTransporter,
WebhookTransporter,
CallbackTransporter,
)
| Class | Constructor | Description |
|---|---|---|
HTMLReportTransporter |
(output_dir="./reports", session_name="promptise") |
Self-contained HTML report |
JSONFileTransporter |
(output_dir="./reports", session_name="promptise", stream=True) |
NDJSON streaming or full JSON dump |
StructuredLogTransporter |
(log_file=None, session_name="promptise", service_name="promptise", correlation_id=None) |
ELK/Datadog/Splunk-compatible structured logs |
ConsoleTransporter |
(live=True, verbose=False) |
Rich-powered real-time terminal output |
PrometheusTransporter |
(port=0) |
Prometheus metrics endpoint (counters, histograms) |
OTLPTransporter |
(endpoint="http://localhost:4317", service_name="promptise") |
OpenTelemetry spans export |
WebhookTransporter |
(url, headers=None, batch_size=0, max_retries=3, timeout=10.0) |
HTTP POST to external endpoint |
CallbackTransporter |
(callback) |
Invoke custom callable per event |
All implement on_event(entry), flush(), and close().
Custom transporter selection:
config = ObservabilityConfig(
level=ObserveLevel.FULL,
transporters=[TransporterType.HTML, TransporterType.PROMETHEUS],
)
agent = await build_agent(
servers=servers, model="openai:gpt-5-mini", observe=config,
)
Custom callback example:
def my_handler(entry):
print(f"[{entry.event_type}] {entry.agent_id}: {entry.description}")
transporter = CallbackTransporter(callback=my_handler)
Configuration Fields¶
| Field | Type | Default | Description |
|---|---|---|---|
level |
ObserveLevel |
STANDARD |
Detail level |
session_name |
str |
"promptise" |
Human-readable session identifier |
record_prompts |
bool |
False |
Store full prompt/response text (off by default for privacy) |
max_entries |
int |
100_000 |
Max timeline entries before eviction |
transporters |
list[TransporterType] |
[HTML] |
Active transporters |
output_dir |
str \| None |
None |
Directory for HTML and JSON output |
log_file |
str \| None |
None |
File path for STRUCTURED_LOG transporter |
console_live |
bool |
False |
Real-time console printing |
webhook_url |
str \| None |
None |
Target URL for WEBHOOK transporter |
webhook_headers |
dict[str, str] |
{} |
Custom HTTP headers for webhooks |
otlp_endpoint |
str |
"http://localhost:4317" |
gRPC endpoint for OTLP |
prometheus_port |
int |
9090 |
Port for Prometheus metrics |
on_event |
Callable \| None |
None |
User callback for CALLBACK transporter |
correlation_id |
str \| None |
None |
Ties all events to an external request/trace |
PromptiseCallbackHandler¶
PromptiseCallbackHandler is the LangChain callback that bridges LLM events into the observability collector. It is instantiated once per agent and reused across multiple ainvoke() calls.
Constructor¶
from promptise.callback_handler import PromptiseCallbackHandler
from promptise.observability_config import ObserveLevel
handler = PromptiseCallbackHandler(
collector, # ObservabilityCollector instance
agent_id="my-agent", # Optional agent identifier for timeline entries
record_prompts=False, # Store full prompt/response text (default: False)
level=ObserveLevel.STANDARD, # Detail level (default: STANDARD)
)
| Parameter | Type | Default | Description |
|---|---|---|---|
collector |
ObservabilityCollector |
required | The collector that receives timeline events |
agent_id |
str \| None |
None |
Agent identifier for the collector timeline |
record_prompts |
bool |
False |
Whether to include full prompt/response text in events |
level |
ObserveLevel |
STANDARD |
Controls detail level: BASIC (tools + errors), STANDARD (+ LLM turns), FULL (+ streaming tokens) |
Auto-Tracked Events¶
The handler automatically captures the following without any additional code:
- LLM turns: Start/end with latency timing, model name extraction
- Token counts: Prompt tokens, completion tokens, total tokens (from
LLMResultandusage_metadata) - Tool calls: Start/end with tool name, arguments, results, and latency
- Errors: LLM errors, tool errors, and chain-level errors with traceback
- Retries: Retry attempts with attempt number and triggering error
- Chain events: Top-level agent input/output with aggregate statistics
- Streaming tokens: Token-by-token accumulation at
FULLlevel
Session Totals¶
After running the agent, cumulative totals are available on the handler:
| Attribute | Type | Description |
|---|---|---|
total_prompt_tokens |
int |
Total input tokens |
total_completion_tokens |
int |
Total output tokens |
total_tokens |
int |
Total tokens (prompt + completion) |
llm_call_count |
int |
Number of LLM calls |
tool_call_count |
int |
Number of tool calls |
error_count |
int |
Number of errors |
retry_count |
int |
Number of retries |
Use handler.get_summary() to retrieve all metrics as a dict.
Post-Run Analysis¶
After running an agent with observability enabled, use the built-in reporting methods:
# Get runtime statistics
stats = agent.get_stats()
# Generate a detailed report
report = agent.generate_report()
Enterprise Configuration Example¶
A production setup with multiple transporters:
from promptise.observability_config import ObservabilityConfig, ObserveLevel, TransporterType
config = ObservabilityConfig(
level=ObserveLevel.FULL,
session_name="production-audit",
record_prompts=True,
transporters=[
TransporterType.HTML,
TransporterType.STRUCTURED_LOG,
TransporterType.CONSOLE,
TransporterType.PROMETHEUS,
],
output_dir="./observability",
log_file="./logs/events.jsonl",
console_live=True,
prometheus_port=9090,
correlation_id="req-abc-123",
)
This configuration:
- Generates an interactive HTML report in
./observability/ - Writes structured JSON log lines to
./logs/events.jsonl(compatible with ELK, Datadog, Splunk) - Prints color-coded events to the console in real time
- Exposes Prometheus metrics on port 9090
- Tags all events with the correlation ID
req-abc-123
Privacy
record_prompts is False by default. Enable it only when you need full prompt/response content in your traces. This is particularly important in production environments handling sensitive data.
What's Next?¶
- CLI Reference --
--observeflag and CLI commands - Memory -- persistent memory with vector search