openllmetry icon indicating copy to clipboard operation
openllmetry copied to clipboard

🐛 Bug Report: Duplicate OpenAI chat completion traces when using httpx instrumentor

Open SecretiveShell opened this issue 7 months ago • 2 comments

Which component is this bug for?

OpenAI Instrumentation

📜 Description

If you use the httpx instrumentor and the openai instrumentor you get duplicated traces recorded.

👟 Reproduction steps

Here is some minimal code to recreate the issue

# install 
# "openai>=1.76.0",
# "opentelemetry-api>=1.32.1",
# "opentelemetry-exporter-otlp>=1.32.1",
# "opentelemetry-instrumentation-httpx>=0.53b1",
# "opentelemetry-instrumentation-openai>=0.39.3",

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

from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor
from opentelemetry.instrumentation.openai import OpenAIInstrumentor

resource = Resource(attributes={"service.name": "test-service"})

provider = TracerProvider(resource=resource)
trace.set_tracer_provider(provider)

OTEL_ENDPOINT = "http://localhost:4318/v1/traces"  # Replace with your OpenTelemetry Collector endpoint

otlp_exporter = OTLPSpanExporter(endpoint=OTEL_ENDPOINT)
span_processor = BatchSpanProcessor(otlp_exporter)
provider.add_span_processor(span_processor)

HTTPXClientInstrumentor().instrument()
OpenAIInstrumentor().instrument()


from openai import AsyncOpenAI
import asyncio

OPENAI_API_KEY = "key"

async def run():
    client = AsyncOpenAI(api_key=OPENAI_API_KEY)

    tracer = trace.get_tracer(__name__)

    with tracer.start_as_current_span("test-span"):
        resp = await client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {
                    "role": "user",
                    "content": "say hello only",
                }
            ],
        )
        print(resp.choices[0].message.content)

def main():
    asyncio.run(run())

if __name__ == "__main__":
    main()

👍 Expected behavior

Ideally the openai instrumentor should suppress all traces from httpx.

👎 Actual Behavior with Screenshots

Image

Image

as you can see in the jaeger screenshot there are duplicated spans. These do not follow the typical parent child relationship you would expect from nested spans (which would also mitigate the issue)

🤖 Python Version

3.13

📃 Provide any additional context for the Bug.

No response

👀 Have you spent some time to check if this bug has been raised before?

  • [x] I checked and didn't find similar issue

Are you willing to submit PR?

Yes I am willing to submit a PR!

SecretiveShell avatar Apr 27 '25 14:04 SecretiveShell

Interesting @SecretiveShell - you suggest we should set the suppress flag within the OpenAI instrumentation?

nirga avatar Apr 28 '25 07:04 nirga

Yes, this seems ideal. The openai instrumentor is already capturing the same information at a higher level, and I am not interacting with the raw internal httpx transport inside of the openai client in my code.

SecretiveShell avatar Apr 28 '25 08:04 SecretiveShell