feat: mcp and backfill fixes
This commit is contained in:
1 parent
c6984a7286
commit
17cd31c41e
20 files changed
+566
-37
No files matched your search
@@ -6,12 +6,15 @@ import asyncpg
|
||||
from fastmcp import FastMCP
|
||||
from pydantic import BaseModel
|
||||
|
||||
from api.mcp.format import build_transcript, load_notes, load_photos, resolve_names
|
||||
from dependencies.container import container
|
||||
from utils.jobs import enqueue
|
||||
from utils.read import annotations, chats, media, peers, presence, social, watches
|
||||
from utils.read.accounts import self_user_id
|
||||
from utils.read.models import DEFAULT_LIMIT, Page
|
||||
from utils.search.models import SearchFilters
|
||||
from utils.search.repository import search_messages
|
||||
from utils.storage import ContentAddressedStorage
|
||||
|
||||
INSTRUCTIONS = """\
|
||||
beavergram archives Telegram data (chats, messages, media, presence, stories,
|
||||
@@ -27,7 +30,18 @@ Identifiers:
|
||||
tools that need a specific chat.
|
||||
|
||||
Typical flows:
|
||||
- Browse: `list_chats` -> pick a `chat_id` -> `get_chat_history`.
|
||||
- Read a chat: `list_chats` -> pick a `chat_id` -> `get_chat_history` for a
|
||||
human-readable transcript (names, timestamps, voice STT, inline photos), or
|
||||
`get_chat_history_raw` for structured JSON. Both return messages oldest
|
||||
first; default gives the latest `limit`. Page to older messages with
|
||||
`before_id` (first id you saw), to newer with `after_id` (last id you saw);
|
||||
begin a full forward walk with `after_id=0`. Each transcript line starts
|
||||
with its `#message_id`, and photos show as `[photo #message_id]` — pass that
|
||||
id to `get_media` (with `fetch=True` to download) or to annotation tools.
|
||||
Lines marked
|
||||
"📝 [your private note, NOT in Telegram]" are the user's own annotations
|
||||
attached locally via the web UI; they never existed in Telegram. Treat them
|
||||
as private notes from the user to you, not as chat content.
|
||||
- Find something: `search_messages_tool` (full-text over message text and STT
|
||||
transcripts; supports chat/sender/date filters and regex).
|
||||
- Forensics: `get_deleted_messages` and `get_message_versions` recover content
|
||||
@@ -91,24 +105,80 @@ async def list_chats(
|
||||
return _dump(await chats.list_chats(await _pool(), account_id, page))
|
||||
|
||||
|
||||
@mcp.tool
|
||||
@mcp.tool(output_schema=None)
|
||||
async def get_chat_history(
|
||||
account_id: int,
|
||||
chat_id: int,
|
||||
limit: int = DEFAULT_LIMIT,
|
||||
offset: int = 0,
|
||||
before_id: int | None = None,
|
||||
after_id: int | None = None,
|
||||
include_deleted: bool = True,
|
||||
include_images: bool = True,
|
||||
max_images: int = 20,
|
||||
) -> list[Any]:
|
||||
"""Read a chat as a readable transcript, oldest first.
|
||||
|
||||
Renders "Name (time): text", voice notes as
|
||||
"(Voice message, STT Content: ...)", and inlines downloaded photos as
|
||||
images. Default returns the latest `limit` messages; page to older
|
||||
messages with `before_id` (the first id you saw) or to newer ones with
|
||||
`after_id` (the last id you saw).
|
||||
"""
|
||||
pool = await _pool()
|
||||
views = await chats.get_chat_history(
|
||||
pool,
|
||||
account_id,
|
||||
chat_id,
|
||||
Page(limit=limit),
|
||||
include_deleted=include_deleted,
|
||||
before_id=before_id,
|
||||
after_id=after_id,
|
||||
)
|
||||
if after_id is None:
|
||||
views = list(reversed(views))
|
||||
names = await resolve_names(pool, account_id, views)
|
||||
notes = await load_notes(pool, account_id, chat_id, views)
|
||||
self_id = await self_user_id(pool, account_id)
|
||||
photos: list[tuple[int, bytes, str]] = []
|
||||
truncated = False
|
||||
if include_images:
|
||||
storage = await container.get(ContentAddressedStorage)
|
||||
photos, truncated = await load_photos(
|
||||
pool, storage, account_id, views, limit=max_images
|
||||
)
|
||||
return build_transcript(
|
||||
views, names, self_id, photos, notes=notes, truncated=truncated
|
||||
)
|
||||
|
||||
|
||||
@mcp.tool
|
||||
async def get_chat_history_raw(
|
||||
account_id: int,
|
||||
chat_id: int,
|
||||
limit: int = DEFAULT_LIMIT,
|
||||
before_id: int | None = None,
|
||||
after_id: int | None = None,
|
||||
include_deleted: bool = True,
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Read archived messages of a chat, newest first."""
|
||||
return _dump(
|
||||
await chats.get_chat_history(
|
||||
await _pool(),
|
||||
account_id,
|
||||
chat_id,
|
||||
Page(limit=limit, offset=offset),
|
||||
include_deleted=include_deleted,
|
||||
)
|
||||
"""Structured chat messages as JSON, oldest first.
|
||||
|
||||
Default returns the latest `limit` messages. Walk the whole chat forward
|
||||
with `after_id` set to the last returned message_id (begin at
|
||||
`after_id=0`), or backward with `before_id` set to the first returned
|
||||
message_id.
|
||||
"""
|
||||
views = await chats.get_chat_history(
|
||||
await _pool(),
|
||||
account_id,
|
||||
chat_id,
|
||||
Page(limit=limit),
|
||||
include_deleted=include_deleted,
|
||||
before_id=before_id,
|
||||
after_id=after_id,
|
||||
)
|
||||
if after_id is None:
|
||||
views = list(reversed(views))
|
||||
return _dump(views)
|
||||
|
||||
|
||||
@mcp.tool
|
||||
|
||||
Reference in new issue
Block a user