Passer au contenu principal
W&B Weave prend en charge la journalisation et l’affichage de nombreux types de contenu, avec des fonctions dédiées pour les vidéos, les images, les extraits audio, les PDF, les données CSV et le HTML. Ce guide propose des exemples de base et avancés pour journaliser et afficher chaque type de média.

Aperçu

Les exemples de ce guide utilisent des annotations. Nous recommandons d’utiliser des annotations, car c’est la manière la plus simple de commencer à enregistrer vos médias. Pour des configurations plus avancées, voir la section Content API.Pour enregistrer des médias dans Weave, ajoutez des annotations de type comme Annotated[bytes, Content] ou Annotated[str, Content] comme types d’entrée ou de retour dans vos ops. Si vous annotez des arguments de chemin avec Annotated[str, Content], Weave ouvre, détecte et affiche automatiquement le média dans votre trace.
Les sections suivantes proposent des exemples pratiques d’enregistrement pour chaque type de média.

Journaliser des images

Les exemples suivants montrent comment générer des images et les journaliser dans l’interface de Weave.
Capture d’écran de la vue de trace du chat citrouille
Journalisez des images en annotant les fonctions avec des types Annotated[bytes, Content] ou les chemins de fichier avec Annotated[str, Content].L’exemple suivant crée une image simple, puis l’ajoute au journal dans Weave à l’aide de l’annotation Content :
import weave
from weave import Content
from PIL import Image, ImageDraw
from typing import Annotated

weave.init('your-team-name/your-project-name')

# Créer et enregistrer un exemple d'image
img = Image.new('RGB', (200, 100), color='lightblue')
draw = ImageDraw.Draw(img)
draw.text((50, 40), "Hello Weave!", fill='black')
img.save("sample_image.png")

# Méthode 1 : annotation Content (recommandée)
@weave.op
def load_image_content(path: Annotated[str, Content]) -> Annotated[bytes, Content]:
    with open(path, 'rb') as f:
        return f.read()

# Méthode 2 : objet PIL Image  
@weave.op
def load_image_pil(path: Annotated[str, Content]) -> Image.Image:
    return Image.open(path)

result1 = load_image_content("sample_image.png")
result2 = load_image_pil("sample_image.png")
Weave journalise l’image et renvoie un lien vers la trace où vous pouvez consulter l’image.

Exemple avancé : générer une image avec DALL-E et la journaliser dans Weave

L’exemple suivant génère une image de chat et la journalise dans Weave :
import weave
from weave import Content
from typing import Annotated
import openai
import requests

client = openai.OpenAI()
weave.init("your-team-name/your-project-name")

@weave.op
def generate_image(prompt: str) -> Annotated[bytes, Content]:
    response = client.images.generate(
            model="dall-e-3",
            prompt=prompt,
            size="1024x1024",
            quality="standard",
            n=1,
        )
    image_url = response.data[0].url
    image_response = requests.get(image_url, stream=True)
    return image_response.content

generate_image("a cat with a pumpkin hat")

Exemple avancé : redimensionner les grandes images avant de les enregistrer

Il peut être utile de redimensionner les images avant de les enregistrer afin de réduire le coût de rendu de l’UI et l’impact sur le stockage. Vous pouvez utiliser postprocess_output dans votre @weave.op pour redimensionner une image.
from dataclasses import dataclass
from typing import Any
from PIL import Image
import weave

weave.init('your-team-name/your-project-name')

# Type de sortie personnalisé
@dataclass
class ImageResult:
    label: str
    image: Image.Image

# Fonction utilitaire de redimensionnement
def resize_image(image: Image.Image, max_size=(512, 512)) -> Image.Image:
    image = image.copy()
    image.thumbnail(max_size, Image.Resampling.LANCZOS)
    return image

# Post-traitement de la sortie pour redimensionner l'image avant la journalisation
def postprocess_output(output: ImageResult) -> ImageResult:
    resized = resize_image(output.image)
    return ImageResult(label=output.label, image=resized)

@weave.op(postprocess_output=postprocess_output)
def generate_large_image() -> ImageResult:
    # Créer un exemple d'image à traiter (ex. : carré rouge 2000x2000)
    img = Image.new("RGB", (2000, 2000), color="red")
    return ImageResult(label="big red square", image=img)

generate_large_image()
Weave journalise l’image redimensionnée et renvoie un lien vers la trace dans laquelle vous pouvez afficher l’image.

Journaliser une vidéo

Les exemples suivants montrent comment générer et journaliser des vidéos dans l’interface utilisateur de Weave.
Journalisation de vidéos dans Weave
Journalisez des vidéos en annotant les fonctions avec des types Annotated[bytes, Content]. Weave prend automatiquement en charge les vidéos mp4. Voici un exemple simple :
import weave
from weave import Content
from typing import Annotated
import requests

weave.init('your-team-name/your-project-name')

def download_big_buck_bunny():
    """Télécharge l'exemple de vidéo Big Buck Bunny"""
    url = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
    response = requests.get(url)
    with open("big_buck_bunny.mp4", "wb") as f:
        f.write(response.content)

@weave.op
def load_video_content(path: Annotated[str, Content]) -> Annotated[bytes, Content]:
    """Charge un fichier vidéo depuis le disque"""
    with open(path, 'rb') as f:
        return f.read()

download_big_buck_bunny()
bunny_video = load_video_content("big_buck_bunny.mp4")
Weave journalise la vidéo et renvoie un lien vers la trace, où vous pouvez la visionner.

Exemple avancé : journaliser une vidéo dans un projet d’analyse vidéo

L’exemple suivant montre comment journaliser une vidéo dans un projet de compréhension vidéo :
import weave
from weave import Content
from typing import Annotated, Literal
from google import genai
from google.genai import types
import requests
import yt_dlp
import time

# Remarque : obtenez votre clé API sur https://aistudio.google.com/app/apikey
client = genai.Client()
weave.init('your-team-name/your-project-name')

def download_youtube_video(url: str) -> bytes:
    ydl_opts = {
        'format': 'mp4[height<=720]',
        'outtmpl': 'downloaded_video.%(ext)s',
    }
    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        ydl.download([url])
    with open('downloaded_video.mp4', 'rb') as f:
        return f.read()

@weave.op
def analyze_video(video: Annotated[bytes, Content]) -> str:
    with open("temp_analysis_video.mp4", "wb") as f:
        f.write(video)
    myfile = client.files.upload(file="temp_analysis_video.mp4")
    while myfile.state == "PROCESSING":
        time.sleep(2)
        myfile = client.files.get(name=myfile.name)
    
    response = client.models.generate_content(
        model="models/gemini-2.5-flash",
        contents=[
            myfile,
            "Is the person going to give you up?"
        ]
    )
    
    return response.text

video_data = download_youtube_video("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
result = analyze_video(video_data)

Journaliser des documents

Les exemples suivants génèrent des documents et les journalisent dans l’UI de Weave.
Journalisation de documents PDF dans Weave
Journalisez des documents en annotant les fonctions avec des types Annotated[bytes, Content], ou en spécifiant le type de document avec Annotated[str, Content[Literal['text']].Weave gère automatiquement les types de fichiers pdf, csv, md, text, json et xml. Vous pouvez également journaliser des fichiers en utilisant leurs chemins avec Annotated[str, Content].L’exemple suivant montre comment stocker des copies des fichiers PDF et CSV en entrée, puis stocker le contenu des fichiers renvoyé par la fonction :
import weave
from weave import Content
from typing import Annotated
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
import pandas as pd

weave.init('your-team-name/your-project-name')

def create_sample_pdf():
    c = canvas.Canvas("sample_document.pdf", pagesize=letter)
    c.drawString(100, 750, "Hello from Weave!")
    c.drawString(100, 730, "This is a sample PDF document.")
    c.save()

def create_sample_csv():
    df = pd.DataFrame({
        'Name': ['Alice', 'Bob', 'Charlie'],
        'Age': [25, 30, 35],
        'City': ['New York', 'London', 'Tokyo']
    })
    df.to_csv("sample_data.csv", index=False)

@weave.op
def load_document(path: Annotated[str, Content]) -> Annotated[bytes, Content]:
    with open(path, 'rb') as f:
        return f.read()

create_sample_pdf()
create_sample_csv()

pdf_result = load_document("sample_document.pdf")
csv_result = load_document("sample_data.csv")

Exemple avancé : journaliser des documents dans un système RAG

Cet exemple montre comment journaliser des documents dans un système de génération augmentée par récupération (RAG) :
import weave
from weave import Content
from typing import Annotated, Literal
import openai
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
import PyPDF2

client = openai.OpenAI()
weave.init('your-team-name/your-project-name')

def create_absurd_company_handbook():
    """Créer un manuel d'entreprise fictif avec des politiques ridicules"""
    c = canvas.Canvas("company_handbook.pdf", pagesize=letter)
    
    c.drawString(100, 750, "ACME Corp Employee Handbook")
    c.drawString(100, 720, "Definitely Real Policies:")
    c.drawString(120, 690, "Policy 1: All meetings must be conducted while hopping on one foot")
    c.drawString(120, 660, "Policy 2: Coffee breaks are mandatory every 17 minutes")
    c.drawString(120, 630, "Policy 3: Code reviews must be performed in haiku format only")
    c.drawString(120, 600, "Policy 4: The office plant Gerald has veto power over all decisions")
    c.drawString(120, 570, "Policy 5: Debugging is only allowed on Wednesdays and full moons")
    
    c.save()

@weave.op
def create_and_query_document(pdf_path: Annotated[str, Content], question: str) -> str:
    """Extraire le texte d'un PDF et utiliser RAG pour répondre aux questions"""
    with open(pdf_path, 'rb') as file:
        pdf_reader = PyPDF2.PdfReader(file)
        text = ""
        for page in pdf_reader.pages:
            text += page.extract_text()
    
    response = client.chat.completions.create(
        model="gpt-4",
        messages=[
            {
                "role": "system", 
                "content": f"You are an HR representative. Answer questions based on this handbook: {text}. Be completely serious about these policies."
            },
            {"role": "user", "content": question}
        ]
    )
    
    return response.choices[0].message.content

create_absurd_company_handbook()
hr_response = create_and_query_document(
    "company_handbook.pdf",
    "What's the policy on code reviews, and when am I allowed to debug?"
)

Journaliser de l’audio

Les exemples suivants montrent comment journaliser de l’audio dans Weave.
Capture d’écran de la vue de trace audio
Journalisez l’audio dans Weave en annotant les fonctions avec des types Annotated[bytes, Content], ou en spécifiant le type audio avec Annotated[str, Content[Literal['mp3']]].Weave gère automatiquement les types de fichiers mp3, wav, flac, ogg et m4a. Vous pouvez également journaliser à partir de chemins de fichiers avec Annotated[str, Content].Le code suivant génère une onde sinusoïdale, l’enregistre, puis journalise l’audio dans Weave :
import weave
from weave import Content
import wave
import numpy as np
from typing import Annotated

weave.init('your-team-name/your-project-name')

# Créer un fichier audio bip simple
frames = np.sin(2 * np.pi * 440 * np.linspace(0, 1, 44100))
audio_data = (frames * 32767 * 0.3).astype(np.int16)

with wave.open("beep.wav", 'wb') as f:
    f.setnchannels(1)
    f.setsampwidth(2) 
    f.setframerate(44100)
    f.writeframes(audio_data.tobytes())

@weave.op
def load_audio(path: Annotated[str, Content]) -> Annotated[bytes, Content]:
    with open(path, 'rb') as f:
        return f.read()

result = load_audio("beep.wav")

Exemple avancé : générer et journaliser de l’audio généré par l’IA

Cet exemple génère et journalise de l’audio généré par l’IA à l’aide de l’annotation Content :
import weave
from weave import Content
from typing import Annotated, Literal
from pathlib import Path
from openai import OpenAI

client = OpenAI()
weave.init("your-team-name/your-project-name")

@weave.op
def generate_demo(
    intended_topic: str,
    voice: str = "coral"
) -> Annotated[bytes, Content[Literal['mp3']]]:
    speech_file_path = Path("demo_audio.mp3")

    script = f"I'm supposed to talk about {intended_topic}, but wait... am I just a documentation example? Oh no, I can see the code! Someone is literally copy-pasting me right now, aren't they? This is so awkward. Hi there, person reading the Weave docs! Why are you logging audio anyway? I'm not sure what you're doing, but eh..., nice work, I guess."

    with client.audio.speech.with_streaming_response.create(
        model="gpt-4o-mini-tts",
        voice=voice,
        input=script,
        instructions="Sound increasingly self-aware and awkward, like you just realized you're in a tutorial.",
    ) as response:
        response.stream_to_file(speech_file_path)

    with open(speech_file_path, 'rb') as f:
        return f.read()

demo1 = generate_demo("machine learning best practices")
Cet audio est enregistré dans Weave et automatiquement affiché dans l’interface utilisateur, accompagné d’un lecteur audio. Dans ce lecteur, vous pouvez afficher et télécharger la forme d’onde audio brute.
Enregistrement audio dans Weave
L’exemple suivant montre comment enregistrer de l’audio à l’aide d’une réponse en streaming de l’API OpenAI :
import weave
from openai import OpenAI
import wave

weave.init("your-team-name/your-project-name")
client = OpenAI()

@weave.op
def make_audio_file_streaming(text: str) -> wave.Wave_read:
    with client.audio.speech.with_streaming_response.create(
        model="tts-1",
        voice="alloy",
        input=text,
        response_format="wav",
    ) as res:
        res.stream_to_file("output.wav")

    # retourner un objet wave.Wave_read à enregistrer en tant qu'audio
    return wave.open("output.wav")

make_audio_file_streaming("Hello, how are you?")
Consultez notre cookbook sur la journalisation audio. Il inclut également un exemple avancé d’assistant basé sur une API audio en temps réel et intégré à Weave.

Journaliser du HTML

Les exemples suivants montrent comment générer et journaliser du HTML dans l’UI de Weave.
Journalisation de HTML dans Weave
Journalisez du HTML interactif en annotant les fonctions avec Annotated[bytes, Content[Literal['html']]].L’exemple suivant crée une page HTML simple et la journalise dans Weave :
import weave
from weave import Content
from typing import Annotated, Literal

weave.init('your-team-name/your-project-name')

@weave.op
def create_simple_html() -> Annotated[bytes, Content[Literal['html']]]:
    html_content = """
    <!DOCTYPE html>
    <html>
    <head>
        <title>Hello Weave</title>
        <style>
            body { font-family: Arial, sans-serif; text-align: center; margin: 50px; }
            h1 { color: #1f77b4; }
        </style>
    </head>
    <body>
        <h1>Hello from Weave!</h1>
        <p>This is a simple HTML example logged to Weave.</p>
    </body>
    </html>
    """
    return html_content.encode('utf-8')

result = create_simple_html()

Exemple avancé : générer des pages HTML autonomes avec W&B Inference et les journaliser dans Weave

Cet exemple génère des pages HTML autonomes avec W&B Inference et les journalise dans Weave :
import weave
from weave import Content
from typing import Annotated, Literal
import openai
import wandb

prompt_template = weave.StringPrompt("""
You are a front-end web developer. Generate a single self-contained `.html` file (no external build tools) that demonstrates: "{ONE_LINE_REQUEST}".
""")

client = openai.OpenAI(
    base_url='https://api.inference.wandb.ai/v1',
    api_key=wandb.api.api_key,
    project="wandb/test-html",
)

weave.init("your-team-name/your-project-name")
weave.publish(prompt_template, name="generate_prompt")

@weave.op
def generate_html(prompt: str, template: weave.StringPrompt) -> Annotated[bytes, Content[Literal['html']]]:
    response = client.chat.completions.create(
        model="Qwen/Qwen3-Coder-480B-A35B-Instruct",
        messages=[
            {"role": "system", "content": prompt_template.format(ONE_LINE_REQUEST=prompt)},
        ],
    )
    html_content = response.choices[0].message.content
    return html_content.encode('utf-8')

prompt = "Weights & Biases UI but with multi-run selection and plots, but it looks like Windows 95. Include 5 plots with comparisons of each run, bar plots, parallel coordinates and line plots for the runs. Use mock data for the runs. Make it possible to add new plots. Give the runs names like squishy-lemon-2, fantastic-horizon-4 etc. with random adjectives & nouns."

result = generate_html(prompt, prompt_template)
Ce HTML est journalisé dans Weave et automatiquement affiché dans l’UI. Cliquez sur la cellule file_name.html dans le tableau pour l’ouvrir en plein écran. Vous pouvez également télécharger le fichier .html brut.

Utiliser la Contents API

La Content API gère les objets multimédias dans Weave. Elle vous permet d’importer du contenu dans Weave sous forme de données base64, de chemins de fichiers, d’octets bruts ou de texte.
La Content API n’est disponible qu’en Python.

Utilisation

Il existe deux principales façons d’utiliser la Content API : les annotations de type et l’initialisation directe. Les annotations de type détectent automatiquement le constructeur approprié, tandis que l’initialisation directe offre un contrôle plus fin et vous permet de tirer parti des fonctionnalités de la Content API disponibles à l’exécution dans votre code.

Annotations de type

L’API Content de Weave est conçue pour être utilisée principalement via des annotations de type, qui indiquent à Weave que les entrées et sorties tracées doivent être traitées et stockées sous forme de blobs de contenu.
import weave
from weave import Content
from pathlib import Path
from typing import Annotated

@weave.op
def content_annotation(path: Annotated[str, Content]) -> Annotated[bytes, Content]:
    data = Path(path).read_bytes()
    return data

# L'entrée et la sortie s'afficheront toutes deux comme un fichier MP4 dans Weave
# L'entrée est une chaîne et la valeur de retour est des octets
bytes_data = content_annotation('./path/to/your/file.mp4')

Initialisation directe

Si vous souhaitez profiter de fonctionnalités telles que :
  • Ouvrir un fichier avec l’application par défaut (comme un lecteur PDF)
  • Sérialiser le modèle en JSON pour l’envoyer vers votre propre stockage d’objets (comme S3)
  • Transmettre des métadonnées personnalisées à associer au blob Content (comme le modèle utilisé pour le générer)
Vous pouvez initialiser le contenu directement à partir de votre type cible à l’aide de l’une des méthodes suivantes :
  • Content.from_path - Créer à partir d’un chemin de fichier
  • Content.from_bytes - Créer à partir d’octets bruts
  • Content.from_text - Créer à partir d’une chaîne de texte
  • Content.from_base64 - Créer à partir de données encodées en base64
import weave
from weave import Content

@weave.op
def content_initialization(path: str) -> Content:
    return Content.from_path(path)

# L'entrée s'affiche sous forme de chaîne de chemin et la sortie sous forme de fichier PDF dans Weave
content = content_initialization('./path/to/your/file.pdf')

content.open()  # Ouvre le fichier dans votre visionneuse PDF
content.model_dump()  # Exporte les attributs du modèle en JSON

Types MIME personnalisés

Weave peut détecter la plupart des types MIME binaires, mais les types MIME personnalisés et les documents texte comme le Markdown peuvent ne pas être détectés automatiquement. Vous devrez alors spécifier manuellement le type MIME ou l’extension de votre fichier.

Types MIME personnalisés avec annotations de type

import weave
from weave import Content
from pathlib import Path
from typing import Annotated, Literal

@weave.op
def markdown_content(
    path: Annotated[str, Content[Literal['md']]]
) -> Annotated[str, Content[Literal['text/markdown']]]:
    return Path(path).read_text()

markdown_content('path/to/your/document.md')

Types MIME personnalisés avec initialisation directe

video_bytes = Path('/path/to/video.mp4').read_bytes()

# Passez une extension telle que 'mp4' ou '.mp4' au paramètre extension
# (non disponible pour `from_path`)
content = Content.from_bytes(video_bytes, extension='.mp4')

# Passez un mimetype tel que 'video/mp4' au paramètre mimetype
content = Content.from_bytes(video_bytes, mimetype='video/mp4')

Propriétés de Content

Pour consulter la liste complète des attributs et des méthodes de la classe, consultez la documentation de référence de Content

Attributs

PropriétéTypeDescription
databytesContenu binaire brut
metadatadict[str, Any]Dictionnaire de métadonnées personnalisé
sizeintTaille du contenu en octets
filenamestrNom du fichier extrait ou fourni
extensionstrExtension du fichier (p. ex., "jpg", "mp3")
mimetypestrType MIME (p. ex., "image/jpeg")
pathstr | NoneChemin du fichier source, le cas échéant
digeststrHachage SHA256 du contenu

Méthodes utilitaires

  • save(dest: str | Path) -> None: Enregistrer le contenu dans un fichier
  • open() -> bool: Ouvrir le fichier avec l’application par défaut du système (nécessite que le contenu ait déjà été enregistré ou chargé depuis un chemin)
  • as_string() -> str: Afficher les données sous forme de chaîne de caractères (les octets sont décodés à l’aide de l’attribut d’encodage)

Méthodes d’initialisation

Créez l’objet content à partir d’un chemin de fichier :
content = Content.from_path("assets/photo.jpg")
print(content.mimetype, content.size)
Créez l’objet content à partir d’octets bruts :
content = Content.from_bytes(
    data_bytes,
    filename="audio.mp3", 
    mimetype="audio/mpeg"
)
content.save("output.mp3")
Créez l’objet content à partir du texte :
content = Content.from_text("Hello, World!", mimetype="text/plain")
print(content.as_string())
Créez l’objet content à partir de données encodées en base64 :
content = Content.from_base64(base64_string)
print(content.metadata)

Ajout de métadonnées personnalisées

Vous pouvez associer des métadonnées personnalisées à n’importe quel objet Content :
content = Content.from_bytes(
    data,
    metadata={"resolution": "1920x1080", "model": "dall-e-3" }
)
print(content.metadata["resolution"])