obol-ai 0.2.27 → 0.2.28

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 0.2.28
2
+ - fix telegram 429 rate limiting with auto-retry, slower timers, verbose batching
3
+
1
4
  ## 0.2.26
2
5
  - show cleaning status instead of processing during /clean
3
6
  - fix ask deadlock, clean writes tests and audits secrets
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "obol-ai",
3
- "version": "0.2.27",
3
+ "version": "0.2.28",
4
4
  "description": "Self-evolving AI assistant that learns, remembers, and acts on its own. Persistent vector memory, self-rewriting personality, proactive heartbeats.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -25,6 +25,7 @@
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
27
  "@anthropic-ai/sdk": "^0.78.0",
28
+ "@grammyjs/auto-retry": "^2.0.2",
28
29
  "@grammyjs/runner": "^2.0.3",
29
30
  "@supabase/supabase-js": "^2.49.1",
30
31
  "@xenova/transformers": "^2.17.2",
package/src/background.js CHANGED
@@ -60,7 +60,7 @@ class BackgroundRunner {
60
60
  const elapsed = Math.round((Date.now() - statusStart) / 1000);
61
61
  const html = buildStatusHtml({ route: routeInfo, elapsed, toolStatus: statusText, title });
62
62
  ctx.api.editMessageText(ctx.chat.id, statusMsgId, html, { parse_mode: 'HTML' }).catch(() => {});
63
- }, 1000);
63
+ }, 5000);
64
64
  };
65
65
 
66
66
  startStatusTimer();
@@ -2,6 +2,7 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const { Bot, GrammyError, HttpError, InlineKeyboard } = require('grammy');
4
4
  const { run, sequentialize } = require('@grammyjs/runner');
5
+ const { autoRetry } = require('@grammyjs/auto-retry');
5
6
  const { getTenant } = require('../tenant');
6
7
  const { DEDUP_TTL_MS, DEDUP_MAX_SIZE } = require('./constants');
7
8
  const { sendHtml } = require('./utils');
@@ -20,6 +21,7 @@ const { registerCallbackHandler } = require('./handlers/callbacks');
20
21
 
21
22
  function createBot(telegramConfig, config) {
22
23
  const bot = new Bot(telegramConfig.token);
24
+ bot.api.config.use(autoRetry());
23
25
  const allowedUsers = new Set(telegramConfig.allowedUsers || []);
24
26
  const pendingAsks = new Map();
25
27
  const processedUpdates = new Map();
@@ -133,6 +135,7 @@ function createBot(telegramConfig, config) {
133
135
  console.error(` Unknown error:`, e?.message || e);
134
136
  }
135
137
 
138
+ if (e instanceof GrammyError && e.description?.includes('Too Many Requests')) return;
136
139
  ctx?.reply?.('⚠️ Something went wrong. I\'m still alive though.').catch(() => {});
137
140
  });
138
141
 
@@ -7,6 +7,29 @@ const { EVOLUTION_IDLE_MS, TEXT_BUFFER_GAP_MS, TEXT_BUFFER_MAX_PARTS, TEXT_BUFFE
7
7
 
8
8
  const _evolutionTimers = new Map();
9
9
  const textBuffers = new Map();
10
+ const VERBOSE_FLUSH_MS = 2000;
11
+
12
+ function createVerboseBatcher(ctx) {
13
+ /** @type {string[]} */
14
+ let buffer = [];
15
+ /** @type {ReturnType<typeof setTimeout> | null} */
16
+ let timer = null;
17
+
18
+ const flush = () => {
19
+ if (timer) { clearTimeout(timer); timer = null; }
20
+ if (buffer.length === 0) return;
21
+ const combined = buffer.join('\n');
22
+ buffer = [];
23
+ sendHtml(ctx, `<code>${combined}</code>`).catch(() => {});
24
+ };
25
+
26
+ const notify = (/** @type {string} */ msg) => {
27
+ buffer.push(msg);
28
+ if (!timer) timer = setTimeout(flush, VERBOSE_FLUSH_MS);
29
+ };
30
+
31
+ return { notify, flush };
32
+ }
10
33
 
11
34
  function createChatContext(ctx, tenant, config, { allowedUsers, bot, createAsk }) {
12
35
  const userId = ctx.from.id;
@@ -23,9 +46,8 @@ function createChatContext(ctx, tenant, config, { allowedUsers, bot, createAsk }
23
46
  toolPrefs: tenant.toolPrefs,
24
47
  config,
25
48
  verbose: tenant.verbose,
26
- _verboseNotify: tenant.verbose ? (msg) => {
27
- sendHtml(ctx, `\`${msg}\``).catch(() => {});
28
- } : undefined,
49
+ _verboseBatcher: null,
50
+ _verboseNotify: undefined,
29
51
  telegramAsk: (message, options, timeout) => createAsk(ctx, message, options, timeout),
30
52
  _notifyFn: (targetUserId, message, opts = {}) => {
31
53
  if (!allowedUsers.has(targetUserId)) throw new Error('Cannot notify user outside allowed list');
@@ -61,7 +83,7 @@ function createStatusTracker(ctx) {
61
83
  const elapsed = Math.round((Date.now() - statusStart) / 1000);
62
84
  const html = buildStatusHtml({ route: routeInfo, elapsed, toolStatus: statusText });
63
85
  ctx.api.editMessageText(ctx.chat.id, statusMsgId, html, { parse_mode: 'HTML', reply_markup: stopBtn }).catch(() => {});
64
- }, 1000);
86
+ }, 5000);
65
87
  };
66
88
 
67
89
  return {
@@ -111,10 +133,11 @@ async function processTextMessage(ctx, fullMessage, { config, allowedUsers, bot,
111
133
  const stopTyping = startTyping(ctx);
112
134
  const status = createStatusTracker(ctx);
113
135
 
136
+ const batcher = tenant.verbose ? createVerboseBatcher(ctx) : null;
114
137
  try {
115
138
  if (tenant.messageLog) {
116
- if (tenant.verbose) {
117
- tenant.messageLog._verboseCallbacks.set(ctx.chat.id, (msg) => sendHtml(ctx, `\`${msg}\``).catch(() => {}));
139
+ if (batcher) {
140
+ tenant.messageLog._verboseCallbacks.set(ctx.chat.id, batcher.notify);
118
141
  } else {
119
142
  tenant.messageLog._verboseCallbacks.delete(ctx.chat.id);
120
143
  }
@@ -122,6 +145,10 @@ async function processTextMessage(ctx, fullMessage, { config, allowedUsers, bot,
122
145
  tenant.messageLog?.log(ctx.chat.id, 'user', chatMessage);
123
146
 
124
147
  const chatContext = createChatContext(ctx, tenant, config, { allowedUsers, bot, createAsk });
148
+ if (batcher) {
149
+ chatContext._verboseNotify = batcher.notify;
150
+ chatContext._verboseBatcher = batcher;
151
+ }
125
152
  chatContext._onRouteDecision = (info) => {
126
153
  status.setRouteInfo(info);
127
154
  status.start();
@@ -146,6 +173,7 @@ async function processTextMessage(ctx, fullMessage, { config, allowedUsers, bot,
146
173
 
147
174
  const { text: response, usage, model } = await tenant.claude.chat(chatMessage, chatContext);
148
175
 
176
+ batcher?.flush();
149
177
  status.stopTimer();
150
178
  status.updateFormatting();
151
179
 
@@ -230,6 +258,7 @@ async function processTextMessage(ctx, fullMessage, { config, allowedUsers, bot,
230
258
 
231
259
  status.deleteMsg();
232
260
  } catch (e) {
261
+ batcher?.flush();
233
262
  status.clear();
234
263
  stopTyping();
235
264
  console.error('Message handling error:', e.message);
@@ -50,7 +50,7 @@ function startTyping(ctx) {
50
50
  ctx.replyWithChatAction('typing').catch(() => {});
51
51
  const interval = setInterval(() => {
52
52
  ctx.replyWithChatAction('typing').catch(() => {});
53
- }, 4000);
53
+ }, 8000);
54
54
  return () => clearInterval(interval);
55
55
  }
56
56