Skip to main content

Messages

Verifiers uses a provider-agnostic message format compatible with OpenAI and Anthropic APIs.

Overview

The Messages type represents conversations as lists of message objects, where each message has a role and content.

Type Definition

Messages = list[Message]

Message = (
    SystemMessage
    | UserMessage
    | AssistantMessage
    | ToolMessage
    | TextMessage
)

Message Types

SystemMessage

class SystemMessage(CustomBaseModel):
    role: Literal["system"] = "system"
    content: MessageContent
System instructions that set context for the conversation. Example:
{
    "role": "system",
    "content": "You are a helpful math tutor."
}

UserMessage

class UserMessage(CustomBaseModel):
    role: Literal["user"] = "user"
    content: MessageContent
User input or environment observations. Example:
{
    "role": "user",
    "content": "What is 2+2?"
}
Multimodal:
{
    "role": "user",
    "content": [
        {"type": "text", "text": "What's in this image?"},
        {"type": "image_url", "image_url": {"url": "https://..."}}
    ]
}

AssistantMessage

class AssistantMessage(CustomBaseModel):
    role: Literal["assistant"] = "assistant"
    content: MessageContent | None = None
    reasoning_content: str | None = None
    thinking_blocks: list[ThinkingBlock] | None = None
    tool_calls: list[ToolCall] | None = None
Model responses, including text, reasoning, and tool calls. Text response:
{
    "role": "assistant",
    "content": "The answer is 4."
}
With tool calls:
{
    "role": "assistant",
    "content": "I'll search for that.",
    "tool_calls": [
        {
            "id": "call_123",
            "name": "search",
            "arguments": '{"query": "weather"}'
        }
    ]
}
With reasoning (OpenAI o1/o3):
{
    "role": "assistant",
    "content": "The answer is 4.",
    "reasoning_content": "First I add 2+2..."
}
With thinking blocks (Claude 3.7 Sonnet):
{
    "role": "assistant",
    "content": "The answer is 4.",
    "thinking_blocks": [
        {"type": "thinking", "thinking": "Let me calculate..."}
    ]
}

ToolMessage

class ToolMessage(CustomBaseModel):
    role: Literal["tool"] = "tool"
    tool_call_id: str
    content: MessageContent
Tool execution results returned to the model. Example:
{
    "role": "tool",
    "tool_call_id": "call_123",
    "content": "Weather: Sunny, 72°F"
}

TextMessage

class TextMessage(CustomBaseModel):
    role: Literal["text"] = "text"
    content: str
Legacy format for completion-style APIs. Example:
{
    "role": "text",
    "content": "The capital of France is Paris."
}

MessageContent

MessageContent = str | list[ContentPart]

ContentPart = (
    TextContentPart
    | ImageUrlContentPart
    | InputAudioContentPart
    | GenericContentPart
    | dict[str, Any]
)
Content can be:
  • String: Simple text
  • List of parts: Multimodal content (text, images, audio)

TextContentPart

class TextContentPart(CustomBaseModel):
    type: Literal["text"] = "text"
    text: str
Example:
{"type": "text", "text": "Hello"}

ImageUrlContentPart

class ImageUrlContentPart(CustomBaseModel):
    type: Literal["image_url"] = "image_url"
    image_url: ImageUrlSource

class ImageUrlSource(CustomBaseModel):
    url: str
Example:
{
    "type": "image_url",
    "image_url": {"url": "https://example.com/image.jpg"}
}

InputAudioContentPart

class InputAudioContentPart(CustomBaseModel):
    type: Literal["input_audio"] = "input_audio"
    input_audio: InputAudioSource

class InputAudioSource(CustomBaseModel):
    data: str  # Base64-encoded audio
    format: str  # e.g., "wav", "mp3"
Example:
{
    "type": "input_audio",
    "input_audio": {
        "data": "base64_encoded_audio_data",
        "format": "wav"
    }
}

ToolCall

class ToolCall(CustomBaseModel):
    id: str
    name: str
    arguments: str  # JSON string
Represents a tool invocation from the assistant. Example:
{
    "id": "call_abc123",
    "name": "get_weather",
    "arguments": '{"location": "San Francisco"}'
}

Example Usage

Building Conversations

import verifiers as vf

messages: vf.Messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "What is Python?"},
    {
        "role": "assistant",
        "content": "Python is a programming language."
    },
    {"role": "user", "content": "Show me an example"},
]

Tool Call Flow

messages: vf.Messages = [
    {"role": "user", "content": "What's the weather?"},
    {
        "role": "assistant",
        "content": None,
        "tool_calls": [{
            "id": "call_1",
            "name": "get_weather",
            "arguments": '{"city": "NYC"}'
        }]
    },
    {
        "role": "tool",
        "tool_call_id": "call_1",
        "content": "Sunny, 70°F"
    },
    {
        "role": "assistant",
        "content": "It's sunny and 70°F in NYC."
    },
]

Multimodal Messages

messages: vf.Messages = [
    {
        "role": "user",
        "content": [
            {"type": "text", "text": "Describe this image:"},
            {
                "type": "image_url",
                "image_url": {"url": "https://example.com/cat.jpg"}
            }
        ]
    },
    {
        "role": "assistant",
        "content": "I see a cat sitting on a windowsill."
    },
]

Extracting Content

def get_text_content(message: vf.Message) -> str:
    """Extract text from a message."""
    content = message.get("content")
    
    if isinstance(content, str):
        return content
    elif isinstance(content, list):
        # Extract text parts
        text_parts = [
            part["text"]
            for part in content
            if isinstance(part, dict) and part.get("type") == "text"
        ]
        return " ".join(text_parts)
    else:
        return ""

Formatting for Display

def format_conversation(messages: vf.Messages) -> str:
    """Format messages for display."""
    output = []
    
    for msg in messages:
        role = msg["role"]
        content = msg.get("content", "")
        
        if role == "system":
            output.append(f"[SYSTEM] {content}")
        elif role == "user":
            output.append(f"[USER] {content}")
        elif role == "assistant":
            if msg.get("tool_calls"):
                calls = ", ".join(
                    tc["name"] for tc in msg["tool_calls"]
                )
                output.append(f"[ASSISTANT] Calling tools: {calls}")
            else:
                output.append(f"[ASSISTANT] {content}")
        elif role == "tool":
            output.append(f"[TOOL] {content}")
    
    return "\n".join(output)

Provider Compatibility

Verifiers messages are compatible with:
  • OpenAI: Direct usage with openai.ChatCompletion.create()
  • Anthropic: Automatic conversion by AnthropicClient
  • Custom APIs: Can be serialized/deserialized as needed

Validation

Messages use Pydantic models for validation:
from verifiers.types import UserMessage

# Valid
msg = UserMessage(role="user", content="Hello")

# Invalid - will raise ValidationError
try:
    msg = UserMessage(role="assistant", content="Hello")
except ValidationError as e:
    print(e)

Dict-like Access

Message objects support dict-like access:
msg = vf.UserMessage(role="user", content="Hello")

# Dict-style access
role = msg["role"]           # "user"
content = msg.get("content") # "Hello"
"role" in msg                # True

# Attribute access
role = msg.role              # "user"
content = msg.content        # "Hello"

See Also

  • Response - API response format
  • State - Rollout state containing messages
  • Tool - Tool definition format

Build docs developers (and LLMs) love