Create Relay Bot MVP
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import tiktoken
|
||||
|
||||
from app.database.models import Message, Summary
|
||||
|
||||
|
||||
class ContextBuilder:
|
||||
def __init__(self, token_limit: int):
|
||||
self.token_limit = token_limit
|
||||
try:
|
||||
self.encoder = tiktoken.get_encoding("cl100k_base")
|
||||
except Exception:
|
||||
self.encoder = None
|
||||
|
||||
def count_tokens(self, text: str) -> int:
|
||||
return len(self.encoder.encode(text)) if self.encoder else max(1, len(text) // 4)
|
||||
|
||||
def build(self, summary: Summary | None, messages: list[Message]) -> str:
|
||||
sections = [f"Current memory summary:\n{summary.text}" if summary else ""]
|
||||
recent: list[str] = []
|
||||
budget = self.token_limit - self.count_tokens(sections[0])
|
||||
for item in reversed(messages):
|
||||
line = f"[{item.sent_at.isoformat()}] {item.user_name}: {item.text}"
|
||||
cost = self.count_tokens(line)
|
||||
if cost > budget:
|
||||
break
|
||||
recent.append(line)
|
||||
budget -= cost
|
||||
if recent:
|
||||
sections.append("Recent messages:\n" + "\n".join(reversed(recent)))
|
||||
return "\n\n".join(part for part in sections if part)
|
||||
@@ -0,0 +1,49 @@
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from app.database.models import Message, TelegramUser
|
||||
from app.database.repositories import MemoryRepository
|
||||
|
||||
|
||||
class MemoryService:
|
||||
def __init__(self, repository: MemoryRepository):
|
||||
self.repository = repository
|
||||
|
||||
async def record(
|
||||
self,
|
||||
*,
|
||||
chat_id: int,
|
||||
message_id: int,
|
||||
thread_id: int | None,
|
||||
user_id: int | None,
|
||||
user_name: str,
|
||||
text: str,
|
||||
sent_at: datetime,
|
||||
reply_to: int | None,
|
||||
message_type: str,
|
||||
attachment_data: dict | None = None,
|
||||
edited: bool = False,
|
||||
) -> Message:
|
||||
if user_id is not None:
|
||||
user = await self.repository.session.get(TelegramUser, user_id)
|
||||
if user is None:
|
||||
self.repository.session.add(
|
||||
TelegramUser(telegram_id=user_id, display_name=user_name)
|
||||
)
|
||||
else:
|
||||
user.display_name = user_name
|
||||
return await self.repository.save_message(
|
||||
Message(
|
||||
chat_id=chat_id,
|
||||
telegram_message_id=message_id,
|
||||
thread_id=thread_id,
|
||||
user_id=user_id,
|
||||
user_name=user_name,
|
||||
text=text,
|
||||
sent_at=sent_at.astimezone(UTC),
|
||||
reply_to_message_id=reply_to,
|
||||
message_type=message_type,
|
||||
attachment_data=attachment_data,
|
||||
edited=edited,
|
||||
edited_at=datetime.now(UTC) if edited else None,
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
from app.database.models import Summary
|
||||
from app.database.repositories import MemoryRepository
|
||||
from app.llm.prompts import SUMMARY_PROMPT
|
||||
from app.memory.context_builder import ContextBuilder
|
||||
|
||||
|
||||
class Summarizer:
|
||||
def __init__(self, memory: MemoryRepository, builder: ContextBuilder, llm, trigger_tokens: int):
|
||||
self.memory, self.builder, self.llm, self.trigger_tokens = (
|
||||
memory,
|
||||
builder,
|
||||
llm,
|
||||
trigger_tokens,
|
||||
)
|
||||
|
||||
async def needs_summary(self, chat_id: int, thread_id: int | None) -> bool:
|
||||
messages = await self.memory.context_messages(chat_id, thread_id, limit=300)
|
||||
return self.builder.count_tokens("\n".join(m.text for m in messages)) >= self.trigger_tokens
|
||||
|
||||
async def summarize(self, chat_id: int, thread_id: int | None) -> Summary | None:
|
||||
previous = await self.memory.latest_summary(chat_id, thread_id)
|
||||
messages = await self.memory.context_messages(chat_id, thread_id, limit=300)
|
||||
if not messages:
|
||||
return None
|
||||
context = self.builder.build(previous, messages)
|
||||
text = await self.llm.complete(
|
||||
[{"role": "user", "content": f"{SUMMARY_PROMPT}\n\n{context}"}]
|
||||
)
|
||||
summary = Summary(
|
||||
chat_id=chat_id,
|
||||
thread_id=thread_id,
|
||||
version=(previous.version if previous else 0) + 1,
|
||||
text=text,
|
||||
from_message_id=messages[0].telegram_message_id,
|
||||
to_message_id=messages[-1].telegram_message_id,
|
||||
)
|
||||
await self.memory.save_summary(summary)
|
||||
return summary
|
||||
Reference in New Issue
Block a user