fix(claude_sdk,conversations,rotation): context size from the last api call, new-day text by rotation reason
This commit is contained in:
@@ -23,7 +23,7 @@ import logging
|
||||
import re
|
||||
import uuid
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import UTC, date, datetime, timedelta
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from claude_agent_sdk import (
|
||||
@@ -134,6 +134,19 @@ class SeedContext:
|
||||
title: str | None
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class NewDayContext:
|
||||
"""What the new master hears first.
|
||||
|
||||
``reason`` is ``ночь`` / ``возраст`` / ``транскрипт`` - only the first
|
||||
one is actually a new day.
|
||||
"""
|
||||
|
||||
day: date
|
||||
reason: str
|
||||
moved: int
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ConversationTexts:
|
||||
"""Texts the gateway cannot invent for a setup.
|
||||
@@ -155,7 +168,10 @@ class ConversationTexts:
|
||||
"Этот мастер закрывается ({reason}). Напиши хендаут за {day}: справку "
|
||||
"на утро, не задание - прошедшее время, без повелительного наклонения."
|
||||
)
|
||||
new_day: str = "Новый день: мастер сменился, хендаут за {day} записан."
|
||||
new_day: Callable[[NewDayContext], Awaitable[str] | str] | str = (
|
||||
"Мастер сменился ({reason}), хендаут за {day} записан."
|
||||
)
|
||||
"""First inject of the new master; a callable sees the rotation reason."""
|
||||
distill: Callable[[DistillContext], Awaitable[str] | str] | None = None
|
||||
"""The distiller fork's first message (§8.4): which chat, what day, where
|
||||
the digest goes; ``None`` uses a path-less default."""
|
||||
@@ -553,9 +569,7 @@ class Conversations:
|
||||
.limit(1)
|
||||
)
|
||||
).first()
|
||||
if row is None:
|
||||
return 0
|
||||
return row.input_tokens + row.cache_read_tokens + row.cache_creation_tokens
|
||||
return context_of(row)
|
||||
|
||||
async def usage_tokens(self, since: datetime) -> int:
|
||||
async with self._db.session() as session:
|
||||
@@ -1213,9 +1227,18 @@ class Conversations:
|
||||
_log.exception("%s could not mark %s", fe.name, conv.external_id)
|
||||
return marked
|
||||
|
||||
async def new_day(self, conv: Conversation, *, moved: int = 0) -> InjectQueueItem:
|
||||
day = datetime.now(UTC).astimezone().date().isoformat()
|
||||
text = self._texts.new_day.format(day=day)
|
||||
async def new_day(
|
||||
self, conv: Conversation, *, reason: str = "ночь", moved: int = 0
|
||||
) -> InjectQueueItem:
|
||||
ctx = NewDayContext(
|
||||
day=datetime.now(UTC).astimezone().date(), reason=reason, moved=moved
|
||||
)
|
||||
source = self._texts.new_day
|
||||
if isinstance(source, str):
|
||||
text = source.format(day=ctx.day.isoformat(), reason=ctx.reason)
|
||||
else:
|
||||
produced: Any = source(ctx)
|
||||
text = await produced if inspect.isawaitable(produced) else produced
|
||||
if moved:
|
||||
text += f" Инжектов переехало из старого мастера: {moved}."
|
||||
return await self.inject(
|
||||
@@ -1902,3 +1925,17 @@ def _usage_dict(capture: TurnCapture) -> dict[str, Any] | None:
|
||||
"cost_usd": usage.cost_usd,
|
||||
"duration_ms": usage.duration_ms,
|
||||
}
|
||||
|
||||
|
||||
def context_of(row: Usage | None) -> int:
|
||||
"""Context size of a turn.
|
||||
|
||||
The last API call's input, or, for rows written before it was recorded,
|
||||
the per-call average of the turn's input sums.
|
||||
"""
|
||||
if row is None:
|
||||
return 0
|
||||
if row.context_tokens:
|
||||
return row.context_tokens
|
||||
total = row.input_tokens + row.cache_read_tokens + row.cache_creation_tokens
|
||||
return round(total / max(row.num_turns or 1, 1))
|
||||
|
||||
Reference in New Issue
Block a user