Send traces to Arize Phoenix
In shortSend 10xGraph spans to Arize Phoenix over OpenTelemetry using a helper, a publisher, or API server configuration.
- 4 min read
- 9 sections
- Updated
- v1.0.0
- Markdown
Arize Phoenix is an open-source observability platform for AI applications that accepts OpenTelemetry traces. 10xGraph sends its spans (graph execution, node transitions, LLM calls, token usage, tool execution) to Phoenix over OTLP HTTP. Spans carry the GenAI semantic convention attributes, such as model and token usage. Phoenix converts these to its own format when it ingests them, so traces arrive with model and token attributes.
For Logfire or LangSmith, see Send traces to Logfire and LangSmith. The observability levels and user_id_mode options described there apply to Phoenix too. For any other OTLP backend, see Send traces to an OTLP endpoint. For request and task metrics, see Metrics with Prometheus and Grafana.
Phoenix is an observability publisher. It receives graph, node, LLM and tool spans, AI errors included. It does not receive task lifecycle events or API and worker errors: those are in metrics and logs. See Publishers, AI tracing and metrics.
Install
pip install '10xgraph[phoenix]'The phoenix extra installs the OpenTelemetry SDK and the OTLP HTTP exporter. It does not install the Phoenix SDK. 10xgraph[observability] includes Phoenix, Logfire and LangSmith.
Run Phoenix locally
docker run -p 6006:6006 -p 4317:4317 arizephoenix/phoenix:latestOpen the UI at http://localhost:6006. Without Docker, run pip install arize-phoenix && phoenix serve. A local Phoenix needs no API key, and 10xGraph sends to http://localhost:6006 by default.
Option 1: Use the helper
Call setup_phoenix() before graph.compile():
from tenxgraph.core.graph import StateGraph, Agent
from tenxgraph.runtime.publisher import setup_phoenix, ObservabilityLevel
from tenxgraph.utils import END
graph = StateGraph()
graph.add_node("MAIN", Agent(model="gpt-4o"))
graph.set_entry_point("MAIN")
graph.add_edge("MAIN", END)
setup_phoenix(graph, project="my-agent", level=ObservabilityLevel.STANDARD)
app = graph.compile()| Parameter | Type | Default | Notes |
|---|---|---|---|
graph |
StateGraph or None | None |
Graph to instrument. None configures the provider only (API server use). |
api_key |
str | PHOENIX_API_KEY |
Optional. Sent as authorization: Bearer <key>. |
project |
str | None | Falls back to PHOENIX_PROJECT, then PHOENIX_PROJECT_NAME. See Project names. |
endpoint |
str | PHOENIX_COLLECTOR_ENDPOINT |
Base URL. Defaults to http://localhost:6006. /v1/traces is appended unless present. |
level |
ObservabilityLevel | FULL |
SPANS, STANDARD or FULL. See What the level sends. |
tracer_provider |
TracerProvider | None | Attach to your own provider instead of creating one. |
user_id_mode |
str | "raw" |
"raw", "hash" or "mask". |
What the level sends
FULL (the default) records prompts, completions, tool input and output and the graph state, so message content reaches the trace backend. STANDARD drops prompts, completions and tool I/O but still records the graph state on node spans, which can contain the conversation. SPANS records timing and structure only. The examples on this page pass STANDARD explicitly; omit level to get FULL.
Option 2: Use the publisher
To assign the publisher yourself, for example next to other publishers, use PhoenixPublisher. It is an OtelPublisher subclass and is assigned the same way as LangsmithPublisher:
from tenxgraph.runtime.publisher import PhoenixPublisher, ObservabilityLevel
publisher = PhoenixPublisher(project="my-agent", level=ObservabilityLevel.STANDARD)
graph._publisher = publisher
app = graph.compile()It accepts api_key, project, endpoint, level and tracer_provider, as setup_phoenix() does.
Option 3: Configure the API server
In 10xgraph.json:
{
"observability": {
"level": "standard",
"phoenix": {
"enabled": true,
"project": "my-agent",
"endpoint": null
}
}
}level defaults to "full" here too. If the graph already has an observability publisher (for example a PhoenixPublisher passed to the graph or agent), the server reuses it, logs a warning and does not apply this block, including its level. Keep PHOENIX_API_KEY in .env, never in the config file. If the exporter package is missing, the server logs a warning and continues without it. See configuration for all keys.
Phoenix Cloud
Use your space URL as the endpoint and set the key:
export PHOENIX_COLLECTOR_ENDPOINT="https://app.phoenix.arize.com/s/<your-space>"
export PHOENIX_API_KEY="your-phoenix-api-key"Or pass them in code:
setup_phoenix(
graph,
endpoint="https://app.phoenix.arize.com/s/<your-space>",
api_key="your-phoenix-api-key",
)Project names
The project is sent as the OpenTelemetry resource attribute openinference.project.name. It applies only when 10xGraph creates the TracerProvider. If you pass your own tracer_provider, set the attribute before that provider is created:
export OTEL_RESOURCE_ATTRIBUTES="openinference.project.name=my-agent"Combine with Logfire or LangSmith
Any mix of logfire, langsmith and phoenix in the observability block shares one TracerProvider:
{
"observability": {
"logfire": { "enabled": true },
"phoenix": { "enabled": true, "project": "my-agent" }
}
}The same works in code with setup_observability(graph, {...}). With Logfire enabled, the Phoenix project is forwarded to Logfire’s provider only on logfire 5 or newer. On older versions a warning is logged and spans go to Phoenix’s default project; set OTEL_RESOURCE_ATTRIBUTES as shown above instead.
Troubleshooting
Nothing arrives in Phoenix. Check that Phoenix is reachable at the endpoint (port 6006 for the default local setup) and that setup_phoenix() or the publisher ran before graph.compile(). Spans are exported in batches, so allow a few seconds, and make sure the process exits cleanly or stays alive long enough to flush.
401 Unauthorized. The API key is missing or wrong. Set PHOENIX_API_KEY or pass api_key=. Phoenix Cloud requires a key; a local Phoenix does not.
ImportError: opentelemetry-exporter-otlp-proto-http is required. Run pip install '10xgraph[phoenix]'.