Skip to content

Types

The frozen result types returned by the agentic loop: AgenticResult (the full outcome of an analyze() call), Turn (one model exchange), ToolCall (a requested tool invocation), and ToolResult (its outcome). All are immutable.

types

Core types for GAZE.

Provides dataclasses for tool calls, results, conversation turns, and agentic analysis results.

All data types use frozen=True and immutable containers (tuples, MappingProxyType) so that instances are safe to share across async tasks and cannot be accidentally mutated after construction.

ToolCall dataclass

Represents a tool call request from the model.

Attributes:

Name Type Description
id str

Unique identifier for this tool call

name str

Name of the tool to execute

arguments Mapping[str, Any] | str

JSON object or JSON string containing tool arguments

Source code in src/gaze/types.py
@dataclass(frozen=True)
class ToolCall:
    """Represents a tool call request from the model.

    Attributes:
        id: Unique identifier for this tool call
        name: Name of the tool to execute
        arguments: JSON object or JSON string containing tool arguments
    """

    id: str
    name: str
    arguments: Mapping[str, Any] | str

    def __post_init__(self) -> None:
        if not isinstance(self.arguments, str):
            object.__setattr__(self, "arguments", deep_freeze(self.arguments))

ToolResult dataclass

Result of executing a tool.

Attributes:

Name Type Description
tool_name str

Name of the tool that was executed

description str

Human-readable description of what happened

error str | None

Error message if execution failed, None if successful

image_base64 str | None

Base64-encoded image data if tool produced an image

image_mime_type str | None

MIME type of the image (e.g., 'image/png')

metadata Mapping[str, Any]

Additional tool-specific metadata

Source code in src/gaze/types.py
@dataclass(frozen=True)
class ToolResult:
    """Result of executing a tool.

    Attributes:
        tool_name: Name of the tool that was executed
        description: Human-readable description of what happened
        error: Error message if execution failed, None if successful
        image_base64: Base64-encoded image data if tool produced an image
        image_mime_type: MIME type of the image (e.g., 'image/png')
        metadata: Additional tool-specific metadata
    """

    tool_name: str
    description: str
    error: str | None = None
    image_base64: str | None = None
    image_mime_type: str | None = None
    metadata: Mapping[str, Any] = field(default_factory=lambda: {})
    _data_url: str | None = field(default=None, repr=False, compare=False)

    def __post_init__(self) -> None:
        if bool(self.image_base64) != bool(self.image_mime_type):
            raise ValueError("image_base64 and image_mime_type must both be set or both be None")
        # Deep-freeze metadata even when callers pass a pre-wrapped proxy.
        object.__setattr__(self, "metadata", deep_freeze(self.metadata))
        # Pre-compute data URL once to avoid re-creating the large string
        # on every call (consistent with EncodedImage._data_url caching).
        if self.image_base64 and self.image_mime_type:
            object.__setattr__(
                self,
                "_data_url",
                f"data:{self.image_mime_type};base64,{self.image_base64}",
            )

    @property
    def success(self) -> bool:
        """Tool succeeded if no error occurred."""
        return self.error is None

    def get_image_data_url(self) -> str | None:
        """Get data URL for image if present."""
        return self._data_url

    @property
    def formatted_results(self) -> str | None:
        """Optional formatted result string stored in metadata."""
        formatted = self.metadata.get("formatted_results")
        return str(formatted) if formatted is not None else None

success property

success: bool

Tool succeeded if no error occurred.

formatted_results property

formatted_results: str | None

Optional formatted result string stored in metadata.

get_image_data_url

get_image_data_url() -> str | None

Get data URL for image if present.

Source code in src/gaze/types.py
def get_image_data_url(self) -> str | None:
    """Get data URL for image if present."""
    return self._data_url

Turn dataclass

Represents a single turn in the agentic conversation.

Attributes:

Name Type Description
role TurnRole

Message role - must be 'user', 'assistant', or 'tool_result'

content str

Text content of the turn

tool_calls tuple[ToolCall, ...]

Tool calls made in this turn (assistant only)

tool_results tuple[ToolResult, ...]

Tool results (tool_result only)

image_base64 str | None

Optional image data for this turn

Source code in src/gaze/types.py
@dataclass(frozen=True)
class Turn:
    """Represents a single turn in the agentic conversation.

    Attributes:
        role: Message role - must be 'user', 'assistant', or 'tool_result'
        content: Text content of the turn
        tool_calls: Tool calls made in this turn (assistant only)
        tool_results: Tool results (tool_result only)
        image_base64: Optional image data for this turn
    """

    role: TurnRole
    content: str
    tool_calls: tuple[ToolCall, ...] = ()
    tool_results: tuple[ToolResult, ...] = ()
    image_base64: str | None = None

    def __post_init__(self) -> None:
        # Coerce sequences to tuples to enforce immutability.
        # Callers may pass lists for convenience; we freeze them here.
        if not isinstance(self.tool_calls, tuple):
            object.__setattr__(self, "tool_calls", tuple(self.tool_calls))
        if not isinstance(self.tool_results, tuple):
            object.__setattr__(self, "tool_results", tuple(self.tool_results))

RunConfig dataclass

Captures the configuration that produced an AgenticResult.

Stored on AgenticResult.run_config to enable reproducibility without relying on side-band summary files.

Attributes:

Name Type Description
model_name str

Model identifier used for generation.

temperature float

Sampling temperature (0.0 = greedy).

seed int | None

API seed parameter, or None if not set.

max_tokens int

Max completion tokens per turn.

max_turns int

Maximum agentic turns allowed.

Source code in src/gaze/types.py
@dataclass(frozen=True)
class RunConfig:
    """Captures the configuration that produced an AgenticResult.

    Stored on ``AgenticResult.run_config`` to enable reproducibility
    without relying on side-band summary files.

    Attributes:
        model_name: Model identifier used for generation.
        temperature: Sampling temperature (0.0 = greedy).
        seed: API seed parameter, or None if not set.
        max_tokens: Max completion tokens per turn.
        max_turns: Maximum agentic turns allowed.
    """

    model_name: str
    temperature: float
    seed: int | None
    max_tokens: int
    max_turns: int

AgenticResult dataclass

Result of an agentic analysis session.

Attributes:

Name Type Description
final_response Mapping[str, Any]

Complete JSON response from the model

turns tuple[Turn, ...]

All conversation turns in the analysis

total_tokens int

Total tokens consumed across all turns

confidence float

Overall confidence in the analysis (0.0-1.0)

run_config RunConfig | None

Configuration that produced this result (for reproducibility)

Source code in src/gaze/types.py
@dataclass(frozen=True)
class AgenticResult:
    """Result of an agentic analysis session.

    Attributes:
        final_response: Complete JSON response from the model
        turns: All conversation turns in the analysis
        total_tokens: Total tokens consumed across all turns
        confidence: Overall confidence in the analysis (0.0-1.0)
        run_config: Configuration that produced this result (for reproducibility)
    """

    final_response: Mapping[str, Any]
    turns: tuple[Turn, ...]
    total_tokens: int
    confidence: float
    run_config: RunConfig | None = None

    def __post_init__(self) -> None:
        # Deep-freeze mutable containers passed by callers.
        object.__setattr__(self, "final_response", deep_freeze(self.final_response))
        if not isinstance(self.turns, tuple):
            object.__setattr__(self, "turns", tuple(self.turns))
        # Guard against subclass calculate_confidence() returning out-of-range,
        # NaN, or infinity values.  The comparison deliberately uses chained
        # operators so that NaN (which is unordered) fails the check.
        if not (0.0 <= self.confidence <= 1.0):
            raise ValueError(f"confidence must be in [0.0, 1.0], got {self.confidence}")

    @property
    def num_turns(self) -> int:
        """Number of turns in the conversation."""
        return len(self.turns)

    @property
    def tool_call_count(self) -> int:
        """Total number of tool calls made."""
        return sum(len(t.tool_calls) for t in self.turns)

    def get_tools_used(self) -> set[str]:
        """Get set of unique tool names used in the analysis."""
        tools: set[str] = set()
        for turn in self.turns:
            for tc in turn.tool_calls:
                tools.add(tc.name)
        return tools

num_turns property

num_turns: int

Number of turns in the conversation.

tool_call_count property

tool_call_count: int

Total number of tool calls made.

get_tools_used

get_tools_used() -> set[str]

Get set of unique tool names used in the analysis.

Source code in src/gaze/types.py
def get_tools_used(self) -> set[str]:
    """Get set of unique tool names used in the analysis."""
    tools: set[str] = set()
    for turn in self.turns:
        for tc in turn.tool_calls:
            tools.add(tc.name)
    return tools