Skip to main content
The log() function accepts typed dataclasses for structured logging. Each type creates a specific span in the trace.
from caribou import Message, ToolExecution, Grade, Event
caribou.log(item)

Message

Log conversation messages. Assistant messages can include LLM metadata.
caribou.log(Message(role="user", content="Hello"))

caribou.log(Message(
    role="assistant",
    content="Hi there!",
    tool_calls=[{"id": "call_1", "function": {"name": "search", "arguments": "{}"}}],
    model="gpt-4o",
    provider="openai",
    input_tokens=100,
    output_tokens=50,
    cost_usd=0.002,
    duration_ms=1500,
))
Grader LLM calls use the grader_name field:
caribou.log(Message(
    role="assistant",
    content="Score: 0.8",
    grader_name="helpfulness",
    model="gpt-4o",
    input_tokens=200,
    output_tokens=10,
))
FieldTypeDescription
rolestr"user", "assistant", or "system"
contentstr | list[dict] | NoneText or OpenAI-style multimodal content
tool_callslist[dict] | NoneTool calls made by assistant
tool_call_idstr | NoneTool call ID (for tool responses)
namestr | NoneFunction name (for tool responses)
modelstr | NoneLLM model name
providerstr | NoneLLM provider
input_tokensint | NoneInput token count
output_tokensint | NoneOutput token count
cost_usdfloat | NoneCost in USD
duration_msint | NoneDuration in milliseconds
grader_namestr | NoneGrader name (for grader LLM calls)
promptstr | NoneGrader prompt summary

Span naming

Scenariospan_kindspan_name
Regular messagemessagemessage.{role}
Message with grader_namegrader.{grader_name}grader.{grader_name}

ToolExecution

Log tool calls with arguments, results, and error status.
caribou.log(ToolExecution(
    name="read_file",
    call_id="call_abc123",
    arguments='{"path": "/etc/hosts"}',
    result="127.0.0.1 localhost",
    error=None,
    requestor="agent",
    duration_ms=50,
))
FieldTypeDescription
namestrTool name
call_idstr | NoneTool call ID
argumentsdict | str | NoneTool input
resultstr | list[dict] | NoneTool result (text or multimodal)
errorstr | NoneError message if failed
requestorstr | None"agent", "user", or "system"
duration_msint | NoneDuration in milliseconds
Creates a tool.{name} span.

Grade

Log grading results with scores, reasoning, and per-criterion breakdowns.
caribou.log(Grade(
    score=0.85,
    grader_type="MyGrader",
    reasoning="Passed 2 of 3 tests",
    criteria=[
        {"type": "test_pass", "passed": True, "score": 1.0},
        {"type": "test_pass", "passed": False, "score": 0.0},
    ],
    passed_count=2,
    total_count=3,
))
FieldTypeDescription
scorefloatScore (0.0 to 1.0)
grader_typestr | NoneGrader class name
reasoningstr | NoneReasoning explanation
criterialist[dict] | NoneIndividual criteria results
passed_countint | NoneNumber of passed criteria
total_countint | NoneTotal criteria count
Creates a grader or grader.{grader_type} span. Each criterion in criteria creates a child grader.criterion span.

Event

Log lifecycle events with optional details.
caribou.log(Event(name="setup_start"))
caribou.log(Event(name="grading_complete", details={"duration_ms": 500}))
FieldTypeDescription
namestrEvent name
detailsdict | NoneAdditional event details
Creates an event.{name} span.

Multimodal content

Caribou processes base64 images in message content, tool results, tool arguments, grader artifacts, and event details. Images are uploaded to S3 and replaced with caribou-image:// URLs, which Kestrel renders automatically. Use OpenAI-style content parts:
caribou.log(ToolExecution(
    name="render_3d",
    call_id="call_1",
    arguments={"region": "interface"},
    result=[
        {"type": "image_url", "image_url": {"url": "data:image/png;base64,iVBORw0KGgo..."}},
        {"type": "text", "text": "Rendered view of the interface region."},
    ],
))

caribou.log(Message(
    role="user",
    content=[
        {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}},
        {"type": "text", "text": "What does this structure show?"},
    ],
))