/mistral command for chat with Mistral AI

This commit is contained in:
2026-01-16 15:40:15 +03:00
parent d7dfe3f3a5
commit 326cfcafc8
8 changed files with 196 additions and 32 deletions
+5 -3
View File
@@ -9,11 +9,11 @@ import {
startIntervalEditor
} from "../util/utils";
import {Environment} from "../common/environment";
import {bot, googleAi} from "../index";
import {bot} from "../index";
import {MessageStore} from "../common/message-store";
import {Requirements} from "../base/requirements";
import {Requirement} from "../base/requirement";
import {ApiError} from "@google/genai";
import {ApiError, GoogleGenAI} from "@google/genai";
export class GeminiChat extends ChatCommand {
regexp = /^\/gemini\s([^]+)/i;
@@ -22,6 +22,8 @@ export class GeminiChat extends ChatCommand {
requirements = Requirements.Build(Requirement.BOT_CREATOR);
private googleAi = new GoogleGenAI({apiKey: Environment.GEMINI_API_KEY});
async execute(msg: Message, match?: RegExpExecArray): Promise<void> {
console.log("match", match);
return this.executeGemini(msg, match?.[1]);
@@ -65,7 +67,7 @@ export class GeminiChat extends ChatCommand {
}
});
const stream = await googleAi.models.generateContentStream({
const stream = await this.googleAi.models.generateContentStream({
model: "gemini-2.5-flash",
contents: chatContent,
});
+146
View File
@@ -0,0 +1,146 @@
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,
editMessageText,
escapeMarkdownV2Text,
logError,
replyToMessage,
startIntervalEditor
} from "../util/utils";
import {Environment} from "../common/environment";
import {bot} from "../index";
import {MessageStore} from "../common/message-store";
import {Mistral} from "@mistralai/mistralai";
export class MistralChat extends ChatCommand {
regexp = /^\/mistral\s([^]+)/i;
title = "/mistral";
description = "Chat with AI (Mistral)";
requirements = Requirements.Build(Requirement.BOT_CREATOR);
private mistralAi = new Mistral({apiKey: Environment.MISTRAL_API_KEY});
async execute(msg: Message, match?: RegExpExecArray): Promise<void> {
console.log("match", match);
return this.executeMistral(msg, match?.[1]);
}
async executeMistral(msg: Message, text: string): Promise<void> {
if (!text || text.trim().length === 0) return;
const chatId = msg.chat.id;
const messageParts = await collectReplyChainText(msg, "/mistral");
console.log("MESSAGE PARTS", messageParts);
const chatMessages = messageParts.map(part => {
return {
role: part.bot ? "assistant" : "user",
content: part.content
};
});
chatMessages.reverse();
chatMessages.unshift({role: "system", content: Environment.SYSTEM_PROMPT});
// let chatContent = "";
// for (const part of chatMessages) {
// chatContent += `${part.role.toUpperCase()}:\n${part.content}\n\n`;
// }
// chatContent = chatContent.trim();
let waitMessage: Message;
const startTime = new Date().getSeconds();
try {
waitMessage = await bot.sendMessage({
chat_id: chatId,
text: Environment.waitText,
reply_parameters: {
chat_id: chatId,
message_id: msg.message_id
}
});
const stream = await this.mistralAi.chat.stream({
model: "mistral-small-latest",
messages: chatMessages as any
});
let messageText = "";
let shouldBreak = false;
let diff = 0;
const editor = startIntervalEditor({
intervalMs: 4500,
getText: () => messageText,
editFn: async (text) => {
await editMessageText(chatId, waitMessage.message_id, escapeMarkdownV2Text(text), "Markdown");
},
onStop: async () => {
}
});
try {
for await (const chunk of stream) {
// const text = chunk.text;
const text = chunk.data.choices[0].delta.content;
console.log("chunk", chunk);
// const text = "";
const length = (messageText + text).length;
if (length > 4096) {
messageText = messageText.slice(0, 4093) + "...";
shouldBreak = true;
} else {
messageText += text;
}
if (shouldBreak) {
console.log("messageText", messageText);
console.log("length", length);
console.log("break", true);
diff = Math.abs(new Date().getSeconds() - startTime);
await editor.tick();
await editor.stop();
break;
}
console.log("messageText", messageText);
console.log("length", messageText.length);
diff = Math.abs(new Date().getSeconds() - startTime);
}
} finally {
await editor.tick();
await editor.stop();
console.log("time", diff);
console.log("ended", true);
waitMessage.reply_to_message = msg;
waitMessage.text = messageText;
MessageStore.put(waitMessage);
await replyToMessage(waitMessage, `⏱️ ${diff}s`);
}
} catch (error) {
console.error(error);
// if (error instanceof ApiError) {
// if (error.status === 429) {
// await replyToMessage(waitMessage, "На сегодня всё, лимиты закончились.").catch(logError);
// return;
// }
// }
await replyToMessage(waitMessage, `Произошла ошибка!\n${error.toString()}`).catch(logError);
}
}
}
-24
View File
@@ -1,24 +0,0 @@
import {ChatCommand} from "../base/chat-command";
import {Message} from "typescript-telegram-bot-api";
import {oldSendMessage} from "../util/utils";
import {ollama} from "../index";
import {Requirements} from "../base/requirements";
import {Requirement} from "../base/requirement";
export class OllamaKill extends ChatCommand {
regexp = /^\/killollama/i;
title = "/killOllama";
description = "dunno, do some shit";
requirements = Requirements.Build(Requirement.BOT_CREATOR);
async execute(msg: Message): Promise<void> {
try {
ollama.abort();
} catch (e) {
console.error(e);
}
await oldSendMessage(msg, "Остановил все генерации");
}
}
+2
View File
@@ -25,6 +25,7 @@ export class Environment {
static MAX_PHOTO_SIZE: number;
static GEMINI_API_KEY?: string;
static MISTRAL_API_KEY?: string;
static waitText = "⏳ Дайте-ка подумать...";
@@ -50,6 +51,7 @@ export class Environment {
Environment.MAX_PHOTO_SIZE = Number(process.env.MAX_PHOTO_SIZE || "1280");
Environment.GEMINI_API_KEY = process.env.GEMINI_API_KEY;
Environment.MISTRAL_API_KEY = process.env.MISTRAL_API_KEY;
}
static setAdmins(admins: Set<number>) {
+6 -5
View File
@@ -35,13 +35,11 @@ import {OllamaPrompt} from "./commands/ollama-prompt";
import {AdminsAdd} from "./commands/admins-add";
import {AdminsRemove} from "./commands/admins-remove";
import {Shutdown} from "./commands/shutdown";
import {OllamaKill} from "./commands/ollama-kill";
import {Leave} from "./commands/leave";
import {OllamaChat} from "./commands/ollama-chat";
import {Start} from "./commands/start";
import {MessageStore} from "./common/message-store";
import {PrefixResponse} from "./commands/prefix-response";
import {GoogleGenAI} from "@google/genai";
import {GeminiChat} from "./commands/gemini-chat";
import {Choice} from "./commands/choice";
import {Coin} from "./commands/coin";
@@ -57,6 +55,7 @@ import {UserStore} from "./common/user-store";
import {OllamaRequest} from "./model/ollama-request";
import {CallbackCommand} from "./base/callback-command";
import {OllamaCancel} from "./callback_commands/ollama-cancel";
import {MistralChat} from "./commands/mistral-chat";
process.setUncaughtExceptionCaptureCallback(console.error);
@@ -101,8 +100,6 @@ export function abortOllamaRequest(uuid: string): boolean {
}
}
export const googleAi = new GoogleGenAI({apiKey: Environment.GEMINI_API_KEY});
export let systemInfoText: string = "";
export function setSystemInfo(info: string) {
@@ -149,7 +146,7 @@ export const callbackCommands: CallbackCommand[] = [
];
if (Environment.OLLAMA_ADDRESS && Environment.OLLAMA_MODEL && Environment.SYSTEM_PROMPT) {
chatCommands.push(new OllamaChat(), new OllamaPrompt(), new OllamaKill());
chatCommands.push(new OllamaChat(), new OllamaPrompt());
}
if (Environment.OLLAMA_API_KEY) {
@@ -160,6 +157,10 @@ if (Environment.GEMINI_API_KEY) {
chatCommands.push(new GeminiChat());
}
if (Environment.MISTRAL_API_KEY) {
chatCommands.push(new MistralChat());
}
async function main() {
console.log(
`TEST_ENVIRONMENT: ${Environment.TEST_ENVIRONMENT}\n` +