跳过内容

用法

RequestUsage

Usage details for a single API request.

源代码位于 src/agents/usage.py
@dataclass
class RequestUsage:
    """Usage details for a single API request."""

    input_tokens: int
    """Input tokens for this individual request."""

    output_tokens: int
    """Output tokens for this individual request."""

    total_tokens: int
    """Total tokens (input + output) for this individual request."""

    input_tokens_details: InputTokensDetails
    """Details about the input tokens for this individual request."""

    output_tokens_details: OutputTokensDetails
    """Details about the output tokens for this individual request."""

input_tokens 实例属性

input_tokens: int

此单个请求的输入令牌数。

output_tokens 实例属性

output_tokens: int

此单个请求的输出令牌数。

total_tokens 实例属性

total_tokens: int

此单个请求的总令牌数(输入 + 输出)。

input_tokens_details 实例属性

input_tokens_details: InputTokensDetails

此单个请求的输入令牌详情。

output_tokens_details 实例属性

output_tokens_details: OutputTokensDetails

此单个请求的输出令牌详情。

用法

源代码位于 src/agents/usage.py
@dataclass
class Usage:
    requests: int = 0
    """Total requests made to the LLM API."""

    input_tokens: int = 0
    """Total input tokens sent, across all requests."""

    input_tokens_details: Annotated[
        InputTokensDetails, BeforeValidator(_normalize_input_tokens_details)
    ] = field(default_factory=lambda: InputTokensDetails(cached_tokens=0))
    """Details about the input tokens, matching responses API usage details."""
    output_tokens: int = 0
    """Total output tokens received, across all requests."""

    output_tokens_details: Annotated[
        OutputTokensDetails, BeforeValidator(_normalize_output_tokens_details)
    ] = field(default_factory=lambda: OutputTokensDetails(reasoning_tokens=0))
    """Details about the output tokens, matching responses API usage details."""

    total_tokens: int = 0
    """Total tokens sent and received, across all requests."""

    request_usage_entries: list[RequestUsage] = field(default_factory=list)
    """List of RequestUsage entries for accurate per-request cost calculation.

    Each call to `add()` automatically creates an entry in this list if the added usage
    represents a new request (i.e., has non-zero tokens).

    Example:
        For a run that makes 3 API calls with 100K, 150K, and 80K input tokens each,
        the aggregated `input_tokens` would be 330K, but `request_usage_entries` would
        preserve the [100K, 150K, 80K] breakdown, which could be helpful for detailed
        cost calculation or context window management.
    """

    def __post_init__(self) -> None:
        # Some providers don't populate optional token detail fields
        # (cached_tokens, reasoning_tokens), and the OpenAI SDK's generated
        # code can bypass Pydantic validation (e.g., via model_construct),
        # allowing None values. We normalize these to 0 to prevent TypeErrors.
        input_details_none = self.input_tokens_details is None
        input_cached_none = (
            not input_details_none and self.input_tokens_details.cached_tokens is None
        )
        if input_details_none or input_cached_none:
            self.input_tokens_details = InputTokensDetails(cached_tokens=0)

        output_details_none = self.output_tokens_details is None
        output_reasoning_none = (
            not output_details_none and self.output_tokens_details.reasoning_tokens is None
        )
        if output_details_none or output_reasoning_none:
            self.output_tokens_details = OutputTokensDetails(reasoning_tokens=0)

    def add(self, other: Usage) -> None:
        """Add another Usage object to this one, aggregating all fields.

        This method automatically preserves request_usage_entries.

        Args:
            other: The Usage object to add to this one.
        """
        self.requests += other.requests if other.requests else 0
        self.input_tokens += other.input_tokens if other.input_tokens else 0
        self.output_tokens += other.output_tokens if other.output_tokens else 0
        self.total_tokens += other.total_tokens if other.total_tokens else 0

        # Null guards for nested token details (other may bypass validation via model_construct)
        other_cached = (
            other.input_tokens_details.cached_tokens
            if other.input_tokens_details and other.input_tokens_details.cached_tokens
            else 0
        )
        other_reasoning = (
            other.output_tokens_details.reasoning_tokens
            if other.output_tokens_details and other.output_tokens_details.reasoning_tokens
            else 0
        )
        self_cached = (
            self.input_tokens_details.cached_tokens
            if self.input_tokens_details and self.input_tokens_details.cached_tokens
            else 0
        )
        self_reasoning = (
            self.output_tokens_details.reasoning_tokens
            if self.output_tokens_details and self.output_tokens_details.reasoning_tokens
            else 0
        )

        self.input_tokens_details = InputTokensDetails(cached_tokens=self_cached + other_cached)

        self.output_tokens_details = OutputTokensDetails(
            reasoning_tokens=self_reasoning + other_reasoning
        )

        # Automatically preserve request_usage_entries.
        # If the other Usage represents a single request with tokens, record it.
        if other.requests == 1 and other.total_tokens > 0:
            input_details = other.input_tokens_details or InputTokensDetails(cached_tokens=0)
            output_details = other.output_tokens_details or OutputTokensDetails(reasoning_tokens=0)
            request_usage = RequestUsage(
                input_tokens=other.input_tokens,
                output_tokens=other.output_tokens,
                total_tokens=other.total_tokens,
                input_tokens_details=input_details,
                output_tokens_details=output_details,
            )
            self.request_usage_entries.append(request_usage)
        elif other.request_usage_entries:
            # If the other Usage already has individual request breakdowns, merge them.
            self.request_usage_entries.extend(other.request_usage_entries)

requests 类属性 实例属性

requests: int = 0

发往 LLM API 的总请求数。

input_tokens 类属性 实例属性

input_tokens: int = 0

所有请求的总输入令牌数。

input_tokens_details 类属性 实例属性

input_tokens_details: Annotated[
    InputTokensDetails,
    BeforeValidator(_normalize_input_tokens_details),
] = field(
    default_factory=lambda: InputTokensDetails(
        cached_tokens=0
    )
)

输入令牌详情,与响应 API 使用详情匹配。

output_tokens 类属性 实例属性

output_tokens: int = 0

所有请求的总输出令牌数。

output_tokens_details 类属性 实例属性

output_tokens_details: Annotated[
    OutputTokensDetails,
    BeforeValidator(_normalize_output_tokens_details),
] = field(
    default_factory=lambda: OutputTokensDetails(
        reasoning_tokens=0
    )
)

输出令牌详情,与响应 API 使用详情匹配。

total_tokens 类属性 实例属性

total_tokens: int = 0

所有请求的总发送和接收令牌数。

request_usage_entries 类属性 实例属性

request_usage_entries: list[RequestUsage] = field(
    default_factory=list
)

RequestUsage 条目的列表,用于准确计算每个请求的成本。

每次调用 add() 都会在此列表中自动创建一个条目,如果添加的使用代表新的请求(即,具有非零令牌)。

示例

对于发出 3 个 API 调用,每个调用分别发送 100K、150K 和 80K 输入令牌的运行,聚合的 input_tokens 将为 330K,但 request_usage_entries 将保留 [100K, 150K, 80K] 的细分,这对于详细的成本计算或上下文窗口管理可能很有帮助。

添加

add(other: Usage) -> None

将另一个 Usage 对象添加到此对象,聚合所有字段。

此方法会自动保留 request_usage_entries。

参数

名称 类型 描述 默认
其他 用法

要添加到此对象的 Usage 对象。

required
源代码位于 src/agents/usage.py
def add(self, other: Usage) -> None:
    """Add another Usage object to this one, aggregating all fields.

    This method automatically preserves request_usage_entries.

    Args:
        other: The Usage object to add to this one.
    """
    self.requests += other.requests if other.requests else 0
    self.input_tokens += other.input_tokens if other.input_tokens else 0
    self.output_tokens += other.output_tokens if other.output_tokens else 0
    self.total_tokens += other.total_tokens if other.total_tokens else 0

    # Null guards for nested token details (other may bypass validation via model_construct)
    other_cached = (
        other.input_tokens_details.cached_tokens
        if other.input_tokens_details and other.input_tokens_details.cached_tokens
        else 0
    )
    other_reasoning = (
        other.output_tokens_details.reasoning_tokens
        if other.output_tokens_details and other.output_tokens_details.reasoning_tokens
        else 0
    )
    self_cached = (
        self.input_tokens_details.cached_tokens
        if self.input_tokens_details and self.input_tokens_details.cached_tokens
        else 0
    )
    self_reasoning = (
        self.output_tokens_details.reasoning_tokens
        if self.output_tokens_details and self.output_tokens_details.reasoning_tokens
        else 0
    )

    self.input_tokens_details = InputTokensDetails(cached_tokens=self_cached + other_cached)

    self.output_tokens_details = OutputTokensDetails(
        reasoning_tokens=self_reasoning + other_reasoning
    )

    # Automatically preserve request_usage_entries.
    # If the other Usage represents a single request with tokens, record it.
    if other.requests == 1 and other.total_tokens > 0:
        input_details = other.input_tokens_details or InputTokensDetails(cached_tokens=0)
        output_details = other.output_tokens_details or OutputTokensDetails(reasoning_tokens=0)
        request_usage = RequestUsage(
            input_tokens=other.input_tokens,
            output_tokens=other.output_tokens,
            total_tokens=other.total_tokens,
            input_tokens_details=input_details,
            output_tokens_details=output_details,
        )
        self.request_usage_entries.append(request_usage)
    elif other.request_usage_entries:
        # If the other Usage already has individual request breakdowns, merge them.
        self.request_usage_entries.extend(other.request_usage_entries)