@adriangalilea/utils 0.12.0 → 0.14.0

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.
@@ -0,0 +1,589 @@
1
+ /**
2
+ * GramIO LLM toolkit — three primitives that together form a complete
3
+ * Telegram LLM-chatbot pipeline:
4
+ *
5
+ * `streamChat(response)` — INPUT. Parses OpenAI-compatible SSE
6
+ * (OpenAI, vllm, mlx-lm, llama.cpp,
7
+ * Together, Groq, …) into a typed
8
+ * `AsyncGenerator` of `{type, text}` with
9
+ * `content` / `reasoning` separation.
10
+ *
11
+ * `ctx.startStream()` — OUTPUT (low-level). Debounced
12
+ * `editMessageText` to Telegram, local
13
+ * Markdown parse via `@gramio/format`,
14
+ * 4000-char split on paragraph / line /
15
+ * word boundary.
16
+ *
17
+ * `ctx.startChatStream()` — OUTPUT (high-level). Consumes a fetch
18
+ * `Response` (or any `AsyncIterable<LLMChunk>`)
19
+ * and renders BOTH phases of a thinking
20
+ * model: `reasoning` chunks go into one
21
+ * `<blockquote expandable>` message;
22
+ * `content` chunks go into a streamed
23
+ * markdown message below it. Returns
24
+ * `{ content, reasoning }` once the
25
+ * source generator ends.
26
+ *
27
+ * `ctx.llm.add / .get / …` — HISTORY. Per-(user, thread) conversation
28
+ * buffer in OpenAI `ChatMessage` shape.
29
+ * Persisted in the shared `@gramio/session`
30
+ * record under the `llm` field, so the
31
+ * `botMenu` 🗑 Forget button wipes it
32
+ * together with everything else (one
33
+ * record, one delete, no per-plugin
34
+ * registry).
35
+ *
36
+ * The trio composes:
37
+ *
38
+ * fetch(...) ──streamChat──> chunks ──ctx.startStream──> Telegram
39
+ * ▲ │
40
+ * └──── ctx.llm.get() ◀──── ctx.llm.add(assistant) ◀──┘
41
+ *
42
+ * Peer deps: `gramio`, `@gramio/session`, `@gramio/format`, `marked`.
43
+ *
44
+ * @example high-level — thinking model in ~10 LOC
45
+ * import { Bot } from 'gramio'
46
+ * import { session } from '@gramio/session'
47
+ * import { llmStream, llmHistory } from '@adriangalilea/utils/bot/llm'
48
+ *
49
+ * const userSession = session({ storage, key: 'session', initial: () => ({}) })
50
+ * const chat = llmHistory({ session: userSession, maxTurns: 20, retentionDays: 7 })
51
+ *
52
+ * const bot = new Bot(process.env.BOT_TOKEN!)
53
+ * .extend(userSession)
54
+ * .extend(llmStream())
55
+ * .extend(chat.plugin)
56
+ * .on('message', async (ctx) => {
57
+ * ctx.llm.add({ role: 'user', content: ctx.text ?? '' })
58
+ * const response = await fetch(process.env.LLM_URL!, {
59
+ * method: 'POST',
60
+ * headers: { 'Content-Type': 'application/json' },
61
+ * body: JSON.stringify({
62
+ * model: process.env.LLM_MODEL,
63
+ * messages: [{ role: 'system', content: 'You are helpful.' }, ...ctx.llm.get()],
64
+ * stream: true,
65
+ * }),
66
+ * })
67
+ * const { content } = await ctx.startChatStream(response)
68
+ * ctx.llm.add({ role: 'assistant', content })
69
+ * })
70
+ *
71
+ * bot.start()
72
+ *
73
+ * @example low-level — manual loop when you need to map/filter chunks
74
+ * import { streamChat } from '@adriangalilea/utils/bot/llm'
75
+ *
76
+ * const stream = ctx.startStream()
77
+ * for await (const chunk of streamChat(response)) {
78
+ * if (chunk.type === 'content') await stream.append(chunk.text)
79
+ * // reasoning chunks dropped here
80
+ * }
81
+ * await stream.end()
82
+ */
83
+ import { Plugin } from 'gramio';
84
+ import { expandableBlockquote } from '@gramio/format';
85
+ import { markdownToFormattable } from '@gramio/format/markdown';
86
+ import { say } from '../say/index.js';
87
+ /**
88
+ * Parse an OpenAI-compatible chat-completions SSE response into a
89
+ * typed `AsyncGenerator<LLMChunk>`. Reads `response.body` once.
90
+ *
91
+ * Recognised reasoning aliases (as of 2026): `reasoning_content`
92
+ * (vllm, qwen3, DeepSeek-R1, gpt-oss harmony) and `reasoning` (some
93
+ * mlx-lm forks, gemma builds). If a model surfaces a new key, add it
94
+ * here — single source of truth for the field.
95
+ *
96
+ * Constrained-SSE assumption: lines are `\n`-delimited and each event
97
+ * is `data: <json>` or `data: [DONE]`. This matches every OpenAI-compat
98
+ * server in the wild but is NOT the full SSE spec (no comments, no
99
+ * multi-line `data:`, no `retry`/`id` fields). Swap to
100
+ * `eventsource-parser` if you hit a producer that needs them.
101
+ *
102
+ * Malformed JSON lines are silently skipped; the generator ends when
103
+ * the stream closes.
104
+ *
105
+ * @param response the `fetch` `Response` from a `stream: true` chat
106
+ * completion call. Must not be already consumed.
107
+ * @throws if `response.body` is null (non-streaming response).
108
+ *
109
+ * @example framework-agnostic — parser doesn't know about Telegram
110
+ * const res = await fetch(url, { method: 'POST', body })
111
+ * for await (const chunk of streamChat(res)) {
112
+ * if (chunk.type === 'content') process.stdout.write(chunk.text)
113
+ * }
114
+ */
115
+ export async function* streamChat(response) {
116
+ if (!response.body)
117
+ throw new Error('streamChat: response.body is null');
118
+ const reader = response.body.getReader();
119
+ const decoder = new TextDecoder();
120
+ let buffer = '';
121
+ while (true) {
122
+ const { done, value } = await reader.read();
123
+ if (done)
124
+ break;
125
+ buffer += decoder.decode(value, { stream: true });
126
+ const lines = buffer.split('\n');
127
+ buffer = lines.pop() ?? '';
128
+ for (const line of lines) {
129
+ if (!line.startsWith('data: '))
130
+ continue;
131
+ const data = line.slice(6).trim();
132
+ if (data === '[DONE]')
133
+ continue;
134
+ let parsed;
135
+ try {
136
+ parsed = JSON.parse(data);
137
+ }
138
+ catch {
139
+ continue;
140
+ }
141
+ const delta = parsed.choices?.[0]?.delta;
142
+ if (!delta)
143
+ continue;
144
+ const reasoning = delta.reasoning_content ?? delta.reasoning;
145
+ if (typeof reasoning === 'string' && reasoning.length > 0) {
146
+ yield { type: 'reasoning', text: reasoning };
147
+ }
148
+ if (typeof delta.content === 'string' && delta.content.length > 0) {
149
+ yield { type: 'content', text: delta.content };
150
+ }
151
+ }
152
+ }
153
+ }
154
+ // ─── OUTPUT: Telegram streamer ─────────────────────────────────────
155
+ const MAX_LEN = 4000; // Telegram caps at 4096; leave headroom for entity offsets
156
+ const DEFAULT_DEBOUNCE_MS = 800;
157
+ export class MarkdownStreamer {
158
+ buffer = '';
159
+ currentMessageId;
160
+ firstSendPromise;
161
+ debounceTimer;
162
+ inFlight = false;
163
+ dirty = false;
164
+ ended = false;
165
+ /**
166
+ * `true` after `.end()` returns if the stream finished with un-flushed
167
+ * buffer or an outstanding error that the streamer couldn't recover
168
+ * from. Useful for partial-response diagnostics in catch blocks:
169
+ *
170
+ * try { for await (...) await stream.append(...) }
171
+ * finally {
172
+ * await stream.end()
173
+ * if (stream.wasPartial) logger.warn('LLM stream cut off')
174
+ * }
175
+ */
176
+ wasPartial = false;
177
+ chatId;
178
+ threadId;
179
+ // Match gramio's `bot.api` shape structurally. `text` accepts string or any
180
+ // `Formattable` (from `@gramio/format`) — both stringify safely. We don't
181
+ // import gramio's full Bot type to keep the streamer testable in isolation.
182
+ bot;
183
+ opts;
184
+ constructor(ctx, opts) {
185
+ this.chatId = ctx.chat.id;
186
+ // Captured so the streamed reply stays in the same thread.
187
+ // We call bot.api.sendMessage directly (no ctx.send), so the
188
+ // SendMixin's auto-thread doesn't help us here.
189
+ this.threadId = ctx.threadId;
190
+ this.bot = ctx.bot;
191
+ this.opts = {
192
+ debounceMs: opts.debounceMs ?? DEFAULT_DEBOUNCE_MS,
193
+ placeholder: opts.placeholder ?? '…',
194
+ markdown: opts.markdown ?? true,
195
+ onError: opts.onError ?? ((e) => console.error('[bot/llm:stream]', e)),
196
+ };
197
+ }
198
+ /** Append a chunk. Schedules a debounced edit. */
199
+ async append(text) {
200
+ if (this.ended)
201
+ throw new Error('stream already ended');
202
+ if (!text)
203
+ return;
204
+ // first chunk: send the placeholder so we have a message_id to edit.
205
+ // Serialized via firstSendPromise so concurrent appends don't double-send.
206
+ if (this.currentMessageId === undefined && !this.firstSendPromise) {
207
+ this.firstSendPromise = (async () => {
208
+ const sent = await this.bot.api.sendMessage({
209
+ chat_id: this.chatId,
210
+ ...(this.threadId !== undefined && { message_thread_id: this.threadId }),
211
+ text: this.opts.placeholder,
212
+ });
213
+ this.currentMessageId = sent.message_id;
214
+ })();
215
+ }
216
+ if (this.firstSendPromise)
217
+ await this.firstSendPromise;
218
+ // overflow: freeze current message at last good split, start a new one.
219
+ const next = this.buffer + text;
220
+ if (next.length > MAX_LEN) {
221
+ const splitAt = findSplit(next, MAX_LEN);
222
+ const head = next.slice(0, splitAt);
223
+ const tail = next.slice(splitAt).trimStart();
224
+ this.buffer = head;
225
+ this.dirty = true;
226
+ await this.flushNow(); // commits head into current message
227
+ this.buffer = '';
228
+ this.currentMessageId = undefined;
229
+ this.firstSendPromise = undefined;
230
+ this.dirty = false;
231
+ if (this.debounceTimer) {
232
+ clearTimeout(this.debounceTimer);
233
+ this.debounceTimer = undefined;
234
+ }
235
+ if (tail)
236
+ await this.append(tail);
237
+ return;
238
+ }
239
+ this.buffer = next;
240
+ this.dirty = true;
241
+ this.scheduleFlush();
242
+ }
243
+ /** Flush any pending edit and close the stream. Idempotent. */
244
+ async end() {
245
+ if (this.ended)
246
+ return;
247
+ this.ended = true;
248
+ if (this.debounceTimer) {
249
+ clearTimeout(this.debounceTimer);
250
+ this.debounceTimer = undefined;
251
+ }
252
+ while (this.inFlight)
253
+ await sleep(50);
254
+ if (this.dirty) {
255
+ await this.flushNow();
256
+ // If `dirty` is still set after the final flush, an edit error
257
+ // re-armed it and there's nothing more we'll do. Mark partial
258
+ // so the caller can log / fall back.
259
+ if (this.dirty)
260
+ this.wasPartial = true;
261
+ }
262
+ }
263
+ scheduleFlush() {
264
+ if (this.debounceTimer)
265
+ return;
266
+ this.debounceTimer = setTimeout(async () => {
267
+ this.debounceTimer = undefined;
268
+ if (this.inFlight) {
269
+ this.scheduleFlush();
270
+ return;
271
+ }
272
+ await this.flushNow();
273
+ if (this.dirty && !this.ended)
274
+ this.scheduleFlush();
275
+ }, this.opts.debounceMs);
276
+ }
277
+ async flushNow() {
278
+ if (!this.dirty)
279
+ return;
280
+ if (this.currentMessageId === undefined) {
281
+ // No active message yet — likely the first send is still in flight
282
+ // or the recovery path cleared it. Caller's next append() will
283
+ // re-create one.
284
+ return;
285
+ }
286
+ this.inFlight = true;
287
+ this.dirty = false;
288
+ const snapshot = this.buffer;
289
+ try {
290
+ const payload = this.opts.markdown ? markdownToFormattable(snapshot) : snapshot;
291
+ await this.bot.api.editMessageText({
292
+ chat_id: this.chatId,
293
+ message_id: this.currentMessageId,
294
+ text: payload,
295
+ });
296
+ }
297
+ catch (e) {
298
+ const msg = String(e?.message ?? e);
299
+ if (msg.includes('message is not modified')) {
300
+ // identical content — fine
301
+ }
302
+ else if (msg.includes('message to edit not found')) {
303
+ // message gone (deleted by user, etc.) — restart with a fresh send
304
+ this.currentMessageId = undefined;
305
+ this.firstSendPromise = undefined;
306
+ this.dirty = true;
307
+ }
308
+ else {
309
+ this.dirty = true;
310
+ this.opts.onError(e);
311
+ }
312
+ }
313
+ finally {
314
+ this.inFlight = false;
315
+ }
316
+ }
317
+ }
318
+ /**
319
+ * GramIO plugin. Adds `ctx.startStream(opts?)` on every message context.
320
+ *
321
+ * Defaults set here apply to every stream; per-call options in
322
+ * `ctx.startStream({...})` override them.
323
+ */
324
+ export const llmStream = (defaults = {}) => new Plugin('@adriangalilea/utils/bot/llm/stream').derive('message', (ctx) => ({
325
+ // gramio's `message` scope guarantees `ctx.chat`. `ctx.bot.api` is on
326
+ // every Context. Structural compat → no cast needed.
327
+ startStream: (opts = {}) => new MarkdownStreamer(ctx, { ...defaults, ...opts }),
328
+ /**
329
+ * One-call helper for chat completions: consumes a `Response`
330
+ * (or any `AsyncIterable<LLMChunk>`), renders reasoning to an
331
+ * expandable blockquote message + content to a streamed markdown
332
+ * message, returns `{ content, reasoning }` when the stream ends.
333
+ */
334
+ startChatStream: (source, opts = {}) => consumeChatStream(ctx, source, { ...defaults, ...opts }),
335
+ }));
336
+ function findSplit(text, maxLen) {
337
+ // Prefer paragraph break, then line, then space. Reject splits in the
338
+ // first half — better to truncate at maxLen than to leave a stub.
339
+ for (const sep of ['\n\n', '\n', ' ']) {
340
+ const idx = text.lastIndexOf(sep, maxLen);
341
+ if (idx > maxLen / 2)
342
+ return idx;
343
+ }
344
+ return maxLen;
345
+ }
346
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
347
+ /**
348
+ * One-shot helper that consumes an LLM stream and renders BOTH phases
349
+ * (reasoning, content) to Telegram with the canonical pattern:
350
+ *
351
+ * - reasoning chunks → one message containing a single
352
+ * `<blockquote expandable>…</blockquote>` (HTML), debounced edits,
353
+ * stays expanded after the stream ends so the user can review it
354
+ * collapsed/uncollapsed at will
355
+ * - content chunks → a fresh `MarkdownStreamer` message below the
356
+ * reasoning one (auto-threaded via SendMixin)
357
+ *
358
+ * Returns when the source generator ends. Equivalent to (and replaces)
359
+ * the ~50 LOC of bookkeeping every thinking-model bot would otherwise
360
+ * write by hand around `streamChat` + `ctx.startStream`.
361
+ *
362
+ * Accepts either a raw `fetch` `Response` (wrapped with `streamChat`
363
+ * internally) or an `AsyncIterable<LLMChunk>` (when you want to map /
364
+ * filter chunks before the stream hits Telegram).
365
+ *
366
+ * @example via ctx.startChatStream from the llmStream plugin
367
+ * const response = await fetch(LLM_URL, { method: 'POST', body })
368
+ * const { content } = await ctx.startChatStream(response)
369
+ * ctx.llm.add({ role: 'assistant', content })
370
+ *
371
+ * @example standalone, framework-agnostic
372
+ * const { content, reasoning } = await consumeChatStream(ctx, response)
373
+ */
374
+ export const consumeChatStream = async (ctx, source, opts = {}) => {
375
+ const chatId = ctx.chat.id;
376
+ const threadId = ctx.threadId;
377
+ const bot = ctx.bot;
378
+ const debounceMs = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
379
+ const gen = source instanceof Response ? streamChat(source) : source;
380
+ // ─── reasoning state ────────────────────────────────────────────
381
+ let reasoning = '';
382
+ let reasoningMessageId;
383
+ let reasoningTimer;
384
+ let reasoningDirty = false;
385
+ let reasoningInFlight = false;
386
+ // Same graceful-degradation pipeline as the content phase:
387
+ // markdownToFormattable parses the reasoning (lists, code blocks,
388
+ // bold — thinking models do emit markdown) into a FormattableString
389
+ // with native Telegram entities, and `expandableBlockquote` wraps
390
+ // the whole thing in a collapsed-by-default block quotation by
391
+ // adding the `expandable_blockquote` entity over the full text.
392
+ // Malformed mid-stream markdown degrades to plain text inside the
393
+ // blockquote instead of failing the message.
394
+ const renderReasoning = () => expandableBlockquote(markdownToFormattable(reasoning));
395
+ const sendReasoningMessage = async () => {
396
+ const params = {
397
+ chat_id: chatId,
398
+ ...(threadId !== undefined && { message_thread_id: threadId }),
399
+ text: renderReasoning(),
400
+ };
401
+ const sent = await bot.api.sendMessage(params);
402
+ reasoningMessageId = sent.message_id;
403
+ };
404
+ const flushReasoning = async () => {
405
+ if (!reasoningDirty || reasoningMessageId === undefined)
406
+ return;
407
+ reasoningInFlight = true;
408
+ reasoningDirty = false;
409
+ try {
410
+ await bot.api.editMessageText({
411
+ chat_id: chatId,
412
+ message_id: reasoningMessageId,
413
+ text: renderReasoning(),
414
+ });
415
+ }
416
+ catch (e) {
417
+ const msg = String(e?.message ?? e);
418
+ // "message is not modified" is benign when reasoning chunks
419
+ // arrive faster than the debounce can render.
420
+ if (!msg.includes('message is not modified')) {
421
+ reasoningDirty = true;
422
+ opts.onError?.(e);
423
+ }
424
+ }
425
+ finally {
426
+ reasoningInFlight = false;
427
+ }
428
+ };
429
+ const scheduleReasoningFlush = () => {
430
+ if (reasoningTimer)
431
+ return;
432
+ reasoningTimer = setTimeout(async () => {
433
+ reasoningTimer = undefined;
434
+ if (reasoningInFlight) {
435
+ scheduleReasoningFlush();
436
+ return;
437
+ }
438
+ await flushReasoning();
439
+ if (reasoningDirty)
440
+ scheduleReasoningFlush();
441
+ }, debounceMs);
442
+ };
443
+ // ─── content state ──────────────────────────────────────────────
444
+ let contentStreamer;
445
+ let content = '';
446
+ // ─── main loop ──────────────────────────────────────────────────
447
+ for await (const chunk of gen) {
448
+ if (chunk.type === 'reasoning') {
449
+ reasoning += chunk.text;
450
+ if (reasoningMessageId === undefined) {
451
+ await sendReasoningMessage();
452
+ }
453
+ else {
454
+ reasoningDirty = true;
455
+ scheduleReasoningFlush();
456
+ }
457
+ }
458
+ else if (chunk.type === 'content') {
459
+ if (!contentStreamer) {
460
+ // Close out reasoning cleanly before content starts: flush any
461
+ // pending edit, drop the debounce. The blockquote stays in
462
+ // the chat as a finished message.
463
+ if (reasoningTimer) {
464
+ clearTimeout(reasoningTimer);
465
+ reasoningTimer = undefined;
466
+ }
467
+ if (reasoningDirty)
468
+ await flushReasoning();
469
+ contentStreamer = new MarkdownStreamer({ chat: { id: chatId }, threadId, bot }, opts);
470
+ }
471
+ content += chunk.text;
472
+ await contentStreamer.append(chunk.text);
473
+ }
474
+ }
475
+ // Trailing reasoning (model emitted reasoning but no content yet).
476
+ if (reasoningTimer) {
477
+ clearTimeout(reasoningTimer);
478
+ reasoningTimer = undefined;
479
+ }
480
+ if (reasoningDirty)
481
+ await flushReasoning();
482
+ await contentStreamer?.end();
483
+ return { content, reasoning };
484
+ };
485
+ const GENERAL_THREAD = 'general';
486
+ const threadKey = (ctx) => {
487
+ // Message events: ctx.threadId. Callback events: ctx.message.threadId.
488
+ const tid = ctx.threadId ?? ctx.message?.threadId;
489
+ return tid !== undefined ? String(tid) : GENERAL_THREAD;
490
+ };
491
+ const prune = (items, maxTurns, retentionDays) => {
492
+ const cutoffSec = Math.floor(Date.now() / 1000) - retentionDays * 86400;
493
+ const fresh = items.filter((m) => m.date >= cutoffSec);
494
+ return fresh.slice(-maxTurns);
495
+ };
496
+ const pruneAll = (shards, maxTurns, retentionDays) => {
497
+ const out = {};
498
+ for (const k of Object.keys(shards)) {
499
+ const pruned = prune(shards[k], maxTurns, retentionDays);
500
+ if (pruned.length > 0)
501
+ out[k] = pruned;
502
+ }
503
+ return out;
504
+ };
505
+ /**
506
+ * Per-(user, thread) LLM conversation history. Opt-in. Persists in the
507
+ * shared `@gramio/session` record under `llm`, so 🗑 Forget from
508
+ * `botMenu` wipes it together with everything else — one record, one
509
+ * delete, no per-plugin registry.
510
+ *
511
+ * @example
512
+ * const chat = llmHistory({ session: userSession, maxTurns: 20, retentionDays: 7 })
513
+ * bot.extend(chat.plugin)
514
+ * .on('message', (ctx) => {
515
+ * ctx.llm.add({ role: 'user', content: ctx.text ?? '' })
516
+ * const messages = ctx.llm.get() // ChatMessage[] for current thread
517
+ * // ... call LLM with messages, then:
518
+ * ctx.llm.add({ role: 'assistant', content: reply })
519
+ * })
520
+ */
521
+ export const llmHistory = (opts) => {
522
+ if (opts.maxTurns <= 0)
523
+ throw new Error('llmHistory: maxTurns must be > 0');
524
+ if (opts.retentionDays <= 0)
525
+ throw new Error('llmHistory: retentionDays must be > 0');
526
+ const plugin = buildHistoryPlugin({
527
+ sessionPlugin: opts.session,
528
+ maxTurns: opts.maxTurns,
529
+ retentionDays: opts.retentionDays,
530
+ });
531
+ const itemLabel = opts.menuLabels?.item ?? {
532
+ en: '🧹 Clear this thread',
533
+ es: '🧹 Limpiar este hilo',
534
+ };
535
+ const clearedToast = opts.menuLabels?.cleared ?? {
536
+ en: '🧹 Cleared.',
537
+ es: '🧹 Limpio.',
538
+ };
539
+ const menuItem = {
540
+ id: 'llmClear',
541
+ label: itemLabel,
542
+ action: async (ctx) => {
543
+ const c = ctx;
544
+ c.llm?.clear();
545
+ await c.answer?.({ text: say(clearedToast, c.lang ?? 'en') });
546
+ },
547
+ };
548
+ return { plugin, menuItem };
549
+ };
550
+ const buildHistoryPlugin = (args) => {
551
+ const { sessionPlugin, maxTurns, retentionDays } = args;
552
+ return new Plugin('@adriangalilea/utils/bot/llm/history')
553
+ .extend(sessionPlugin)
554
+ .derive(['message', 'callback_query'], (ctx) => {
555
+ const key = threadKey(ctx);
556
+ // Always operate against a freshly-pruned view. Writes go to the
557
+ // pruned base so stale entries never resurface after a read.
558
+ const readShards = () => pruneAll(ctx.session.llm?.shards ?? {}, maxTurns, retentionDays);
559
+ const writeShards = (shards) => {
560
+ ctx.session.llm = { shards };
561
+ };
562
+ return {
563
+ llm: {
564
+ add: (message) => {
565
+ const shards = readShards();
566
+ const cur = shards[key] ?? [];
567
+ const entry = {
568
+ role: message.role,
569
+ content: message.content,
570
+ date: message.date ?? Math.floor(Date.now() / 1000),
571
+ };
572
+ shards[key] = [...cur, entry].slice(-maxTurns);
573
+ writeShards(shards);
574
+ },
575
+ get: () => (readShards()[key] ?? []),
576
+ clear: () => {
577
+ const shards = readShards();
578
+ delete shards[key];
579
+ writeShards(shards);
580
+ },
581
+ all: () => readShards(),
582
+ clearAll: () => {
583
+ writeShards({});
584
+ },
585
+ },
586
+ };
587
+ });
588
+ };
589
+ //# sourceMappingURL=llm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm.js","sourceRoot":"","sources":["../../src/bot/llm.ts"],"names":[],"mappings":"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"}