Client Migration.mdc

globs:

Views0
PublishedFeb 7, 2026

Loading actions...

5 minBeginnerpromptSingle file

Skill content

Main instructions and any bundled files for this skill.

markdown

Client Migration

The arize-phoenix package consists of a sub-package named arize-phoenix-client which has the full functionality to communicate to the phoenix server. There is a legacy client exported at the top level of the arize-phoenix package that is being deprecated. Here are common migration patterns.

Quick Reference for LLMs

Automatic Migration Rules:

  1. Replace: import phoenix as pxfrom phoenix.client import Client (or AsyncClient)
  2. Replace: px.Client()Client() (variable name: px_client)
  3. Replace: client.query_spans(...)client.spans.get_spans_dataframe(...)
  4. Replace: client.get_spans_dataframe()client.spans.get_spans_dataframe()
  5. Replace: client.upload_dataset(...)client.datasets.create_dataset(...)
  6. Replace: client.get_dataset(...)client.datasets.get_dataset(...)
  7. Replace: px.Client().log_evaluations(SpanEvaluations(...))px_client.spans.log_span_annotations_dataframe(...)
  8. Replace: px.Client().log_evaluations(DocumentEvaluations(...))px_client.spans.log_document_annotations_dataframe(...)
  9. Replace: from phoenix.experiments importfrom phoenix.client.experiments import
  10. Replace: from phoenix.trace.dsl import SpanQueryfrom phoenix.client.types.spans import SpanQuery
  11. Replace: get_spans_dataframe(query="filter_string")get_spans_dataframe(query=SpanQuery().where("filter_string"))
  12. Parameter: project_name=project_identifier=
  13. Parameter: dataset_name=name=
  14. Parameter: eval_name=annotation_name=

Pattern Matching for LLMs

Identify Legacy Patterns (RegEx-like matching):

  • import phoenix as px
  • px\.Client\(\)
  • \.query_spans\(
  • \.get_spans_dataframe\(\)
  • \.upload_dataset\(
  • \.get_dataset\(
  • \.log_evaluations\(SpanEvaluations\(
  • \.log_evaluations\(DocumentEvaluations\(
  • from phoenix\.experiments import
  • from phoenix\.trace\.dsl import SpanQuery
  • get_spans_dataframe\(query=".*"\)
  • project_name\s*=
  • dataset_name\s*=
  • eval_name\s*=

Validation Rules After Migration:

  • ✅ Must have: from phoenix.client import at top
  • ✅ Must use: px_client as variable name (preferred)
  • ✅ Must add: annotator_kind="LLM" for span evaluations
  • ✅ Must await: AsyncClient method calls with await
  • ❌ No more: import phoenix as px (unless used for other purposes)
  • ❌ No more: SpanEvaluations or DocumentEvaluations imports

Decision Tree for LLMs

Client Type Selection:

IF file extension == ".ipynb":
    USE AsyncClient
    ADD await before method calls
ELIF file extension == ".py":
    USE Client (synchronous)
    NO await needed

Evaluations Migration:

IF found "log_evaluations(SpanEvaluations(":
    MIGRATE to "spans.log_span_annotations_dataframe("
    ADD "annotator_kind='LLM'," parameter
    CHANGE "eval_name=" to "annotation_name="

IF found "log_evaluations(DocumentEvaluations(":
    MIGRATE to "spans.log_document_annotations_dataframe("
    ADD "annotator_kind='LLM'," parameter
    CHANGE "eval_name=" to "annotation_name="

Import Consolidation:

IF file contains multiple phoenix.client imports:
    CONSOLIDATE to single line: "from phoenix.client import Client, AsyncClient"

IF file uses both Client and other legacy phoenix features:
    KEEP both imports:
    - "import phoenix as px" (for legacy features like launch_app)
    - "from phoenix.client import Client" (for new client)

Common Error Patterns to Avoid

❌ Wrong Patterns:

# DON'T mix old and new imports incorrectly
import phoenix as px
from phoenix.client import Client
client = px.Client()  # Should use Client()

# DON'T forget await with AsyncClient
px_client = AsyncClient()
px_client.spans.get_spans_dataframe()  # Missing await

# DON'T forget required annotator_kind
px_client.spans.log_span_annotations_dataframe(
    dataframe=df,
    annotation_name="test"  # Missing annotator_kind="LLM"
)

Basic Client Import Patterns

Legacy Pattern

# Complete context - typical legacy file header
import phoenix as px

# Legacy client instantiation
client = px.Client()
# or
px_client = px.Client()

New Patterns

Synchronous Client (for .py files):

# Complete context - new file header
from phoenix.client import Client

# New client instantiation
px_client = Client()

Asynchronous Client (for .ipynb notebooks):

# Complete context - new notebook cell
from phoenix.client import AsyncClient

# New async client instantiation
px_client = AsyncClient()

Key Changes

  • Import path: import phoenix as pxfrom phoenix.client import Client/AsyncClient
  • Client instantiation: px.Client()Client() or AsyncClient()
  • Recommended variable name: px_client (instead of generic client)

Client Query Patterns

Legacy Pattern

import phoenix as px

# Querying spans
spans_df = px.Client().query_spans(query, project_name="my-project")

# Getting spans dataframe
spans_df = px.Client().get_spans_dataframe()

New Patterns

Synchronous:

from phoenix.client import Client

px_client = Client()
spans_df = px_client.spans.get_spans_dataframe(query=query, project_identifier="my-project")

Asynchronous:

from phoenix.client import AsyncClient

px_client = AsyncClient()
spans_df = await px_client.spans.get_spans_dataframe(query=query, project_identifier="my-project")

Key Changes

  • query_spans()spans.get_spans_dataframe()
  • get_spans_dataframe()spans.get_spans_dataframe()
  • project_nameproject_identifier
  • Resource-based API: methods now accessed via client.spans.*

Experiments Migration

Legacy Pattern

from phoenix.experiments import run_experiment, evaluate_experiment

New Pattern

from phoenix.client.experiments import run_experiment, evaluate_experiment

Key Changes

  • Import path: phoenix.experimentsphoenix.client.experiments

SpanQuery Migration

Legacy Pattern

from phoenix.trace.dsl import SpanQuery

# Old way with string query filters
spans_df = px.Client().get_spans_dataframe(query="span_kind == 'LLM'")

# Or with SpanQuery object (older import)
query = SpanQuery().where("span_kind == 'LLM'").select(input="input.value")
spans_df = px.Client().query_spans(query)

New Pattern

from phoenix.client import AsyncClient
from phoenix.client.types.spans import SpanQuery

px_client = AsyncClient()

# New way: SpanQuery object only (no string queries)
query = SpanQuery().where("span_kind == 'LLM'")
spans_df = await px_client.spans.get_spans_dataframe(query=query)

Key Changes

  • Import path: phoenix.trace.dslphoenix.client.types.spans
  • SpanQuery usage remains the same after import
  • No more string query filters: Use SpanQuery object instead of query string parameters

Datasets Migration

Legacy Pattern

import phoenix as px

dataset = px.Client().upload_dataset(
    dataframe=df,
    dataset_name="my-dataset",
    input_keys=["question"],
    output_keys=["answer"]
)

dataset = px.Client().get_dataset(name="my-dataset")

New Pattern

from phoenix.client import Client

px_client = Client()
dataset = px_client.datasets.create_dataset(
    dataframe=df,
    name="my-dataset",
    input_keys=["question"],
    output_keys=["answer"]
)

dataset = px_client.datasets.get_dataset(dataset="my-dataset")

Key Changes

  • upload_dataset()datasets.create_dataset()
  • get_dataset()datasets.get_dataset()
  • dataset_namename
  • name parameter → dataset parameter (for get_dataset)

Log Evaluation Migration

Span Evaluations (MIGRATE)

Legacy Pattern (Complete Example):

# Complete legacy file context
import phoenix as px
from phoenix.trace import SpanEvaluations
import pandas as pd

# Some evaluation dataframe
relevance_df = pd.DataFrame({"score": [0.8, 0.9], "label": ["good", "excellent"]})
hallucination_df = pd.DataFrame({"score": [0.1, 0.2], "label": ["low", "low"]})

# Legacy single evaluation
px.Client().log_evaluations(
    SpanEvaluations(
        dataframe=relevance_df,
        eval_name="Recommendation Relevance",
    ),
)

# Legacy multiple evaluations (single call)
px.Client().log_evaluations(
    SpanEvaluations(eval_name="Hallucination", dataframe=hallucination_df),
    SpanEvaluations(eval_name="QA Correctness", dataframe=qa_correctness_df),
)

New Pattern (Synchronous - for .py files):

# Complete new file context
from phoenix.client import Client
import pandas as pd

# Same evaluation dataframes
relevance_df = pd.DataFrame({"score": [0.8, 0.9], "label": ["good", "excellent"]})
hallucination_df = pd.DataFrame({"score": [0.1, 0.2], "label": ["low", "low"]})

# New single evaluation
px_client = Client()
px_client.spans.log_span_annotations_dataframe(
    dataframe=relevance_df,
    annotation_name="Recommendation Relevance",
    annotator_kind="LLM",
)

# New multiple evaluations (separate calls)
px_client.spans.log_span_annotations_dataframe(
    dataframe=hallucination_df,
    annotation_name="Hallucination",
    annotator_kind="LLM",
)
px_client.spans.log_span_annotations_dataframe(
    dataframe=qa_correctness_df,
    annotation_name="QA Correctness",
    annotator_kind="LLM",
)

New Pattern (Asynchronous - for .ipynb notebooks):

# Complete new notebook cell context
from phoenix.client import AsyncClient
import pandas as pd

# Same evaluation dataframes
relevance_df = pd.DataFrame({"score": [0.8, 0.9], "label": ["good", "excellent"]})
hallucination_df = pd.DataFrame({"score": [0.1, 0.2], "label": ["low", "low"]})

# New async single evaluation
px_client = AsyncClient()
await px_client.spans.log_span_annotations_dataframe(
    dataframe=relevance_df,
    annotation_name="Recommendation Relevance",
    annotator_kind="LLM",
)

# New async multiple evaluations (separate calls with await)
await px_client.spans.log_span_annotations_dataframe(
    dataframe=hallucination_df,
    annotation_name="Hallucination",
    annotator_kind="LLM",
)
await px_client.spans.log_span_annotations_dataframe(
    dataframe=qa_correctness_df,
    annotation_name="QA Correctness",
    annotator_kind="LLM",
)

Key Changes

  • log_evaluations(SpanEvaluations(...))annotations.log_span_annotations_dataframe(...)
  • eval_nameannotation_name
  • Added required annotator_kind parameter
  • Multiple evaluations require separate function calls
  • Import: Remove SpanEvaluations import

Document Evaluations (MIGRATE)

Legacy Pattern (Complete Example):

# Complete legacy file context
import phoenix as px
from phoenix.trace import DocumentEvaluations
import pandas as pd

# Document evaluation dataframe with required columns: span_id, document_position
document_relevance_df = pd.DataFrame({
    "span_id": ["span_1", "span_1", "span_2"],
    "document_position": [0, 1, 0],
    "score": [1, 1, 0],
    "label": ["relevant", "relevant", "irrelevant"],
    "explanation": ["it's apropos", "it's germane", "it's rubbish"]
})

# Legacy single document evaluation
px.Client().log_evaluations(
    DocumentEvaluations(
        dataframe=document_relevance_df,
        eval_name="Relevance",
    ),
)

# Legacy multiple evaluations (single call)
px.Client().log_evaluations(
    DocumentEvaluations(eval_name="Relevance", dataframe=document_relevance_df),
    DocumentEvaluations(eval_name="Accuracy", dataframe=document_accuracy_df),
)

New Pattern (Synchronous - for .py files):

# Complete new file context
from phoenix.client import Client
import pandas as pd

# Same document evaluation dataframe
document_relevance_df = pd.DataFrame({
    "span_id": ["span_1", "span_1", "span_2"],
    "document_position": [0, 1, 0],
    "score": [1, 1, 0],
    "label": ["relevant", "relevant", "irrelevant"],
    "explanation": ["it's apropos", "it's germane", "it's rubbish"]
})

# New single document evaluation
px_client = Client()
px_client.spans.log_document_annotations_dataframe(
    dataframe=document_relevance_df,
    annotation_name="Relevance",
    annotator_kind="LLM",
)

# New multiple evaluations (separate calls)
px_client.spans.log_document_annotations_dataframe(
    dataframe=document_relevance_df,
    annotation_name="Relevance",
    annotator_kind="LLM",
)
px_client.spans.log_document_annotations_dataframe(
    dataframe=document_accuracy_df,
    annotation_name="Accuracy",
    annotator_kind="LLM",
)

New Pattern (Asynchronous - for .ipynb notebooks):

# Complete new notebook cell context
from phoenix.client import AsyncClient
import pandas as pd

# Same document evaluation dataframe
document_relevance_df = pd.DataFrame({
    "span_id": ["span_1", "span_1", "span_2"],
    "document_position": [0, 1, 0],
    "score": [1, 1, 0],
    "label": ["relevant", "relevant", "irrelevant"],
    "explanation": ["it's apropos", "it's germane", "it's rubbish"]
})

# New async single document evaluation
px_client = AsyncClient()
await px_client.spans.log_document_annotations_dataframe(
    dataframe=document_relevance_df,
    annotation_name="Relevance",
    annotator_kind="LLM",
)

# New async multiple evaluations (separate calls with await)
await px_client.spans.log_document_annotations_dataframe(
    dataframe=document_relevance_df,
    annotation_name="Relevance",
    annotator_kind="LLM",
)
await px_client.spans.log_document_annotations_dataframe(
    dataframe=document_accuracy_df,
    annotation_name="Accuracy",
    annotator_kind="LLM",
)

Key Changes for Document Evaluations

  • log_evaluations(DocumentEvaluations(...))spans.log_document_annotations_dataframe(...)
  • eval_nameannotation_name
  • Added required annotator_kind parameter
  • Multiple evaluations require separate function calls
  • DataFrame must include span_id and document_position columns
  • Import: Remove DocumentEvaluations import

Complete Parameter Mapping Table for LLMs

Evaluations Parameter Changes:

Legacy ParameterNew ParameterNotes
eval_nameannotation_nameName of the evaluation
N/Aannotator_kindRequired: typically "LLM"
dataframedataframeSame parameter name

DocumentEvaluations DataFrame Requirements:

Required ColumnDescription
span_idID of the span containing the documents
document_position0-based index of document within the span
scoreOptional: numeric evaluation score
labelOptional: categorical evaluation label
explanationOptional: text explanation of the evaluation

Complete API Transformation Table:

Legacy PatternNew PatternKey Changes
px.Client().query_spans(project_name=...)px_client.spans.get_spans_dataframe(project_identifier=...)Resource path + parameter name
px.Client().get_spans_dataframe()px_client.spans.get_spans_dataframe()Resource path only
px.Client().upload_dataset(dataset_name=...)px_client.datasets.create_dataset(name=...)Resource path + parameter name
px.Client().get_dataset(name=...)px_client.datasets.get_dataset(dataset=...)Resource path + parameter name
px.Client().log_evaluations(SpanEvaluations(eval_name=...))px_client.spans.log_span_annotations_dataframe(annotation_name=..., annotator_kind="LLM")Resource path + parameters + required field
px.Client().log_evaluations(DocumentEvaluations(eval_name=...))px_client.spans.log_document_annotations_dataframe(annotation_name=..., annotator_kind="LLM")Resource path + parameters + required field
from phoenix.trace.dsl import SpanQueryfrom phoenix.client.types.spans import SpanQueryImport path only
get_spans_dataframe(query="filter_string")get_spans_dataframe(query=SpanQuery().where("filter_string"))String queries → SpanQuery objects

Import Statement Cleanup

Remove unused imports after migration:

# Remove these after migration:
from phoenix.trace import SpanEvaluations  # ❌ Remove
from phoenix.trace import DocumentEvaluations  # ❌ Remove
from phoenix.trace.dsl import SpanQuery  # ❌ Remove
import phoenix as px  # ❌ Remove if only used for Client()

# Keep these:
import phoenix as px  # ✅ Keep if used for other functionality (launch_app, etc.)

# Replace with new imports:
from phoenix.client import Client  # ✅ New client import
from phoenix.client import AsyncClient  # ✅ New async client import
from phoenix.client.types.spans import SpanQuery  # ✅ New SpanQuery import
Share: