Skip to content

auto_instrument()

Automatically patches all installed LLM libraries to emit OpenInference-compatible spans. Libraries that are not installed are silently skipped.


Signature

def auto_instrument(
    tracer_provider: TracerProvider | None = None,
    suppress_errors: bool = True,
    include_libraries: list[str] | None = None,
    exclude_libraries: list[str] | None = None,
) -> dict[str, str]
interface AutoInstrumentOptions {
    tracerProvider?: TracerProvider;
    suppressErrors?: boolean;
    includeLibraries?: string[];
    excludeLibraries?: string[];
    /** Where to look for the instrumentation packages (see below). */
    resolveFrom?: string;
}

function autoInstrument(options?: AutoInstrumentOptions): Record<string, InstrumentationStatus>

The TypeScript SDK also provides an async variant:

async function autoInstrumentAsync(
    options?: AutoInstrumentOptions,
): Promise<Record<string, InstrumentationStatus>>

Where the instrumentors are looked for. The SDK resolves each @arizeai/… package from its own location first and then from the process's working directory, which covers both an ordinary install and a strict pnpm store (where the SDK's node_modules cannot see the app's). If your layout is stranger than that, pass resolveFrom — the directory of the project whose dependencies should be used.

Versions before 1.22.0

autoInstrument() returned "not_installed" for every library, including installed ones, and the agent then ran with no LLM spans at all — no model, no tokens, no cost, and nothing for the judge to score. If you are on an older version and your traces show no model calls, upgrade: nothing about your dependencies was wrong.


Parameters

Parameter Type Default Description
tracer_provider TracerProvider \| None None Custom TracerProvider to use. When None, uses the global provider set by register().
suppress_errors bool True When True, instrumentation errors are logged as warnings instead of raising exceptions.
include_libraries list[str] \| None None If provided, only instrument these libraries (allowlist). Library names must match the names in the Supported Libraries table.
exclude_libraries list[str] \| None None If provided, skip these libraries (blocklist).

All parameters are keyword-only (enforced by *).

include_libraries vs exclude_libraries

These parameters are mutually exclusive in practice. If include_libraries is set, libraries not in the list are simply not attempted (they do not appear in the results at all). If exclude_libraries is set, excluded libraries appear in results with status "excluded".


Returns

dict[str, str] -- A dictionary mapping each library name to its instrumentation status.

Status Values

Status Meaning
"success" Library was found and successfully instrumented.
"not_installed" The OpenInference instrumentor package for this library is not installed (for pydantic_ai and google_adk, the framework itself).
"already_instrumented" Library was already instrumented (e.g., by a previous call).
"error" Instrumentation was attempted but failed. Check logs for details.
"excluded" Library was skipped because it is in exclude_libraries.

Semantic conventions

By default, instrumented spans carry OpenInference attributes: LLM messages are emitted as structured, indexed keys — llm.input_messages.{i}.message.role / .content, plus tool calls under llm.output_messages.{i}.message.tool_calls.{j}.tool_call.*.

The SDK can additionally emit the same messages in the OTel GenAI latest experimental shape — gen_ai.input.messages, gen_ai.output.messages, gen_ai.system_instructions — the normalised, cross-vendor representation. This is opt-in and additive (OpenInference attributes are preserved); enable it on register():

import coalex

# via argument…
coalex.register(endpoint="https://traces.coalex.ai", api_key="your-key", genai_semconv=True)

# …or via environment variable (no code change)
#   COALEX_GENAI_SEMCONV=1

Each LLM span then also carries, for example:

{
  "gen_ai.system_instructions": [{"type": "text", "content": "You are a concise assistant."}],
  "gen_ai.input.messages":  [{"role": "user", "parts": [{"type": "text", "content": "Weather in Lisbon?"}]}],
  "gen_ai.output.messages": [{"role": "assistant", "parts": [
    {"type": "tool_call", "id": "call_1", "name": "get_weather", "arguments": {"city": "Lisbon"}}
  ]}]
}

Why not OTEL_SEMCONV_STABILITY_OPT_IN?

The standard OTel opt-in OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental is read only by native OTel instrumentations — it has no effect on OpenInference. genai_semconv derives the gen_ai.* attributes from the OpenInference messages instead, so you get the new conventions without changing instrumentation libraries.


Supported Libraries

auto_instrument() supports the following 16 frameworks: 14 through an OpenInference instrumentor, and pydantic_ai and google_adk, which write their own spans.

Library Name Instrumentor Package Description
openai openinference-instrumentation-openai OpenAI SDK
llamaindex openinference-instrumentation-llama-index LlamaIndex
langchain openinference-instrumentation-langchain LangChain
google_genai openinference-instrumentation-google-genai Google Generative AI (Gemini)
vertexai openinference-instrumentation-vertexai Google Vertex AI
bedrock openinference-instrumentation-bedrock AWS Bedrock
anthropic openinference-instrumentation-anthropic Anthropic Claude
mistralai openinference-instrumentation-mistralai Mistral AI
groq openinference-instrumentation-groq Groq
litellm openinference-instrumentation-litellm LiteLLM
dspy openinference-instrumentation-dspy DSPy
crewai openinference-instrumentation-crewai CrewAI
haystack openinference-instrumentation-haystack Haystack
instructor openinference-instrumentation-instructor Instructor
guardrails openinference-instrumentation-guardrails Guardrails
pydantic_ai none: Pydantic AI writes its own spans Pydantic AI
google_adk none: Google ADK writes its own spans Google Agent Development Kit (ADK)

pydantic_ai needs no instrumentor package and no extra: with pydantic-ai installed, auto_instrument() calls Agent.instrument_all(...), and the agent run, every model request and every tool call become spans. Their prompts, responses and tool arguments and results are exported unless you pass capture_content=False to register(). A default you set yourself with Agent.instrument_all(...) is kept and the status is "already_instrumented", and an agent with its own instrument setting or Instrumentation capability keeps that too.

mistralai, groq, litellm, dspy and crewai are not declared as extras in packages/sdk-python/pyproject.toml, so pip install "coalex[mistralai]" fails. Install only the extras that package declares.

Google ADK

google_adk needs no instrumentor package and no extra, and the SDK does not import ADK. ADK (google-adk) writes its own spans, with the model and the tokens, and auto_instrument() only sets the environment variables that make ADK write its GenAI content on them (the messages, system instructions, tool definitions and tool arguments and results). The status is "success" when google-adk is installed and "not_installed" when it is not, and nothing is set in that case. Call it before the agent runs: ADK reads the variables when it builds each run's telemetry config.

capture_content of register() Variable set Value When
True (the default, also before register()) OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT SPAN_ONLY always
True OTEL_SEMCONV_STABILITY_OPT_IN gen_ai_latest_experimental ADK older than 2.13.0 only
False ADK_CAPTURE_MESSAGE_CONTENT_IN_SPANS false always; the two variables above are not set

A variable you have set yourself is kept, whatever its value: OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=NO_CONTENT keeps ADK from writing content even with capture_content=True. Calling auto_instrument() again changes nothing. A RunConfig(telemetry=...) passed to a run overrides the environment.

With capture_content=False, register() also removes the keys ADK's own tracing writes its content under, whatever the variables say: gcp.vertex.agent.llm_request, llm_response, tool_call_args, tool_response and data. The event, invocation and session ids under the same prefix stay.

ADK older than 1.28 has no GenAI content: nothing the call sets helps, and its spans still carry the model and the tokens.


Install Extras

The Coalex SDK provides install extras to pull in the correct OpenInference instrumentor packages:

# All supported frameworks
pip install coalex[auto-instrument]

# Individual frameworks
pip install coalex[openai]
pip install coalex[langchain]
pip install coalex[llamaindex]
pip install coalex[google-genai]
pip install coalex[vertexai]
pip install coalex[bedrock]
pip install coalex[anthropic]

# Multiple frameworks
pip install coalex[openai,langchain]

Install the OpenInference instrumentor packages alongside the SDK:

# OpenAI
npm install @coalex-ai/sdk @arizeai/openinference-instrumentation-openai

# LangChain
npm install @coalex-ai/sdk @arizeai/openinference-instrumentation-langchain

# Anthropic
npm install @coalex-ai/sdk @arizeai/openinference-instrumentation-anthropic

# Multiple frameworks
npm install @coalex-ai/sdk \
    @arizeai/openinference-instrumentation-openai \
    @arizeai/openinference-instrumentation-langchain

Minimal installs for production

In production, prefer installing only the extras you need. The auto-instrument extra pulls in instrumentors for OpenAI, LangChain, LlamaIndex, Vertex AI, Bedrock, and Anthropic -- which may be more than you use.


Examples

Instrument everything available

import coalex

coalex.register(endpoint="https://traces.coalex.ai", api_key="your-key")

results = coalex.auto_instrument()
print(results)
# {
#     "openai": "success",
#     "llamaindex": "not_installed",
#     "langchain": "not_installed",
#     "vertexai": "not_installed",
#     "bedrock": "not_installed",
#     "anthropic": "success",
#     "mistralai": "not_installed",
#     "groq": "not_installed",
#     "litellm": "not_installed",
#     "dspy": "not_installed",
#     "crewai": "not_installed",
#     "haystack": "not_installed",
#     "instructor": "not_installed",
#     "guardrails": "not_installed",
# }
import { register, autoInstrument } from "@coalex-ai/sdk";

register({ endpoint: "https://traces.coalex.ai", apiKey: "your-key" });

const results = autoInstrument();
console.log(results);
// {
//     openai: "success",
//     langchain: "not_installed",
//     anthropic: "not_installed",
// }

Allowlist specific libraries

results = coalex.auto_instrument(include_libraries=["openai", "anthropic"])
# Only OpenAI and Anthropic are attempted; others are not in the results.
const results = autoInstrument({ includeLibraries: ["openai", "anthropic"] });
// Only OpenAI and Anthropic are attempted; others are not in the results.

Exclude a library

results = coalex.auto_instrument(exclude_libraries=["guardrails"])
# All libraries attempted except guardrails.
# results["guardrails"] == "excluded"
const results = autoInstrument({ excludeLibraries: ["anthropic"] });
// All libraries attempted except anthropic.
// results.anthropic === "excluded"

Custom TracerProvider

from opentelemetry.sdk.trace import TracerProvider

custom_provider = TracerProvider()
# ... configure your own processors and exporters ...

results = coalex.auto_instrument(tracer_provider=custom_provider)
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";

const customProvider = new NodeTracerProvider();
// ... configure your own processors and exporters ...

const results = autoInstrument({ tracerProvider: customProvider });

Convenience Functions

For cases where you only want to instrument a single library, the SDK provides convenience functions. Each returns True on success and False on failure.

import coalex

coalex.instrument_openai()  # -> bool
coalex.instrument_langchain()  # -> bool
coalex.instrument_llamaindex()  # -> bool
coalex.instrument_google_genai()  # -> bool
coalex.instrument_vertexai()  # -> bool
coalex.instrument_bedrock()  # -> bool
coalex.instrument_anthropic()  # -> bool

All convenience functions accept an optional tracer_provider parameter:

coalex.instrument_openai(tracer_provider=my_provider)
import { instrumentOpenAI, instrumentLangChain, instrumentAnthropic } from "@coalex-ai/sdk";

instrumentOpenAI();       // -> boolean
instrumentLangChain();    // -> boolean
instrumentAnthropic();    // -> boolean

All convenience functions accept an optional tracerProvider parameter:

instrumentOpenAI(myProvider);

Notes

  • auto_instrument() should be called after register() so the global TracerProvider is already configured. If called without register() and without a tracer_provider argument, instrumented libraries use the default no-op provider.
  • Calling auto_instrument() multiple times is safe -- already-instrumented libraries return "already_instrumented", and google_adk returns "success" again with the environment as the first call left it.
  • Python: The function is synchronous and returns immediately after patching.
  • TypeScript: Use autoInstrumentAsync() in ESM projects where require() is not available. The sync autoInstrument() variant uses require() internally and may not work in pure ESM environments.
  • TypeScript and LangChain.js: both variants capture LangChain in a CommonJS app and in an ESM app, and report langchain as success on the first call. LangChain.js versions lists the proven lines and the Node version the sync variant needs in an ESM app.
  • For custom pipeline steps that are not covered by auto-instrumentation (e.g., custom retrievers, rerankers), use the extension decorators.

API Reference

coalex.auto_instrument

Auto-instrumentation for LLM frameworks: OpenInference instrumentors, and the own tracing of Pydantic AI and ADK.

Functions:

auto_instrument

auto_instrument(
    tracer_provider: TracerProvider | None = None,
    suppress_errors: bool = True,
    include_libraries: list[str] | None = None,
    exclude_libraries: list[str] | None = None,
) -> dict[str, str]

Automatically instrument all available LLM libraries.

Only libraries actually installed are instrumented — missing libraries are silently skipped. Most go through an OpenInference instrumentor. Pydantic AI writes its own spans, so its tracing is switched on instead, with include_content following the capture_content of :func:coalex.register. Google ADK writes its own spans too, and google_adk sets the environment variables that make it write its GenAI content, again following capture_content; no instrumentor is installed for it.

Parameters:

Name Type Description Default
tracer_provider TracerProvider | None

TracerProvider to use (defaults to global provider).

None
suppress_errors bool

If True, log errors instead of raising (default: True).

True
include_libraries list[str] | None

If provided, only instrument these libraries.

None
exclude_libraries list[str] | None

If provided, skip these libraries.

None

Returns:

Type Description
dict[str, str]

Dictionary mapping library name to instrumentation status

dict[str, str]

("success", "not_installed", "already_instrumented", "error", "excluded").

Source code in coalex/auto_instrument.py
def auto_instrument(
    tracer_provider: TracerProvider | None = None,
    suppress_errors: bool = True,
    include_libraries: list[str] | None = None,
    exclude_libraries: list[str] | None = None,
) -> dict[str, str]:
    """Automatically instrument all available LLM libraries.

    Only libraries actually installed are instrumented — missing libraries
    are silently skipped. Most go through an OpenInference instrumentor. Pydantic AI writes its own
    spans, so its tracing is switched on instead, with ``include_content`` following the
    ``capture_content`` of :func:`coalex.register`. Google ADK writes its own spans too, and
    ``google_adk`` sets the environment variables that make it write its GenAI content, again
    following ``capture_content``; no instrumentor is installed for it.

    Args:
        tracer_provider: TracerProvider to use (defaults to global provider).
        suppress_errors: If True, log errors instead of raising (default: True).
        include_libraries: If provided, only instrument these libraries.
        exclude_libraries: If provided, skip these libraries.

    Returns:
        Dictionary mapping library name to instrumentation status
        ("success", "not_installed", "already_instrumented", "error", "excluded").
    """
    results: dict[str, str] = {}

    for config in INSTRUMENTORS:
        lib_name = config["name"]

        if include_libraries and lib_name not in include_libraries:
            continue
        if exclude_libraries and lib_name in exclude_libraries:
            results[lib_name] = "excluded"
            continue

        results[lib_name] = _instrument_library(
            config=config,
            tracer_provider=tracer_provider,
            suppress_errors=suppress_errors,
        )

    if not (include_libraries and "pydantic_ai" not in include_libraries):
        if exclude_libraries and "pydantic_ai" in exclude_libraries:
            results["pydantic_ai"] = "excluded"
        else:
            results["pydantic_ai"] = _enable_pydantic_ai_tracing(
                tracer_provider=tracer_provider,
                suppress_errors=suppress_errors,
            )

    if not (include_libraries and "google_adk" not in include_libraries):
        if exclude_libraries and "google_adk" in exclude_libraries:
            results["google_adk"] = "excluded"
        else:
            results["google_adk"] = _enable_google_adk_content(suppress_errors=suppress_errors)

    success_count = sum(1 for v in results.values() if v == "success")
    total_attempted = sum(1 for v in results.values() if v != "excluded")

    if success_count > 0:
        successful_libs = [k for k, v in results.items() if v == "success"]
        logger.info(
            f"Auto-instrumentation complete: {success_count}/{total_attempted} libraries instrumented "
            f"({', '.join(successful_libs)})"
        )
    else:
        logger.info(
            "Auto-instrumentation complete: no libraries were instrumented. "
            "Install OpenInference packages to enable auto-instrumentation: "
            "pip install 'coalex[auto-instrument]'"
        )

    logger.debug(f"Full instrumentation results: {results}")
    return results

instrument_openai

instrument_openai(
    tracer_provider: TracerProvider | None = None,
) -> bool

Instrument OpenAI SDK specifically.

Source code in coalex/auto_instrument.py
def instrument_openai(tracer_provider: TracerProvider | None = None) -> bool:
    """Instrument OpenAI SDK specifically."""
    return _convenience_instrument("openai", tracer_provider)

instrument_llamaindex

instrument_llamaindex(
    tracer_provider: TracerProvider | None = None,
) -> bool

Instrument LlamaIndex specifically.

Source code in coalex/auto_instrument.py
def instrument_llamaindex(tracer_provider: TracerProvider | None = None) -> bool:
    """Instrument LlamaIndex specifically."""
    return _convenience_instrument("llamaindex", tracer_provider)

instrument_langchain

instrument_langchain(
    tracer_provider: TracerProvider | None = None,
) -> bool

Instrument LangChain specifically.

Source code in coalex/auto_instrument.py
def instrument_langchain(tracer_provider: TracerProvider | None = None) -> bool:
    """Instrument LangChain specifically."""
    return _convenience_instrument("langchain", tracer_provider)

instrument_google_genai

instrument_google_genai(
    tracer_provider: TracerProvider | None = None,
) -> bool

Instrument Google GenAI (google-genai SDK) specifically.

Source code in coalex/auto_instrument.py
def instrument_google_genai(tracer_provider: TracerProvider | None = None) -> bool:
    """Instrument Google GenAI (google-genai SDK) specifically."""
    return _convenience_instrument("google_genai", tracer_provider)

instrument_vertexai

instrument_vertexai(
    tracer_provider: TracerProvider | None = None,
) -> bool

Instrument Google VertexAI specifically.

Source code in coalex/auto_instrument.py
def instrument_vertexai(tracer_provider: TracerProvider | None = None) -> bool:
    """Instrument Google VertexAI specifically."""
    return _convenience_instrument("vertexai", tracer_provider)

instrument_bedrock

instrument_bedrock(
    tracer_provider: TracerProvider | None = None,
) -> bool

Instrument AWS Bedrock specifically.

Source code in coalex/auto_instrument.py
def instrument_bedrock(tracer_provider: TracerProvider | None = None) -> bool:
    """Instrument AWS Bedrock specifically."""
    return _convenience_instrument("bedrock", tracer_provider)

instrument_anthropic

instrument_anthropic(
    tracer_provider: TracerProvider | None = None,
) -> bool

Instrument Anthropic Claude specifically.

Source code in coalex/auto_instrument.py
def instrument_anthropic(tracer_provider: TracerProvider | None = None) -> bool:
    """Instrument Anthropic Claude specifically."""
    return _convenience_instrument("anthropic", tracer_provider)