Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Implementation:Arize ai Phoenix Log Span Annotations

From Leeroopedia
Knowledge Sources
Domains AI Observability, Batch Processing, Span Evaluation
Last Updated 2026-02-14 00:00 GMT

Overview

Concrete tool for submitting multiple span annotations in bulk, provided by the arize-phoenix-client package, with support for both typed dictionaries and pandas DataFrames as input formats.

Description

The Spans.log_span_annotations() method accepts an iterable of SpanAnnotationData typed dictionaries and posts them to the v1/span_annotations REST endpoint in a single HTTP request. When sync=True, the query parameter ?sync=true is appended and the server processes the annotations synchronously, returning a list of InsertedSpanAnnotation objects with their assigned IDs.

The Spans.log_span_annotations_dataframe() method provides a DataFrame-oriented interface. It validates the DataFrame structure, then processes it in chunks of 100 rows. Each chunk is converted to a list of SpanAnnotationData dicts and delegated to log_span_annotations(). Global annotation_name and annotator_kind parameters can override or supplement per-row values.

Additionally, Spans.add_document_annotation() and Spans.log_document_annotations() provide the same single and batch patterns for annotating individual retrieved documents within a span (identified by span_id + document_position). These use the v1/document_annotations endpoint.

Usage

Use log_span_annotations() when:

  • You have a pre-built list of annotation dictionaries from a programmatic evaluation pipeline.
  • You need fine-grained control over the exact annotation data structure.

Use log_span_annotations_dataframe() when:

  • Working in a data-science context where annotations are computed and stored in a pandas DataFrame.
  • Importing evaluation results from a notebook or CSV file.
  • Applying a uniform annotation name or annotator kind to all rows.

Use add_document_annotation() or log_document_annotations() when:

  • Scoring individual documents within a retrieval span (e.g., relevance of each retrieved passage in a RAG pipeline).

Code Reference

Source Location

  • Repository: Phoenix
  • File: packages/phoenix-client/src/phoenix/client/resources/spans/__init__.py
  • Lines: 901-960 (log_span_annotations), 776-900 (log_span_annotations_dataframe), 961-1080 (add_document_annotation), 1082-1170 (log_document_annotations)

Signature

def log_span_annotations(
    self,
    *,
    span_annotations: Iterable[SpanAnnotationData],
    sync: bool = False,
) -> Optional[list[InsertedSpanAnnotation]]:
    ...

def log_span_annotations_dataframe(
    self,
    *,
    dataframe: "pd.DataFrame",
    annotator_kind: Optional[Literal["LLM", "CODE", "HUMAN"]] = None,
    annotation_name: Optional[str] = None,
    sync: bool = False,
) -> Optional[list[InsertedSpanAnnotation]]:
    ...

def add_document_annotation(
    self,
    *,
    span_id: str,
    document_position: int,
    annotation_name: str,
    annotator_kind: Literal["LLM", "CODE", "HUMAN"] = "HUMAN",
    label: Optional[str] = None,
    score: Optional[float] = None,
    explanation: Optional[str] = None,
    metadata: Optional[dict[str, Any]] = None,
    sync: bool = False,
) -> Optional[InsertedSpanDocumentAnnotation]:
    ...

def log_document_annotations(
    self,
    *,
    document_annotations: list[SpanDocumentAnnotationData],
    sync: bool = False,
) -> Optional[list[InsertedSpanDocumentAnnotation]]:
    ...

Import

from phoenix.client import Client

client = Client()
# Access via: client.spans.log_span_annotations(...)
# Access via: client.spans.log_span_annotations_dataframe(...)
# Access via: client.spans.add_document_annotation(...)
# Access via: client.spans.log_document_annotations(...)

I/O Contract

Inputs (log_span_annotations)

Name Type Required Description
span_annotations Iterable[SpanAnnotationData] Yes An iterable of annotation data dictionaries. Each dict must contain: span_id (str), name (str), annotator_kind (str: "HUMAN", "LLM", or "CODE"), and result (dict with optional label, score, explanation). Optional keys: metadata (dict), identifier (str). Must not be empty.
sync bool No If True, the server processes annotations synchronously and returns inserted annotation IDs. If False (default), annotations are queued asynchronously.

Inputs (log_span_annotations_dataframe)

Name Type Required Description
dataframe pd.DataFrame Yes A pandas DataFrame containing annotation data. Must include a span_id column (or use the DataFrame index). Must include either a name or annotation_name column (but not both), or the global annotation_name parameter must be provided. Must include an annotator_kind column or the global annotator_kind parameter. Optional columns: label, score, explanation, metadata, identifier.
annotator_kind Optional[Literal["LLM", "CODE", "HUMAN"]] No Global annotator kind applied to all rows. Overrides the DataFrame column if both are present.
annotation_name Optional[str] No Global annotation name applied to all rows. Overrides the DataFrame column if both are present.
sync bool No If True, returns inserted annotation IDs for all chunks. If False (default), returns None.

Outputs

Name Type Description
(log_span_annotations return) Optional[list[InsertedSpanAnnotation]] When sync=True, a list of dicts each containing the id of the inserted/updated annotation. When sync=False, None.
(log_span_annotations_dataframe return) Optional[list[InsertedSpanAnnotation]] When sync=True, a combined list of all inserted annotations across all chunks. When sync=False, None.

Usage Examples

Batch Annotate with Typed Dictionaries

from phoenix.client import Client

client = Client()

annotations = [
    {
        "span_id": "span_001",
        "name": "relevance",
        "annotator_kind": "LLM",
        "result": {
            "label": "relevant",
            "score": 0.92,
            "explanation": "Response directly answers the question.",
        },
    },
    {
        "span_id": "span_002",
        "name": "relevance",
        "annotator_kind": "LLM",
        "result": {
            "label": "irrelevant",
            "score": 0.15,
            "explanation": "Response discusses unrelated topics.",
        },
    },
]

result = client.spans.log_span_annotations(
    span_annotations=annotations,
    sync=True,
)
print(f"Inserted {len(result)} annotations")

Batch Annotate from a DataFrame

import pandas as pd
from phoenix.client import Client

client = Client()

df = pd.DataFrame({
    "span_id": ["span_001", "span_002", "span_003"],
    "label": ["good", "bad", "good"],
    "score": [0.9, 0.2, 0.85],
    "explanation": [
        "Accurate and helpful",
        "Contains factual errors",
        "Mostly correct with minor omissions",
    ],
})

client.spans.log_span_annotations_dataframe(
    dataframe=df,
    annotation_name="quality",
    annotator_kind="HUMAN",
)

Batch Annotate with Per-Row Names and Annotator Kinds

import pandas as pd
from phoenix.client import Client

client = Client()

df = pd.DataFrame({
    "span_id": ["span_001", "span_002"],
    "annotation_name": ["toxicity", "helpfulness"],
    "annotator_kind": ["CODE", "LLM"],
    "label": ["safe", "helpful"],
    "score": [0.0, 0.88],
})

inserted = client.spans.log_span_annotations_dataframe(
    dataframe=df,
    sync=True,
)
for anno in inserted:
    print(f"Annotation ID: {anno['id']}")

Annotate Individual Documents in a Retrieval Span

from phoenix.client import Client

client = Client()

# Score each retrieved document in a RAG retrieval span
for position in range(5):
    client.spans.add_document_annotation(
        span_id="retrieval_span_001",
        document_position=position,
        annotation_name="relevance",
        annotator_kind="LLM",
        score=0.9 - (position * 0.15),
        label="relevant" if position < 3 else "irrelevant",
    )

Related Pages

Implements Principle

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment