Observability
SynapseKit provides built-in observability through OpenTelemetry-compatible tracing, automatic LLM call instrumentation, and an HTML dashboard for viewing traces.
TracingMiddleware
Auto-trace all LLM calls with zero code changes:
from synapsekit import TracingMiddleware, OpenAILLM
middleware = TracingMiddleware()
llm = OpenAILLM(model="gpt-4o")
traced_llm = middleware.wrap(llm)
# All calls are automatically traced
result = await traced_llm.generate("Hello!")
# View collected spans
for span in middleware.spans:
print(f"{span.name}: {span.duration_ms:.1f}ms")
Span
Individual trace spans contain timing, metadata, and parent-child relationships:
from synapsekit import Span
span = Span(name="llm.generate", attributes={"model": "gpt-4o"})
span.start()
# ... do work ...
span.end()
print(span.duration_ms) # 245.3
print(span.attributes) # {"model": "gpt-4o"}
print(span.trace_id) # "abc123..."
print(span.span_id) # "def456..."
OTelExporter
Export traces in OpenTelemetry format for integration with Jaeger, Zipkin, Grafana Tempo, or any OTLP-compatible backend:
from synapsekit import OTelExporter, TracingMiddleware
exporter = OTelExporter(endpoint="http://localhost:4318/v1/traces")
middleware = TracingMiddleware(exporter=exporter)
llm = middleware.wrap(llm)
result = await llm.generate("Hello!")
# Traces are automatically exported to the configured backend
TracingUI
View traces in a local HTML dashboard:
from synapsekit import TracingUI, TracingMiddleware
middleware = TracingMiddleware()
# ... run some LLM calls ...
ui = TracingUI(middleware.spans)
ui.save("traces.html") # Open in browser
The dashboard shows:
- Timeline view of all spans
- Duration and latency breakdown
- Token usage and cost per call
- Parent-child span relationships
Distributed Tracing
For multi-service architectures, use DistributedTracer to trace requests across services:
from synapsekit import DistributedTracer, TraceSpan
tracer = DistributedTracer(service_name="my-service")
with tracer.start_span("handle_request") as span:
span.set_attribute("user_id", "123")
with tracer.start_span("call_llm", parent=span) as child:
result = await llm.generate("Hello!")
child.set_attribute("tokens", result.usage.total_tokens)
# Export trace context for propagation to other services
trace_context = tracer.get_trace_context()
DistributedTracer and TraceSpan support W3C Trace Context propagation for cross-service correlation.