BrowserStack AI Evals
Tracing

HTTP Instrumentation

Trace LLM calls per HTTP request in Flask, FastAPI, Django, and Starlette.

HTTP Instrumentation

HTTP instrumentation automatically creates a trace for each incoming HTTP request and nests all LLM calls made within that request under it. Session IDs are extracted from standard HTTP headers automatically.

HTTP instrumentation is currently available for Python only. TypeScript and Java HTTP tracing via Spring Boot and Servlet containers is handled automatically by the auto-tracing layer — see Auto-Instrumentation.

How It Works

When Observe.init() runs, the SDK:

  1. Patches the web framework's request lifecycle with OpenTelemetry hooks.
  2. Creates an HTTP span for each incoming request.
  3. Extracts session and request IDs from headers (X-Session-ID, X-Request-ID, etc.).
  4. Nests any LLM calls made during the request under the HTTP span.

Flask

pip install browserstack-ai-sdk opentelemetry-instrumentation-flask

Option 1 — Global instrumentation via Observe.init() (recommended):

import os
from browserstack_ai_sdk import Observe
from flask import Flask, request, jsonify
import openai

Observe.init(
    public_key=os.environ["AISDK_PUBLIC_KEY"],
    secret_key=os.environ["AISDK_SECRET_KEY"],
)

app = Flask(__name__)
openai_client = openai.OpenAI(api_key=os.environ["OPENAI_API_KEY"])

@app.route("/chat", methods=["POST"])
def chat():
    user_message = request.json.get("message", "")
    response = openai_client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": user_message}],
    )
    return jsonify({"reply": response.choices[0].message.content})

if __name__ == "__main__":
    app.run(debug=True)

Option 2 — Instrument a specific Flask app instance:

from browserstack_ai_sdk.http_instrumentation import instrument_flask_app

instrument_flask_app(app)

What is captured per request: HTTP method, path, status code, session ID, all nested LLM calls.

FastAPI

pip install browserstack-ai-sdk opentelemetry-instrumentation-fastapi

Call Observe.init() before creating the FastAPI() instance:

import os
from browserstack_ai_sdk import Observe
from fastapi import FastAPI
from pydantic import BaseModel
import openai

Observe.init(
    public_key=os.environ["AISDK_PUBLIC_KEY"],
    secret_key=os.environ["AISDK_SECRET_KEY"],
)

app = FastAPI()
openai_client = openai.AsyncOpenAI(api_key=os.environ["OPENAI_API_KEY"])

class ChatRequest(BaseModel):
    message: str

@app.post("/chat")
async def chat(body: ChatRequest):
    response = await openai_client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": body.message}],
    )
    return {"reply": response.choices[0].message.content}

To instrument an existing app instance manually:

from browserstack_ai_sdk.http_instrumentation import instrument_fastapi_app

instrument_fastapi_app(app)

What is captured per request: HTTP method, path, status code, all nested LLM calls. send/receive ASGI sub-spans are excluded to reduce noise.

Django

pip install browserstack-ai-sdk opentelemetry-instrumentation-django

Initialize the SDK in your wsgi.py or asgi.py before the app starts:

# myproject/wsgi.py  (or asgi.py)
import os
from browserstack_ai_sdk import Observe

Observe.init(
    public_key=os.environ["AISDK_PUBLIC_KEY"],
    secret_key=os.environ["AISDK_SECRET_KEY"],
)

from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
application = get_wsgi_application()

To instrument Django programmatically:

from browserstack_ai_sdk.http_instrumentation import instrument_django_app

instrument_django_app()

Then use LLM clients normally inside your views:

# myapp/views.py
import openai
from django.http import JsonResponse

openai_client = openai.OpenAI()

def chat_view(request):
    message = request.GET.get("message", "")
    response = openai_client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": message}],
    )
    return JsonResponse({"reply": response.choices[0].message.content})

Starlette

pip install browserstack-ai-sdk opentelemetry-instrumentation-starlette
import os
from browserstack_ai_sdk import Observe
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
import openai

Observe.init(
    public_key=os.environ["AISDK_PUBLIC_KEY"],
    secret_key=os.environ["AISDK_SECRET_KEY"],
)

openai_client = openai.AsyncOpenAI(api_key=os.environ["OPENAI_API_KEY"])

async def chat(request):
    body = await request.json()
    response = await openai_client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": body.get("message", "")}],
    )
    return JSONResponse({"reply": response.choices[0].message.content})

app = Starlette(routes=[Route("/chat", chat, methods=["POST"])])

To instrument an existing Starlette app instance manually:

from browserstack_ai_sdk.http_instrumentation import instrument_starlette_app

instrument_starlette_app(app)

Session ID Extraction

The SDK automatically reads session IDs from these request headers (in order of priority):

HeaderDescription
X-Session-IDPrimary session identifier
Session-IDAlternate session header
X-Request-IDUsed as session ID if no session header is found
X-Correlation-IDUsed as session ID fallback

If no session ID header is present, a UUID is generated automatically for the request.