The ZeroEval Python SDK provides seamless integration with your Python applications through automatic instrumentation and a simple decorator-based API.
Installation
Basic Setup
import zeroeval as ze
# Option 1: ZEROEVAL_API_KEY in your environment variable file
ze.init()
# Option 2: Provide API key directly from
# https://app.zeroeval.com/settings?tab=api-keys
ze.init(api_key="YOUR_API_KEY")
Run zeroeval setup once to save your API key securely to
~/.config/zeroeval/config.json
Patterns
Decorators
The @span decorator is the easiest way to add tracing:
import zeroeval as ze
@ze.span(name="fetch_data")
def fetch_data(user_id: str):
# Function arguments are automatically captured as inputs
# Return values are automatically captured as outputs
return {"user_id": user_id, "name": "John Doe"}
@ze.span(name="process_data", attributes={"version": "1.0"})
def process_data(data: dict):
# Add custom attributes for better filtering
return f"Welcome, {data['name']}!"
Context Manager
For more control over span lifecycles:
import zeroeval as ze
def complex_workflow():
with ze.span(name="data_pipeline") as pipeline_span:
# Fetch stage
with ze.span(name="fetch_stage") as fetch_span:
data = fetch_external_data()
fetch_span.set_io(output_data=str(data))
# Process stage
with ze.span(name="process_stage") as process_span:
processed = transform_data(data)
process_span.set_io(
input_data=str(data),
output_data=str(processed)
)
# Save stage
with ze.span(name="save_stage") as save_span:
result = save_to_database(processed)
save_span.set_io(output_data=f"Saved {result} records")
Artifact Spans
When a single prompt run produces multiple judged outputs (e.g. a final decision and a customer card), use ze.artifact_span to mark each output as a named artifact. The prompt completions page surfaces the primary artifact as the row preview and lets you switch between artifacts in the detail view.
import zeroeval as ze
def resolve_ticket(ticket):
with ze.span(name="resolve-ticket"):
system_prompt = ze.prompt(name="support-copilot", content="...")
decision = run_agent(system_prompt, ticket)
with ze.artifact_span(
name="final-decision",
artifact_type="final_decision",
role="primary",
label="Final Decision",
tags={"judge_target": "support_ops_final_decision"},
) as s:
s.set_io(input_data=ticket.body, output_data=decision.json())
with ze.artifact_span(
name="customer-card",
artifact_type="customer_card",
role="secondary",
label="Customer Card",
tags={"has_customer_card": "true"},
) as card:
card.set_io(input_data=ticket.summary, output_data=decision.json())
card.add_image(base64_data=render_card(decision))
artifact_span defaults to kind="llm" and writes the completion_artifact_* attributes automatically. All other ze.span features (tags, sessions, prompt metadata inheritance) work the same way.
ze.artifact_span is available in the Python SDK only for now.
Advanced Configuration
Fine-tune the tracer behavior:
from zeroeval.observability.tracer import tracer
# Configure tracer settings
tracer.configure(
flush_interval=5.0, # Flush every 5 seconds
max_spans=200, # Buffer up to 200 spans
collect_code_details=True # Capture source code context
)
Context
Access current context information:
# Get the current span
current_span = ze.get_current_span()
# Get the current trace ID
trace_id = ze.get_current_trace()
# Get the current session ID
session_id = ze.get_current_session()
Sessions
Sessions group related spans together, making it easier to track complex workflows, user interactions, or multi-step processes.
Basic Session
Provide a session ID to associate spans with a session:
import uuid
import zeroeval as ze
session_id = str(uuid.uuid4())
@ze.span(name="process_request", session=session_id)
def process_request(data):
return transform_data(data)
Named Sessions
For better organization in the dashboard, provide both an ID and a name:
@ze.span(
name="user_interaction",
session={
"id": session_id,
"name": "Customer Support Chat - User #12345"
}
)
def handle_support_chat(user_id, message):
return generate_response(message)
Session Inheritance
Child spans automatically inherit the session from their parent:
session_info = {
"id": str(uuid.uuid4()),
"name": "Order Processing Pipeline"
}
@ze.span(name="process_order", session=session_info)
def process_order(order_id):
validate_order(order_id)
charge_payment(order_id)
fulfill_order(order_id)
@ze.span(name="validate_order")
def validate_order(order_id):
return check_inventory(order_id)
@ze.span(name="charge_payment")
def charge_payment(order_id):
return process_payment(order_id)
Context Manager Sessions
session_info = {
"id": str(uuid.uuid4()),
"name": "Data Pipeline Run"
}
with ze.span(name="etl_pipeline", session=session_info) as pipeline_span:
with ze.span(name="extract_data") as extract_span:
raw_data = fetch_from_source()
extract_span.set_io(output_data=f"Extracted {len(raw_data)} records")
with ze.span(name="transform_data") as transform_span:
clean_data = transform_records(raw_data)
transform_span.set_io(
input_data=f"{len(raw_data)} raw records",
output_data=f"{len(clean_data)} clean records"
)
Tags are key-value pairs attached to spans, traces, or sessions. They power the facet filters in the console so you can slice your telemetry by user, plan, model, tenant, or anything else.
Tag Once, Inherit Everywhere
Tags on the first span automatically flow down to all child spans:
@ze.span(
name="handle_request",
tags={
"user_id": "42",
"tenant": "acme-corp",
"plan": "enterprise"
}
)
def handle_request():
with ze.span(name="fetch_data"):
...
with ze.span(name="process", tags={"stage": "post"}):
...
Tag a Single Span
Tags provided on a specific span stay only on that span — they are not copied to siblings or parents:
@ze.span(name="top_level")
def top_level():
with ze.span(name="db_call", tags={"table": "customers", "operation": "SELECT"}):
query_database()
with ze.span(name="render"):
render_template()
Granular Tagging
Add tags at the span, trace, or session level after creation:
with ze.span(name="root_invoke", session=session_info, tags={"run": "invoke"}):
current_span = ze.get_current_span()
ze.set_tag(current_span, {"phase": "pre-run"})
current_trace = ze.get_current_trace()
ze.set_tag(current_trace, {"run_mode": "invoke"})
current_session = ze.get_current_session()
ze.set_tag(current_session, {"env": "local"})
Feedback
To attach human or programmatic feedback to completions, see Human Feedback and the Feedback SDK docs. For automated quality evaluations, see Judges.
The Python SDK includes helpful CLI commands:
# Save your API key securely
zeroeval setup
# Run scripts with automatic tracing
zeroeval run my_script.py