import {ChatCommand} from "../base/chat-command"; import {Message} from "typescript-telegram-bot-api"; import {abortOllamaRequest, bot, getOllamaRequest, ollama, ollamaRequests} from "../index"; import { collectReplyChainText, escapeMarkdownV2Text, logError, oldReplyToMessage, startIntervalEditor } from "../util/utils"; import {Environment} from "../common/environment"; import {MessageStore} from "../common/message-store"; import {Cancel} from "../callback_commands/cancel"; import {OllamaCancel} from "../callback_commands/ollama-cancel"; export class OllamaChat extends ChatCommand { command = "ollama"; argsMode = "required" as const; title = "/ollama"; description = "Chat with AI (Ollama)"; async execute(msg: Message, match?: RegExpExecArray | null): Promise { console.log("match", match); return this.executeOllama(msg, match?.[3]); } async executeOllama(msg: Message, text: string): Promise { if (!text || text.trim().length === 0) return; const chatId = msg.chat.id; const messageParts = await collectReplyChainText(msg); console.log("MESSAGE PARTS", messageParts); const chatMessages = messageParts.map(part => { return { role: part.bot ? "assistant" : "user", content: (Environment.USE_NAMES_IN_PROMPT && !part.bot ? `MESSAGE FROM USER "${part.name}":\n` : "") + part.content, images: part.images }; }); chatMessages.reverse(); chatMessages.unshift({role: "system", content: Environment.SYSTEM_PROMPT, images: []}); let waitMessage: Message; const startTime = Date.now(); try { const imagesCount = chatMessages.reduce((total, curr) => { return total + (curr.images?.length ?? 0); }, 0); const uuid = crypto.randomUUID(); const cancelMarkup = {inline_keyboard: [[Cancel.withData(new OllamaCancel().data + " " + uuid).asButton()]]}; waitMessage = await bot.sendMessage({ chat_id: chatId, text: imagesCount ? imagesCount > 1 ? Environment.analyzingPicturesText : Environment.analyzingPictureText : Environment.waitText, reply_parameters: { chat_id: chatId, message_id: msg.message_id } }); const stream = await ollama.chat({ model: Environment.OLLAMA_MODEL, stream: true, think: false, messages: chatMessages, }); const newRequest = { uuid: uuid, stream: stream, done: false, fromId: msg.from.id, chatId: msg.chat.id, }; console.log("Pushing new request", newRequest); ollamaRequests.push(newRequest); await bot.editMessageReplyMarkup( { chat_id: chatId, message_id: waitMessage.message_id, reply_markup: cancelMarkup } ).catch(logError); let currentText = ""; let shouldBreak = false; const editor = startIntervalEditor({ uuid: uuid, intervalMs: 4500, getText: () => currentText, editFn: async (text) => { if (getOllamaRequest(uuid)?.done) return; try { await bot.editMessageText({ chat_id: chatId, message_id: waitMessage.message_id, text: escapeMarkdownV2Text(text), parse_mode: "Markdown", reply_markup: cancelMarkup }).catch(logError); console.log("editMessageText", text); waitMessage.reply_to_message = msg; waitMessage.text = text; await MessageStore.put(waitMessage); } catch (e) { logError(e); } } }); await editor.tick(); try { let isThinking = false; for await (const chunk of stream) { const content = chunk.message.content; if (content === "" || chunk.message.thinking) { if (!isThinking) { await bot.editMessageText({ chat_id: chatId, message_id: waitMessage.message_id, text: "🤔 Размышляю...", parse_mode: "Markdown", }).catch(logError); } isThinking = true; } if (!isThinking) { currentText += content; } if (isThinking && !chunk.message.thinking) { currentText += content; } if (content === "" || !chunk.message.thinking) { isThinking = false; } if (currentText.length > 4096) { currentText = currentText.slice(0, 4093) + "..."; shouldBreak = true; } if (getOllamaRequest(uuid).done) { shouldBreak = true; } if (shouldBreak || chunk.done) { console.log("messageText", currentText); console.log("length", currentText.length); if (shouldBreak) { console.log("break", true); } else { console.log("ended", true); } const diff = Math.abs(Date.now() - startTime) / 1000; await editor.tick(); await editor.stop(); console.log(`aborted request ${uuid}:`, abortOllamaRequest(uuid)); waitMessage.reply_to_message = msg; waitMessage.text = currentText; await MessageStore.put(waitMessage); await oldReplyToMessage(waitMessage, `⏱️ ${diff}s`); break; } } } finally { await bot.editMessageReplyMarkup({ chat_id: chatId, message_id: waitMessage.message_id, reply_markup: {inline_keyboard: []} }).catch(logError); } } catch (error) { if (error.message.toLowerCase().includes("aborted")) return; await bot.editMessageReplyMarkup({ chat_id: chatId, message_id: waitMessage.message_id, reply_markup: {inline_keyboard: []} }).catch(logError); logError(error); await oldReplyToMessage(waitMessage, `Произошла ошибка!\n${error.toString()}`).catch(logError); } } }