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.
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. Le SDK TypeScript fournit des fonctions dédiées pour enregistrer des médias :
weave.weaveImage({ data: Buffer }) - pour les images (format PNG)
weave.weaveAudio({ data: Buffer }) - pour l’audio (format WAV)
Le SDK TypeScript prend actuellement en charge l’enregistrement d’images et d’audio. Pour enregistrer des vidéos, des documents ou du HTML, utilisez plutôt le SDK Python de Weave.
Les sections suivantes proposent des exemples pratiques d’enregistrement pour chaque type de média.
Les exemples suivants montrent comment générer des images et les journaliser dans l’interface de Weave.
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. L’exemple suivant enregistre une image dans le journal à l’aide de la fonction weaveImage :import * as weave from 'weave';
import * as fs from 'fs';
async function main() {
await weave.init('your-team-name/your-project-name');
// Charger et journaliser une image avec weaveImage
const loadImage = weave.op(async function loadImage(path: string) {
const data = fs.readFileSync(path);
return weave.weaveImage({ data });
});
// En supposant que vous disposez d'un fichier image PNG
await loadImage('sample_image.png');
}
main();
Weave journalise l’image et renvoie un lien vers la trace, où vous pouvez consulter l’image.Exemple avancé : journaliser une image générée via l’API DALL-E d’OpenAI
L’exemple suivant journalise une image générée via l’API DALL-E d’OpenAI à l’aide de la fonction weaveImage :import {OpenAI} from 'openai';
import * as weave from 'weave';
async function main() {
await weave.init('your-team-name/your-project-name');
const openai = new OpenAI();
const generateImage = weave.op(async (prompt: string) => {
const response = await openai.images.generate({
model: 'dall-e-3',
prompt: prompt,
size: '1024x1024',
quality: 'standard',
n: 1,
});
const imageUrl = response.data[0].url;
const imgResponse = await fetch(imageUrl);
const data = Buffer.from(await imgResponse.arrayBuffer());
return weave.weaveImage({data});
});
generateImage('a cat with a pumpkin hat');
}
main();
Les exemples suivants montrent comment générer et journaliser des vidéos dans l’interface utilisateur de 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)
Cette fonctionnalité n'est pas encore disponible dans le SDK TypeScript de Weave.
Journaliser des documents
Les exemples suivants génèrent des documents et les journalisent dans l’UI de 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?"
)
Cette fonctionnalité n'est pas encore disponible dans le SDK TypeScript de Weave.
Les exemples suivants montrent comment journaliser de l’audio dans Weave.
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.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?")
L’exemple suivant charge un fichier audio existant et l’enregistre dans Weave :import * as weave from 'weave';
import * as fs from 'fs';
async function main() {
await weave.init('your-team-name/your-project-name');
// Charger et journaliser l'audio avec weaveAudio
const loadAudio = weave.op(async function loadAudio(path: string) {
const data = fs.readFileSync(path);
return weave.weaveAudio({ data });
});
// En supposant que vous disposez d'un fichier audio WAV
await loadAudio('beep.wav');
}
main();
Exemple avancé : générer de l’audio avec l’API TTS d’OpenAI et le journaliser dans Weave
L’exemple suivant génère de l’audio avec l’API TTS d’OpenAI et le journalise dans Weave :import {OpenAI} from 'openai';
import * as weave from 'weave';
async function main() {
await weave.init('your-team-name/your-project-name');
const openai = new OpenAI();
const makeAudioFileStreaming = weave.op(async function audio(text: string) {
const response = await openai.audio.speech.create({
model: 'tts-1',
voice: 'alloy',
input: text,
response_format: 'wav',
});
const chunks: Uint8Array[] = [];
for await (const chunk of response.body) {
chunks.push(chunk);
}
return weave.weaveAudio({data: Buffer.concat(chunks)});
});
await makeAudioFileStreaming('Hello, how are you?');
}
main();
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.
Les exemples suivants montrent comment générer et journaliser du HTML dans l’UI de 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)
Cette fonctionnalité n’est pas encore disponible dans le SDK TypeScript de Weave.
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.
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.
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.
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')
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
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')
Pour consulter la liste complète des attributs et des méthodes de la classe, consultez la documentation de référence de Content
| Propriété | Type | Description |
|---|
data | bytes | Contenu binaire brut |
metadata | dict[str, Any] | Dictionnaire de métadonnées personnalisé |
size | int | Taille du contenu en octets |
filename | str | Nom du fichier extrait ou fourni |
extension | str | Extension du fichier (p. ex., "jpg", "mp3") |
mimetype | str | Type MIME (p. ex., "image/jpeg") |
path | str | None | Chemin du fichier source, le cas échéant |
digest | str | Hachage SHA256 du contenu |
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)
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"])