Auto-Instrumentation
Zero-config automatic tracing for LLM providers and frameworks.
Auto-Instrumentation
Auto-instrumentation patches supported LLM libraries at startup so every call is automatically captured as a trace — no manual trace() or generation() calls required.
Setup
Call Observe.init() before importing or creating any LLM provider clients. This patches supported libraries at startup so every call is automatically captured as a trace.
import { Observe } from '@browserstack/ai-sdk';
// MUST be called before importing any LLM provider SDKs
await Observe.init({
publicKey: process.env.AISDK_PUBLIC_KEY,
secretKey: process.env.AISDK_SECRET_KEY,
});
import OpenAI from 'openai';
const openai = new OpenAI();
// This call is automatically traced
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: 'Hello!' }],
});Observe.init() must be called before importing any LLM provider SDKs (OpenAI, Anthropic, etc.). If you import the provider first, auto-tracing won't work — the libraries are patched at init time.
Credentials can also be read from environment variables automatically:
AISDK_PUBLIC_KEY=pk-...
AISDK_SECRET_KEY=sk-...import { Observe } from '@browserstack/ai-sdk';
await Observe.init(); // reads keys from envCall Observe.init() before importing or creating any LLM provider clients. This starts the global OpenTelemetry tracer and auto-patches all supported libraries.
import os
from browserstack_ai_sdk import Observe
Observe.init(
public_key=os.environ["AISDK_PUBLIC_KEY"],
secret_key=os.environ["AISDK_SECRET_KEY"],
)Call sdk.observe().init() once at application startup, before any LLM clients are created. This installs ByteBuddy bytecode instrumentation at the JVM level.
import com.browserstack.aisdk.TestOps;
TestOps sdk = TestOps.fromEnv();
sdk.observe().init(); // Install ByteBuddy instrumentation — call onceinit() is idempotent — calling it multiple times is safe.
Auto-tracing requires a JDK (not JRE). ByteBuddy needs access to tools.jar or the Java instrumentation API. Running on a JRE will log a warning and disable auto-tracing gracefully.
Model Providers
Select your language and provider:
Installation
Install the SDK along with your provider's package:
npm install @browserstack/ai-sdk openaiUsage
Import the instrumentation as the very first import in your entry point, then use your provider as normal. All calls are automatically traced.
// MUST be first — before any other imports
import '@browserstack/ai-sdk/instrument';
import OpenAI from 'openai';
const openai = new OpenAI(); // uses OPENAI_API_KEY env var
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: 'What is the boiling point of water?' },
],
});
console.log(response.choices[0].message.content);
// A trace appears in the dashboard with model, messages, tokens, and responseEnvironment Variables
OPENAI_API_KEY=sk-...Installation
Install the SDK along with your provider's package and OpenTelemetry instrumentation:
pip install browserstack-ai-sdk openai opentelemetry-instrumentation-openaiUsage
Call Observe.init() before creating any LLM clients, then use your provider as normal. All calls are automatically captured as traces.
import os
from browserstack_ai_sdk import Observe
# MUST be called before importing any LLM provider SDKs
Observe.init(
public_key=os.environ["AISDK_PUBLIC_KEY"],
secret_key=os.environ["AISDK_SECRET_KEY"],
)
import openai
client = openai.OpenAI(api_key=os.environ["OPENAI_API_KEY"])
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is the speed of light?"},
],
)
print(response.choices[0].message.content)What is captured: model, messages, response content, token usage, finish reason, latency.
Environment Variables
OPENAI_API_KEY=...AWS Bedrock auto-instrumentation for Python is coming soon.
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.*;
import com.browserstack.aisdk.TestOps;
TestOps sdk = TestOps.fromEnv();
sdk.observe().init();
OpenAIClient openai = OpenAIOkHttpClient.fromEnv();
ChatCompletion response = openai.chat().completions().create(
ChatCompletionCreateParams.builder()
.model("gpt-4o")
.addMessage(ChatCompletionMessageParam.ofUser("What causes Northern Lights?"))
.build()
);
System.out.println(response.choices().get(0).message().content());
sdk.flush();Instruments ChatCompletionServiceImpl.create(). All chat completion calls are automatically traced as generations.
Vertex AI, Bedrock, Azure, and Google AI Studio auto-instrumentation for Java is coming soon.
Frameworks
Select your language to see available framework integrations:
Installation
Install the SDK along with your framework package:
No additional packages required — the model provider instrumentation handles tracing automatically.
Usage
No additional setup needed — the model provider instrumentation above handles tracing automatically.
Installation
Install the SDK along with your framework package and OpenTelemetry instrumentation:
No additional packages required — the model provider instrumentation handles tracing automatically.
Usage
No additional setup needed — the model provider instrumentation above handles tracing automatically.
No additional setup needed — the model provider instrumentation above handles tracing automatically.
Mixing Auto and Manual Tracing
You can combine auto-instrumentation with manual tracing. Create a trace and then make LLM calls — they are automatically attached to your active trace context.
import { Observe, AISDK } from '@browserstack/ai-sdk';
// MUST be called before importing any LLM provider SDKs
await Observe.init({
publicKey: process.env.AISDK_PUBLIC_KEY,
secretKey: process.env.AISDK_SECRET_KEY,
});
import OpenAI from 'openai';
const client = new AISDK();
const openai = new OpenAI();
const trace = client.trace({
name: 'my-pipeline',
input: { user: 'Alice' },
tags: ['production'],
});
// This OpenAI call is auto-traced AND linked to the trace above
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: 'Hello, Alice!' }],
});
trace.update({ output: response.choices[0].message.content });
await client.shutdown();import os
from browserstack_ai_sdk import Observe, AISDK
# MUST be called before importing any LLM provider SDKs
Observe.init(
public_key=os.environ["AISDK_PUBLIC_KEY"],
secret_key=os.environ["AISDK_SECRET_KEY"],
)
import openai
client = AISDK(
public_key=os.environ["AISDK_PUBLIC_KEY"],
secret_key=os.environ["AISDK_SECRET_KEY"],
)
openai_client = openai.OpenAI()
# Manual trace provides context and metadata
trace = client.trace(
name="my-pipeline",
user_id="alice",
tags=["production"],
)
# This call is auto-traced and linked to the trace above
response = openai_client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello, Alice!"}],
)
trace.update(output=response.choices[0].message.content)
client.flush()TestOps sdk = TestOps.fromEnv();
sdk.observe().init();
TraceManager tm = sdk.traceManager();
// Create a parent trace with context
var trace = tm.trace(TraceBody.builder()
.name("rag-pipeline")
.input(question)
.userId("user-42")
.build());
// This span records retrieval — manual
var span = trace.span(SpanBody.builder().name("retrieval").input(question).build());
List<String> docs = retrieveDocuments(question);
span.end(SpanBody.builder().output(docs).build());
// This OpenAI call is auto-traced as a generation nested under the span
String answer = openai.chat().completions().create(params)
.choices().get(0).message().content();
trace.update(TraceBody.builder().output(answer).build());
tm.flush();Setting Trace Attributes
Use Observe.setAttribute (TypeScript) / Observe.set_attribute (Python) inside an auto-traced context to attach session ID, user ID, or custom attributes to the current trace. This requires an active span — it works with Observe.init(), withTrace, or any auto-instrumented HTTP request.
Available Attributes
| Attribute | Value | Accepts | Description |
|---|---|---|---|
TraceAttribute.SESSION_ID | session.id | string | Group related traces into a session. |
TraceAttribute.USER_ID | user.id | string | Identify the user that triggered the trace. |
TraceAttribute.TRACE_INPUT | AIOPS_INTERNAL.trace.input | any | Set trace input. |
TraceAttribute.TRACE_OUTPUT | AIOPS_INTERNAL.trace.output | any | Set trace output. |
import { Observe } from '@browserstack/ai-sdk';
Observe.setAttribute(Observe.TraceAttribute.SESSION_ID, 'session-abc');
Observe.setAttribute(Observe.TraceAttribute.USER_ID, 'user-42');| Attribute | Value | Accepts | Description |
|---|---|---|---|
TraceAttribute.SESSION_ID | session.id | str | Group related traces into a session. |
TraceAttribute.USER_ID | user.id | str | Identify the user that triggered the trace. |
TraceAttribute.INPUT | AIOPS_INTERNAL.trace.input | any | Set trace input. |
TraceAttribute.OUTPUT | AIOPS_INTERNAL.trace.output | any | Set trace output. |
from browserstack_ai_sdk import Observe
Observe.set_attribute(Observe.TraceAttribute.SESSION_ID, "session-abc")
Observe.set_attribute(Observe.TraceAttribute.USER_ID, "user-xyz")Custom Attributes
You can also pass any custom key — it will appear in the trace metadata on the dashboard:
Observe.setAttribute('environment', 'production');
Observe.setAttribute('version', '2.1');
Observe.setAttribute('custom-data', { foo: 'bar' });Observe.set_attribute("environment", "production")
Observe.set_attribute("version", "2.1")