Skip to content

SDK Reference

Complete API reference for the Coalex SDKs:

  • Python SDK (coalex v1.9.0) -- published on PyPI
  • TypeScript SDK (@coalex-ai/sdk v1.7.0) -- published on npm

Installation

pip install coalex                    # core SDK
pip install coalex[auto-instrument]   # + all auto-instrumentation
pip install coalex[openai]            # + OpenAI only
npm install @coalex-ai/sdk            # core SDK
npm install @arizeai/openinference-instrumentation-openai  # + OpenAI auto-instrumentation

See Installation for all extras and framework-specific options.


Usage Pattern

Every Coalex integration follows the same pattern:

graph LR
    A[register] --> B[auto_instrument]
    B --> B2[declare_agent]
    B2 --> C[coalex_context]
    C --> D[evaluate]
    D --> E[resolve]

1. Register -- connect to Coalex

Call once at application startup. Configures the global TracerProvider with an OTLP exporter pointing at your Coalex proxy.

import coalex

coalex.register(
    api_key="your-api-key",
    service_name="my-agent",
)
import { register } from "@coalex-ai/sdk";

register({
    apiKey: "your-api-key",
    serviceName: "my-agent",
});

Endpoint is optional

The SDK defaults to the Azure-hosted cloud (traces.azure.coalex.ai). Only set endpoint for on-prem, GCP deployments, or local development.

2. Auto-instrument -- patch LLM frameworks

Automatically patches all installed LLM libraries (OpenAI, LangChain, LlamaIndex, etc.) to emit OpenInference spans.

results = coalex.auto_instrument()
# {"openai": "success", "langchain": "not_installed", ...}
import { autoInstrument } from "@coalex-ai/sdk";

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

3. Wrap with context -- tag every span

All spans created inside a coalex_context block inherit the agent metadata, making them queryable in the dashboard.

with coalex.coalex_context(agent_id="support-bot", request_id="req-123"):
    response = client.chat.completions.create(...)
import { coalexContext } from "@coalex-ai/sdk";

await coalexContext({ agentId: "support-bot", requestId: "req-123" }, async () => {
    const response = await client.chat.completions.create(...);
});

4. Declare agent (optional) -- register before traces

Pre-register your agent so the dashboard recognizes it before traces arrive. This step is optional but recommended for a better dashboard experience.

agent = coalex.declare_agent(agent_id="support-bot", display_name="Support Bot")
# AgentDeclaration(agent_id="support-bot", lifecycle="active", created=True)
import { declareAgent } from "@coalex-ai/sdk";

const agent = await declareAgent({ agentId: "support-bot", displayName: "Support Bot" });
// { agentId: "support-bot", lifecycle: "active", created: true }

5. Evaluate -- assess risk

Submit agent output for automated risk assessment. Low-risk outputs are auto-approved; high-risk ones are escalated for human review.

decision = coalex.evaluate(
    request_id="req-123",
    input={"question": "What is the policy?"},
    output={"answer": "The policy states..."},
    metrics={"answer": ["f1", "semantic_similarity"]},
)
import { evaluate } from "@coalex-ai/sdk";

const decision = await evaluate({
    requestId: "req-123",
    input: { question: "What is the policy?" },
    output: { answer: "The policy states..." },
    metrics: { answer: ["f1", "semantic_similarity"] },
});

6. Resolve -- human-in-the-loop

When an output is escalated, a human reviewer approves, rejects, or corrects it.

result = coalex.resolve(
    escalation_id=decision.escalation_id,
    decision="approved",
    reviewer={"name": "Dr. Smith", "email": "dr.smith@hospital.org"},
    reason="Output is clinically accurate.",
)
import { resolve } from "@coalex-ai/sdk";

const result = await resolve({
    escalationId: decision.escalationId,
    decision: "approved",
    reviewer: { name: "Dr. Smith", email: "dr.smith@hospital.org" },
    reason: "Output is clinically accurate.",
});

Public API

Core Functions

Function Description Reference
register() Configure the OTLP exporter and global TracerProvider Details
declare_agent() Pre-register an agent before traces arrive Details
coalex_context() Create a parent span with agent metadata Details
auto_instrument() Patch all installed LLM frameworks Details
get_prompt() Fetch prompt templates from Prompt Vault (Python) Details
evaluate() Submit output for risk-based evaluation Details
resolve() Submit human review for an escalation Details

Data Classes

Class Description Reference
AgentDeclaration Result of declare_agent() Details
PromptVersion Result of get_prompt() Details
EvaluationDecision Result of evaluate() Details
ResolutionResult Result of resolve() Details
MetricResult Per-field metric scores Details

Extension Decorators / Wrappers

Instrument custom pipeline steps without LlamaIndex or LangChain. See Extensions.

Python Decorator TypeScript Wrapper Span Kind Reference
@retrieval_span retrievalSpan() RETRIEVER Details
@embedding_span embeddingSpan() EMBEDDING Details
@reranker_span rerankerSpan() RERANKER Details
@tool_span toolSpan() TOOL Details
@guardrail_span guardrailSpan() GUARDRAIL Details

Full Example

import os
import coalex
from coalex.ext.retrieval import retrieval_span, Document
from openai import OpenAI

# 1. Register
coalex.register(
    api_key=os.environ["COALEX_API_KEY"],
    service_name="medical-agent",
)

# 2. Auto-instrument
coalex.auto_instrument()

# 3. Declare agent (recommended)
coalex.declare_agent(agent_id="medical-bot", display_name="Medical Bot")

# 4. Custom retrieval step
@retrieval_span(name="knowledge_base", query_arg="query")
def retrieve(query: str) -> list[Document]:
    # Your retrieval logic here
    return [Document(content="...", id="doc-1", score=0.95)]

# 5. Agent invocation
client = OpenAI()

with coalex.coalex_context(agent_id="medical-bot", request_id="req-456"):
    docs = retrieve(query="What are the side effects?")
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": f"Based on: {docs[0].content}"}],
    )
    answer = response.choices[0].message.content

    # 6. Evaluate
    decision = coalex.evaluate(
        request_id="req-456",
        input={"question": "What are the side effects?"},
        output={"answer": answer},
        metrics={"answer": ["f1", "semantic_similarity"]},
    )

    # 7. Resolve (if escalated)
    if decision.status == "escalated":
        result = coalex.resolve(
            escalation_id=decision.escalation_id,
            decision="approved",
            reviewer={"name": "Dr. Smith", "email": "dr.smith@hospital.org"},
        )
import { register, coalexContext, autoInstrument, declareAgent, evaluate, resolve } from "@coalex-ai/sdk";
import { retrievalSpan, type Document } from "@coalex-ai/sdk/ext";
import OpenAI from "openai";

// 1. Register
register({
    apiKey: process.env.COALEX_API_KEY!,
    serviceName: "medical-agent",
});

// 2. Auto-instrument
autoInstrument();

// 3. Declare agent (recommended)
await declareAgent({ agentId: "medical-bot", displayName: "Medical Bot" });

// 4. Custom retrieval step
const retrieve = retrievalSpan(
    { name: "knowledge_base" },
    async (query: string): Promise<Document[]> => {
        // Your retrieval logic here
        return [{ content: "...", id: "doc-1", score: 0.95 }];
    },
);

// 5. Agent invocation
const client = new OpenAI();

await coalexContext({ agentId: "medical-bot", requestId: "req-456" }, async () => {
    const docs = await retrieve("What are the side effects?");
    const response = await client.chat.completions.create({
        model: "gpt-4o",
        messages: [{ role: "user", content: `Based on: ${docs[0].content}` }],
    });
    const answer = response.choices[0].message.content;

    // 6. Evaluate
    const decision = await evaluate({
        requestId: "req-456",
        input: { question: "What are the side effects?" },
        output: { answer },
        metrics: { answer: ["f1", "semantic_similarity"] },
    });

    // 7. Resolve (if escalated)
    if (decision.status === "escalated") {
        const result = await resolve({
            escalationId: decision.escalationId!,
            decision: "approved",
            reviewer: { name: "Dr. Smith", email: "dr.smith@hospital.org" },
        });
    }
});

API Reference

coalex

Coalex Python SDK — AI governance observability.

Classes

AgentDeclaration dataclass

Result of a declare_agent() call.

Source code in coalex/agents.py
@dataclasses.dataclass(frozen=True)
class AgentDeclaration:
    """Result of a declare_agent() call."""

    agent_id: str
    lifecycle: str  # "declared" | "active"
    created: bool

EvaluationDecision dataclass

Result of an evaluate() call.

Source code in coalex/evaluate.py
@dataclasses.dataclass(frozen=True)
class EvaluationDecision:
    """Result of an evaluate() call."""

    status: str  # "auto_approved" | "escalated" | "rejected"
    risk_score: float  # 0.0-1.0
    escalation_id: str | None  # present when status == "escalated"

PromptVersion dataclass

A single prompt version returned by the Prompt Vault.

Source code in coalex/prompts.py
@dataclasses.dataclass(frozen=True)
class PromptVersion:
    """A single prompt version returned by the Prompt Vault."""

    id: str
    agent_id: str
    name: str
    prompt_type: str  # "system" | "guardrails"
    version: int
    content: str
    status: str  # "draft" | "staging" | "production"
    metadata: dict

ResolutionResult dataclass

Result of a resolve() call.

Source code in coalex/resolve.py
@dataclasses.dataclass(frozen=True)
class ResolutionResult:
    """Result of a resolve() call."""

    escalation_id: str
    status: str  # "approved" | "rejected" | "corrected"
    metrics: list[MetricResult]
    resolved_at: str
    resolved_by: str | None = None
    reviewer_name: str | None = None
    reviewer_email: str | None = None

Functions

declare_agent

declare_agent(
    *,
    agent_id: str,
    display_name: str | None = None,
    metadata: dict | None = None,
    _config: CoalexConfig | None = None,
    _api_key: str | None = None,
) -> AgentDeclaration

Register or declare an agent before traces arrive.

This is the building block for lazy agent registration. When COA-264's set_prompt() is implemented, it will call this internally to ensure the agent exists.

Parameters:

Name Type Description Default
agent_id str

Unique agent identifier (the merge key).

required
display_name str | None

Optional human-friendly name.

None
metadata dict | None

Optional metadata (accepted but not persisted yet).

None
_config CoalexConfig | None

Override config (testing only).

None
_api_key str | None

Override API key (testing only).

None
Source code in coalex/agents.py
def declare_agent(
    *,
    agent_id: str,
    display_name: str | None = None,
    metadata: dict | None = None,
    _config: CoalexConfig | None = None,
    _api_key: str | None = None,
) -> AgentDeclaration:
    """Register or declare an agent before traces arrive.

    This is the building block for lazy agent registration.
    When COA-264's set_prompt() is implemented, it will call
    this internally to ensure the agent exists.

    Args:
        agent_id: Unique agent identifier (the merge key).
        display_name: Optional human-friendly name.
        metadata: Optional metadata (accepted but not persisted yet).
        _config: Override config (testing only).
        _api_key: Override API key (testing only).
    """
    import coalex as _sdk

    cfg = _config or _sdk._config
    api_key = _api_key or _sdk._api_key
    if cfg is None:
        raise RuntimeError("coalex.register() must be called before declare_agent()")

    payload: dict = {"agent_id": agent_id}
    if display_name is not None:
        payload["display_name"] = display_name
    if metadata is not None:
        payload["metadata"] = metadata

    with httpx.Client(timeout=30.0) as client:
        resp = client.post(
            f"{cfg.endpoint}/api/v1/agents",
            json=payload,
            headers={"Authorization": f"Bearer {api_key}"},
        )
        resp.raise_for_status()

    data = resp.json()
    return AgentDeclaration(
        agent_id=data["agent_id"],
        lifecycle=data["lifecycle"],
        created=data["created"],
    )

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)

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_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_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_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_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_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)

coalex_context

coalex_context(
    *,
    agent_id: str,
    request_id: str | None = None,
    version: str | None = None,
) -> Generator[None, None, None]

Create a parent span with Coalex attributes for all child spans.

All spans created inside this context are descendants of a coalex.invocation span that carries the agent metadata. This makes every child span queryable by agent_id, request_id, and version in the Bronze layer.

Context vars are set before the span is created so that CoalexAttributePropagator can propagate attributes even across trace boundaries (e.g. when LangChain auto-instrumentors create new root spans with separate trace IDs).

Parameters:

Name Type Description Default
agent_id str

Identifier for the AI agent.

required
request_id str | None

Unique request/invocation ID.

None
version str | None

Agent version string.

None
Source code in coalex/context.py
@contextlib.contextmanager
def coalex_context(
    *,
    agent_id: str,
    request_id: str | None = None,
    version: str | None = None,
) -> Generator[None, None, None]:
    """Create a parent span with Coalex attributes for all child spans.

    All spans created inside this context are descendants of a
    ``coalex.invocation`` span that carries the agent metadata.
    This makes every child span queryable by agent_id, request_id,
    and version in the Bronze layer.

    Context vars are set before the span is created so that
    ``CoalexAttributePropagator`` can propagate attributes even
    across trace boundaries (e.g. when LangChain auto-instrumentors
    create new root spans with separate trace IDs).

    Args:
        agent_id: Identifier for the AI agent.
        request_id: Unique request/invocation ID.
        version: Agent version string.
    """
    token_a = _coalex_agent_id.set(agent_id)
    token_r = _coalex_request_id.set(request_id)
    token_v = _coalex_agent_version.set(version)

    try:
        tracer = trace.get_tracer("coalex")

        attrs: dict[str, str] = {"coalex.agent_id": agent_id}
        if request_id is not None:
            attrs["coalex.request_id"] = request_id
        if version is not None:
            attrs["coalex.agent_version"] = version

        with tracer.start_as_current_span("coalex.invocation", attributes=attrs) as span:
            yield
            span.set_status(Status(StatusCode.OK))
    finally:
        _coalex_agent_id.reset(token_a)
        _coalex_request_id.reset(token_r)
        _coalex_agent_version.reset(token_v)

get_prompt

get_prompt(
    *,
    agent: str,
    name: str,
    version: str = "production",
    _config: CoalexConfig | None = None,
    _api_key: str | None = None,
) -> PromptVersion

Fetch a prompt template from the Prompt Vault.

Parameters:

Name Type Description Default
agent str

Agent ID (e.g., "sales-copilot").

required
name str

Prompt name (e.g., "system").

required
version str

"production" (default), "staging", "draft", "latest", or integer.

'production'
_config CoalexConfig | None

Override config (testing only).

None
_api_key str | None

Override API key (testing only).

None
Source code in coalex/prompts.py
def get_prompt(
    *,
    agent: str,
    name: str,
    version: str = "production",
    _config: CoalexConfig | None = None,
    _api_key: str | None = None,
) -> PromptVersion:
    """Fetch a prompt template from the Prompt Vault.

    Args:
        agent: Agent ID (e.g., "sales-copilot").
        name: Prompt name (e.g., "system").
        version: "production" (default), "staging", "draft", "latest", or integer.
        _config: Override config (testing only).
        _api_key: Override API key (testing only).
    """
    import coalex as _sdk

    cfg = _config or _sdk._config
    api_key = _api_key or _sdk._api_key
    if cfg is None:
        raise RuntimeError("coalex.register() must be called before get_prompt()")

    with httpx.Client(timeout=30.0) as client:
        resp = client.get(
            f"{cfg.endpoint}/api/v2/prompts/{agent}/{name}",
            params={"version": version},
            headers={"Authorization": f"Bearer {api_key}"},
        )
        resp.raise_for_status()

    data = resp.json()
    return PromptVersion(
        id=data["id"],
        agent_id=data["agent_id"],
        name=data["prompt_type"],
        prompt_type=data["prompt_type"],
        version=data["version"],
        content=data["content"],
        status=data["status"],
        metadata=data.get("metadata", {}),
    )

register

register(
    *,
    endpoint: str = "https://traces.azure.coalex.ai",
    api_key: str = "dev",
    service_name: str = "coalex-sdk-python",
    filter_non_ai_spans: bool = False,
    suppress_export_errors: bool = True,
) -> None

Configure the Coalex OTLP exporter.

Call once at application startup. Sets the global TracerProvider with a BatchSpanProcessor exporting OTLP/HTTP to the Coalex proxy.

The SDK always sends through the proxy (via LB) so that auth validation is in the path. Never send directly to the collector.

Parameters:

Name Type Description Default
endpoint str

Coalex proxy HTTP endpoint (default: cloud).

'https://traces.azure.coalex.ai'
api_key str

API key for proxy authentication (default: "dev" for local).

'dev'
service_name str

Service name for OTLP resource (default: "coalex-sdk-python").

'coalex-sdk-python'
filter_non_ai_spans bool

If True, only export AI/LLM spans (default: False).

False
suppress_export_errors bool

If True, suppress export errors gracefully (default: True).

True
Source code in coalex/__init__.py
def register(
    *,
    endpoint: str = "https://traces.azure.coalex.ai",
    api_key: str = "dev",
    service_name: str = "coalex-sdk-python",
    filter_non_ai_spans: bool = False,
    suppress_export_errors: bool = True,
) -> None:
    """Configure the Coalex OTLP exporter.

    Call once at application startup. Sets the global TracerProvider
    with a BatchSpanProcessor exporting OTLP/HTTP to the Coalex proxy.

    The SDK always sends through the proxy (via LB) so that auth
    validation is in the path. Never send directly to the collector.

    Args:
        endpoint: Coalex proxy HTTP endpoint (default: cloud).
        api_key: API key for proxy authentication (default: "dev" for local).
        service_name: Service name for OTLP resource (default: "coalex-sdk-python").
        filter_non_ai_spans: If True, only export AI/LLM spans (default: False).
        suppress_export_errors: If True, suppress export errors gracefully (default: True).
    """
    global _config, _api_key

    from opentelemetry import trace
    from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
        OTLPSpanExporter,
    )
    from opentelemetry.sdk.resources import Resource
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor

    from coalex.filtering_exporter import FilteringSpanExporter
    from coalex.safe_exporter import SafeOTLPSpanExporter
    from coalex.span_processor import CoalexAttributePropagator

    _config = CoalexConfig(endpoint=endpoint, service_name=service_name)
    _api_key = api_key

    resource = Resource({"service.name": service_name})

    base_exporter = OTLPSpanExporter(
        endpoint=f"{_config.endpoint}/v1/traces",
        headers={"Authorization": f"Bearer {api_key}"},
    )

    # Wrap exporter chain: OTLP -> Safe -> Filtering (when enabled)
    safe_exporter = SafeOTLPSpanExporter(base_exporter, suppress_errors=suppress_export_errors)
    final_exporter = FilteringSpanExporter(safe_exporter) if filter_non_ai_spans else safe_exporter

    provider = TracerProvider(resource=resource)
    provider.add_span_processor(CoalexAttributePropagator())
    provider.add_span_processor(BatchSpanProcessor(final_exporter))
    trace.set_tracer_provider(provider)