Source code for oumi.analyze.analyzers.turn_stats
# Copyright 2025 - Oumi
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Turn statistics analyzer implementation and result model."""
from pydantic import BaseModel, Field
from oumi.analyze.base import ConversationAnalyzer
from oumi.core.registry import register_sample_analyzer
from oumi.core.types.conversation import Conversation, Role
__all__ = ["TurnStatsMetrics", "TurnStatsAnalyzer"]
[docs]
class TurnStatsMetrics(BaseModel):
"""Result model for turn statistics analysis of conversations.
Example:
>>> result = TurnStatsMetrics(
... num_turns=4,
... num_user_turns=2,
... num_assistant_turns=2,
... has_system_message=False,
... first_turn_role="user",
... last_turn_role="assistant",
... )
>>> print(result.num_turns)
4
"""
num_turns: int = Field(
description="Total number of turns (messages) in the conversation"
)
num_user_turns: int = Field(description="Number of user turns in the conversation")
num_assistant_turns: int = Field(
description="Number of assistant turns in the conversation"
)
num_tool_turns: int = Field(
default=0, description="Number of tool turns in the conversation"
)
has_system_message: bool = Field(
description="Whether the conversation has a system message"
)
first_turn_role: str | None = Field(
default=None,
description="Role of the first message in the conversation, or None if empty",
)
last_turn_role: str | None = Field(
default=None,
description="Role of the last message in the conversation, or None if empty",
)
[docs]
@register_sample_analyzer("turn_stats")
class TurnStatsAnalyzer(ConversationAnalyzer[TurnStatsMetrics]):
"""Analyzer for computing turn statistics of conversations.
Computes turn counts and per-role statistics to help understand
conversation structure and balance.
Example:
>>> from oumi.analyze.analyzers.turn_stats import TurnStatsAnalyzer
>>> from oumi.core.types.conversation import Conversation, Message, Role
>>>
>>> analyzer = TurnStatsAnalyzer()
>>> conversation = Conversation(messages=[
... Message(role=Role.USER, content="What is Python?"),
... Message(
... role=Role.ASSISTANT,
... content="Python is a programming language.",
... ),
... ])
>>> result = analyzer.analyze(conversation)
>>> print(f"Turns: {result.num_turns}")
Turns: 2
"""
[docs]
def analyze(self, conversation: Conversation) -> TurnStatsMetrics:
"""Analyze turn statistics for a conversation.
Args:
conversation: The conversation to analyze.
Returns:
TurnStatsMetrics containing turn counts and statistics.
"""
role_counts: dict[Role, int] = {role: 0 for role in Role}
first_role: str | None = None
last_role: str | None = None
for i, message in enumerate(conversation.messages):
if i == 0:
first_role = message.role.value
last_role = message.role.value
if message.role in role_counts:
role_counts[message.role] += 1
return TurnStatsMetrics(
num_turns=len(conversation.messages),
num_user_turns=role_counts[Role.USER],
num_assistant_turns=role_counts[Role.ASSISTANT],
num_tool_turns=role_counts[Role.TOOL],
has_system_message=role_counts[Role.SYSTEM] > 0,
first_turn_role=first_role,
last_turn_role=last_role,
)