Skip to main content
Graphite is an event-driven Python framework for building domain-specific AI agents, published as the grafi package. It composes assistants from workflows, nodes, tools, and topics, and instruments each of those layers with OpenTelemetry. Graphite reads its tracer from the ExecutionServices bundle you hand to GrafiRuntime, so pointing it at Arize AX is a matter of giving it a tracer from an Arize AX tracer provider. The framework’s own spans then nest assistant → workflow → node → tool, and the OpenInference OpenAI instrumentor adds the LLM span underneath with prompts, token counts, and cost.
Do not use Graphite’s own setup_tracing() helper for Arize AX. It builds a plaintext gRPC exporter (insecure=True) with no request headers, so exports to Arize AX fail with StatusCode.UNAVAILABLE ... Connection reset by peer, and the resource it builds carries service.name instead of the openinference.project.name that Arize AX reads to assign a project. Build the provider with arize.otel.register instead, as below.

Prerequisites

  • Python 3.11+ (required by grafi)
  • An Arize AX account (sign up)
  • Your Arize AX Space ID and API Key
  • An OPENAI_API_KEY from the OpenAI Platform

Launch Arize AX

  1. Sign in to your Arize AX account.
  2. From Space Settings, copy your Space ID and API Key. You will set them as ARIZE_SPACE_ID and ARIZE_API_KEY below.

Install

grafi already depends on openinference-instrumentation-openai and the OpenTelemetry SDK, so only the Arize AX exporter is extra:

Configure credentials

Setup tracing

register returns the tracer provider. Instrument OpenAI with it for the LLM spans, then hand Graphite a tracer taken from it. Graphite names its span attributes after its own metadata model, so the span kind arrives as oi_span_type, the payloads as input and output, and the conversation as conversation_id. Arize AX reads the OpenInference names, so without a rename Graphite’s spans show with no span kind and no session. Graphite calls set_attribute on whatever tracer you give it, so wrapping the tracer is enough — the wrapper hands back a span proxy that renames keys on the way in, and changes nothing else:
The rename passes Graphite’s own values through rather than second-guessing them, so each layer is labeled the way Graphite labels it — the workflow as an agent span, the node as a chain span, its OpenAI tool as an LLM span. Drop the wrapper and hand tracer_provider.get_tracer("grafi") straight to Graphite if you would rather see its raw attribute names.
For EU spaces, pass endpoint=Endpoint.ARIZE_EUROPE to register (import Endpoint from arize.otel).

Run Graphite

Pass the tracer in ExecutionServices. GrafiRuntime.invoke binds those services for the duration of the invocation, so every assistant, workflow, node, and tool span lands in the same trace:

Expected output

Verify in Arize AX

  1. Open your Arize AX space and select project graphite-tracing-example.
  2. Open the newest trace. Graphite’s layers nest, with the OpenInference LLM span at the leaf:

Check from the skill, CLI, or SDK

Confirm spans are actually reaching your Arize AX project. Use whichever fits your workflow — the skill and CLI work for any framework; the SDK check is shown for each language.
Install the Arize Skills plugin and let your coding agent check for you:
Then prompt your agent:
Use the arize-trace skill to export and analyze recent traces from my project. Confirm spans are arriving, and summarize any errors or latency issues.

What Arize AX captures

  • One span per Graphite layer — assistant, workflow, node, and tool — carrying its name, type, latency_ms, and the invocation’s conversation_id, invoke_id, and assistant_request_id
  • A fully attributed LLM span from the OpenInference OpenAI instrumentor: prompts and completions, llm.model_name, token counts, and cost
  • The assistant’s input and output payloads, size-bounded by Graphite
  • The invocation’s conversation_id as the session, so a multi-turn conversation groups in the Sessions view
The span kinds, input.value / output.value, and the session come from the rename in Setup tracing. Without it Graphite’s own spans arrive under its own attribute names, so they show with no span kind and no session grouping. A rename in Graphite itself would make the wrapper unnecessary.

Troubleshooting

  • Invalid type dict for attribute 'kwargs' value on startup. Graphite copies its metadata model onto the span and one field is a dictionary, which OpenTelemetry rejects. The wrapper in Setup tracing drops that field, so the warning means the wrapper is not in the path — check that ExecutionServices is given the wrapped tracer.
  • StatusCode.UNAVAILABLE or Connection reset by peer when exporting. Something is exporting plaintext gRPC to Arize AX — usually Graphite’s setup_tracing(). Build the provider with arize.otel.register as shown in Setup tracing.
  • Traces land in a project you did not name. Only register sets openinference.project.name. If a Graphite helper created the provider instead, the resource carries service.name and Arize AX cannot map it to your project.
  • RuntimeError: No ExecutionServices bound for the current invocation. The assistant was invoked directly instead of through runtime.invoke(...). Either invoke through the runtime, or wrap the call in grafi.runtime.bind_services(...).
  • Graphite’s spans show with no span kind. ExecutionServices was given the raw tracer rather than the OpenInferenceTracer wrapper from Setup tracing.
  • Spans stop at the assistant with no LLM child. instrumentation must be imported before the assistant is constructed, so OpenAIInstrumentor is in place when the OpenAI client is created.

Resources

Graphite documentation

Graphite on GitHub

OpenInference OpenAI Instrumentor

arize-otel configuration