@llblab/pi-telegram 0.6.3 → 0.7.1
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/AGENTS.md +153 -0
- package/BACKLOG.md +5 -0
- package/CHANGELOG.md +148 -0
- package/README.md +40 -39
- package/docs/README.md +1 -0
- package/docs/architecture.md +36 -27
- package/docs/attachment-handlers.md +4 -6
- package/docs/callback-namespaces.md +36 -0
- package/docs/command-templates.md +53 -9
- package/docs/locks.md +4 -0
- package/docs/outbound-handlers.md +6 -5
- package/index.ts +60 -7
- package/lib/api.ts +1 -0
- package/lib/attachment-handlers.ts +21 -10
- package/lib/attachments.ts +1 -0
- package/lib/command-templates.ts +37 -3
- package/lib/commands.ts +363 -88
- package/lib/config.ts +6 -2
- package/lib/keyboard.ts +14 -0
- package/lib/lifecycle.ts +26 -0
- package/lib/locks.ts +3 -2
- package/lib/media.ts +1 -0
- package/lib/menu-model.ts +881 -0
- package/lib/menu-queue.ts +610 -0
- package/lib/menu-status.ts +226 -0
- package/lib/menu-thinking.ts +171 -0
- package/lib/menu.ts +143 -1019
- package/lib/model.ts +1 -0
- package/lib/outbound-handlers.ts +28 -19
- package/lib/pi.ts +8 -0
- package/lib/polling.ts +1 -0
- package/lib/preview.ts +97 -50
- package/lib/prompt-templates.ts +150 -0
- package/lib/prompts.ts +2 -0
- package/lib/queue.ts +60 -15
- package/lib/rendering.ts +1 -0
- package/lib/replies.ts +90 -2
- package/lib/routing.ts +106 -14
- package/lib/runtime.ts +2 -0
- package/lib/setup.ts +1 -0
- package/lib/status.ts +18 -6
- package/lib/turns.ts +1 -0
- package/lib/updates.ts +55 -6
- package/package.json +5 -2
package/lib/menu.ts
CHANGED
|
@@ -1,131 +1,114 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Telegram menu and inline-keyboard rendering helpers
|
|
3
|
-
*
|
|
3
|
+
* Zones: telegram ui, controls, status menu
|
|
4
|
+
* Owns app-menu/status state, inline UI text, and callback composition while model/thinking/queue menu details live in dedicated domains
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
createTelegramModelMenuStateBuilder,
|
|
9
|
+
handleTelegramModelMenuCallbackAction,
|
|
10
|
+
openTelegramModelMenu,
|
|
11
|
+
sendTelegramModelMenuMessage,
|
|
12
|
+
updateTelegramModelMenuMessage,
|
|
13
|
+
type TelegramMenuMessageRuntimeDeps,
|
|
14
|
+
type TelegramModelMenuState,
|
|
15
|
+
type TelegramModelMenuStateBuilderContext,
|
|
16
|
+
type TelegramModelMenuStateBuilderDeps,
|
|
17
|
+
} from "./menu-model.ts";
|
|
18
|
+
import {
|
|
19
|
+
handleTelegramStatusMenuCallbackAction,
|
|
20
|
+
openTelegramStatusMenu,
|
|
21
|
+
sendTelegramStatusMessage,
|
|
22
|
+
updateTelegramStatusMessage,
|
|
23
|
+
} from "./menu-status.ts";
|
|
24
|
+
import {
|
|
25
|
+
handleTelegramThinkingMenuCallbackAction,
|
|
26
|
+
openTelegramThinkingMenu,
|
|
27
|
+
updateTelegramThinkingMenuMessage,
|
|
28
|
+
} from "./menu-thinking.ts";
|
|
29
|
+
import {
|
|
9
30
|
type MenuModel,
|
|
10
|
-
modelsMatch,
|
|
11
|
-
parseTelegramCliScopedModelPatterns,
|
|
12
|
-
resolveScopedModelPatterns,
|
|
13
31
|
type ScopedTelegramModel,
|
|
14
|
-
sortScopedModels,
|
|
15
|
-
THINKING_LEVELS,
|
|
16
32
|
type ThinkingLevel,
|
|
17
33
|
} from "./model.ts";
|
|
18
|
-
const TELEGRAM_MODEL_MENU_CACHE_TTL_MS = 5000;
|
|
19
|
-
const TELEGRAM_MODEL_MENU_STATE_TTL_MS = 10 * 60 * 1000;
|
|
20
|
-
const MAX_STORED_TELEGRAM_MODEL_MENUS = 50;
|
|
21
|
-
|
|
22
|
-
export type TelegramModelScope = "all" | "scoped";
|
|
23
|
-
|
|
24
|
-
export interface TelegramModelMenuState<TModel extends MenuModel = MenuModel> {
|
|
25
|
-
chatId: number;
|
|
26
|
-
messageId: number;
|
|
27
|
-
page: number;
|
|
28
|
-
scope: TelegramModelScope;
|
|
29
|
-
scopedModels: ScopedTelegramModel<TModel>[];
|
|
30
|
-
allModels: ScopedTelegramModel<TModel>[];
|
|
31
|
-
note?: string;
|
|
32
|
-
mode: "status" | "model" | "thinking";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface StoredTelegramModelMenuState<
|
|
36
|
-
TModel extends MenuModel = MenuModel,
|
|
37
|
-
> {
|
|
38
|
-
state: TelegramModelMenuState<TModel>;
|
|
39
|
-
updatedAt: number;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface TelegramModelMenuStoreOptions {
|
|
43
|
-
maxAgeMs: number;
|
|
44
|
-
maxStoredMenus: number;
|
|
45
|
-
now?: number;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface CachedTelegramModelMenuInputs<
|
|
49
|
-
TModel extends MenuModel = MenuModel,
|
|
50
|
-
> {
|
|
51
|
-
expiresAt: number;
|
|
52
|
-
availableModels: TModel[];
|
|
53
|
-
configuredScopedModelPatterns: string[];
|
|
54
|
-
cliScopedModelPatterns?: string[];
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface TelegramModelMenuInputCacheDeps<
|
|
58
|
-
TModel extends MenuModel = MenuModel,
|
|
59
|
-
> {
|
|
60
|
-
cacheTtlMs: number;
|
|
61
|
-
now?: number;
|
|
62
|
-
reloadSettings: () => Promise<void>;
|
|
63
|
-
refreshAvailableModels: () => TModel[];
|
|
64
|
-
getConfiguredScopedModelPatterns: () => string[] | undefined;
|
|
65
|
-
getCliScopedModelPatterns: () => string[] | undefined;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface TelegramModelMenuRuntimeContext<
|
|
69
|
-
TModel extends MenuModel = MenuModel,
|
|
70
|
-
> {
|
|
71
|
-
modelRegistry: {
|
|
72
|
-
refresh: () => void;
|
|
73
|
-
getAvailable: () => TModel[];
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface TelegramModelMenuRuntimeOptions<
|
|
78
|
-
TContext extends TelegramModelMenuRuntimeContext<TModel>,
|
|
79
|
-
TModel extends MenuModel = MenuModel,
|
|
80
|
-
> {
|
|
81
|
-
chatId: number;
|
|
82
|
-
activeModel: TModel | undefined;
|
|
83
|
-
cachedInputs: CachedTelegramModelMenuInputs<TModel> | undefined;
|
|
84
|
-
cacheTtlMs: number;
|
|
85
|
-
ctx: TContext;
|
|
86
|
-
reloadSettings: () => Promise<void>;
|
|
87
|
-
getConfiguredScopedModelPatterns: () => string[] | undefined;
|
|
88
|
-
getCliScopedModelPatterns?: () => string[] | undefined;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export interface MenuSettingsManager {
|
|
92
|
-
reload: () => Promise<void>;
|
|
93
|
-
getEnabledModels: () => string[] | undefined;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export type TelegramModelMenuStateBuilderContext<
|
|
97
|
-
TModel extends MenuModel = MenuModel,
|
|
98
|
-
> = TelegramModelMenuRuntimeContext<TModel> & { cwd: string };
|
|
99
|
-
|
|
100
|
-
export interface TelegramModelMenuStateBuilderDeps<
|
|
101
|
-
TModel extends MenuModel = MenuModel,
|
|
102
|
-
TContext extends TelegramModelMenuStateBuilderContext<TModel> =
|
|
103
|
-
TelegramModelMenuStateBuilderContext<TModel>,
|
|
104
|
-
> {
|
|
105
|
-
runtime: TelegramModelMenuRuntime<TModel>;
|
|
106
|
-
createSettingsManager: (cwd: string) => MenuSettingsManager;
|
|
107
|
-
getActiveModel: (ctx: TContext) => TModel | undefined;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export type TelegramReplyMarkup = {
|
|
111
|
-
inline_keyboard: Array<Array<{ text: string; callback_data: string }>>;
|
|
112
|
-
};
|
|
113
34
|
|
|
114
|
-
export
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
35
|
+
export {
|
|
36
|
+
applyTelegramModelPageSelection,
|
|
37
|
+
applyTelegramModelScopeSelection,
|
|
38
|
+
buildModelMenuReplyMarkup,
|
|
39
|
+
buildModelPageMenuReplyMarkup,
|
|
40
|
+
buildTelegramModelCallbackPlan,
|
|
41
|
+
buildTelegramModelMenuRenderPayload,
|
|
42
|
+
buildTelegramModelPageMenuRenderPayload,
|
|
43
|
+
buildTelegramModelMenuState,
|
|
44
|
+
buildTelegramModelMenuStateRuntime,
|
|
45
|
+
createTelegramModelMenuRuntime,
|
|
46
|
+
createTelegramModelMenuStateBuilder,
|
|
47
|
+
formatScopedModelButtonText,
|
|
48
|
+
getModelMenuItems,
|
|
49
|
+
getStoredTelegramModelMenuState,
|
|
50
|
+
getTelegramModelMenuPage,
|
|
51
|
+
getTelegramModelSelection,
|
|
52
|
+
handleTelegramModelMenuCallbackAction,
|
|
53
|
+
MODEL_MENU_TITLE,
|
|
54
|
+
MODEL_PAGE_MENU_TITLE,
|
|
55
|
+
openTelegramModelMenu,
|
|
56
|
+
pruneStoredTelegramModelMenus,
|
|
57
|
+
resolveCachedTelegramModelMenuInputs,
|
|
58
|
+
sendTelegramModelMenuMessage,
|
|
59
|
+
storeTelegramModelMenuState,
|
|
60
|
+
TELEGRAM_MODEL_PAGE_SIZE,
|
|
61
|
+
updateTelegramModelMenuMessage,
|
|
62
|
+
} from "./menu-model.ts";
|
|
63
|
+
export type {
|
|
64
|
+
BuildTelegramModelCallbackPlanParams,
|
|
65
|
+
BuildTelegramModelMenuStateParams,
|
|
66
|
+
CachedTelegramModelMenuInputs,
|
|
67
|
+
MenuSettingsManager,
|
|
68
|
+
StoredTelegramModelMenuState,
|
|
69
|
+
TelegramMenuMessageRuntimeDeps,
|
|
70
|
+
TelegramMenuMutationResult,
|
|
71
|
+
TelegramMenuRenderPayload,
|
|
72
|
+
TelegramMenuSelectionResult,
|
|
73
|
+
TelegramModelCallbackPlan,
|
|
74
|
+
TelegramModelMenuCallbackDeps,
|
|
75
|
+
TelegramModelMenuInputCacheDeps,
|
|
76
|
+
TelegramModelMenuOpenDeps,
|
|
77
|
+
TelegramModelMenuPage,
|
|
78
|
+
TelegramModelMenuRuntime,
|
|
79
|
+
TelegramModelMenuRuntimeContext,
|
|
80
|
+
TelegramModelMenuRuntimeOptions,
|
|
81
|
+
TelegramModelMenuState,
|
|
82
|
+
TelegramModelMenuStateBuilderContext,
|
|
83
|
+
TelegramModelMenuStateBuilderDeps,
|
|
84
|
+
TelegramModelMenuStoreOptions,
|
|
85
|
+
TelegramModelScope,
|
|
86
|
+
TelegramReplyMarkup,
|
|
87
|
+
} from "./menu-model.ts";
|
|
88
|
+
export {
|
|
89
|
+
buildStatusReplyMarkup,
|
|
90
|
+
buildTelegramStatusMenuRenderPayload,
|
|
91
|
+
handleTelegramStatusMenuCallbackAction,
|
|
92
|
+
openTelegramStatusMenu,
|
|
93
|
+
sendTelegramStatusMessage,
|
|
94
|
+
updateTelegramStatusMessage,
|
|
95
|
+
} from "./menu-status.ts";
|
|
96
|
+
export type {
|
|
97
|
+
TelegramStatusMenuCallbackDeps,
|
|
98
|
+
TelegramStatusMenuOpenDeps,
|
|
99
|
+
} from "./menu-status.ts";
|
|
100
|
+
export {
|
|
101
|
+
buildTelegramThinkingMenuRenderPayload,
|
|
102
|
+
buildThinkingMenuReplyMarkup,
|
|
103
|
+
buildThinkingMenuText,
|
|
104
|
+
handleTelegramThinkingMenuCallbackAction,
|
|
105
|
+
openTelegramThinkingMenu,
|
|
106
|
+
updateTelegramThinkingMenuMessage,
|
|
107
|
+
} from "./menu-thinking.ts";
|
|
108
|
+
export type {
|
|
109
|
+
TelegramThinkingMenuCallbackDeps,
|
|
110
|
+
TelegramThinkingMenuOpenDeps,
|
|
111
|
+
} from "./menu-thinking.ts";
|
|
129
112
|
|
|
130
113
|
export interface TelegramMenuEffectPort<TModel extends MenuModel = MenuModel> {
|
|
131
114
|
answerCallbackQuery: (
|
|
@@ -145,37 +128,6 @@ export interface TelegramMenuEffectPort<TModel extends MenuModel = MenuModel> {
|
|
|
145
128
|
) => Promise<boolean> | boolean;
|
|
146
129
|
}
|
|
147
130
|
|
|
148
|
-
export type TelegramStatusMenuCallbackDeps<
|
|
149
|
-
TModel extends MenuModel = MenuModel,
|
|
150
|
-
> = Pick<
|
|
151
|
-
TelegramMenuEffectPort<TModel>,
|
|
152
|
-
"updateModelMenuMessage" | "updateThinkingMenuMessage" | "answerCallbackQuery"
|
|
153
|
-
>;
|
|
154
|
-
|
|
155
|
-
export type TelegramThinkingMenuCallbackDeps<
|
|
156
|
-
TModel extends MenuModel = MenuModel,
|
|
157
|
-
> = Pick<
|
|
158
|
-
TelegramMenuEffectPort<TModel>,
|
|
159
|
-
| "setThinkingLevel"
|
|
160
|
-
| "getCurrentThinkingLevel"
|
|
161
|
-
| "updateStatusMessage"
|
|
162
|
-
| "answerCallbackQuery"
|
|
163
|
-
>;
|
|
164
|
-
|
|
165
|
-
export type TelegramModelMenuCallbackDeps<
|
|
166
|
-
TModel extends MenuModel = MenuModel,
|
|
167
|
-
> = Pick<
|
|
168
|
-
TelegramMenuEffectPort<TModel>,
|
|
169
|
-
| "updateModelMenuMessage"
|
|
170
|
-
| "updateStatusMessage"
|
|
171
|
-
| "answerCallbackQuery"
|
|
172
|
-
| "setModel"
|
|
173
|
-
| "setCurrentModel"
|
|
174
|
-
| "setThinkingLevel"
|
|
175
|
-
| "stagePendingModelSwitch"
|
|
176
|
-
| "restartInterruptedTelegramTurn"
|
|
177
|
-
>;
|
|
178
|
-
|
|
179
131
|
export interface TelegramMenuCallbackEntryDeps {
|
|
180
132
|
handleStatusAction: () => Promise<boolean>;
|
|
181
133
|
handleThinkingAction: () => Promise<boolean>;
|
|
@@ -256,40 +208,6 @@ export interface TelegramMenuCallbackRuntimeDeps<
|
|
|
256
208
|
) => Promise<boolean> | boolean;
|
|
257
209
|
}
|
|
258
210
|
|
|
259
|
-
export interface TelegramStatusMenuOpenDeps<
|
|
260
|
-
TModel extends MenuModel = MenuModel,
|
|
261
|
-
> {
|
|
262
|
-
isIdle: () => boolean;
|
|
263
|
-
sendBusyMessage: () => Promise<void>;
|
|
264
|
-
getModelMenuState: () => Promise<TelegramModelMenuState<TModel>>;
|
|
265
|
-
buildStatusHtml: () => string;
|
|
266
|
-
getActiveModel: () => TModel | undefined;
|
|
267
|
-
getThinkingLevel: () => ThinkingLevel;
|
|
268
|
-
sendStatusMenu: (
|
|
269
|
-
state: TelegramModelMenuState<TModel>,
|
|
270
|
-
statusHtml: string,
|
|
271
|
-
activeModel: TModel | undefined,
|
|
272
|
-
thinkingLevel: ThinkingLevel,
|
|
273
|
-
) => Promise<number | undefined>;
|
|
274
|
-
storeModelMenuState: (state: TelegramModelMenuState<TModel>) => void;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
export interface TelegramModelMenuOpenDeps<
|
|
278
|
-
TModel extends MenuModel = MenuModel,
|
|
279
|
-
> {
|
|
280
|
-
isIdle: () => boolean;
|
|
281
|
-
canOfferInFlightModelSwitch: () => boolean;
|
|
282
|
-
sendBusyMessage: () => Promise<void>;
|
|
283
|
-
sendNoModelsMessage: () => Promise<void>;
|
|
284
|
-
getModelMenuState: () => Promise<TelegramModelMenuState<TModel>>;
|
|
285
|
-
getActiveModel: () => TModel | undefined;
|
|
286
|
-
sendModelMenu: (
|
|
287
|
-
state: TelegramModelMenuState<TModel>,
|
|
288
|
-
activeModel: TModel | undefined,
|
|
289
|
-
) => Promise<number | undefined>;
|
|
290
|
-
storeModelMenuState: (state: TelegramModelMenuState<TModel>) => void;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
211
|
export interface TelegramMenuActionRuntimeDeps<
|
|
294
212
|
TContext,
|
|
295
213
|
TModel extends MenuModel = MenuModel,
|
|
@@ -300,6 +218,7 @@ export interface TelegramMenuActionRuntimeDeps<
|
|
|
300
218
|
) => Promise<TelegramModelMenuState<TModel>>;
|
|
301
219
|
getActiveModel: (ctx: TContext) => TModel | undefined;
|
|
302
220
|
getThinkingLevel: () => ThinkingLevel;
|
|
221
|
+
getQueueItemCount?: () => number;
|
|
303
222
|
buildStatusHtml: (ctx: TContext) => string;
|
|
304
223
|
storeModelMenuState: (state: TelegramModelMenuState<TModel>) => void;
|
|
305
224
|
isIdle: (ctx: TContext) => boolean;
|
|
@@ -337,332 +256,23 @@ export interface TelegramMenuActionRuntime<
|
|
|
337
256
|
replyToMessageId: number,
|
|
338
257
|
ctx: TContext,
|
|
339
258
|
) => Promise<void>;
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
TModel extends MenuModel = MenuModel,
|
|
346
|
-
>(
|
|
347
|
-
menus: Map<number, StoredTelegramModelMenuState<TModel>>,
|
|
348
|
-
options: TelegramModelMenuStoreOptions,
|
|
349
|
-
): void {
|
|
350
|
-
const now = options.now ?? Date.now();
|
|
351
|
-
for (const [messageId, entry] of menus.entries()) {
|
|
352
|
-
if (now - entry.updatedAt <= options.maxAgeMs) continue;
|
|
353
|
-
menus.delete(messageId);
|
|
354
|
-
}
|
|
355
|
-
while (menus.size > options.maxStoredMenus) {
|
|
356
|
-
const oldestMessageId = menus.keys().next().value as number | undefined;
|
|
357
|
-
if (oldestMessageId === undefined) return;
|
|
358
|
-
menus.delete(oldestMessageId);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
export function storeTelegramModelMenuState<
|
|
363
|
-
TModel extends MenuModel = MenuModel,
|
|
364
|
-
>(
|
|
365
|
-
menus: Map<number, StoredTelegramModelMenuState<TModel>>,
|
|
366
|
-
state: TelegramModelMenuState<TModel>,
|
|
367
|
-
options: TelegramModelMenuStoreOptions,
|
|
368
|
-
): void {
|
|
369
|
-
const now = options.now ?? Date.now();
|
|
370
|
-
pruneStoredTelegramModelMenus(menus, { ...options, now });
|
|
371
|
-
menus.set(state.messageId, { state, updatedAt: now });
|
|
372
|
-
pruneStoredTelegramModelMenus(menus, { ...options, now });
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
export function getStoredTelegramModelMenuState<
|
|
376
|
-
TModel extends MenuModel = MenuModel,
|
|
377
|
-
>(
|
|
378
|
-
menus: Map<number, StoredTelegramModelMenuState<TModel>>,
|
|
379
|
-
messageId: number | undefined,
|
|
380
|
-
options: TelegramModelMenuStoreOptions,
|
|
381
|
-
): TelegramModelMenuState<TModel> | undefined {
|
|
382
|
-
if (messageId === undefined) return undefined;
|
|
383
|
-
const now = options.now ?? Date.now();
|
|
384
|
-
pruneStoredTelegramModelMenus(menus, { ...options, now });
|
|
385
|
-
const entry = menus.get(messageId);
|
|
386
|
-
if (!entry) return undefined;
|
|
387
|
-
menus.delete(messageId);
|
|
388
|
-
entry.updatedAt = now;
|
|
389
|
-
menus.set(messageId, entry);
|
|
390
|
-
return entry.state;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
export interface TelegramModelMenuRuntime<
|
|
394
|
-
TModel extends MenuModel = MenuModel,
|
|
395
|
-
> {
|
|
396
|
-
storeState: (state: TelegramModelMenuState<TModel>) => void;
|
|
397
|
-
getState: (
|
|
398
|
-
messageId: number | undefined,
|
|
399
|
-
) => TelegramModelMenuState<TModel> | undefined;
|
|
400
|
-
clear: () => void;
|
|
401
|
-
buildState: <TContext extends TelegramModelMenuRuntimeContext<TModel>>(
|
|
402
|
-
options: Omit<
|
|
403
|
-
TelegramModelMenuRuntimeOptions<TContext, TModel>,
|
|
404
|
-
"cachedInputs" | "cacheTtlMs"
|
|
405
|
-
>,
|
|
406
|
-
) => Promise<TelegramModelMenuState<TModel>>;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
export function createTelegramModelMenuRuntime<
|
|
410
|
-
TModel extends MenuModel = MenuModel,
|
|
411
|
-
>(
|
|
412
|
-
options: Partial<TelegramModelMenuStoreOptions> = {},
|
|
413
|
-
): TelegramModelMenuRuntime<TModel> {
|
|
414
|
-
const menus = new Map<number, StoredTelegramModelMenuState<TModel>>();
|
|
415
|
-
let cachedInputs: CachedTelegramModelMenuInputs<TModel> | undefined;
|
|
416
|
-
const getStoreOptions = (): TelegramModelMenuStoreOptions => ({
|
|
417
|
-
maxAgeMs: options.maxAgeMs ?? TELEGRAM_MODEL_MENU_STATE_TTL_MS,
|
|
418
|
-
maxStoredMenus: options.maxStoredMenus ?? MAX_STORED_TELEGRAM_MODEL_MENUS,
|
|
419
|
-
now: options.now,
|
|
420
|
-
});
|
|
421
|
-
return {
|
|
422
|
-
storeState: (state) => {
|
|
423
|
-
storeTelegramModelMenuState(menus, state, getStoreOptions());
|
|
424
|
-
},
|
|
425
|
-
getState: (messageId) =>
|
|
426
|
-
getStoredTelegramModelMenuState(menus, messageId, getStoreOptions()),
|
|
427
|
-
clear: () => {
|
|
428
|
-
menus.clear();
|
|
429
|
-
cachedInputs = undefined;
|
|
430
|
-
},
|
|
431
|
-
buildState: async (stateOptions) => {
|
|
432
|
-
const result = await buildTelegramModelMenuStateRuntime({
|
|
433
|
-
...stateOptions,
|
|
434
|
-
cachedInputs,
|
|
435
|
-
cacheTtlMs: TELEGRAM_MODEL_MENU_CACHE_TTL_MS,
|
|
436
|
-
});
|
|
437
|
-
cachedInputs = result.cachedInputs;
|
|
438
|
-
return result.state;
|
|
439
|
-
},
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
export function createTelegramModelMenuStateBuilder<
|
|
444
|
-
TModel extends MenuModel = MenuModel,
|
|
445
|
-
TContext extends TelegramModelMenuStateBuilderContext<TModel> =
|
|
446
|
-
TelegramModelMenuStateBuilderContext<TModel>,
|
|
447
|
-
>(
|
|
448
|
-
deps: TelegramModelMenuStateBuilderDeps<TModel, TContext>,
|
|
449
|
-
): (chatId: number, ctx: TContext) => Promise<TelegramModelMenuState<TModel>> {
|
|
450
|
-
return async (chatId, ctx) => {
|
|
451
|
-
const settingsManager = deps.createSettingsManager(ctx.cwd);
|
|
452
|
-
return deps.runtime.buildState({
|
|
453
|
-
chatId,
|
|
454
|
-
activeModel: deps.getActiveModel(ctx),
|
|
455
|
-
ctx,
|
|
456
|
-
reloadSettings: () => settingsManager.reload(),
|
|
457
|
-
getConfiguredScopedModelPatterns: () =>
|
|
458
|
-
settingsManager.getEnabledModels(),
|
|
459
|
-
});
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
export async function resolveCachedTelegramModelMenuInputs<
|
|
464
|
-
TModel extends MenuModel = MenuModel,
|
|
465
|
-
>(
|
|
466
|
-
cachedInputs: CachedTelegramModelMenuInputs<TModel> | undefined,
|
|
467
|
-
deps: TelegramModelMenuInputCacheDeps<TModel>,
|
|
468
|
-
): Promise<CachedTelegramModelMenuInputs<TModel>> {
|
|
469
|
-
const now = deps.now ?? Date.now();
|
|
470
|
-
if (cachedInputs && cachedInputs.expiresAt > now) return cachedInputs;
|
|
471
|
-
await deps.reloadSettings();
|
|
472
|
-
const availableModels = deps.refreshAvailableModels();
|
|
473
|
-
const cliScopedModelPatterns = deps.getCliScopedModelPatterns();
|
|
474
|
-
const configuredScopedModelPatterns =
|
|
475
|
-
cliScopedModelPatterns ?? deps.getConfiguredScopedModelPatterns() ?? [];
|
|
476
|
-
return {
|
|
477
|
-
expiresAt: now + deps.cacheTtlMs,
|
|
478
|
-
availableModels,
|
|
479
|
-
configuredScopedModelPatterns,
|
|
480
|
-
cliScopedModelPatterns,
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
function getTelegramCliScopedModelPatterns(): string[] | undefined {
|
|
485
|
-
return parseTelegramCliScopedModelPatterns(process.argv.slice(2));
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
export const MODEL_MENU_TITLE = "<b>Choose a model:</b>";
|
|
489
|
-
|
|
490
|
-
export interface BuildTelegramModelMenuStateParams<
|
|
491
|
-
TModel extends MenuModel = MenuModel,
|
|
492
|
-
> {
|
|
493
|
-
chatId: number;
|
|
494
|
-
activeModel: TModel | undefined;
|
|
495
|
-
availableModels: TModel[];
|
|
496
|
-
configuredScopedModelPatterns: string[];
|
|
497
|
-
cliScopedModelPatterns?: string[];
|
|
259
|
+
openThinkingMenu: (
|
|
260
|
+
chatId: number,
|
|
261
|
+
replyToMessageId: number,
|
|
262
|
+
ctx: TContext,
|
|
263
|
+
) => Promise<void>;
|
|
498
264
|
}
|
|
499
265
|
|
|
500
266
|
export type TelegramMenuCallbackAction =
|
|
501
267
|
| { kind: "ignore" }
|
|
502
|
-
| { kind: "status"; action: "model" | "thinking" }
|
|
268
|
+
| { kind: "status"; action: "model" | "thinking" | "queue" }
|
|
503
269
|
| { kind: "thinking:set"; level: string }
|
|
504
270
|
| {
|
|
505
271
|
kind: "model";
|
|
506
|
-
action: "noop" | "scope" | "page" | "pick";
|
|
272
|
+
action: "noop" | "scope" | "page" | "pages" | "pick";
|
|
507
273
|
value?: string;
|
|
508
274
|
};
|
|
509
275
|
|
|
510
|
-
export type TelegramMenuMutationResult = "invalid" | "unchanged" | "changed";
|
|
511
|
-
export type TelegramMenuSelectionResult<TModel extends MenuModel = MenuModel> =
|
|
512
|
-
| { kind: "invalid" }
|
|
513
|
-
| { kind: "missing" }
|
|
514
|
-
| { kind: "selected"; selection: ScopedTelegramModel<TModel> };
|
|
515
|
-
|
|
516
|
-
export interface TelegramModelMenuPage<TModel extends MenuModel = MenuModel> {
|
|
517
|
-
page: number;
|
|
518
|
-
pageCount: number;
|
|
519
|
-
start: number;
|
|
520
|
-
items: ScopedTelegramModel<TModel>[];
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
export interface TelegramMenuRenderPayload {
|
|
524
|
-
nextMode: TelegramModelMenuState["mode"];
|
|
525
|
-
text: string;
|
|
526
|
-
mode: "html" | "plain";
|
|
527
|
-
replyMarkup: TelegramReplyMarkup;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
export type TelegramModelCallbackPlan<TModel extends MenuModel = MenuModel> =
|
|
531
|
-
| { kind: "ignore" }
|
|
532
|
-
| { kind: "answer"; text?: string }
|
|
533
|
-
| { kind: "update-menu"; text?: string }
|
|
534
|
-
| {
|
|
535
|
-
kind: "refresh-status";
|
|
536
|
-
selection: ScopedTelegramModel<TModel>;
|
|
537
|
-
callbackText: string;
|
|
538
|
-
shouldApplyThinkingLevel: boolean;
|
|
539
|
-
}
|
|
540
|
-
| {
|
|
541
|
-
kind: "switch-model";
|
|
542
|
-
selection: ScopedTelegramModel<TModel>;
|
|
543
|
-
mode: "idle" | "restart-now" | "restart-after-tool";
|
|
544
|
-
callbackText: string;
|
|
545
|
-
};
|
|
546
|
-
|
|
547
|
-
export interface BuildTelegramModelCallbackPlanParams<
|
|
548
|
-
TModel extends MenuModel = MenuModel,
|
|
549
|
-
> {
|
|
550
|
-
data: string | undefined;
|
|
551
|
-
state: TelegramModelMenuState<TModel>;
|
|
552
|
-
activeModel: TModel | undefined;
|
|
553
|
-
currentThinkingLevel: ThinkingLevel;
|
|
554
|
-
isIdle: boolean;
|
|
555
|
-
canRestartBusyRun: boolean;
|
|
556
|
-
hasActiveToolExecutions: boolean;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
function truncateTelegramButtonLabel(label: string, maxLength = 56): string {
|
|
560
|
-
return label.length <= maxLength
|
|
561
|
-
? label
|
|
562
|
-
: `${label.slice(0, maxLength - 1)}…`;
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
export function formatScopedModelButtonText<
|
|
566
|
-
TModel extends MenuModel = MenuModel,
|
|
567
|
-
>(
|
|
568
|
-
entry: ScopedTelegramModel<TModel>,
|
|
569
|
-
currentModel: TModel | undefined,
|
|
570
|
-
): string {
|
|
571
|
-
let label = `${modelsMatch(entry.model, currentModel) ? "✅ " : ""}${entry.model.id} [${entry.model.provider}]`;
|
|
572
|
-
if (entry.thinkingLevel) {
|
|
573
|
-
label += ` · ${entry.thinkingLevel}`;
|
|
574
|
-
}
|
|
575
|
-
return truncateTelegramButtonLabel(label);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
export function formatStatusButtonLabel(label: string, value: string): string {
|
|
579
|
-
return truncateTelegramButtonLabel(`${label}: ${value}`, 64);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
export function getModelMenuItems<TModel extends MenuModel = MenuModel>(
|
|
583
|
-
state: TelegramModelMenuState<TModel>,
|
|
584
|
-
): ScopedTelegramModel<TModel>[] {
|
|
585
|
-
return state.scope === "scoped" && state.scopedModels.length > 0
|
|
586
|
-
? state.scopedModels
|
|
587
|
-
: state.allModels;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
export function buildTelegramModelMenuState<
|
|
591
|
-
TModel extends MenuModel = MenuModel,
|
|
592
|
-
>(
|
|
593
|
-
params: BuildTelegramModelMenuStateParams<TModel>,
|
|
594
|
-
): TelegramModelMenuState<TModel> {
|
|
595
|
-
const allModels = sortScopedModels(
|
|
596
|
-
params.availableModels.map((model) => ({ model })),
|
|
597
|
-
params.activeModel,
|
|
598
|
-
);
|
|
599
|
-
const scopedModels =
|
|
600
|
-
params.configuredScopedModelPatterns.length > 0
|
|
601
|
-
? sortScopedModels(
|
|
602
|
-
resolveScopedModelPatterns(
|
|
603
|
-
params.configuredScopedModelPatterns,
|
|
604
|
-
params.availableModels,
|
|
605
|
-
),
|
|
606
|
-
params.activeModel,
|
|
607
|
-
)
|
|
608
|
-
: [];
|
|
609
|
-
let note: string | undefined;
|
|
610
|
-
if (
|
|
611
|
-
params.configuredScopedModelPatterns.length > 0 &&
|
|
612
|
-
scopedModels.length === 0
|
|
613
|
-
) {
|
|
614
|
-
note = params.cliScopedModelPatterns
|
|
615
|
-
? "No CLI scoped models matched the current auth configuration. Showing all available models."
|
|
616
|
-
: "No scoped models matched the current auth configuration. Showing all available models.";
|
|
617
|
-
}
|
|
618
|
-
return {
|
|
619
|
-
chatId: params.chatId,
|
|
620
|
-
messageId: 0,
|
|
621
|
-
page: 0,
|
|
622
|
-
scope: scopedModels.length > 0 ? "scoped" : "all",
|
|
623
|
-
scopedModels,
|
|
624
|
-
allModels,
|
|
625
|
-
note,
|
|
626
|
-
mode: "status",
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
export async function buildTelegramModelMenuStateRuntime<
|
|
631
|
-
TContext extends TelegramModelMenuRuntimeContext<TModel>,
|
|
632
|
-
TModel extends MenuModel = MenuModel,
|
|
633
|
-
>(
|
|
634
|
-
options: TelegramModelMenuRuntimeOptions<TContext, TModel>,
|
|
635
|
-
): Promise<{
|
|
636
|
-
state: TelegramModelMenuState<TModel>;
|
|
637
|
-
cachedInputs: CachedTelegramModelMenuInputs<TModel>;
|
|
638
|
-
}> {
|
|
639
|
-
const cachedInputs = await resolveCachedTelegramModelMenuInputs(
|
|
640
|
-
options.cachedInputs,
|
|
641
|
-
{
|
|
642
|
-
cacheTtlMs: options.cacheTtlMs,
|
|
643
|
-
reloadSettings: options.reloadSettings,
|
|
644
|
-
refreshAvailableModels: () => {
|
|
645
|
-
options.ctx.modelRegistry.refresh();
|
|
646
|
-
return options.ctx.modelRegistry.getAvailable();
|
|
647
|
-
},
|
|
648
|
-
getConfiguredScopedModelPatterns:
|
|
649
|
-
options.getConfiguredScopedModelPatterns,
|
|
650
|
-
getCliScopedModelPatterns:
|
|
651
|
-
options.getCliScopedModelPatterns ?? getTelegramCliScopedModelPatterns,
|
|
652
|
-
},
|
|
653
|
-
);
|
|
654
|
-
return {
|
|
655
|
-
cachedInputs,
|
|
656
|
-
state: buildTelegramModelMenuState({
|
|
657
|
-
chatId: options.chatId,
|
|
658
|
-
activeModel: options.activeModel,
|
|
659
|
-
availableModels: cachedInputs.availableModels,
|
|
660
|
-
configuredScopedModelPatterns: cachedInputs.configuredScopedModelPatterns,
|
|
661
|
-
cliScopedModelPatterns: cachedInputs.cliScopedModelPatterns,
|
|
662
|
-
}),
|
|
663
|
-
};
|
|
664
|
-
}
|
|
665
|
-
|
|
666
276
|
export function parseTelegramMenuCallbackAction(
|
|
667
277
|
data: string | undefined,
|
|
668
278
|
): TelegramMenuCallbackAction {
|
|
@@ -670,6 +280,7 @@ export function parseTelegramMenuCallbackAction(
|
|
|
670
280
|
if (data === "status:thinking") {
|
|
671
281
|
return { kind: "status", action: "thinking" };
|
|
672
282
|
}
|
|
283
|
+
if (data === "status:queue") return { kind: "status", action: "queue" };
|
|
673
284
|
if (data?.startsWith("thinking:set:")) {
|
|
674
285
|
return {
|
|
675
286
|
kind: "thinking:set",
|
|
@@ -682,6 +293,7 @@ export function parseTelegramMenuCallbackAction(
|
|
|
682
293
|
action === "noop" ||
|
|
683
294
|
action === "scope" ||
|
|
684
295
|
action === "page" ||
|
|
296
|
+
action === "pages" ||
|
|
685
297
|
action === "pick"
|
|
686
298
|
) {
|
|
687
299
|
return { kind: "model", action, value };
|
|
@@ -690,155 +302,6 @@ export function parseTelegramMenuCallbackAction(
|
|
|
690
302
|
return { kind: "ignore" };
|
|
691
303
|
}
|
|
692
304
|
|
|
693
|
-
export function applyTelegramModelScopeSelection(
|
|
694
|
-
state: TelegramModelMenuState,
|
|
695
|
-
value: string | undefined,
|
|
696
|
-
): TelegramMenuMutationResult {
|
|
697
|
-
if (value !== "all" && value !== "scoped") return "invalid";
|
|
698
|
-
if (value === state.scope) return "unchanged";
|
|
699
|
-
state.scope = value;
|
|
700
|
-
state.page = 0;
|
|
701
|
-
return "changed";
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
export function applyTelegramModelPageSelection(
|
|
705
|
-
state: TelegramModelMenuState,
|
|
706
|
-
value: string | undefined,
|
|
707
|
-
): TelegramMenuMutationResult {
|
|
708
|
-
const page = Number(value);
|
|
709
|
-
if (!Number.isFinite(page)) return "invalid";
|
|
710
|
-
if (page === state.page) return "unchanged";
|
|
711
|
-
state.page = page;
|
|
712
|
-
return "changed";
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
export function getTelegramModelSelection<TModel extends MenuModel = MenuModel>(
|
|
716
|
-
state: TelegramModelMenuState<TModel>,
|
|
717
|
-
value: string | undefined,
|
|
718
|
-
): TelegramMenuSelectionResult<TModel> {
|
|
719
|
-
const index = Number(value);
|
|
720
|
-
if (!Number.isFinite(index)) return { kind: "invalid" };
|
|
721
|
-
const selection = getModelMenuItems(state)[index];
|
|
722
|
-
if (!selection) return { kind: "missing" };
|
|
723
|
-
return { kind: "selected", selection };
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
export function buildTelegramModelCallbackPlan<
|
|
727
|
-
TModel extends MenuModel = MenuModel,
|
|
728
|
-
>(
|
|
729
|
-
params: BuildTelegramModelCallbackPlanParams<TModel>,
|
|
730
|
-
): TelegramModelCallbackPlan<TModel> {
|
|
731
|
-
const action = parseTelegramMenuCallbackAction(params.data);
|
|
732
|
-
if (action.kind !== "model") return { kind: "ignore" };
|
|
733
|
-
if (action.action === "noop") return { kind: "answer" };
|
|
734
|
-
if (action.action === "scope") {
|
|
735
|
-
const scopeResult = applyTelegramModelScopeSelection(
|
|
736
|
-
params.state,
|
|
737
|
-
action.value,
|
|
738
|
-
);
|
|
739
|
-
if (scopeResult === "invalid") {
|
|
740
|
-
return { kind: "answer", text: "Unknown model scope." };
|
|
741
|
-
}
|
|
742
|
-
if (scopeResult === "unchanged") {
|
|
743
|
-
return { kind: "answer" };
|
|
744
|
-
}
|
|
745
|
-
return {
|
|
746
|
-
kind: "update-menu",
|
|
747
|
-
text: params.state.scope === "scoped" ? "Scoped models" : "All models",
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
if (action.action === "page") {
|
|
751
|
-
const pageResult = applyTelegramModelPageSelection(
|
|
752
|
-
params.state,
|
|
753
|
-
action.value,
|
|
754
|
-
);
|
|
755
|
-
if (pageResult === "invalid") {
|
|
756
|
-
return { kind: "answer", text: "Invalid page." };
|
|
757
|
-
}
|
|
758
|
-
if (pageResult === "unchanged") {
|
|
759
|
-
return { kind: "answer" };
|
|
760
|
-
}
|
|
761
|
-
return { kind: "update-menu" };
|
|
762
|
-
}
|
|
763
|
-
if (action.action !== "pick") {
|
|
764
|
-
return { kind: "answer" };
|
|
765
|
-
}
|
|
766
|
-
const selectionResult = getTelegramModelSelection(params.state, action.value);
|
|
767
|
-
if (selectionResult.kind === "invalid") {
|
|
768
|
-
return { kind: "answer", text: "Invalid model selection." };
|
|
769
|
-
}
|
|
770
|
-
if (selectionResult.kind === "missing") {
|
|
771
|
-
return { kind: "answer", text: "Selected model is no longer available." };
|
|
772
|
-
}
|
|
773
|
-
const selection = selectionResult.selection;
|
|
774
|
-
if (modelsMatch(selection.model, params.activeModel)) {
|
|
775
|
-
return {
|
|
776
|
-
kind: "refresh-status",
|
|
777
|
-
selection,
|
|
778
|
-
callbackText: `Model: ${selection.model.id}`,
|
|
779
|
-
shouldApplyThinkingLevel:
|
|
780
|
-
!!selection.thinkingLevel &&
|
|
781
|
-
selection.thinkingLevel !== params.currentThinkingLevel,
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
if (!params.isIdle) {
|
|
785
|
-
if (!params.canRestartBusyRun) {
|
|
786
|
-
return { kind: "answer", text: "Pi is busy. Send /stop first." };
|
|
787
|
-
}
|
|
788
|
-
return {
|
|
789
|
-
kind: "switch-model",
|
|
790
|
-
selection,
|
|
791
|
-
mode: params.hasActiveToolExecutions
|
|
792
|
-
? "restart-after-tool"
|
|
793
|
-
: "restart-now",
|
|
794
|
-
callbackText: params.hasActiveToolExecutions
|
|
795
|
-
? `Switched to ${selection.model.id}. Restarting after the current tool finishes…`
|
|
796
|
-
: `Switching to ${selection.model.id} and continuing…`,
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
return {
|
|
800
|
-
kind: "switch-model",
|
|
801
|
-
selection,
|
|
802
|
-
mode: "idle",
|
|
803
|
-
callbackText: `Switched to ${selection.model.id}`,
|
|
804
|
-
};
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
export async function openTelegramStatusMenu<
|
|
808
|
-
TModel extends MenuModel = MenuModel,
|
|
809
|
-
>(deps: TelegramStatusMenuOpenDeps<TModel>): Promise<void> {
|
|
810
|
-
const state = await deps.getModelMenuState();
|
|
811
|
-
const messageId = await deps.sendStatusMenu(
|
|
812
|
-
state,
|
|
813
|
-
deps.buildStatusHtml(),
|
|
814
|
-
deps.getActiveModel(),
|
|
815
|
-
deps.getThinkingLevel(),
|
|
816
|
-
);
|
|
817
|
-
if (messageId === undefined) return;
|
|
818
|
-
state.messageId = messageId;
|
|
819
|
-
state.mode = "status";
|
|
820
|
-
deps.storeModelMenuState(state);
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
export async function openTelegramModelMenu<
|
|
824
|
-
TModel extends MenuModel = MenuModel,
|
|
825
|
-
>(deps: TelegramModelMenuOpenDeps<TModel>): Promise<void> {
|
|
826
|
-
if (!deps.isIdle() && !deps.canOfferInFlightModelSwitch()) {
|
|
827
|
-
await deps.sendBusyMessage();
|
|
828
|
-
return;
|
|
829
|
-
}
|
|
830
|
-
const state = await deps.getModelMenuState();
|
|
831
|
-
if (state.allModels.length === 0) {
|
|
832
|
-
await deps.sendNoModelsMessage();
|
|
833
|
-
return;
|
|
834
|
-
}
|
|
835
|
-
const messageId = await deps.sendModelMenu(state, deps.getActiveModel());
|
|
836
|
-
if (messageId === undefined) return;
|
|
837
|
-
state.messageId = messageId;
|
|
838
|
-
state.mode = "model";
|
|
839
|
-
deps.storeModelMenuState(state);
|
|
840
|
-
}
|
|
841
|
-
|
|
842
305
|
export async function handleTelegramMenuCallbackEntry(
|
|
843
306
|
callbackQueryId: string,
|
|
844
307
|
data: string | undefined,
|
|
@@ -979,6 +442,16 @@ export async function handleTelegramMenuCallbackRuntime<
|
|
|
979
442
|
ctx: TContext,
|
|
980
443
|
deps: TelegramMenuCallbackRuntimeDeps<TContext, TModel>,
|
|
981
444
|
): Promise<void> {
|
|
445
|
+
if (query.data === "menu:back") {
|
|
446
|
+
const state = deps.getStoredModelMenuState(query.message?.message_id);
|
|
447
|
+
if (!state) {
|
|
448
|
+
await deps.answerCallbackQuery(query.id, "Interactive message expired.");
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
await deps.updateStatusMessage(state, ctx);
|
|
452
|
+
await deps.answerCallbackQuery(query.id);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
982
455
|
await handleStoredTelegramMenuCallback(query, {
|
|
983
456
|
getStoredModelMenuState: deps.getStoredModelMenuState,
|
|
984
457
|
handleStatusAction: async (state) =>
|
|
@@ -1050,264 +523,6 @@ export async function handleTelegramMenuCallbackRuntime<
|
|
|
1050
523
|
});
|
|
1051
524
|
}
|
|
1052
525
|
|
|
1053
|
-
export async function handleTelegramModelMenuCallbackAction<
|
|
1054
|
-
TModel extends MenuModel = MenuModel,
|
|
1055
|
-
>(
|
|
1056
|
-
callbackQueryId: string,
|
|
1057
|
-
params: BuildTelegramModelCallbackPlanParams<TModel>,
|
|
1058
|
-
deps: TelegramModelMenuCallbackDeps<TModel>,
|
|
1059
|
-
): Promise<boolean> {
|
|
1060
|
-
const plan = buildTelegramModelCallbackPlan(params);
|
|
1061
|
-
if (plan.kind === "ignore") return false;
|
|
1062
|
-
if (plan.kind === "answer") {
|
|
1063
|
-
await deps.answerCallbackQuery(callbackQueryId, plan.text);
|
|
1064
|
-
return true;
|
|
1065
|
-
}
|
|
1066
|
-
if (plan.kind === "update-menu") {
|
|
1067
|
-
await deps.updateModelMenuMessage();
|
|
1068
|
-
await deps.answerCallbackQuery(callbackQueryId, plan.text);
|
|
1069
|
-
return true;
|
|
1070
|
-
}
|
|
1071
|
-
if (plan.kind === "refresh-status") {
|
|
1072
|
-
if (plan.shouldApplyThinkingLevel && plan.selection.thinkingLevel) {
|
|
1073
|
-
deps.setThinkingLevel(plan.selection.thinkingLevel);
|
|
1074
|
-
}
|
|
1075
|
-
await deps.updateStatusMessage();
|
|
1076
|
-
await deps.answerCallbackQuery(callbackQueryId, plan.callbackText);
|
|
1077
|
-
return true;
|
|
1078
|
-
}
|
|
1079
|
-
const changed = await deps.setModel(plan.selection.model);
|
|
1080
|
-
if (changed === false) {
|
|
1081
|
-
await deps.answerCallbackQuery(callbackQueryId, "Model is not available.");
|
|
1082
|
-
return true;
|
|
1083
|
-
}
|
|
1084
|
-
deps.setCurrentModel(plan.selection.model);
|
|
1085
|
-
if (plan.selection.thinkingLevel) {
|
|
1086
|
-
deps.setThinkingLevel(plan.selection.thinkingLevel);
|
|
1087
|
-
}
|
|
1088
|
-
await deps.updateStatusMessage();
|
|
1089
|
-
if (plan.mode === "restart-after-tool") {
|
|
1090
|
-
deps.stagePendingModelSwitch(plan.selection);
|
|
1091
|
-
await deps.answerCallbackQuery(callbackQueryId, plan.callbackText);
|
|
1092
|
-
return true;
|
|
1093
|
-
}
|
|
1094
|
-
if (plan.mode === "restart-now") {
|
|
1095
|
-
const restarted = await deps.restartInterruptedTelegramTurn(plan.selection);
|
|
1096
|
-
if (!restarted) {
|
|
1097
|
-
await deps.answerCallbackQuery(
|
|
1098
|
-
callbackQueryId,
|
|
1099
|
-
"Pi is busy. Send /stop first.",
|
|
1100
|
-
);
|
|
1101
|
-
return true;
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
await deps.answerCallbackQuery(callbackQueryId, plan.callbackText);
|
|
1105
|
-
return true;
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
export async function handleTelegramStatusMenuCallbackAction(
|
|
1109
|
-
callbackQueryId: string,
|
|
1110
|
-
data: string | undefined,
|
|
1111
|
-
activeModel: MenuModel | undefined,
|
|
1112
|
-
deps: TelegramStatusMenuCallbackDeps,
|
|
1113
|
-
): Promise<boolean> {
|
|
1114
|
-
const action = parseTelegramMenuCallbackAction(data);
|
|
1115
|
-
if (action.kind === "status" && action.action === "model") {
|
|
1116
|
-
await deps.updateModelMenuMessage();
|
|
1117
|
-
await deps.answerCallbackQuery(callbackQueryId);
|
|
1118
|
-
return true;
|
|
1119
|
-
}
|
|
1120
|
-
if (!(action.kind === "status" && action.action === "thinking")) {
|
|
1121
|
-
return false;
|
|
1122
|
-
}
|
|
1123
|
-
if (!activeModel?.reasoning) {
|
|
1124
|
-
await deps.answerCallbackQuery(
|
|
1125
|
-
callbackQueryId,
|
|
1126
|
-
"This model has no reasoning controls.",
|
|
1127
|
-
);
|
|
1128
|
-
return true;
|
|
1129
|
-
}
|
|
1130
|
-
await deps.updateThinkingMenuMessage();
|
|
1131
|
-
await deps.answerCallbackQuery(callbackQueryId);
|
|
1132
|
-
return true;
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
export async function handleTelegramThinkingMenuCallbackAction(
|
|
1136
|
-
callbackQueryId: string,
|
|
1137
|
-
data: string | undefined,
|
|
1138
|
-
activeModel: MenuModel | undefined,
|
|
1139
|
-
deps: TelegramThinkingMenuCallbackDeps,
|
|
1140
|
-
): Promise<boolean> {
|
|
1141
|
-
const action = parseTelegramMenuCallbackAction(data);
|
|
1142
|
-
if (action.kind !== "thinking:set") return false;
|
|
1143
|
-
if (!isThinkingLevel(action.level)) {
|
|
1144
|
-
await deps.answerCallbackQuery(callbackQueryId, "Invalid thinking level.");
|
|
1145
|
-
return true;
|
|
1146
|
-
}
|
|
1147
|
-
if (!activeModel?.reasoning) {
|
|
1148
|
-
await deps.answerCallbackQuery(
|
|
1149
|
-
callbackQueryId,
|
|
1150
|
-
"This model has no reasoning controls.",
|
|
1151
|
-
);
|
|
1152
|
-
return true;
|
|
1153
|
-
}
|
|
1154
|
-
deps.setThinkingLevel(action.level);
|
|
1155
|
-
await deps.updateStatusMessage();
|
|
1156
|
-
await deps.answerCallbackQuery(
|
|
1157
|
-
callbackQueryId,
|
|
1158
|
-
`Thinking: ${deps.getCurrentThinkingLevel()}`,
|
|
1159
|
-
);
|
|
1160
|
-
return true;
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
export function buildThinkingMenuText(
|
|
1164
|
-
activeModel: MenuModel | undefined,
|
|
1165
|
-
currentThinkingLevel: ThinkingLevel,
|
|
1166
|
-
): string {
|
|
1167
|
-
const lines = ["Choose a thinking level"];
|
|
1168
|
-
if (activeModel) {
|
|
1169
|
-
lines.push(`Model: ${getCanonicalModelId(activeModel)}`);
|
|
1170
|
-
}
|
|
1171
|
-
lines.push(`Current: ${currentThinkingLevel}`);
|
|
1172
|
-
return lines.join("\n");
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
export function getTelegramModelMenuPage(
|
|
1176
|
-
state: TelegramModelMenuState,
|
|
1177
|
-
pageSize: number,
|
|
1178
|
-
): TelegramModelMenuPage {
|
|
1179
|
-
const items = getModelMenuItems(state);
|
|
1180
|
-
const pageCount = Math.max(1, Math.ceil(items.length / pageSize));
|
|
1181
|
-
const page = Math.max(0, Math.min(state.page, pageCount - 1));
|
|
1182
|
-
const start = page * pageSize;
|
|
1183
|
-
return {
|
|
1184
|
-
page,
|
|
1185
|
-
pageCount,
|
|
1186
|
-
start,
|
|
1187
|
-
items: items.slice(start, start + pageSize),
|
|
1188
|
-
};
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
export function buildModelMenuReplyMarkup(
|
|
1192
|
-
state: TelegramModelMenuState,
|
|
1193
|
-
currentModel: MenuModel | undefined,
|
|
1194
|
-
pageSize: number,
|
|
1195
|
-
): TelegramReplyMarkup {
|
|
1196
|
-
const menuPage = getTelegramModelMenuPage(state, pageSize);
|
|
1197
|
-
const rows = menuPage.items.map((entry, index) => [
|
|
1198
|
-
{
|
|
1199
|
-
text: formatScopedModelButtonText(entry, currentModel),
|
|
1200
|
-
callback_data: `model:pick:${menuPage.start + index}`,
|
|
1201
|
-
},
|
|
1202
|
-
]);
|
|
1203
|
-
if (menuPage.pageCount > 1) {
|
|
1204
|
-
const previousPage =
|
|
1205
|
-
menuPage.page === 0 ? menuPage.pageCount - 1 : menuPage.page - 1;
|
|
1206
|
-
const nextPage =
|
|
1207
|
-
menuPage.page === menuPage.pageCount - 1 ? 0 : menuPage.page + 1;
|
|
1208
|
-
rows.push([
|
|
1209
|
-
{ text: "⬅️", callback_data: `model:page:${previousPage}` },
|
|
1210
|
-
{
|
|
1211
|
-
text: `${menuPage.page + 1}/${menuPage.pageCount}`,
|
|
1212
|
-
callback_data: "model:noop",
|
|
1213
|
-
},
|
|
1214
|
-
{ text: "➡️", callback_data: `model:page:${nextPage}` },
|
|
1215
|
-
]);
|
|
1216
|
-
}
|
|
1217
|
-
if (state.scopedModels.length > 0) {
|
|
1218
|
-
rows.push([
|
|
1219
|
-
{
|
|
1220
|
-
text: state.scope === "scoped" ? "✅ Scoped" : "Scoped",
|
|
1221
|
-
callback_data: "model:scope:scoped",
|
|
1222
|
-
},
|
|
1223
|
-
{
|
|
1224
|
-
text: state.scope === "all" ? "✅ All" : "All",
|
|
1225
|
-
callback_data: "model:scope:all",
|
|
1226
|
-
},
|
|
1227
|
-
]);
|
|
1228
|
-
}
|
|
1229
|
-
return { inline_keyboard: rows };
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
export function buildThinkingMenuReplyMarkup(
|
|
1233
|
-
currentThinkingLevel: ThinkingLevel,
|
|
1234
|
-
): TelegramReplyMarkup {
|
|
1235
|
-
return {
|
|
1236
|
-
inline_keyboard: THINKING_LEVELS.map((level) => [
|
|
1237
|
-
{
|
|
1238
|
-
text: level === currentThinkingLevel ? `✅ ${level}` : level,
|
|
1239
|
-
callback_data: `thinking:set:${level}`,
|
|
1240
|
-
},
|
|
1241
|
-
]),
|
|
1242
|
-
};
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
export function buildStatusReplyMarkup(
|
|
1246
|
-
activeModel: MenuModel | undefined,
|
|
1247
|
-
currentThinkingLevel: ThinkingLevel,
|
|
1248
|
-
): TelegramReplyMarkup {
|
|
1249
|
-
const rows: Array<Array<{ text: string; callback_data: string }>> = [];
|
|
1250
|
-
rows.push([
|
|
1251
|
-
{
|
|
1252
|
-
text: formatStatusButtonLabel(
|
|
1253
|
-
"Model",
|
|
1254
|
-
activeModel ? getCanonicalModelId(activeModel) : "unknown",
|
|
1255
|
-
),
|
|
1256
|
-
callback_data: "status:model",
|
|
1257
|
-
},
|
|
1258
|
-
]);
|
|
1259
|
-
if (activeModel?.reasoning) {
|
|
1260
|
-
rows.push([
|
|
1261
|
-
{
|
|
1262
|
-
text: formatStatusButtonLabel("Thinking", currentThinkingLevel),
|
|
1263
|
-
callback_data: "status:thinking",
|
|
1264
|
-
},
|
|
1265
|
-
]);
|
|
1266
|
-
}
|
|
1267
|
-
return { inline_keyboard: rows };
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
export function buildTelegramModelMenuRenderPayload(
|
|
1271
|
-
state: TelegramModelMenuState,
|
|
1272
|
-
activeModel: MenuModel | undefined,
|
|
1273
|
-
): TelegramMenuRenderPayload {
|
|
1274
|
-
return {
|
|
1275
|
-
nextMode: "model",
|
|
1276
|
-
text: MODEL_MENU_TITLE,
|
|
1277
|
-
mode: "html",
|
|
1278
|
-
replyMarkup: buildModelMenuReplyMarkup(
|
|
1279
|
-
state,
|
|
1280
|
-
activeModel,
|
|
1281
|
-
TELEGRAM_MODEL_PAGE_SIZE,
|
|
1282
|
-
),
|
|
1283
|
-
};
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
export function buildTelegramThinkingMenuRenderPayload(
|
|
1287
|
-
activeModel: MenuModel | undefined,
|
|
1288
|
-
currentThinkingLevel: ThinkingLevel,
|
|
1289
|
-
): TelegramMenuRenderPayload {
|
|
1290
|
-
return {
|
|
1291
|
-
nextMode: "thinking",
|
|
1292
|
-
text: buildThinkingMenuText(activeModel, currentThinkingLevel),
|
|
1293
|
-
mode: "plain",
|
|
1294
|
-
replyMarkup: buildThinkingMenuReplyMarkup(currentThinkingLevel),
|
|
1295
|
-
};
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
|
-
export function buildTelegramStatusMenuRenderPayload(
|
|
1299
|
-
statusText: string,
|
|
1300
|
-
activeModel: MenuModel | undefined,
|
|
1301
|
-
currentThinkingLevel: ThinkingLevel,
|
|
1302
|
-
): TelegramMenuRenderPayload {
|
|
1303
|
-
return {
|
|
1304
|
-
nextMode: "status",
|
|
1305
|
-
text: statusText,
|
|
1306
|
-
mode: "html",
|
|
1307
|
-
replyMarkup: buildStatusReplyMarkup(activeModel, currentThinkingLevel),
|
|
1308
|
-
};
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
526
|
export interface TelegramMenuActionRuntimeWithStateBuilderDeps<
|
|
1312
527
|
TModel extends MenuModel = MenuModel,
|
|
1313
528
|
TContext extends TelegramModelMenuStateBuilderContext<TModel> =
|
|
@@ -1332,6 +547,7 @@ export function createTelegramMenuActionRuntimeWithStateBuilder<
|
|
|
1332
547
|
}),
|
|
1333
548
|
getActiveModel: deps.getActiveModel,
|
|
1334
549
|
getThinkingLevel: deps.getThinkingLevel,
|
|
550
|
+
getQueueItemCount: deps.getQueueItemCount,
|
|
1335
551
|
buildStatusHtml: deps.buildStatusHtml,
|
|
1336
552
|
storeModelMenuState: deps.storeModelMenuState,
|
|
1337
553
|
isIdle: deps.isIdle,
|
|
@@ -1365,6 +581,7 @@ export function createTelegramMenuActionRuntime<
|
|
|
1365
581
|
deps.getActiveModel(ctx),
|
|
1366
582
|
deps.getThinkingLevel(),
|
|
1367
583
|
deps,
|
|
584
|
+
deps.getQueueItemCount?.() ?? 0,
|
|
1368
585
|
),
|
|
1369
586
|
sendStatusMessage: (chatId, replyToMessageId, ctx) =>
|
|
1370
587
|
openTelegramStatusMenu({
|
|
@@ -1373,20 +590,28 @@ export function createTelegramMenuActionRuntime<
|
|
|
1373
590
|
await deps.sendTextReply(
|
|
1374
591
|
chatId,
|
|
1375
592
|
replyToMessageId,
|
|
1376
|
-
"Cannot open status while
|
|
593
|
+
"Cannot open status while π is busy. Send /abort, /next, or /stop.",
|
|
1377
594
|
);
|
|
1378
595
|
},
|
|
1379
596
|
getModelMenuState: () => deps.getModelMenuState(chatId, ctx),
|
|
1380
597
|
buildStatusHtml: () => deps.buildStatusHtml(ctx),
|
|
1381
598
|
getActiveModel: () => deps.getActiveModel(ctx),
|
|
1382
599
|
getThinkingLevel: deps.getThinkingLevel,
|
|
1383
|
-
|
|
600
|
+
getQueueItemCount: deps.getQueueItemCount,
|
|
601
|
+
sendStatusMenu: (
|
|
602
|
+
state,
|
|
603
|
+
statusHtml,
|
|
604
|
+
activeModel,
|
|
605
|
+
thinkingLevel,
|
|
606
|
+
queueItemCount,
|
|
607
|
+
) =>
|
|
1384
608
|
sendTelegramStatusMessage(
|
|
1385
609
|
state,
|
|
1386
610
|
statusHtml,
|
|
1387
611
|
activeModel,
|
|
1388
612
|
thinkingLevel,
|
|
1389
613
|
deps,
|
|
614
|
+
queueItemCount,
|
|
1390
615
|
),
|
|
1391
616
|
storeModelMenuState: deps.storeModelMenuState,
|
|
1392
617
|
}),
|
|
@@ -1399,7 +624,7 @@ export function createTelegramMenuActionRuntime<
|
|
|
1399
624
|
await deps.sendTextReply(
|
|
1400
625
|
chatId,
|
|
1401
626
|
replyToMessageId,
|
|
1402
|
-
"Cannot switch model while
|
|
627
|
+
"Cannot switch model while π is busy. Send /abort, /next, or /stop.",
|
|
1403
628
|
);
|
|
1404
629
|
},
|
|
1405
630
|
sendNoModelsMessage: async () => {
|
|
@@ -1415,115 +640,14 @@ export function createTelegramMenuActionRuntime<
|
|
|
1415
640
|
sendTelegramModelMenuMessage(state, activeModel, deps),
|
|
1416
641
|
storeModelMenuState: deps.storeModelMenuState,
|
|
1417
642
|
}),
|
|
643
|
+
openThinkingMenu: (chatId, _replyToMessageId, ctx) =>
|
|
644
|
+
openTelegramThinkingMenu({
|
|
645
|
+
getModelMenuState: () => deps.getModelMenuState(chatId, ctx),
|
|
646
|
+
getActiveModel: () => deps.getActiveModel(ctx),
|
|
647
|
+
getThinkingLevel: deps.getThinkingLevel,
|
|
648
|
+
storeModelMenuState: deps.storeModelMenuState,
|
|
649
|
+
editInteractiveMessage: deps.editInteractiveMessage,
|
|
650
|
+
sendInteractiveMessage: deps.sendInteractiveMessage,
|
|
651
|
+
}),
|
|
1418
652
|
};
|
|
1419
653
|
}
|
|
1420
|
-
|
|
1421
|
-
function applyTelegramMenuRenderPayload(
|
|
1422
|
-
state: TelegramModelMenuState,
|
|
1423
|
-
payload: TelegramMenuRenderPayload,
|
|
1424
|
-
): TelegramMenuRenderPayload {
|
|
1425
|
-
state.mode = payload.nextMode;
|
|
1426
|
-
return payload;
|
|
1427
|
-
}
|
|
1428
|
-
|
|
1429
|
-
async function editTelegramMenuMessage(
|
|
1430
|
-
state: TelegramModelMenuState,
|
|
1431
|
-
payload: TelegramMenuRenderPayload,
|
|
1432
|
-
deps: TelegramMenuMessageRuntimeDeps,
|
|
1433
|
-
): Promise<void> {
|
|
1434
|
-
const appliedPayload = applyTelegramMenuRenderPayload(state, payload);
|
|
1435
|
-
await deps.editInteractiveMessage(
|
|
1436
|
-
state.chatId,
|
|
1437
|
-
state.messageId,
|
|
1438
|
-
appliedPayload.text,
|
|
1439
|
-
appliedPayload.mode,
|
|
1440
|
-
appliedPayload.replyMarkup,
|
|
1441
|
-
);
|
|
1442
|
-
}
|
|
1443
|
-
|
|
1444
|
-
function sendTelegramMenuMessage(
|
|
1445
|
-
state: TelegramModelMenuState,
|
|
1446
|
-
payload: TelegramMenuRenderPayload,
|
|
1447
|
-
deps: TelegramMenuMessageRuntimeDeps,
|
|
1448
|
-
): Promise<number | undefined> {
|
|
1449
|
-
const appliedPayload = applyTelegramMenuRenderPayload(state, payload);
|
|
1450
|
-
return deps.sendInteractiveMessage(
|
|
1451
|
-
state.chatId,
|
|
1452
|
-
appliedPayload.text,
|
|
1453
|
-
appliedPayload.mode,
|
|
1454
|
-
appliedPayload.replyMarkup,
|
|
1455
|
-
);
|
|
1456
|
-
}
|
|
1457
|
-
|
|
1458
|
-
export async function updateTelegramModelMenuMessage(
|
|
1459
|
-
state: TelegramModelMenuState,
|
|
1460
|
-
activeModel: MenuModel | undefined,
|
|
1461
|
-
deps: TelegramMenuMessageRuntimeDeps,
|
|
1462
|
-
): Promise<void> {
|
|
1463
|
-
await editTelegramMenuMessage(
|
|
1464
|
-
state,
|
|
1465
|
-
buildTelegramModelMenuRenderPayload(state, activeModel),
|
|
1466
|
-
deps,
|
|
1467
|
-
);
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
|
-
export async function updateTelegramThinkingMenuMessage(
|
|
1471
|
-
state: TelegramModelMenuState,
|
|
1472
|
-
activeModel: MenuModel | undefined,
|
|
1473
|
-
currentThinkingLevel: ThinkingLevel,
|
|
1474
|
-
deps: TelegramMenuMessageRuntimeDeps,
|
|
1475
|
-
): Promise<void> {
|
|
1476
|
-
await editTelegramMenuMessage(
|
|
1477
|
-
state,
|
|
1478
|
-
buildTelegramThinkingMenuRenderPayload(activeModel, currentThinkingLevel),
|
|
1479
|
-
deps,
|
|
1480
|
-
);
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
export async function updateTelegramStatusMessage(
|
|
1484
|
-
state: TelegramModelMenuState,
|
|
1485
|
-
statusText: string,
|
|
1486
|
-
activeModel: MenuModel | undefined,
|
|
1487
|
-
currentThinkingLevel: ThinkingLevel,
|
|
1488
|
-
deps: TelegramMenuMessageRuntimeDeps,
|
|
1489
|
-
): Promise<void> {
|
|
1490
|
-
await editTelegramMenuMessage(
|
|
1491
|
-
state,
|
|
1492
|
-
buildTelegramStatusMenuRenderPayload(
|
|
1493
|
-
statusText,
|
|
1494
|
-
activeModel,
|
|
1495
|
-
currentThinkingLevel,
|
|
1496
|
-
),
|
|
1497
|
-
deps,
|
|
1498
|
-
);
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
export function sendTelegramStatusMessage(
|
|
1502
|
-
state: TelegramModelMenuState,
|
|
1503
|
-
statusText: string,
|
|
1504
|
-
activeModel: MenuModel | undefined,
|
|
1505
|
-
currentThinkingLevel: ThinkingLevel,
|
|
1506
|
-
deps: TelegramMenuMessageRuntimeDeps,
|
|
1507
|
-
): Promise<number | undefined> {
|
|
1508
|
-
return sendTelegramMenuMessage(
|
|
1509
|
-
state,
|
|
1510
|
-
buildTelegramStatusMenuRenderPayload(
|
|
1511
|
-
statusText,
|
|
1512
|
-
activeModel,
|
|
1513
|
-
currentThinkingLevel,
|
|
1514
|
-
),
|
|
1515
|
-
deps,
|
|
1516
|
-
);
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
export function sendTelegramModelMenuMessage(
|
|
1520
|
-
state: TelegramModelMenuState,
|
|
1521
|
-
activeModel: MenuModel | undefined,
|
|
1522
|
-
deps: TelegramMenuMessageRuntimeDeps,
|
|
1523
|
-
): Promise<number | undefined> {
|
|
1524
|
-
return sendTelegramMenuMessage(
|
|
1525
|
-
state,
|
|
1526
|
-
buildTelegramModelMenuRenderPayload(state, activeModel),
|
|
1527
|
-
deps,
|
|
1528
|
-
);
|
|
1529
|
-
}
|