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

概要

このガイドの例ではアノテーションを使用しています。アノテーションはメディアのログ記録を開始する最もシンプルな方法であるため、推奨されています。より高度な設定については、Content API セクション を参照してください。Weave にメディアをログ記録するには、ops の入力または戻り値の型として Annotated[bytes, Content]Annotated[str, Content] のような型アノテーションを追加します。パス引数を 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.op 内で postprocess_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 はリサイズされた画像をログに記録し、画像を表示できるトレースへのリンクを返します。

動画のログ記録

以下の例では、動画を生成して 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 システム内でのドキュメントログ記録

この例では、検索拡張生成 (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 にログ記録する方法を示します。
audio 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?")
Audio Logging のクックブックをお試しください。このクックブックには、Weave と統合されたリアルタイムオーディオ 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 ファイルをダウンロードすることも可能です。

Content 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 にダンプして、独自のブロブストレージ(S3 など)にアップロードする
  • Content ブロブに関連付けるカスタムメタデータを渡す(生成に使用されたモデル名など)
以下のいずれかのメソッドを使用して、ターゲットタイプから直接コンテンツを初期化できます:
  • 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 にダンプします

カスタムマイムタイプ

Weave はほとんどのバイナリマイムタイプを検出できますが、カスタムマイムタイプや Markdown などのテキストドキュメントは自動的に検出されない場合があり、ファイルのマイムタイプや拡張子を手動で指定する必要があります。

型アノテーションでのカスタムマイムタイプ

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

直接初期化でのカスタムマイムタイプ

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

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

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

コンテンツのプロパティ

クラスの属性とメソッドの包括的なリストについては、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: データを文字列として表示(バイト列は encoding 属性を使用してデコードされます)

初期化メソッド

ファイルパスから 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"])