zett_agent.extensions.usage_activity 源代码
"""Record provider token usage as daily activity without coupling storage backends."""
from __future__ import annotations
from collections.abc import Callable
from datetime import date, datetime
from typing import Protocol
from pydantic import BaseModel, ConfigDict, Field
from .._compat import UTC
from ..agent import AgentRunContext
from ..model import ModelResponse
from .base import AgentExtension
[文档]
class ModelUsageActivityRecord(BaseModel):
"""One completed model request recorded for activity visualization."""
model_config = ConfigDict(frozen=True)
session_id: str
request_id: str | None
provider: str | None
model: str | None
occurred_at: datetime
input_tokens: int = Field(ge=0)
output_tokens: int = Field(ge=0)
cache_read_tokens: int = Field(ge=0)
cache_write_tokens: int = Field(ge=0)
reasoning_tokens: int = Field(ge=0)
@property
def total_tokens(self) -> int:
"""Return the tokens represented by this request."""
return self.input_tokens + self.output_tokens
[文档]
class ModelUsageActivityDay(BaseModel):
"""Aggregated model usage for one UTC calendar day."""
model_config = ConfigDict(frozen=True)
date: date
requests: int = Field(ge=0)
input_tokens: int = Field(ge=0)
output_tokens: int = Field(ge=0)
cache_read_tokens: int = Field(ge=0)
cache_write_tokens: int = Field(ge=0)
reasoning_tokens: int = Field(ge=0)
@property
def total_tokens(self) -> int:
"""Return input plus output tokens for this day."""
return self.input_tokens + self.output_tokens
[文档]
class ModelUsageActivityStorage(Protocol):
"""Persistence boundary consumed by :class:`UsageActivityExtension`."""
[文档]
async def record(self, record: ModelUsageActivityRecord) -> None:
"""Persist one completed model request."""
[文档]
async def activity(self, *, start: date, end: date) -> list[ModelUsageActivityDay]:
"""Return daily aggregates in the inclusive date range."""
...
[文档]
class UsageActivityExtension(AgentExtension):
"""Record every completed model request through an injected storage adapter."""
def __init__(
self,
storage: ModelUsageActivityStorage,
*,
clock: Callable[[], datetime] | None = None,
) -> None:
self.storage = storage
self.clock = clock or (lambda: datetime.now(UTC))
[文档]
async def after_model(self, context: AgentRunContext, response: ModelResponse) -> None:
"""Persist provider-reported usage for one completed primary model call."""
usage = response.usage
await self.storage.record(
ModelUsageActivityRecord(
session_id=context.session_id,
request_id=context.config.request_id,
provider=response.message.provider,
model=response.message.model,
occurred_at=self.clock(),
input_tokens=usage.input_tokens,
output_tokens=usage.output_tokens,
cache_read_tokens=usage.cache_read_tokens,
cache_write_tokens=usage.cache_write_tokens,
reasoning_tokens=usage.reasoning_tokens,
)
)
[文档]
async def activity(self, *, start: date, end: date) -> list[ModelUsageActivityDay]:
"""Read daily activity through the configured storage."""
return await self.storage.activity(start=start, end=end)