BrowserStack AI Evals
Tracing

Media & File Uploads

Attach images, PDFs, and other binary files to traces and observations in TypeScript and Python.

Media & File Uploads

The SDK lets you attach binary content — images, PDFs, audio, and other files — to traces and observations. Media is uploaded asynchronously when the trace is flushed and referenced via a unique media ID.

Creating a Media Object

import { Media } from '@browserstack/ai-sdk';

Media accepts four construction patterns:

From a file path:

import { Media } from '@browserstack/ai-sdk';

const media = new Media({
  filePath: './screenshot.png',
  contentType: 'image/png',
});

From a Buffer or Uint8Array:

import * as fs from 'fs';

const imageBuffer = fs.readFileSync('./chart.png');

const media = new Media({
  contentBytes: imageBuffer,          
  contentType: 'image/png',
});

From a Base64 data URI:

const media = new Media({
  base64DataUri: 'data:image/jpeg;base64,/9j/4AAQSkZJRgAB...',
});

From an unsigned URL:

const media = new Media({
  unsignedUrl: 'https://your-storage.example.com/screenshot.png',
});

Constructor options:

new Media(options?: {
  contentType?: MediaContentType;   // MIME type (e.g. 'image/png')
  data?: Buffer | Uint8Array;       // raw bytes
  contentBytes?: Buffer | Uint8Array; // alias for data
  base64DataUri?: string;           // data URI string
  filePath?: string;                // local file path (sync read)
  unsignedUrl?: string;             // reference a hosted URL
})
from browserstack_ai_sdk import Media

From a file path:

image = Media(
    file_path="/path/to/screenshot.png",
    content_type="image/png",
)

From bytes:

with open("/path/to/image.jpg", "rb") as f:
    image_bytes = f.read()

image = Media(
    content_bytes=image_bytes,
    content_type="image/jpeg",
)

From a Base64 data URI:

image = Media(
    base64_data_uri="data:image/png;base64,iVBORw0KGgo...",
)

Constructor parameters:

ParameterTypeDescription
file_pathstrPath to a local file
content_bytesbytesRaw binary content
base64_data_uristrBase64-encoded data URI (includes content type)
content_typestrMIME type (e.g., "image/png", "image/jpeg")

Provide exactly one of file_path, content_bytes, or base64_data_uri.

Media and file upload support for the Java SDK is coming soon. Track progress in our SDK changelog.

Supported Media Types

TypeExamples
Imagesimage/png, image/jpeg, image/gif, image/webp
Documentsapplication/pdf
Audioaudio/mpeg, audio/wav
Videovideo/mp4
Texttext/plain, text/html
Binaryapplication/octet-stream

Attaching Media to Traces

Pass Media objects anywhere an input, output, or metadata field is accepted. The SDK serializes them to reference tokens and uploads the binary content when flushing.

In a generation input:

import { AISDK, Media } from '@browserstack/ai-sdk';
import OpenAI from 'openai';
import { readFileSync } from 'node:fs';

const testOps = new AISDK({
  publicKey: process.env.AISDK_PUBLIC_KEY,
  secretKey: process.env.AISDK_SECRET_KEY,
});
const openai = new OpenAI();

const screenshot = new Media({
  filePath: './page.png',
  contentType: 'image/png',
});

const imageDataUrl = `data:image/png;base64,${readFileSync('./page.png').toString('base64')}`;

const trace = testOps.trace({ name: 'vision-analysis' });
const generation = trace.generation({
  name: 'describe-image',
  model: 'gpt-4o',
  input: [
    {
      role: 'user',
      content: [
        { type: 'text', text: 'Describe what you see in this screenshot.' },
        {
          type: 'image_url',
          image_url: { url: imageDataUrl },
        },
      ],
    },
  ],
  metadata: { screenshot }, // attach media to metadata for storage
});

const response = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [
    {
      role: 'user',
      content: [
        { type: 'text', text: 'Describe what you see in this screenshot.' },
        { type: 'image_url', image_url: { url: imageDataUrl } },
      ],
    },
  ],
});

generation.end({ output: response.choices[0].message.content });
trace.update({ output: response.choices[0].message.content });
await testOps.shutdown();

In trace metadata:

const report = new Media({
  filePath: './evaluation-report.pdf',
  contentType: 'application/pdf',
});

const trace = testOps.trace({
  name: 'document-processing',
  input: { filename: 'evaluation-report.pdf' },
  metadata: { sourceDocument: report },
});

Pass Media objects in the input or output fields of a generation:

import os
from browserstack_ai_sdk import AISDK, Media

client = AISDK(
    public_key=os.environ["AISDK_PUBLIC_KEY"],
    secret_key=os.environ["AISDK_SECRET_KEY"],
)

screenshot = Media(
    file_path="/tmp/ui_screenshot.png",
    content_type="image/png",
)

trace = client.trace(name="ui-analysis", session_id="session-abc")

generation = trace.start_generation(
    name="analyze-screenshot",
    model="gpt-4o",
    input=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Describe any UI issues in this screenshot."},
                screenshot,
            ],
        }
    ],
)

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

response = openai_client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Describe any UI issues in this screenshot."},
                {
                    "type": "image_url",
                    "image_url": {"url": f"data:image/png;base64,<base64>"},
                },
            ],
        }
    ],
)

generation.update(output=response.choices[0].message.content)
generation.end()
trace.update(output=response.choices[0].message.content)
client.flush()

Using @observe with media:

import os
from browserstack_ai_sdk import observe, AISDK, Media
import openai

client = AISDK(
    public_key=os.environ["AISDK_PUBLIC_KEY"],
    secret_key=os.environ["AISDK_SECRET_KEY"],
)

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

@observe(name="analyze-image")
def analyze_image(image_path: str) -> str:
    image = Media(file_path=image_path, content_type="image/png")

    with open(image_path, "rb") as f:
        import base64
        b64 = base64.b64encode(f.read()).decode()

    response = openai_client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": "What do you see in this image?"},
                    {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}},
                ],
            }
        ],
    )
    return response.choices[0].message.content

result = analyze_image("/tmp/photo.png")
print(result)

Media and file upload support for the Java SDK is coming soon. Track progress in our SDK changelog.

Reference String Format

Uploaded media is referenced in traces with the format:

@@@AIOPS_INTERNALMedia:type=image/png|id=<media-id>|source=<source>@@@

Parse reference strings back to their components:

const ref = '@@@AIOPS_INTERNALMedia:type=image/png|id=abc123|source=s3@@@';

const parsed = Media.parseReferenceString(ref);
// { mediaId: 'abc123', source: 's3', contentType: 'image/png' }
from browserstack_ai_sdk import Media

parsed = Media.parse_reference_string(
    "@@@AIOPS_INTERNALMedia:type=image/png|id=abc123|source=upload@@@"
)
print(parsed["media_id"])     # "abc123"
print(parsed["content_type"]) # "image/png"

Media and file upload support for the Java SDK is coming soon.