メインコンテンツへスキップ
W&B Weave は、さまざまなコンテンツタイプのログ記録と表示をサポートしており、動画、画像、オーディオクリップ、PDF、CSV データ、HTML を表示するための専用関数を備えています。このガイドでは、各メディアタイプをログして表示する基本的な例から応用例まで紹介します。

概要

このガイドの例ではアノテーションを使用します。メディアのログを始める最も簡単な方法であるため、アノテーションの使用を推奨します。より高度な設定については、Content API セクションを参照してください。メディアを Weave にログするには、Annotated[bytes, Content]Annotated[str, Content] のような型アノテーションを、op の入力型または戻り値の型として追加します。パス引数に Annotated[str, Content] でアノテーションを付けると、Weave はトレース内でそのメディアを自動的に開いて検出し、表示します。
以下のセクションでは、各タイプのメディアをログする実践的な例を紹介します。

画像をログする

以下の例では、画像を生成し、Weave の UI にログする方法を示します。
pumpkin cat の Trace view のスクリーンショット
Annotated[bytes, Content] を関数の型注釈として使用するか、Annotated[str, Content] をファイルパスの型注釈として使用して、画像をログできます。次の例では、シンプルな画像を描画し、その後 Content アノテーションを使って Weave にログします。
import weave
from weave import Content
from PIL import Image, ImageDraw
from typing import Annotated

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

# サンプル画像を作成して保存する
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")

# 方法1: Contentアノテーション(推奨)
@weave.op
def load_image_content(path: Annotated[str, Content]) -> Annotated[bytes, Content]:
    with open(path, 'rb') as f:
        return f.read()

# 方法2: 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 は画像をログして、画像を表示できるトレースへのリンクを返します。

応用例: DALL-E で画像を生成して Weave にログする

次の例では、猫の画像を生成して 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")

高度な例: ログする前に大きな画像をリサイズする

UI の表示コストやストレージへの影響を抑えるため、ログする前に画像をリサイズしておくと便利です。画像をリサイズするには、@weave.oppostprocess_output を使用できます。
from dataclasses import dataclass
from typing import Any
from PIL import Image
import weave

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

# カスタム出力タイプ
@dataclass
class ImageResult:
    label: str
    image: Image.Image

# リサイズヘルパー
def resize_image(image: Image.Image, max_size=(512, 512)) -> Image.Image:
    image = image.copy()
    image.thumbnail(max_size, Image.Resampling.LANCZOS)
    return image

# ログする前に画像をリサイズするための後処理出力
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:
    # 処理するサンプル画像を作成(例:2000x2000の赤い正方形)
    img = Image.new("RGB", (2000, 2000), color="red")
    return ImageResult(label="big red square", image=img)

generate_large_image()
Weave はサイズ変更した画像をログし、その画像を確認できる trace へのリンクを返します。

動画をログする

以下の例では、動画を生成して Weave の UI にログする方法を示します。
Weave での動画ログ
関数に Annotated[bytes, Content] 型のアノテーションを付けることで、動画をログできます。Weave は mp4 動画を自動的に処理します。以下は簡単な例です。
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():
    """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]:
    """ディスクから動画ファイルを読み込む"""
    with open(path, 'rb') as f:
        return f.read()

download_big_buck_bunny()
bunny_video = load_video_content("big_buck_bunny.mp4")
Weave は動画をログし、その動画を表示できるトレースへのリンクを返します。

応用例: 動画解析プロジェクト内で動画をログする

以下の例は、動画理解プロジェクト内で動画をログする方法を示しています。
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

# 注: APIキーは 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)

ドキュメントをログする

以下の例では、ドキュメントを生成し、Weave の UI にログします。
Weave での PDF ドキュメントのログ
Annotated[bytes, Content] 型で関数にアノテーションを付けるか、Annotated[str, Content[Literal['text']] でドキュメントのタイプを指定して、ドキュメントをログできます。Weave は pdfcsvmdtextjsonxml のファイルタイプを自動的に処理します。Annotated[str, Content] を使って、ファイルパス経由でログすることもできます。次の例では、入力された PDF ファイルと CSV ファイルのコピーを保存し、その後、関数から返されたファイルの内容も保存します。
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")

応用例: RAG システム内でドキュメントをログする

この例では、Retrieval-Augmented Generation (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():
    """ばかげた規則を載せた架空の社員ハンドブックを作成する"""
    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:
    """PDF からテキストを抽出し、RAG を使って質問に回答する"""
    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?"
)

オーディオをログする

以下の例では、オーディオを Weave にログする方法を示します。
オーディオの Trace view のスクリーンショット
関数に Annotated[bytes, Content] タイプの注釈を付けるか、Annotated[str, Content[Literal['mp3']]] でオーディオのタイプを指定すると、オーディオを Weave にログできます。Weave は mp3wavflacoggm4a のファイルタイプを自動的に処理します。Annotated[str, Content] を使って、ファイルパスからログすることもできます。次のコードスニペットは、正弦波を生成して録音し、そのオーディオを 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')

# シンプルなビープ音のオーディオファイルを作成
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")

高度な例: AI 作成のオーディオを生成してログする

この例では、Content アノテーションを使用して、AI 作成のオーディオを生成してログします。
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")
このオーディオは Weave にログされ、オーディオプレーヤーとともに UI に自動的に表示されます。オーディオプレーヤーでは、元の音声波形を表示したり、ダウンロードしたりできます。
Weave でのオーディオのログ
次の例では、OpenAI API からのストリーミングレスポンスを使ってオーディオをログする方法を示します。
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")

    # オーディオとしてログするための wave.Wave_read オブジェクトを返す
    return wave.open("output.wav")

make_audio_file_streaming("Hello, how are you?")
オーディオのログ のクックブックをお試しください。このクックブックには、Weave と統合された Real Time Audio API ベースのアシスタントの高度な例も含まれています。

HTML をログする

以下の例では、HTML を生成して Weave の UI にログする方法を示します。
Weave での HTML のログ
Annotated[bytes, Content[Literal['html']]] で関数にアノテーションを付けることで、インタラクティブな HTML をログできます。次の例では、シンプルな HTML ページを作成して 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()

高度な例: W&B Inference を使用して単体で完結する HTML ページを生成し、Weave にログする

この例では、W&B Inference を使用して単体で完結する HTML ページを生成し、それらのページを 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)
この HTML は Weave にログされ、UI に自動的に表示されます。表内の file_name.html セルをクリックすると、フルスクリーンで開きます。元の .html ファイルをダウンロードすることもできます。

Contents API の使用

Content API は Weave 内のメディアオブジェクトを扱います。コンテンツは、base64 データ、ファイルパス、生のバイト列、またはテキストとして Weave にインポートできます。
Content API は Python でのみ利用できます。

使用方法

Content API の主な使用方法は 2 つあります。型アノテーションと直接初期化です。 型アノテーションでは、使用すべき適切なコンストラクターが自動的に判別されます。一方、直接初期化では、より細かな制御が可能になり、コード内で Content API のランタイム機能を活用できます。

型アノテーション

Weave Content API は主に型アノテーションを通じて使用するように設計されており、これにより、トレースされた入力と出力をコンテンツブロブとして処理・保存する必要があることを Weave に示します。
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

# 入力と出力はどちらもWeaveでMP4ファイルとして表示されます
# 入力は文字列で、戻り値はバイト列です
bytes_data = content_annotation('./path/to/your/file.mp4')

直接初期化

次のような機能を活用したい場合:
  • デフォルトのアプリケーション (PDFビューアーなど) でファイルを開く
  • モデルをJSONにダンプして、独自のblob storage (S3など) にアップロードする
  • Content blob に関連付けるカスタムメタデータ (その生成に使用したモデルなど) を渡す
次のいずれかのmethodを使用して、対象のタイプからコンテンツを直接初期化できます:
  • Content.from_path - ファイルパスから作成
  • Content.from_bytes - 生のバイト列から作成
  • Content.from_text - テキスト文字列から作成
  • Content.from_base64 - base64エンコードされたデータから作成
import weave
from weave import Content

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

# 入力はパス文字列として、出力はWeaveのPDFファイルとして表示されます
content = content_initialization('./path/to/your/file.pdf')

content.open()  # PDFビューアでファイルを開きます
content.model_dump()  # モデルの属性をJSONにダンプします

カスタムMIMEタイプ

Weave はほとんどのバイナリMIMEタイプを検出できますが、カスタムMIMEタイプや markdown などのテキスト文書は自動的に検出されないことがあるため、ファイルのMIMEタイプまたは拡張子を手動で指定する必要があります。

型アノテーションを使ったカスタムMIMEタイプ

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')

直接初期化でカスタムMIMEタイプを指定する

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

# 'mp4' や '.mp4' などの拡張子を extension パラメーターに渡す
# (`from_path` では使用不可)
content = Content.from_bytes(video_bytes, extension='.mp4')

# 'video/mp4' などの MIMEタイプ を MIMEタイプ パラメーターに渡す
content = Content.from_bytes(video_bytes, mimetype='video/mp4')

Contentのプロパティ

クラス属性とmethodの詳細な一覧については、Content リファレンスドキュメントを参照してください

属性

プロパティタイプ説明
databytes生のバイナリデータ
metadatadict[str, Any]カスタムメタデータの辞書
sizeintコンテンツのサイズ (バイト単位)
filenamestr抽出または指定されたファイル名
extensionstrファイル拡張子 (例: "jpg""mp3")
mimetypestrMIME タイプ (例: "image/jpeg")
pathstr | None該当する場合のソースファイルのパス
digeststrコンテンツの SHA256 ハッシュ

ユーティリティメソッド

  • save(dest: str | Path) -> None: コンテンツをファイルに保存します
  • open() -> bool: システムのデフォルトアプリケーションでファイルを開きます (事前にコンテンツが保存されているか、パスから読み込まれている必要があります)
  • as_string() -> str: データを文字列として表示します (bytes は encoding 属性を使用してデコードされます)

初期化 method

ファイルパスからcontentオブジェクトを作成します。
content = Content.from_path("assets/photo.jpg")
print(content.mimetype, content.size)
生のバイトデータから content オブジェクトを作成します:
content = Content.from_bytes(
    data_bytes,
    filename="audio.mp3", 
    mimetype="audio/mpeg"
)
content.save("output.mp3")
テキストから content オブジェクトを作成します:
content = Content.from_text("Hello, World!", mimetype="text/plain")
print(content.as_string())
Base64 エンコードされたデータから content オブジェクトを作成します:
content = Content.from_base64(base64_string)
print(content.metadata)

カスタムメタデータを追加する

任意の Content オブジェクトにカスタムメタデータを添付できます:
content = Content.from_bytes(
    data,
    metadata={"resolution": "1920x1080", "model": "dall-e-3" }
)
print(content.metadata["resolution"])