Skip to content

Update _responses_instrumentor.py#47371

Open
howieleung wants to merge 1 commit into
mainfrom
howie/fix-span-recording
Open

Update _responses_instrumentor.py#47371
howieleung wants to merge 1 commit into
mainfrom
howie/fix-span-recording

Conversation

@howieleung
Copy link
Copy Markdown
Member

This script fails to run, and this PR is the fix.

import os
import logging

1) MUST be set before Azure SDK imports/client construction os.environ.setdefault("AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING", "true")

Tracing setup (required order)

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.sampling import ALWAYS_OFF

from azure.core.settings import settings
from azure.core.tracing.ext.opentelemetry_span import OpenTelemetrySpan from azure.ai.projects.telemetry import AIProjectInstrumentor from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential

def main() -> None:
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("azure").setLevel(logging.DEBUG)
logging.getLogger("openai").setLevel(logging.DEBUG)

endpoint = os.environ.get("FOUNDRY_PROJECT_ENDPOINT")
model = os.environ.get("FOUNDRY_MODEL_NAME")
if not endpoint:
    raise RuntimeError("FOUNDRY_PROJECT_ENDPOINT is not set")
if not model:
    raise RuntimeError("FOUNDRY_MODEL_NAME is not set")

# 2) Global tracer provider with ALWAYS_OFF -> NonRecordingSpan
trace.set_tracer_provider(TracerProvider(sampler=ALWAYS_OFF))

# 3) Use OpenTelemetry span implementation for azure-core tracing
settings.tracing_implementation = OpenTelemetrySpan

# 4) Explicitly activate AI Projects instrumentor exactly once
AIProjectInstrumentor().instrument()

credential = DefaultAzureCredential()
client = AIProjectClient(endpoint=endpoint, credential=credential)

# 5) Responses API call through OpenAI client
openai_client = client.get_openai_client()
response = openai_client.responses.create(
    model=model,
    input=[{"role": "user", "content": "hello from repro for #46544"}],
)

output_text = getattr(response, "output_text", None) or ""
print(f"response_id={response.id}")
print(f"output_text={output_text}")

if name == "main":
main()

Description

Please add an informative description that covers that changes made by the pull request and link all relevant issues.

If an SDK is being regenerated based on a new API spec, a link to the pull request containing these API spec changes should be included above.

All SDK Contribution checklist:

  • The pull request does not introduce [breaking changes]
  • CHANGELOG is updated for new features, bug fixes or other significant changes.
  • I have read the contribution guidelines.

General Guidelines and Best Practices

  • Title of the pull request is clear and informative.
  • There are a small number of commits, each of which have an informative message. This means that previously merged commits do not appear in the history of the PR. For more information on cleaning up the commits in your PR, see this page.

Testing Guidelines

  • Pull request includes test coverage for the included changes.

This script fails to run, and this PR is the fix.

import os
import logging

# 1) MUST be set before Azure SDK imports/client construction
os.environ.setdefault("AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING", "true")

# Tracing setup (required order)
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.sampling import ALWAYS_OFF

from azure.core.settings import settings
from azure.core.tracing.ext.opentelemetry_span import OpenTelemetrySpan
from azure.ai.projects.telemetry import AIProjectInstrumentor
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential


def main() -> None:
    logging.basicConfig(level=logging.DEBUG)
    logging.getLogger("azure").setLevel(logging.DEBUG)
    logging.getLogger("openai").setLevel(logging.DEBUG)

    endpoint = os.environ.get("FOUNDRY_PROJECT_ENDPOINT")
    model = os.environ.get("FOUNDRY_MODEL_NAME")
    if not endpoint:
        raise RuntimeError("FOUNDRY_PROJECT_ENDPOINT is not set")
    if not model:
        raise RuntimeError("FOUNDRY_MODEL_NAME is not set")

    # 2) Global tracer provider with ALWAYS_OFF -> NonRecordingSpan
    trace.set_tracer_provider(TracerProvider(sampler=ALWAYS_OFF))

    # 3) Use OpenTelemetry span implementation for azure-core tracing
    settings.tracing_implementation = OpenTelemetrySpan

    # 4) Explicitly activate AI Projects instrumentor exactly once
    AIProjectInstrumentor().instrument()

    credential = DefaultAzureCredential()
    client = AIProjectClient(endpoint=endpoint, credential=credential)

    # 5) Responses API call through OpenAI client
    openai_client = client.get_openai_client()
    response = openai_client.responses.create(
        model=model,
        input=[{"role": "user", "content": "hello from repro for #46544"}],
    )

    output_text = getattr(response, "output_text", None) or ""
    print(f"response_id={response.id}")
    print(f"output_text={output_text}")


if __name__ == "__main__":
    main()
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Responses API telemetry instrumentor to avoid touching span internals when OpenTelemetry is using a non-recording span, preventing crashes during attribute-based message accumulation (e.g., when ALWAYS_OFF / NonRecordingSpan is in effect).

Changes:

  • Add an is_recording() guard in _append_to_message_attribute before reading span.span_instance.attributes.
  • Early-return when the underlying OpenTelemetry span is not recording to prevent AttributeError on NonRecordingSpan.

Comment on lines +561 to +563
if not span.span_instance.is_recording():
return

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants