obol-ai 0.3.23 → 0.3.24
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "obol-ai",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.24",
|
|
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": {
|
|
@@ -31,7 +31,20 @@ class BackgroundRunner {
|
|
|
31
31
|
const verbose = parentContext?.verbose || false;
|
|
32
32
|
const verboseNotify = parentContext?._verboseNotify;
|
|
33
33
|
|
|
34
|
-
const
|
|
34
|
+
const inherited = parentContext ? {
|
|
35
|
+
toolPrefs: parentContext.toolPrefs,
|
|
36
|
+
config: parentContext.config,
|
|
37
|
+
scheduler: parentContext.scheduler,
|
|
38
|
+
messageLog: parentContext.messageLog,
|
|
39
|
+
userId: parentContext.userId,
|
|
40
|
+
userDir: parentContext.userDir,
|
|
41
|
+
telegramAsk: parentContext.telegramAsk,
|
|
42
|
+
_notifyFn: parentContext._notifyFn,
|
|
43
|
+
} : {};
|
|
44
|
+
|
|
45
|
+
const mergedExtra = { ...inherited, ...(opts.extraContext || extraContext) };
|
|
46
|
+
|
|
47
|
+
const promise = this._runTask(claude, task, taskState, ctx, memory, verbose, verboseNotify, opts.model, mergedExtra, opts.silent || false);
|
|
35
48
|
taskState.promise = promise;
|
|
36
49
|
|
|
37
50
|
return taskId;
|
|
@@ -94,18 +107,25 @@ TASK: ${task}`;
|
|
|
94
107
|
},
|
|
95
108
|
});
|
|
96
109
|
|
|
97
|
-
taskState.status = 'done';
|
|
98
|
-
taskState.result = result;
|
|
99
110
|
claude.clearHistory(`bg-${taskState.id}`);
|
|
100
|
-
|
|
101
111
|
clearStatus();
|
|
102
112
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
113
|
+
if (!result?.trim()) {
|
|
114
|
+
taskState.status = 'error';
|
|
115
|
+
taskState.error = 'No result returned';
|
|
116
|
+
if (!silent) {
|
|
117
|
+
await ctx.reply(`⚠️ BG #${taskState.id} finished but produced no result.`).catch(() => {});
|
|
118
|
+
}
|
|
106
119
|
} else {
|
|
107
|
-
|
|
108
|
-
|
|
120
|
+
taskState.status = 'done';
|
|
121
|
+
taskState.result = result;
|
|
122
|
+
const elapsed = Math.floor((Date.now() - taskState.startedAt) / 1000);
|
|
123
|
+
if (silent) {
|
|
124
|
+
await sendLong(ctx, result);
|
|
125
|
+
} else {
|
|
126
|
+
const header = `✅ <b>BG #${taskState.id}</b> done (${formatDuration(elapsed)})\n\n`;
|
|
127
|
+
await sendLong(ctx, header + result);
|
|
128
|
+
}
|
|
109
129
|
}
|
|
110
130
|
|
|
111
131
|
if (memory) {
|
package/src/status.js
CHANGED
|
@@ -33,4 +33,19 @@ function formatToolCall(toolName, inputSummary) {
|
|
|
33
33
|
return `${toolName} "${truncated}"`;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
/**
|
|
37
|
+
* @param {{ model: string, usage: { input_tokens: number, output_tokens: number }, startTime: number | null }} params
|
|
38
|
+
* @returns {string | null}
|
|
39
|
+
*/
|
|
40
|
+
function formatTokenStats({ model, usage, startTime }) {
|
|
41
|
+
if (!usage || !model) return null;
|
|
42
|
+
const tag = model.includes('opus') ? 'opus' : model.includes('haiku') ? 'haiku' : 'sonnet';
|
|
43
|
+
const tokIn = usage.input_tokens >= 1000 ? `${(usage.input_tokens / 1000).toFixed(1)}k` : usage.input_tokens;
|
|
44
|
+
const tokOut = usage.output_tokens >= 1000 ? `${(usage.output_tokens / 1000).toFixed(1)}k` : usage.output_tokens;
|
|
45
|
+
const dur = startTime ? ((Date.now() - startTime) / 1000).toFixed(1) : null;
|
|
46
|
+
const parts = [`◈ ${tag}`, `${tokIn} in`, `${tokOut} out`];
|
|
47
|
+
if (dur) parts.push(`${dur}s`);
|
|
48
|
+
return `<code>${parts.join(' ▪ ')}</code>`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = { buildStatusHtml, formatToolCall, formatTokenStats, TERM_WIDTH };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const { getTenant } = require('../../tenant');
|
|
3
|
-
const { buildStatusHtml, formatToolCall } = require('../../status');
|
|
3
|
+
const { buildStatusHtml, formatToolCall, formatTokenStats } = require('../../status');
|
|
4
4
|
const media = require('../../media');
|
|
5
5
|
const { sendHtml, startTyping, splitMessage } = require('../utils');
|
|
6
6
|
const { MAX_MEDIA_SIZE, MEDIA_GROUP_DELAY_MS } = require('../constants');
|
|
@@ -124,17 +124,8 @@ async function processMediaItems(ctx, items, { config, allowedUsers, bot, create
|
|
|
124
124
|
|
|
125
125
|
const statsPref = tenant.toolPrefs?.get('model_stats');
|
|
126
126
|
const showStats = statsPref ? statsPref.enabled : true;
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const tokIn = usage.input_tokens >= 1000 ? `${(usage.input_tokens/1000).toFixed(1)}k` : usage.input_tokens;
|
|
130
|
-
const tokOut = usage.output_tokens >= 1000 ? `${(usage.output_tokens/1000).toFixed(1)}k` : usage.output_tokens;
|
|
131
|
-
const dur = status.statusStart ? ((Date.now() - status.statusStart)/1000).toFixed(1) : null;
|
|
132
|
-
const parts = [`◈ ${tag}`, `${tokIn} in`, `${tokOut} out`];
|
|
133
|
-
if (dur) parts.push(`${dur}s`);
|
|
134
|
-
await ctx.reply(`<code>${parts.join(' ▪ ')}</code>`, { parse_mode: 'HTML' }).catch(() => {});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
status.deleteMsg();
|
|
127
|
+
const statsHtml = showStats ? formatTokenStats({ model, usage, startTime: status.statusStart }) : null;
|
|
128
|
+
status.finalize(statsHtml);
|
|
138
129
|
} catch (e) {
|
|
139
130
|
status.clear();
|
|
140
131
|
stopTyping();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { getTenant } = require('../../tenant');
|
|
2
|
-
const { formatToolCall } = require('../../status');
|
|
2
|
+
const { formatToolCall, formatTokenStats } = require('../../status');
|
|
3
3
|
const { sendHtml, startTyping, splitMessage } = require('../utils');
|
|
4
4
|
const { createChatContext, createStatusTracker } = require('./text');
|
|
5
5
|
|
|
@@ -120,17 +120,8 @@ async function processSpecial(ctx, prompt, deps) {
|
|
|
120
120
|
|
|
121
121
|
const statsPref = tenant.toolPrefs?.get('model_stats');
|
|
122
122
|
const showStats = statsPref ? statsPref.enabled : true;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const tokIn = usage.input_tokens >= 1000 ? `${(usage.input_tokens / 1000).toFixed(1)}k` : usage.input_tokens;
|
|
126
|
-
const tokOut = usage.output_tokens >= 1000 ? `${(usage.output_tokens / 1000).toFixed(1)}k` : usage.output_tokens;
|
|
127
|
-
const dur = status.statusStart ? ((Date.now() - status.statusStart) / 1000).toFixed(1) : null;
|
|
128
|
-
const parts = [`◈ ${tag}`, `${tokIn} in`, `${tokOut} out`];
|
|
129
|
-
if (dur) parts.push(`${dur}s`);
|
|
130
|
-
await ctx.reply(`<code>${parts.join(' ▪ ')}</code>`, { parse_mode: 'HTML' }).catch(() => {});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
status.deleteMsg();
|
|
123
|
+
const statsHtml = showStats ? formatTokenStats({ model, usage, startTime: status.statusStart }) : null;
|
|
124
|
+
status.finalize(statsHtml);
|
|
134
125
|
} catch (e) {
|
|
135
126
|
status.clear();
|
|
136
127
|
stopTyping();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { InlineKeyboard } = require('grammy');
|
|
2
2
|
const { getTenant } = require('../../tenant');
|
|
3
|
-
const { buildStatusHtml, formatToolCall } = require('../../status');
|
|
3
|
+
const { buildStatusHtml, formatToolCall, formatTokenStats } = require('../../status');
|
|
4
4
|
const { sendHtml, startTyping, splitMessage } = require('../utils');
|
|
5
5
|
const { TEXT_BUFFER_GAP_MS, TEXT_BUFFER_MAX_PARTS, TEXT_BUFFER_MAX_CHARS, TEXT_BUFFER_THRESHOLD } = require('../constants');
|
|
6
6
|
|
|
@@ -142,6 +142,18 @@ function createStatusTracker(ctx, botName) {
|
|
|
142
142
|
deleteMsg() {
|
|
143
143
|
if (statusMsgId) ctx.api.deleteMessage(ctx.chat.id, statusMsgId).catch(() => {});
|
|
144
144
|
},
|
|
145
|
+
finalize(statsHtml) {
|
|
146
|
+
if (statusTimer) { clearInterval(statusTimer); statusTimer = null; }
|
|
147
|
+
if (statsHtml && statusMsgId) {
|
|
148
|
+
ctx.api.editMessageText(ctx.chat.id, statusMsgId, statsHtml, { parse_mode: 'HTML' }).catch(() => {
|
|
149
|
+
ctx.api.deleteMessage(ctx.chat.id, statusMsgId).catch(() => {});
|
|
150
|
+
});
|
|
151
|
+
statusMsgId = null;
|
|
152
|
+
} else {
|
|
153
|
+
if (statusMsgId) { ctx.api.deleteMessage(ctx.chat.id, statusMsgId).catch(() => {}); statusMsgId = null; }
|
|
154
|
+
if (statsHtml) ctx.reply(statsHtml, { parse_mode: 'HTML' }).catch(() => {});
|
|
155
|
+
}
|
|
156
|
+
},
|
|
145
157
|
};
|
|
146
158
|
}
|
|
147
159
|
|
|
@@ -233,17 +245,8 @@ async function processTextMessage(ctx, fullMessage, { config, allowedUsers, bot,
|
|
|
233
245
|
|
|
234
246
|
const statsPref = tenant.toolPrefs?.get('model_stats');
|
|
235
247
|
const showStats = statsPref ? statsPref.enabled : true;
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const tokIn = usage.input_tokens >= 1000 ? `${(usage.input_tokens/1000).toFixed(1)}k` : usage.input_tokens;
|
|
239
|
-
const tokOut = usage.output_tokens >= 1000 ? `${(usage.output_tokens/1000).toFixed(1)}k` : usage.output_tokens;
|
|
240
|
-
const dur = status.statusStart ? ((Date.now() - status.statusStart)/1000).toFixed(1) : null;
|
|
241
|
-
const parts = [`◈ ${tag}`, `${tokIn} in`, `${tokOut} out`];
|
|
242
|
-
if (dur) parts.push(`${dur}s`);
|
|
243
|
-
await ctx.reply(`<code>${parts.join(' ▪ ')}</code>`, { parse_mode: 'HTML' }).catch(() => {});
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
status.deleteMsg();
|
|
248
|
+
const statsHtml = showStats ? formatTokenStats({ model, usage, startTime: status.statusStart }) : null;
|
|
249
|
+
status.finalize(statsHtml);
|
|
247
250
|
} catch (e) {
|
|
248
251
|
batcher?.flush();
|
|
249
252
|
status.clear();
|