TeLLMgramBot 3.15.2__tar.gz → 3.15.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/PKG-INFO +2 -1
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/README.md +1 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/TeLLMgramBot.py +96 -9
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/initialize.py +2 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot.egg-info/PKG-INFO +2 -1
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/setup.py +1 -1
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/LICENSE +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/__init__.py +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/archive.py +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/conversation.py +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/database.py +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/message_handlers.py +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/models.py +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/providers/__init__.py +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/providers/anthropic_provider.py +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/providers/base.py +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/providers/factory.py +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/providers/openai_provider.py +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/tools.py +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/utils.py +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot/web_utils.py +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot.egg-info/SOURCES.txt +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot.egg-info/dependency_links.txt +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot.egg-info/requires.txt +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/TeLLMgramBot.egg-info/top_level.txt +0 -0
- {tellmgrambot-3.15.2 → tellmgrambot-3.15.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TeLLMgramBot
|
|
3
|
-
Version: 3.15.
|
|
3
|
+
Version: 3.15.4
|
|
4
4
|
Summary: LLM-powered Telegram bot (OpenAI + Anthropic)
|
|
5
5
|
Home-page: https://github.com/Digital-Heresy/TeLLMgramBot
|
|
6
6
|
Author: Digital Heresy
|
|
@@ -168,6 +168,7 @@ When the bot is triggered in a group and about to respond (not deferring to anot
|
|
|
168
168
|
- `archive_days`: Days before messages are eligible for archival (optional; default 60, minimum 1). Older messages are distilled into daily summaries, then progressively compressed into monthly digests. Once archived their respective raw messages do not return to the LLM context any more, only when searching messages.
|
|
169
169
|
- `document_processing`: Optional bool (default: true). Set to false to disable document and text file summarisation.
|
|
170
170
|
- `allow_local_webhooks`: Set to `true` to permit webhook/MCP URLs targeting loopback or link-local addresses (optional; default `false`). Useful when tools like Home Assistant run on the same host.
|
|
171
|
+
- `max_conversations`: Optional max chats kept in memory at once (default: 500, minimum 1). Least-recently-used chats beyond this cap are evicted and reload from the database on their next message. Useful for deployments with memory constraints; evicted chats retain all persisted data.
|
|
171
172
|
- `tools`: Optional list of webhook and MCP tool definitions (admin-only, private chat only). See [docs/tools.md](docs/tools.md) for schema and examples.
|
|
172
173
|
4. **Disable group privacy mode in BotFather:**
|
|
173
174
|
```
|
|
@@ -131,6 +131,7 @@ When the bot is triggered in a group and about to respond (not deferring to anot
|
|
|
131
131
|
- `archive_days`: Days before messages are eligible for archival (optional; default 60, minimum 1). Older messages are distilled into daily summaries, then progressively compressed into monthly digests. Once archived their respective raw messages do not return to the LLM context any more, only when searching messages.
|
|
132
132
|
- `document_processing`: Optional bool (default: true). Set to false to disable document and text file summarisation.
|
|
133
133
|
- `allow_local_webhooks`: Set to `true` to permit webhook/MCP URLs targeting loopback or link-local addresses (optional; default `false`). Useful when tools like Home Assistant run on the same host.
|
|
134
|
+
- `max_conversations`: Optional max chats kept in memory at once (default: 500, minimum 1). Least-recently-used chats beyond this cap are evicted and reload from the database on their next message. Useful for deployments with memory constraints; evicted chats retain all persisted data.
|
|
134
135
|
- `tools`: Optional list of webhook and MCP tool definitions (admin-only, private chat only). See [docs/tools.md](docs/tools.md) for schema and examples.
|
|
135
136
|
4. **Disable group privacy mode in BotFather:**
|
|
136
137
|
```
|
|
@@ -5,6 +5,7 @@ import logging
|
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
7
|
import re
|
|
8
|
+
from collections import OrderedDict
|
|
8
9
|
from math import floor
|
|
9
10
|
|
|
10
11
|
from telegram import Bot, Update, Message, Chat, User, ReactionTypeEmoji, MessageEntity, InlineKeyboardButton, InlineKeyboardMarkup
|
|
@@ -79,6 +80,23 @@ def _validated_allow_local(value) -> bool:
|
|
|
79
80
|
return value is True
|
|
80
81
|
|
|
81
82
|
|
|
83
|
+
def _validated_max_conversations(value) -> int:
|
|
84
|
+
"""
|
|
85
|
+
Validate the max_conversations config value, defaulting to 500 on invalid input.
|
|
86
|
+
|
|
87
|
+
Caps how many Conversation objects self.conversations keeps in memory at once;
|
|
88
|
+
least-recently-used chats beyond this cap are evicted and reload from DB on
|
|
89
|
+
their next message.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
value: Raw `max_conversations` value from bot config.
|
|
93
|
+
"""
|
|
94
|
+
if value is not None and not (type(value) is int and value >= 1):
|
|
95
|
+
logger.warning(f"Invalid max_conversations '{value}' (must be an integer >= 1); using default 500")
|
|
96
|
+
return 500
|
|
97
|
+
return value if value is not None else 500
|
|
98
|
+
|
|
99
|
+
|
|
82
100
|
_SEARCH_TOOL = {
|
|
83
101
|
"name": "search_messages",
|
|
84
102
|
"description": (
|
|
@@ -438,6 +456,12 @@ class TelegramBot:
|
|
|
438
456
|
session (get_past_interaction) or checks for new messages since the last load
|
|
439
457
|
(refresh_user_context).
|
|
440
458
|
|
|
459
|
+
self.conversations is an OrderedDict capped at self.llm['max_conversations'] entries.
|
|
460
|
+
Every call moves chat_id to the most-recently-used end; inserting past the cap evicts
|
|
461
|
+
the least-recently-used chat (and its token_warning entry) from memory. Eviction never
|
|
462
|
+
deletes persisted data - an evicted chat's next message cold-loads from DB exactly like
|
|
463
|
+
a chat the bot has never seen before.
|
|
464
|
+
|
|
441
465
|
Args:
|
|
442
466
|
chat_id: Telegram chat ID.
|
|
443
467
|
chat_type: 'private', 'group', or 'supergroup'.
|
|
@@ -447,10 +471,15 @@ class TelegramBot:
|
|
|
447
471
|
Returns:
|
|
448
472
|
The active Conversation for this chat.
|
|
449
473
|
"""
|
|
450
|
-
if chat_id
|
|
474
|
+
if chat_id in self.conversations:
|
|
475
|
+
self.conversations.move_to_end(chat_id)
|
|
476
|
+
else:
|
|
451
477
|
self.conversations[chat_id] = Conversation(
|
|
452
478
|
chat_id, chat_type, self.llm['prompt'], self.llm['chat_model'], chat_title,
|
|
453
479
|
)
|
|
480
|
+
if len(self.conversations) > self.llm['max_conversations']:
|
|
481
|
+
evicted_id, _ = self.conversations.popitem(last=False)
|
|
482
|
+
self.token_warning.pop(evicted_id, None)
|
|
454
483
|
conv = self.conversations[chat_id]
|
|
455
484
|
conv.update_datetime()
|
|
456
485
|
|
|
@@ -1250,12 +1279,60 @@ class TelegramBot:
|
|
|
1250
1279
|
|
|
1251
1280
|
Registered as python-telegram-bot's post_init hook so a large archival backlog or slow/unreachable MCP
|
|
1252
1281
|
servers never block tele_handle_message/tele_handle_document from answering the first incoming update.
|
|
1253
|
-
|
|
1254
|
-
|
|
1282
|
+
|
|
1283
|
+
Schedules both tasks via _schedule_background_task(), which wraps asyncio.create_task()
|
|
1284
|
+
directly. PTB's own Application.create_task() only tracks a task for graceful shutdown
|
|
1285
|
+
once application.running is True; post_init runs before Application.start() sets that
|
|
1286
|
+
flag, so scheduling through Application.create_task() here would go untracked anyway and
|
|
1287
|
+
emit a PTBUserWarning. Task references are kept in self._background_tasks until each task
|
|
1288
|
+
completes, since asyncio does not hold a strong reference to a task on your behalf.
|
|
1255
1289
|
"""
|
|
1256
1290
|
if self._mcp_entries:
|
|
1257
|
-
|
|
1258
|
-
|
|
1291
|
+
self._schedule_background_task(self._discover_mcp_tools_background())
|
|
1292
|
+
self._schedule_background_task(run_archival(self.llm))
|
|
1293
|
+
|
|
1294
|
+
def _schedule_background_task(self, coro) -> asyncio.Task:
|
|
1295
|
+
"""
|
|
1296
|
+
Run coro as a fire-and-forget asyncio task, keeping a reference until it completes.
|
|
1297
|
+
|
|
1298
|
+
asyncio does not keep a strong reference to a task once its creator's local
|
|
1299
|
+
variable goes out of scope, so a task can be garbage-collected mid-execution
|
|
1300
|
+
without this. The done-callback removes the task from self._background_tasks
|
|
1301
|
+
and retrieves any exception via _on_background_task_done(), so an unhandled
|
|
1302
|
+
failure surfaces through our own logging instead of asyncio's "Task exception
|
|
1303
|
+
was never retrieved" warning on garbage collection.
|
|
1304
|
+
|
|
1305
|
+
Args:
|
|
1306
|
+
coro: The coroutine to schedule.
|
|
1307
|
+
|
|
1308
|
+
Returns:
|
|
1309
|
+
The created asyncio.Task.
|
|
1310
|
+
"""
|
|
1311
|
+
task = asyncio.create_task(coro)
|
|
1312
|
+
self._background_tasks.add(task)
|
|
1313
|
+
task.add_done_callback(self._on_background_task_done)
|
|
1314
|
+
return task
|
|
1315
|
+
|
|
1316
|
+
def _on_background_task_done(self, task: asyncio.Task) -> None:
|
|
1317
|
+
"""
|
|
1318
|
+
Done-callback for _schedule_background_task(): discard the reference and log
|
|
1319
|
+
any unhandled exception.
|
|
1320
|
+
|
|
1321
|
+
Retrieving the exception here (rather than leaving it unread) prevents asyncio's
|
|
1322
|
+
default exception handler from emitting "Task exception was never retrieved" when
|
|
1323
|
+
the task is garbage-collected. A cancelled task has no exception to retrieve -
|
|
1324
|
+
task.exception() raises CancelledError in that case, so cancellation is checked
|
|
1325
|
+
first and treated as a normal, silent outcome.
|
|
1326
|
+
|
|
1327
|
+
Args:
|
|
1328
|
+
task: The completed (or cancelled) background task.
|
|
1329
|
+
"""
|
|
1330
|
+
self._background_tasks.discard(task)
|
|
1331
|
+
if task.cancelled():
|
|
1332
|
+
return
|
|
1333
|
+
exc = task.exception()
|
|
1334
|
+
if exc is not None:
|
|
1335
|
+
log_error(exc, 'BackgroundTask')
|
|
1259
1336
|
|
|
1260
1337
|
async def _discover_mcp_tools_background(self) -> None:
|
|
1261
1338
|
"""
|
|
@@ -1298,6 +1375,7 @@ class TelegramBot:
|
|
|
1298
1375
|
persona_temp = INIT_BOT_CONFIG['persona_temp'],
|
|
1299
1376
|
archive_days = INIT_BOT_CONFIG['archive_days'],
|
|
1300
1377
|
document_processing = INIT_BOT_CONFIG['document_processing'],
|
|
1378
|
+
max_conversations = INIT_BOT_CONFIG['max_conversations'],
|
|
1301
1379
|
persona_prompt = INIT_BOT_CONFIG['persona_prompt'],
|
|
1302
1380
|
key_status: ApiKeyStatus | None = None,
|
|
1303
1381
|
instance_name: str | None = None,
|
|
@@ -1326,6 +1404,9 @@ class TelegramBot:
|
|
|
1326
1404
|
archive_days: Days before messages are eligible for Tier 1 archival (default: 60).
|
|
1327
1405
|
Must be an integer >= 1; invalid values log a warning and fall back to 60.
|
|
1328
1406
|
Tier 2 compression triggers at archive_days * 2.
|
|
1407
|
+
max_conversations: Max chats kept in self.conversations at once (default: 500).
|
|
1408
|
+
Validated by _validated_max_conversations(); least-recently-used
|
|
1409
|
+
chats beyond this cap are evicted in _get_or_load_conversation().
|
|
1329
1410
|
webhook_schemas: Provider-compatible tool schema dicts for webhook tools (from build_tool_registry).
|
|
1330
1411
|
If None, no webhook tools are registered.
|
|
1331
1412
|
webhook_defs: Resolved webhook tool definitions keyed by tool name (from build_tool_registry).
|
|
@@ -1348,11 +1429,14 @@ class TelegramBot:
|
|
|
1348
1429
|
|
|
1349
1430
|
# Initialize some variables
|
|
1350
1431
|
self.token_warning = {} # Determines whether user has reached token limit by AI model
|
|
1351
|
-
|
|
1432
|
+
# Provides Conversation class per chat_id; an OrderedDict so _get_or_load_conversation()
|
|
1433
|
+
# can track recency via move_to_end() and evict the least-recently-used entry on overflow.
|
|
1434
|
+
self.conversations: OrderedDict = OrderedDict()
|
|
1352
1435
|
self.webhook_schemas = webhook_schemas or [] # Provider-compatible schemas for webhook tools
|
|
1353
1436
|
self.webhook_defs = webhook_defs or {} # Resolved tool definitions keyed by name
|
|
1354
1437
|
self._mcp_entries = mcp_entries or [] # Raw mcp_server entries; discovered in _post_init()
|
|
1355
1438
|
self._allow_local_webhooks = allow_local_webhooks
|
|
1439
|
+
self._background_tasks: set = set() # Keeps fire-and-forget tasks referenced until done
|
|
1356
1440
|
owners = bot_owner if isinstance(bot_owner, list) else [bot_owner]
|
|
1357
1441
|
self.telegram = {
|
|
1358
1442
|
'bot_id' : 0, # overwritten by _tele_info(); 0 is a safe sentinel
|
|
@@ -1366,13 +1450,14 @@ class TelegramBot:
|
|
|
1366
1450
|
}
|
|
1367
1451
|
# Check for event running loops before getting the bot's information
|
|
1368
1452
|
try:
|
|
1369
|
-
|
|
1453
|
+
asyncio.get_running_loop()
|
|
1370
1454
|
except RuntimeError:
|
|
1371
1455
|
# No running event loop; safe to run synchronously
|
|
1372
1456
|
asyncio.run(self._tele_info())
|
|
1373
1457
|
else:
|
|
1374
|
-
# Already in an event loop; schedule as a background task
|
|
1375
|
-
|
|
1458
|
+
# Already in an event loop; schedule as a background task, keeping a
|
|
1459
|
+
# reference via self._background_tasks so it isn't garbage-collected mid-run
|
|
1460
|
+
self._schedule_background_task(self._tele_info())
|
|
1376
1461
|
|
|
1377
1462
|
# Build our application with handlers for Commands, Messages, and Errors
|
|
1378
1463
|
self.telegram['app'] = (
|
|
@@ -1422,6 +1507,7 @@ class TelegramBot:
|
|
|
1422
1507
|
'top_p' : 0.9,
|
|
1423
1508
|
'archive_days' : archive_days if archive_days is not None else 60,
|
|
1424
1509
|
'document_processing' : document_processing if document_processing is not None else True,
|
|
1510
|
+
'max_conversations' : _validated_max_conversations(max_conversations),
|
|
1425
1511
|
}
|
|
1426
1512
|
# Set a rounded-down integer to prune a lengthy conversation by 500 tokens
|
|
1427
1513
|
# Note if the upper limit is below 500, the lower limit is set to 0
|
|
@@ -1499,6 +1585,7 @@ class TelegramBot:
|
|
|
1499
1585
|
persona_temp = config['persona_temp'],
|
|
1500
1586
|
archive_days = config['archive_days'],
|
|
1501
1587
|
document_processing = config['document_processing'],
|
|
1588
|
+
max_conversations = config['max_conversations'],
|
|
1502
1589
|
persona_prompt = prompt,
|
|
1503
1590
|
key_status = key_status,
|
|
1504
1591
|
instance_name = config['instance_name'],
|
|
@@ -113,6 +113,7 @@ INIT_BOT_CONFIG = {
|
|
|
113
113
|
'archive_days': None,
|
|
114
114
|
'allow_local_webhooks': None,
|
|
115
115
|
'document_processing': None,
|
|
116
|
+
'max_conversations': None,
|
|
116
117
|
'persona_prompt': 'You are a generic test bot powered by a user-configured LLM.'
|
|
117
118
|
}
|
|
118
119
|
|
|
@@ -124,6 +125,7 @@ INIT_BOT_CONFIG_COMMENTS = {
|
|
|
124
125
|
'archive_days': '# Optional, days before messages are eligible for Tier 1 archival (default: 60, min: 1). Tier 2 triggers at 2x this value.',
|
|
125
126
|
'allow_local_webhooks': '# Optional, set to true to permit webhook/MCP URLs targeting loopback or link-local addresses (default: false)',
|
|
126
127
|
'document_processing': '# Optional, set to false to disable document summarisation (default: true)',
|
|
128
|
+
'max_conversations': '# Optional, max chats kept in memory at once; least-recently-used chats beyond this cap are evicted and reload from DB on their next message (default: 500, min: 1)',
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
# Append the framework-owned system appendix to the persona prompt.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TeLLMgramBot
|
|
3
|
-
Version: 3.15.
|
|
3
|
+
Version: 3.15.4
|
|
4
4
|
Summary: LLM-powered Telegram bot (OpenAI + Anthropic)
|
|
5
5
|
Home-page: https://github.com/Digital-Heresy/TeLLMgramBot
|
|
6
6
|
Author: Digital Heresy
|
|
@@ -168,6 +168,7 @@ When the bot is triggered in a group and about to respond (not deferring to anot
|
|
|
168
168
|
- `archive_days`: Days before messages are eligible for archival (optional; default 60, minimum 1). Older messages are distilled into daily summaries, then progressively compressed into monthly digests. Once archived their respective raw messages do not return to the LLM context any more, only when searching messages.
|
|
169
169
|
- `document_processing`: Optional bool (default: true). Set to false to disable document and text file summarisation.
|
|
170
170
|
- `allow_local_webhooks`: Set to `true` to permit webhook/MCP URLs targeting loopback or link-local addresses (optional; default `false`). Useful when tools like Home Assistant run on the same host.
|
|
171
|
+
- `max_conversations`: Optional max chats kept in memory at once (default: 500, minimum 1). Least-recently-used chats beyond this cap are evicted and reload from the database on their next message. Useful for deployments with memory constraints; evicted chats retain all persisted data.
|
|
171
172
|
- `tools`: Optional list of webhook and MCP tool definitions (admin-only, private chat only). See [docs/tools.md](docs/tools.md) for schema and examples.
|
|
172
173
|
4. **Disable group privacy mode in BotFather:**
|
|
173
174
|
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|