Freeplay <> OpenTelemetry

Getting started with OpenTelemetry Tracing in Freeplay

Building LLM applications with modern frameworks is great—until you need to understand what's actually happening under the hood. Traditional observability tools weren't built for the nuances of LLM interactions: token counts, model parameters, prompt templates, tool calls, and multi-step agent workflows.

That's where OpenTelemetry (OTel) comes in. By integrating Freeplay with OTel, you get purpose-built LLM observability that works with any framework or orchestration approach. Whether you're using Langgraph, building custom agents, or mixing frameworks, Freeplay captures what matters—automatically.

Why OTel + Freeplay?

  • Framework flexibility: Your team uses Langgraph. Another team built custom agents. A third is evaluating Google ADK. With OTel, one integration supports all of them.
  • LLM-native insights: We automatically capture model parameters, token counts, tool schemas, and prompt templates—the data you actually need to improve your AI applications.
  • No architectural changes: Freeplay observes your orchestration logic without becoming part of it. Your code stays clean, your flexibility stays intact.
  • Built on standards: OpenInference semantic conventions mean your instrumentation is portable and future-proof.
🚧

Freeplay focuses on LLM observability

We only record traces and spans containing meaningful LLM information per OpenInference semantic conventions—not general application telemetry.

Recommended approach:

  1. Use OpenInference instrumentation libraries when available (easiest option)
  2. Follow OpenInference semantic conventions for custom instrumentation
  3. For advanced control, use our OTel-compliant API directly

If your framework lacks an OpenInference library, you can still record data using standard OpenInference attributes like input.value, output.value, and gen_ai.request.model. See the supported attributes reference below.

Getting Started

Step 1: Install dependencies

pip install freeplay opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp

Step 2: Configure the OTel exporter

Set up Freeplay as your OTel span processing endpoint:

from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk import trace as trace_sdk
import os

freeplay_api_url = "https://app.freeplay.ai/api/v2"

exporter = OTLPSpanExporter(
    endpoint=f"{freeplay_api_url}/v0/otel/v1/traces",
    headers={
        "Authorization": f"Bearer {os.environ['FREEPLAY_API_KEY']}",
        "X-Freeplay-Project-Id": os.environ["FREEPLAY_PROJECT_ID"],
    },
)

tracer_provider = trace_sdk.TracerProvider()
tracer_provider.add_span_processor(SimpleSpanProcessor(exporter))
💡

Getting your credentials:

Step 3: Instrument your application

We recommend using OpenInference instrumentation libraries when available. For example, with Google ADK:

from google.adk.instrumentation import GoogleADKInstrumentor

GoogleADKInstrumentor().instrument(tracer_provider=tracer_provider)

That's it! Your LLM interactions are now being captured and sent to Freeplay.

Step 4: Enable debugging (optional but recommended)

During development, print spans to your console for easier debugging:

from opentelemetry.sdk.trace.export import ConsoleSpanExporter

tracer_provider.add_span_processor(
    SimpleSpanProcessor(ConsoleSpanExporter())
)

Framework examples

Langgraph/Langchain

Custom instrumentation

If you're building with a framework that doesn't have an OpenInference instrumentation library, you can manually instrument your code following OpenInference conventions:

from opentelemetry import trace

tracer = trace.get_tracer(__name__)

with tracer.start_as_current_span("llm_call") as span:
    # Set OpenInference attributes
    span.set_attribute("openinference.span.kind", "LLM")
    span.set_attribute("gen_ai.request.model", "gpt-4")
    span.set_attribute("input.value", user_query)
    
    # Make your LLM call
    response = your_llm_call(user_query)
    
    # Record output and token usage
    span.set_attribute("output.value", response)
    span.set_attribute("gen_ai.usage.input_tokens", token_count.input)
    span.set_attribute("gen_ai.usage.output_tokens", token_count.output)

Key concepts

Span types

Freeplay processes different span types based on openinference.span.kind:

  • LLM: Direct LLM API calls with prompts, completions, and token usage
  • AGENT: Higher-level agentic workflows with decision-making
  • CHAIN: Sequential operations or pipelines
  • TOOL: External tool or function calls

Set the appropriate span kind to ensure Freeplay correctly interprets your traces.

Sessions and traces

Use session and trace identifiers to organize related interactions:

# Link multiple traces to the same conversation session
span.set_attribute("session.id", session_id)

# Or use Freeplay-specific attributes (takes precedence)
span.set_attribute("freeplay.session.id", session_id)

# Name your agent for better organization
span.set_attribute("agent.name", "customer_support_agent")

This enables you to track multi-turn conversations and group related agent runs in Freeplay's observability UI.

Environment tagging

Tag traces with your deployment environment to separate production from development data:

span.set_attribute("freeplay.environment", "production")
# or "staging", "development", etc.

Supported attributes reference

OpenInference attributes

Freeplay maps OpenInference attributes to internal fields for consistent observability. These are the standard attributes you should use when instrumenting your LLM applications:

OTEL AttributeFreeplay FieldPayload TypeNotes
openinference.span.kindN/ARecordPayloadDetermines span type (LLM, AGENT, CHAIN, TOOL)
gen_ai.request.modelcompletion.model_nameRecordPayloadLLM model name
llm.model_namecompletion.model_nameRecordPayloadAlternative LLM model name
gen_ai.usage.input_tokenscompletion.token_counts.prompt_token_countRecordPayloadInput token count
llm.token_count.promptcompletion.token_counts.prompt_token_countRecordPayloadAlternative input token count
gen_ai.usage.output_tokenscompletion.token_counts.return_token_countRecordPayloadOutput token count
llm.token_count.completioncompletion.token_counts.return_token_countRecordPayloadAlternative output token count
llm.providercompletion.provider_nameRecordPayloadLLM provider (openai, anthropic, google)
gen_ai.systemcompletion.provider_nameRecordPayloadAlternative provider identifier
llm.input_messages.*completion.api_messagesRecordPayloadInput messages (flattened array format)
llm.output_messages.*completion.api_messagesRecordPayloadOutput messages (flattened array format)
llm.tools.*completion.tool_schemaRecordPayloadTool/function definitions
llm.prompt_template.variablescompletion.inputsRecordPayloadVariables applied to template (merged into inputs)
llm.invocation_parameterscompletion.llm_parametersRecordPayloadParameters used during LLM invocation (JSON)
input.valuetrace.inputTracePayloadTrace input data
output.valuetrace.outputTracePayloadTrace output data
tool.nametrace.nameTracePayloadTool span name
agent.nametrace.agent_nameTracePayloadAgent span name
session.idcompletion.session_idRecordPayloadSession identifier
session.idtrace.session_idTracePayloadSession identifier

Freeplay-specific attributes

Enhance your traces with Freeplay-specific metadata for tighter integration with Freeplay features:

OTEL AttributeFreeplay FieldPayload TypeNotes
freeplay.session.idcompletion.session_idRecordPayloadFreeplay session ID (takes precedence over session.id)
freeplay.session.idtrace.session_idTracePayloadFreeplay session ID (takes precedence over session.id)
freeplay.environmentcompletion.tagRecordPayloadEnvironment tag (e.g., "production", "staging")
freeplay.prompt_template.idcompletion.prompt_template_idRecordPayloadLink to Freeplay prompt template
freeplay.prompt_template.version.idcompletion.prompt_template_version_idRecordPayloadSpecific prompt template version
freeplay.test_run.idcompletion.test_run_idRecordPayloadAssociate with test run
freeplay.test_case.idcompletion.test_case_idRecordPayloadAssociate with specific test case
freeplay.input_variablescompletion.inputsRecordPayloadInput variables (JSON)
freeplay.metadata.*completion.custom_metadataRecordPayloadCustom metadata (flattened dict format)
freeplay.eval_results.*completion.eval_resultsRecordPayloadEvaluation results (flattened dict format)

Next steps

Once you've integrated OTel with Freeplay, you can:

  • View traces in the Freeplay observability dashboard
  • Run evaluations on your logged interactions to measure quality
  • Build datasets from production traces for systematic testing
  • Monitor performance across different model versions and configurations
  • Track costs with automatic token usage and cost calculations

Additional resources