Skip to content
Pyrula
Pre-release: the packages are not on PyPI yet, so pip install does not resolve. Build from source to try it today.

Send existing traces

Cloud accepts OpenTelemetry GenAI spans over OTLP/HTTP. Set env vars on the process that exports the spans; no pyrula install or code change is needed.

This page uses Python 3.11 to 3.15 and a POSIX shell, in the directory your app runs from.

In Cloud, open Setup for the project and environment. It shows the API origin and the key (a key is for one environment).

Set these where the app starts, with your key. Hosted Cloud’s API origin is https://api.pyrula.io; a self-hosted deployment shows its own under Setup:

Terminal window
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.pyrula.io/otel
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <PYRULA_CLOUD_API_KEY>"
export OTEL_RESOURCE_ATTRIBUTES="${OTEL_RESOURCE_ATTRIBUTES:+$OTEL_RESOURCE_ATTRIBUTES,}deployment.environment=production"
  • OTLP over HTTP only. Many SDKs default to gRPC, hence the protocol line. The exporter appends /v1/traces itself.
  • The last line appends to OTEL_RESOURCE_ATTRIBUTES so it keeps service.name, which Cloud groups runs by.
  • Replace production with your key’s environment. Without the attribute the intake uses the key’s environment; an SDK-set deployment.environment.name takes precedence.

Name the service if your instrumentation does not:

Terminal window
export OTEL_SERVICE_NAME=support-bot

The following script sends one model-call span.

Terminal window
pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http

Save this as send_trace.py:

send_trace.py
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
# TracerProvider() reads OTEL_SERVICE_NAME and OTEL_RESOURCE_ATTRIBUTES, and
# OTLPSpanExporter() reads the OTEL_EXPORTER_OTLP_* variables: nothing else to set.
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
tracer = provider.get_tracer("quickstart")
with tracer.start_as_current_span("chat gpt-6-luna") as span:
span.set_attribute("gen_ai.operation.name", "chat")
span.set_attribute("gen_ai.request.model", "gpt-6-luna")
span.set_attribute("gen_ai.usage.input_tokens", 120)
span.set_attribute("gen_ai.usage.output_tokens", 45)
provider.shutdown()

Run it in the same shell:

Terminal window
python send_trace.py

It prints nothing. In Runs for your environment, a run named chat gpt-6-luna appears under the service name with one model step of 120 input and 45 output tokens. Its id is otel- plus the trace id.

Each trace becomes one run and each service.name one telemetry runtime. Only GenAI spans are mapped: model calls become model steps (with tokens and cost), execute_tool tool steps, invoke_agent agent steps. Other spans are dropped.

An imported run has no worker behind it, so Cloud can’t replay, pause or cancel it. For that, run the app on pyrula (Connect to Pyrula Cloud).

A trace becomes a run only if it has a span that looks like a model call: a gen_ai.operation.name of chat, text_completion, generate_content or embeddings, or a model or token-usage attribute (gen_ai.request.model, gen_ai.usage.input_tokens, or the older llm.* spellings). HTTP-only traces produce nothing.

If the exporter cannot connect, check that the endpoint ends in /otel, the protocol is http/protobuf, and the key belongs to the project you are viewing.

View this page as Markdown