> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreweave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Reference media in your own bucket (BYOB) using agent spans

> Render images and video from your buckets (BYOB) inline with your captured conversations.

Use this technique when the media you want to see in Agent Lens already lives in your own bucket, such as:

* Frames from a video pipeline.
* Screenshots from a browser agent.
* Images a model wrote to S3.

Instead of re-uploading those bytes, you log the object's URI as a string, and Agent Lens renders the image or video inline from your bucket. Only the URI is sent to Agent Lens; the bytes stay in your bucket. Logging a 2 GB video costs the same as logging its path.

<Note>
  Reference buckets are separate from the [secure storage connector](/products/wandb/platform/hosting/data-security/secure-storage-connector), which backs Weights & Biases artifact and run storage. Reference buckets are configured per team and only need read access.
</Note>

## How it works

Reference media rendering works in three steps:

1. You store a media object in your bucket, for example `s3://[YOUR-BUCKET]/frames/000123.png`.
2. You log that URI as a string value in a span, such as a tool result.
3. When you open the conversation, Agent Lens resolves the URI to a short-lived presigned URL and renders it inline. The file extension decides how it renders. Common image formats (such as `.png`, `.jpg`, and `.gif`) and video formats (such as `.mp4`, `.mov`, and `.webm`) render inline. Other formats render as a download link.

Agent Lens only resolves URIs that point at a bucket registered for your team. Supported schemes are `s3://` and `gs://`.

## Prerequisites

* A CoreWeave Forge account and [API key](https://forge.coreweave.com/settings#apikeys).
* A cloud storage bucket you control (S3 or GCS), containing at least one object.
* Organization admin permissions. Adding a reference bucket requires the same permission as creating a team in the organization.

## Configure your bucket

Grant CoreWeave Forge read access to the bucket, and allow the browser to fetch objects with CORS.

When you view a conversation, Forge presigns a short-lived download URL for each referenced object. Grant the integration principal read access on the bucket so that presigning can succeed. This is read-only access for inline rendering.

<Tabs>
  <Tab title="AWS S3">
    1. Apply a bucket policy that grants the integration principal `s3:GetObject`. Save the following as `bucket-policy.json`, replacing `[YOUR-BUCKET]` with your bucket name:

       ```json lines theme={"system"}
       {
         "Version": "2012-10-17",
         "Id": "WandBAccess",
         "Statement": [{
           "Sid": "WandbAccess",
           "Effect": "Allow",
           "Principal": {"AWS": "arn:aws:iam::725579432336:role/WandbIntegration"},
           "Action": "s3:GetObject",
           "Resource": "arn:aws:s3:::[YOUR-BUCKET]/*"
         }]
       }
       ```

       Then apply it to your bucket:

       ```bash theme={"system"}
       aws s3api put-bucket-policy --bucket [YOUR-BUCKET] --policy file://bucket-policy.json
       ```

    2. Apply a CORS configuration that allows the Forge app to fetch objects in the browser. Save the following as `cors.json`:

       ```json lines theme={"system"}
       {
         "CORSRules": [{
           "AllowedHeaders": ["*"],
           "AllowedMethods": ["GET", "HEAD"],
           "AllowedOrigins": ["https://forge.coreweave.com"],
           "ExposeHeaders": ["ETag"],
           "MaxAgeSeconds": 3000
         }]
       }
       ```

       Then apply it to your bucket:

       ```bash theme={"system"}
       aws s3api put-bucket-cors --bucket [YOUR-BUCKET] --cors-configuration file://cors.json
       ```

    <Note>
      If an object is encrypted with a customer-managed KMS key, its key policy must also grant `arn:aws:iam::725579432336:role/WandbIntegration` `kms:Decrypt`. The bucket policy alone isn't sufficient.
    </Note>
  </Tab>

  <Tab title="Google Cloud Storage">
    1. Grant the integration service account `roles/storage.objectViewer` on the bucket. Replace `[YOUR-BUCKET]` with your bucket name:

       ```bash theme={"system"}
       gcloud storage buckets add-iam-policy-binding gs://[YOUR-BUCKET] \
         --member="serviceAccount:wandb-integration@wandb-production.iam.gserviceaccount.com" \
         --role="roles/storage.objectViewer"
       ```

    2. Apply a CORS configuration that allows the Forge app to fetch objects in the browser. Save the following as `cors.json`:

       ```json lines theme={"system"}
       [{
         "origin": ["https://forge.coreweave.com"],
         "method": ["GET", "HEAD"],
         "responseHeader": ["ETag"],
         "maxAgeSeconds": 3000
       }]
       ```

       Then apply it to your bucket:

       ```bash theme={"system"}
       gcloud storage buckets update gs://[YOUR-BUCKET] --cors-file=cors.json
       ```
  </Tab>
</Tabs>

If you configured the bucket earlier for Weave with only `https://wandb.ai` as an allowed origin, add `https://forge.coreweave.com`. Without it, the browser blocks the request, and referenced media doesn't render in Agent Lens.

## Register the reference bucket

Register the bucket in your team settings so that Agent Lens can presign and display referenced URIs:

1. In [CoreWeave Forge](https://forge.coreweave.com), select **Weights & Biases** from the product menu.
2. In the side menu, under **Teams**, select your team, and then select the **Settings** tab. The team settings page is at `https://forge.coreweave.com/wandb/[YOUR-TEAM]/settings`.
3. In the **Reference buckets** section, select **Add bucket**.
4. Select your cloud provider (**AWS** or **Google Cloud**).
5. For the bucket name, enter `[YOUR-BUCKET]` only, without the `s3://` or `gs://` prefix.
6. For the object key, enter the key of an existing object in the bucket, for example `photos/cat.png`. Enter the object key only, not a full URL. Forge reads this one object to confirm that it has read access and that CORS is configured correctly. After the bucket is verified, presigning works for any object in the bucket.
7. Optionally, select **Test connection** to validate the configuration without saving.
8. Select **Add bucket** to save.

<Warning>
  Until the bucket is registered, referenced URIs don't render.
</Warning>

## Log a media reference using agent spans

Return a bucket URI as an agent tool result and CoreWeave Agent Lens renders it inline in the **Conversations** tab, on the tool call that produced it. The following example opens a conversation, a turn, and a tool span, and sets a bucket URI as the tool's result. Replace `[YOUR-TEAM]`, `[YOUR-PROJECT]`, and `[YOUR-BUCKET]` with your own values.

<Tabs>
  <Tab title="Python">
    ```python lines theme={"system"}
    from coreweave.forge.agentlens import tracing

    tracing.init("[YOUR-TEAM]/[YOUR-PROJECT]")

    # A tool that returns an object that already lives in your bucket.
    def get_frame(frame_id: int) -> str:
        return f"s3://[YOUR-BUCKET]/frames/{frame_id:06d}.png"

    with tracing.start_conversation(agent_name="frame-labeler") as conversation:
        with tracing.start_turn(user_message="Show me frame 123", model="gpt-4o-mini"):
            with tracing.start_tool(
                name="get_frame",
                arguments='{"frame_id": 123}',
                tool_call_id="call_1",
            ) as tool:
                # The result is a bucket URI string; it renders inline in the Conversations tab.
                tool.result = get_frame(123)

    tracing.shutdown()
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript lines theme={"system"}
    import { tracing } from '@coreweave/forge-sdk/agentlens';

    await tracing.init('[YOUR-TEAM]/[YOUR-PROJECT]');

    // A tool that returns an object that already lives in your bucket.
    function getFrame(frameId: number): string {
      return `s3://[YOUR-BUCKET]/frames/${String(frameId).padStart(6, '0')}.png`;
    }

    await tracing.runIsolated(async () => {
      const conversation = tracing.startConversation({ agentName: 'frame-labeler' });
      const turn = tracing.startTurn({ userMessage: 'Show me frame 123', model: 'gpt-4o-mini' });
      const tool = tracing.startTool({
        name: 'getFrame',
        args: JSON.stringify({ frameId: 123 }),
        toolCallId: 'call_1',
      });
      // The result is a bucket URI string; it renders inline in the Conversations tab.
      tool.end({ result: getFrame(123) });
      turn.end();
      conversation.end();
    });

    await tracing.shutdown();
    ```
  </Tab>
</Tabs>

<Note>
  This example uses Agent Lens agent spans. For the full multi-turn setup, including LLM calls and a complete agent loop, see the [custom agents quickstart](/products/agent-lens/get-started/custom-agents).
</Note>

The Agent Lens SDK doesn't upload media into Agent Lens storage. It records the URI you return and Agent Lens resolves it against your registered bucket when someone views the conversation. Prefer URIs over inline bytes for large media, because inline content counts toward the size limit of each export request.

## View the reference in Agent Lens

Open your project in Agent Lens and select the conversation on the **Conversations** tab. The referenced image or video renders inline in the **Thread** tab, on the tool call that returned the URI. If Agent Lens can't resolve a URI, for example because the object is missing or the bucket isn't registered, it shows the plain URI string.
