How to Display Images in Langfuse Traces Output

When you’re tracing AI operations with Langfuse, you often want to include images in your outputs and have them displayed in the Langfuse interface.

Here’s our final result:
Render Output Images in langfuse Traces.gif

The Problem

You have an image (maybe an annotated screenshot, a chart, or processed data visualization) that you want to show in your Langfuse trace output.

If you add it as base64 it will show as an attachment which you then need to click to see. And that opens in a separate tab.

Pasted image 20250627172209.png

We don’t want that.

The Solution

Upload your image to external storage (like Backblaze B2, AWS S3, or any cloud storage), get the public URL, then use markdown format as your Langfuse output.

# Simple markdown string as output
output = f"![Image Description]({public_url})"
Python

Step-by-Step Implementation

1. Set Up Your Storage

I’m using Backblaze B2. Check out this tutorial to set it up.

import os
import b2sdk.v2 as b2
from dotenv import load_dotenv
from uuid import uuid4
load_dotenv()
B2_KEY_ID = os.getenv("B2_KEY_ID")
B2_APP_KEY = os.getenv("B2_APPLICATION_KEY")
BUCKET_NAME = "your-bucket-name"
Python

2. Create Upload Function

# Global variables for lazy initialization
_b2_api = None
_b2_bucket = None
def upload_image_to_b2(image_bytes: bytes, filename: str) -> str:
    """Upload image bytes and return public URL"""
    global _b2_api, _b2_bucket
    
    # Initialize B2 API once
    if _b2_api is None:
        info = b2.InMemoryAccountInfo()
        _b2_api = b2.B2Api(info)
        _b2_api.authorize_account("production", B2_KEY_ID, B2_APP_KEY)
        _b2_bucket = _b2_api.get_bucket_by_name(BUCKET_NAME)
    
    # Upload and get public URL
    _b2_bucket.upload_bytes(image_bytes, filename)
    return _b2_api.get_download_url_for_file_name(BUCKET_NAME, filename)
Python

3. Use in Langfuse Traces

from langfuse.decorators import langfuse_context, observe
import requests
@observe()
def process_image():
    # Download a demo image (or use your existing image)
    response = requests.get("https://picsum.photos/400/300")
    image_bytes = response.content
    
    # Upload to storage with unique filename
    filename = f"demo_image_{uuid4().hex[:8]}.jpg"
    image_url = upload_image_to_b2(image_bytes, filename)
    
    # Create markdown output
    markdown_output = f"![Processed Image]({image_url})"
    
    # Force Langfuse to show this as output (NOT JSON)
    langfuse_context.update_current_observation(output=markdown_output)
    
    return markdown_output
Python

Key Points

1. Output Must Be Non-JSON

Don’t do this:

# WRONG - JSON format won't render image
output = {"image": f"![Image]({url})"}
Python

Do this:

# RIGHT - Direct markdown string
output = f"![Image Description]({url})"
Python

2. Force the Output

Use update_current_observation() to set exactly what you want to display:

3. Make URLs Public

Ensure your storage bucket/files are publicly accessible, or Langfuse won’t be able to display them.

Complete Working Example

import os
import b2sdk.v2 as b2
import requests
from uuid import uuid4
from langfuse.decorators import langfuse_context, observe
from dotenv import load_dotenv
load_dotenv()
# B2 setup
_b2_api = None
_b2_bucket = None
def upload_to_b2(data: bytes, filename: str) -> str:
    global _b2_api, _b2_bucket
    
    if _b2_api is None:
        info = b2.InMemoryAccountInfo()
        _b2_api = b2.B2Api(info)
        _b2_api.authorize_account("production", 
                                 os.getenv("B2_KEY_ID"), 
                                 os.getenv("B2_APPLICATION_KEY"))
        _b2_bucket = _b2_api.get_bucket_by_name("your-bucket-name")
    
    _b2_bucket.upload_bytes(data, filename)
    return _b2_api.get_download_url_for_file_name("your-bucket-name", filename)
@observe()
def demo_image_processing():
    # Get a demo image
    response = requests.get("https://picsum.photos/400/300")
    image_bytes = response.content
    
    # Upload to B2
    filename = f"demo_image_{uuid4().hex[:8]}.jpg"
    public_url = upload_to_b2(image_bytes, filename)
    
    # Create markdown output for Langfuse
    markdown_output = f"![Demo Image]({public_url})"
    
    # Force this to be the output (replaces any LLM response)
    langfuse_context.update_current_observation(output=markdown_output)
    
    return markdown_output
# Run the demo
result = demo_image_processing()
print(f"Langfuse will show: {result}")
Python

What You Get

In Langfuse, instead of seeing JSON or raw text, you’ll see your actual image rendered inline. The image is stored on your infrastructure, and Langfuse displays it via the public URL.

This approach works for any visual content: charts, screenshots, processed images, or generated graphics. Just upload to storage, get the URL, format as markdown, and force it as your Langfuse output.

Leave a Reply

Your email address will not be published. Required fields are marked *