commands: switch AI commands to unified runtime

This commit is contained in:
2026-05-10 22:53:22 +03:00
parent 1b94760b21
commit 3d14e3c0d5
24 changed files with 465 additions and 3580 deletions
+10 -170
View File
@@ -1,185 +1,25 @@
import {Message} from "typescript-telegram-bot-api";
import {ChatCommand} from "../base/chat-command";
import {Requirements} from "../base/requirements";
import {Requirement} from "../base/requirement";
import {Message} from "typescript-telegram-bot-api";
import {
collectReplyChainText,
escapeMarkdownV2Text,
logError,
oldReplyToMessage,
replyToMessage,
startIntervalEditor
} from "../util/utils";
import {AiProvider} from "../model/ai-provider";
import {runUnifiedAi} from "../ai/unified-ai-runner";
import {Environment} from "../common/environment";
import {bot, commands, mistralAi} from "../index";
import {MessageStore} from "../common/message-store";
import {ChatCommand} from "../base/chat-command";
import {MistralGetModel} from "./mistral-get-model";
export class MistralChat extends ChatCommand {
command = "mistral";
command = ["mistral", "mistral-chat"];
argsMode = "required" as const;
requirements = Requirements.Build(Requirement.BOT_CREATOR);
title = "/mistral";
description = "Chat with AI (Mistral)";
title = Environment.commandTitles.mistralChat;
description = Environment.commandDescriptions.mistralChat;
async execute(msg: Message, match?: RegExpExecArray): Promise<void> {
console.log("match", match);
return this.executeMistral(msg, match?.[3] || "");
}
async executeMistral(msg: Message, text: string): Promise<void> {
if (!text || !text.trim().length) return;
const chatId = msg.chat.id;
const storedMsg = await MessageStore.get(chatId, msg.message_id);
const messageParts = await collectReplyChainText(storedMsg);
console.log("MESSAGE PARTS", messageParts);
const chatMessages = messageParts.map(part => {
const content = [];
content.push({
type: "text",
text: (Environment.USE_NAMES_IN_PROMPT && !part.bot ? `MESSAGE FROM USER "${part.name}":\n` : "") + part.content,
});
for (const image of part.images) {
content.push({
type: "image_url",
imageUrl: "data:image/jpeg;base64," + image
});
}
return {
role: part.bot ? "assistant" : "user",
content: content,
};
});
chatMessages.reverse();
if (Environment.SYSTEM_PROMPT && Environment.USE_SYSTEM_PROMPT) {
chatMessages.unshift({role: "system", content: [{type: "text", text: Environment.SYSTEM_PROMPT}]});
}
let waitMessage: Message | null = null;
const startTime = Date.now();
try {
const imagesCount = chatMessages.reduce((total, curr) => {
return total + (curr.content.filter(c => c.type === "image_url")?.length ?? 0);
}, 0);
if (imagesCount) {
try {
const modelInfo = await commands.find(c => c instanceof MistralGetModel)?.getModelCapabilities();
if (modelInfo) {
if (!modelInfo.vision?.supported) {
await replyToMessage({
message: msg,
text: "Моя текущая модель не умеет анализировать изображения 🥹"
});
return;
}
}
} catch (e) {
logError(e);
}
}
waitMessage = await bot.sendMessage({
chat_id: chatId,
text: imagesCount ?
imagesCount > 1 ? Environment.analyzingPicturesText : Environment.analyzingPictureText
: Environment.waitThinkText,
reply_parameters: {
chat_id: chatId,
message_id: msg.message_id
}
});
const stream = await mistralAi.chat.stream({
model: Environment.MISTRAL_MODEL,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
messages: chatMessages as any
});
let currentText = "";
let shouldBreak = false;
const editor = startIntervalEditor({
intervalMs: 4500,
getText: () => currentText,
editFn: async (text) => {
await bot.editMessageText(
{
chat_id: chatId,
message_id: <number>waitMessage?.message_id,
text: escapeMarkdownV2Text(text),
parse_mode: "MarkdownV2"
}
).catch(logError);
console.log("editMessageText", text);
if (waitMessage) {
waitMessage.reply_to_message = msg;
waitMessage.text = text;
await MessageStore.put(waitMessage);
}
},
onStop: async () => {
}
});
await editor.tick();
try {
for await (const chunk of stream) {
console.log("chunk", chunk);
const text = chunk.data.choices[0].delta.content;
currentText += text;
if (currentText.length > 4096) {
currentText = currentText.slice(0, 4093) + "...";
shouldBreak = true;
}
console.log("messageText", currentText);
console.log("length", currentText.length);
if (shouldBreak) {
console.log("break", true);
break;
}
}
} finally {
await editor.tick();
await editor.stop();
if (!shouldBreak) {
console.log("ended", true);
}
const diff = Math.abs(Date.now() - startTime) / 1000.0;
console.log("time", diff);
waitMessage.reply_to_message = msg;
waitMessage.text = currentText;
await MessageStore.put(waitMessage);
if (Environment.SEND_TIME_TOOK) {
await replyToMessage({message: waitMessage, text: `⏱️ ${diff}s`});
}
}
} catch (e: any) {
logError(e);
if (waitMessage) {
await oldReplyToMessage(waitMessage, `Произошла ошибка!\n${e.toString()}`).catch(logError);
}
}
async executeMistral(msg: Message, text: string, stream: boolean = true): Promise<void> {
await runUnifiedAi({provider: AiProvider.MISTRAL, msg, text, stream});
}
}
}