From 3848dd82d9e03bb83c9541ba35943ee082a373d2 Mon Sep 17 00:00:00 2001 From: Danil Nikolaev Date: Mon, 11 May 2026 02:01:44 +0300 Subject: [PATCH] feat(ai): improve runtime capability reporting and context settings Add explicit chat capability tracking, expose formatted runtime capabilities in the info command, and support a max context size option for user AI settings. Also update Ollama base URL resolution to use OLLAMA_ADDRESS and simplify provider chat command execution. --- locales/ru.json | 4 +- locales/ua.json | 3 +- src/ai/ai-runtime-target.ts | 2 +- src/ai/provider-model-runtime.ts | 60 +- src/ai/unified-ai-runner.ts | 82 ++- src/callback_commands/user-settings.ts | 2 +- src/commands/gemini-chat.ts | 6 +- src/commands/info.ts | 46 +- src/commands/mistral-chat.ts | 6 +- src/commands/ollama-chat.ts | 12 +- src/commands/openai-chat.ts | 6 +- src/common/environment.ts | 909 +++++++++++++++++++------ src/common/user-ai-settings.ts | 31 +- src/model/ai-model-capabilities.ts | 1 + src/util/utils.ts | 16 +- 15 files changed, 850 insertions(+), 336 deletions(-) diff --git a/locales/ru.json b/locales/ru.json index 0b0f560..7d22aff 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -41,13 +41,13 @@ "idFromLabelText": "id пользователя", "idReplyLabelText": "id ответа", "runtimeProviderLabelText": "провайдер", + "runtimeProviderCurrentLabelText": "текущий", "runtimeModelLabelText": "модель", "runtimeCapabilitiesLabelText": "возможности", "runtimeExternalLabelText": "внешний", "infoAiBlockLabelText": "AI", - "infoSupportedProvidersLabelText": "поддерживаемые провайдеры", + "infoSupportedProvidersLabelText": "провайдеры", "infoToolsBlockLabelText": "инструменты", - "infoCountLabelText": "количество", "infoCommandsBlockLabelText": "команды", "infoPublicLabelText": "публичные", "infoPrivateLabelText": "приватные", diff --git a/locales/ua.json b/locales/ua.json index dc325b0..3ca92b6 100644 --- a/locales/ua.json +++ b/locales/ua.json @@ -45,9 +45,8 @@ "runtimeCapabilitiesLabelText": "можливості", "runtimeExternalLabelText": "зовнішній", "infoAiBlockLabelText": "AI", - "infoSupportedProvidersLabelText": "підтримувані провайдери", + "infoSupportedProvidersLabelText": "провайдери", "infoToolsBlockLabelText": "інструменти", - "infoCountLabelText": "кількість", "infoCommandsBlockLabelText": "команди", "infoPublicLabelText": "публічні", "infoPrivateLabelText": "приватні", diff --git a/src/ai/ai-runtime-target.ts b/src/ai/ai-runtime-target.ts index 2926462..8b38542 100644 --- a/src/ai/ai-runtime-target.ts +++ b/src/ai/ai-runtime-target.ts @@ -74,7 +74,7 @@ function modelEnvNames(provider: AiProvider, purpose: AiRuntimePurpose): string[ export function getProviderBaseUrl(provider: AiProvider): string | undefined { switch (provider) { case AiProvider.OLLAMA: - return env("OLLAMA_ENDPOINT"); + return env("OLLAMA_ADDRESS"); case AiProvider.GEMINI: return env("GEMINI_BASE_URL") ?? env("GEMINI_ENDPOINT") ?? (Environment.GEMINI_API_MODE === "openai" ? GEMINI_OPENAI_BASE_URL : undefined); diff --git a/src/ai/provider-model-runtime.ts b/src/ai/provider-model-runtime.ts index 6da2c13..2a6b772 100644 --- a/src/ai/provider-model-runtime.ts +++ b/src/ai/provider-model-runtime.ts @@ -17,13 +17,8 @@ import { sameRuntimeEndpoint, } from "./ai-runtime-target"; -export type RuntimeModelInfo = { - provider: AiProvider; - model: string; - capabilities: AiModelCapabilities; -}; - const CAPABILITY_NAMES: AiCapabilityName[] = [ + "chat", "vision", "ocr", "thinking", @@ -84,6 +79,7 @@ function capability(supported: boolean, target?: AiRuntimeTarget, runtimeTarget? function buildCapabilities(overrides: Partial>): AiModelCapabilities { return Object.assign(new AiModelCapabilities(), { + chat: {supported: false}, vision: {supported: false}, ocr: {supported: false}, thinking: {supported: false}, @@ -154,6 +150,7 @@ export async function getModelCapabilities( const documentsTarget = resolveAiRuntimeTarget(provider, "documents"); return buildCapabilities({ + chat: capability(true, target, runtimeTarget), vision: capability(has("vision"), target, runtimeTarget), ocr: capability(has("ocr"), target, runtimeTarget), thinking: capability(has("thinking"), target, runtimeTarget), @@ -172,6 +169,7 @@ export async function getModelCapabilities( const ttsTarget = resolveAiRuntimeTarget(provider, "textToSpeech"); return buildCapabilities({ + chat: capability(true, target, runtimeTarget), vision: capability(chatLike, target, runtimeTarget), ocr: capability(chatLike, target, runtimeTarget), thinking: capability(reasoningModel, target, runtimeTarget), @@ -191,6 +189,7 @@ export async function getModelCapabilities( const ttsTarget = resolveAiRuntimeTarget(provider, "textToSpeech"); return buildCapabilities({ + chat: capability(true, target, runtimeTarget), vision: capability(!!caps?.vision, target, runtimeTarget), ocr: capability(!!caps?.ocr, target, runtimeTarget), thinking: capability(!!caps?.reasoning, target, runtimeTarget), @@ -209,6 +208,7 @@ export async function getModelCapabilities( const ttsTarget = resolveAiRuntimeTarget(provider, "textToSpeech"); return buildCapabilities({ + chat: capability(true, target, runtimeTarget), vision: capability(isOpenAiVisionModel(model), target, runtimeTarget), ocr: capability(isOpenAiVisionModel(model), target, runtimeTarget), thinking: capability(reasoningModel, target, runtimeTarget), @@ -227,13 +227,13 @@ export async function getModelCapabilities( } } - export async function getRuntimeCapabilities( provider: AiProvider = Environment.DEFAULT_AI_PROVIDER, - model: string | undefined = getRuntimeModel(provider) + model: string | undefined = getRuntimeModel(provider), + target?: AiRuntimeTarget ): Promise { - const runtimeTarget = resolveAiRuntimeTarget(provider, "chat", model ?? getRuntimeModel(provider)); - const result = await getModelCapabilities(provider, runtimeTarget.model, "chat") ?? buildCapabilities({}); + const runtimeTarget = target ?? resolveAiRuntimeTarget(provider, "chat", model ?? getRuntimeModel(provider)); + const result = await getModelCapabilities(provider, runtimeTarget.model, target?.purpose ?? "chat") ?? buildCapabilities({}); for (const capabilityName of CAPABILITY_NAMES) { const target = resolveAiRuntimeTarget(provider, capabilityName); @@ -249,12 +249,13 @@ export async function getRuntimeCapabilities( return result; } -export async function formatRuntimeModelInfo( +export async function getFormattedCapabilities( provider: AiProvider = Environment.DEFAULT_AI_PROVIDER, model: string | undefined = getRuntimeModel(provider), - caps?: AiModelCapabilities -): Promise { + caps?: AiModelCapabilities, +): Promise { if (!caps) caps = await getRuntimeCapabilities(provider, model); + const line = (title: string, value?: AiCapabilityInfo) => { const state = value?.supported ? "✅" : "❌"; const external = value?.external ?? (!!value?.model && value.model !== model); @@ -267,21 +268,30 @@ export async function formatRuntimeModelInfo( }); }; + return [ + line(Environment.runtimeCapabilityChatText, caps.chat), + line(Environment.runtimeCapabilityVisionText, caps.vision), + line(Environment.runtimeCapabilityOcrText, caps.ocr), + line(Environment.runtimeCapabilityThinkingText, caps.thinking), + line(Environment.runtimeCapabilityExtendedThinkingText, caps.extendedThinking), + line(Environment.runtimeCapabilityToolsText, caps.tools), + line(Environment.runtimeCapabilityAudioText, caps.audio), + line(Environment.runtimeCapabilitySpeechToTextText, caps.speechToText), + line(Environment.runtimeCapabilityTextToSpeechText, caps.textToSpeech), + line(Environment.runtimeCapabilityDocumentsText, caps.documents), + line(Environment.runtimeCapabilityOutputImagesText, caps.outputImages), + ]; +} + +export async function formatRuntimeModelInfo( + provider: AiProvider = Environment.DEFAULT_AI_PROVIDER, + model: string | undefined = getRuntimeModel(provider), + caps?: AiModelCapabilities, +): Promise { return Environment.getRuntimeModelInfoText( provider.toString().toLowerCase(), model, - [ - line(Environment.runtimeCapabilityVisionText, caps.vision), - line(Environment.runtimeCapabilityOcrText, caps.ocr), - line(Environment.runtimeCapabilityThinkingText, caps.thinking), - line(Environment.runtimeCapabilityExtendedThinkingText, caps.extendedThinking), - line(Environment.runtimeCapabilityToolsText, caps.tools), - line(Environment.runtimeCapabilityAudioText, caps.audio), - line(Environment.runtimeCapabilitySpeechToTextText, caps.speechToText), - line(Environment.runtimeCapabilityTextToSpeechText, caps.textToSpeech), - line(Environment.runtimeCapabilityDocumentsText, caps.documents), - line(Environment.runtimeCapabilityOutputImagesText, caps.outputImages), - ], + await getFormattedCapabilities(provider, model, caps) ); } diff --git a/src/ai/unified-ai-runner.ts b/src/ai/unified-ai-runner.ts index 0bbfd81..133252f 100644 --- a/src/ai/unified-ai-runner.ts +++ b/src/ai/unified-ai-runner.ts @@ -75,8 +75,9 @@ const TELEGRAM_LIMIT = 4096; const MAX_TOOL_ROUNDS = 20; const OPENAI_IMAGE_PARTIALS = 3; const AI_REQUEST_TIMEOUT_MS = 10 * 60 * 1000; -const DEFAULT_OLLAMA_CONTEXT_SIZE = 256000; const MIN_OLLAMA_CONTEXT_SIZE = 4096; +const MAX_OLLAMA_CONTEXT_SIZE = 262144; +const DEFAULT_OLLAMA_CONTEXT_SIZE = 32768; const toolResourceLocks = new KeyedAsyncLock(); type UnifiedRunOptions = { @@ -134,11 +135,7 @@ type RuntimeConfigSnapshot = { useNamesInPrompt: boolean; useSystemPrompt: boolean; systemPrompt?: string; - ollamaModel: string; - ollamaImageModel: string; - ollamaThinkModel: string; - ollamaAudioModel: string; - ollamaEmbeddingModel: string; + ollamaChatTarget: AiRuntimeTarget; ollamaVisionTarget: AiRuntimeTarget; ollamaThinkingTarget: AiRuntimeTarget; @@ -152,30 +149,23 @@ type RuntimeConfigSnapshot = { ollamaRagMaxArchiveFiles: number; ollamaRagMaxArchiveBytes: number; ollamaRagMaxArchiveDepth: number; - geminiModel: string; - geminiImageModel: string; - geminiTranscriptionModel: string; + geminiChatTarget: AiRuntimeTarget; - mistralModel: string; - mistralTranscriptionModel: string; + mistralChatTarget: AiRuntimeTarget; - openAiModel: string; - openAiImageModel: string; - openAiTranscriptionModel: string; + openAiChatTarget: AiRuntimeTarget; openAiImageTarget: AiRuntimeTarget; }; -function snapshotRuntimeConfig(): RuntimeConfigSnapshot { +// TODO: 11/05/2026, Danil Nikolaev: remove export +export function snapshotRuntimeConfig(): RuntimeConfigSnapshot { return { useNamesInPrompt: Environment.USE_NAMES_IN_PROMPT, useSystemPrompt: Environment.USE_SYSTEM_PROMPT, + systemPrompt: Environment.SYSTEM_PROMPT, - ollamaModel: Environment.OLLAMA_CHAT_MODEL, - ollamaImageModel: Environment.OLLAMA_IMAGE_MODEL, - ollamaThinkModel: Environment.OLLAMA_THINK_MODEL, - ollamaAudioModel: Environment.OLLAMA_AUDIO_MODEL, - ollamaEmbeddingModel: Environment.OLLAMA_EMBEDDING_MODEL, + ollamaChatTarget: resolveAiRuntimeTarget(AiProvider.OLLAMA, "chat"), ollamaVisionTarget: resolveAiRuntimeTarget(AiProvider.OLLAMA, "vision"), ollamaThinkingTarget: resolveAiRuntimeTarget(AiProvider.OLLAMA, "thinking"), @@ -189,16 +179,11 @@ function snapshotRuntimeConfig(): RuntimeConfigSnapshot { ollamaRagMaxArchiveFiles: Environment.OLLAMA_RAG_MAX_ARCHIVE_FILES, ollamaRagMaxArchiveBytes: Environment.OLLAMA_RAG_MAX_ARCHIVE_BYTES, ollamaRagMaxArchiveDepth: Environment.OLLAMA_RAG_MAX_ARCHIVE_DEPTH, - geminiModel: Environment.GEMINI_MODEL, - geminiImageModel: Environment.GEMINI_IMAGE_MODEL, - geminiTranscriptionModel: Environment.GEMINI_TRANSCRIPTION_MODEL, + geminiChatTarget: resolveAiRuntimeTarget(AiProvider.GEMINI, "chat"), - mistralModel: Environment.MISTRAL_MODEL, - mistralTranscriptionModel: Environment.MISTRAL_TRANSCRIPTION_MODEL, + mistralChatTarget: resolveAiRuntimeTarget(AiProvider.MISTRAL, "chat"), - openAiModel: Environment.OPENAI_MODEL, - openAiImageModel: Environment.OPENAI_IMAGE_MODEL, - openAiTranscriptionModel: Environment.OPENAI_TRANSCRIPTION_MODEL, + openAiChatTarget: resolveAiRuntimeTarget(AiProvider.OPENAI, "chat"), openAiImageTarget: resolveAiRuntimeTarget(AiProvider.OPENAI, "outputImages"), }; @@ -234,12 +219,12 @@ function geminiAudioMimeType(mimeType: string | undefined): string { } } -function snapshotModel(provider: AiProvider, config: RuntimeConfigSnapshot): string { +export function snapshotModel(provider: AiProvider, config: RuntimeConfigSnapshot): string { switch (provider) { case AiProvider.OLLAMA: return config.ollamaChatTarget.model; case AiProvider.GEMINI: - return config.geminiModel; + return config.geminiChatTarget.model; case AiProvider.MISTRAL: return config.mistralChatTarget.model; case AiProvider.OPENAI: @@ -247,6 +232,25 @@ function snapshotModel(provider: AiProvider, config: RuntimeConfigSnapshot): str } } +export function providerTargets(provider: AiProvider, config: RuntimeConfigSnapshot): AiRuntimeTarget[] { + switch (provider) { + case AiProvider.OLLAMA: + return [ + config.ollamaChatTarget, + config.ollamaVisionTarget, + config.ollamaThinkingTarget, + config.ollamaAudioTarget, + config.ollamaDocumentsTarget + ]; + case AiProvider.GEMINI: + return [config.geminiChatTarget]; + case AiProvider.MISTRAL: + return [config.mistralChatTarget]; + case AiProvider.OPENAI: + return [config.openAiChatTarget]; + } +} + function providerName(provider: AiProvider): AiProviderName { switch (provider) { case AiProvider.OLLAMA: @@ -1422,12 +1426,10 @@ async function runOllama( const modelInfo = await ollama.show({model: model}); const contextKey = Object.keys(modelInfo.model_info).find(k => k.endsWith(".context_length")); // @ts-ignore - const maxContextLength = contextKey ? modelInfo?.model_info?.[contextKey] : 32768; - const context = clamp(contextSize ?? DEFAULT_OLLAMA_CONTEXT_SIZE, MIN_OLLAMA_CONTEXT_SIZE, maxContextLength ?? 32768); + const maxContextLength = contextKey ? modelInfo?.model_info?.[contextKey] : DEFAULT_OLLAMA_CONTEXT_SIZE; + const context = clamp(contextSize ?? contextSize === -1 ? MAX_OLLAMA_CONTEXT_SIZE : DEFAULT_OLLAMA_CONTEXT_SIZE, MIN_OLLAMA_CONTEXT_SIZE, maxContextLength ?? DEFAULT_OLLAMA_CONTEXT_SIZE); - console.log("MESSAGES", messages); - - await unloadAllOllamaModels(ollama, [model, config.ollamaEmbeddingModel]); + await unloadAllOllamaModels(ollama, [model, config.ollamaDocumentsTarget.model]); if (!(await isOllamaModelActive(target))) { const currentStatus = streamMessage.getStatus(); @@ -1466,15 +1468,7 @@ async function runOllama( try { for (let round = 0; round < MAX_TOOL_ROUNDS; round++) { - // if (round === 0) { - // streamMessage.setStatus(Environment.weightsLoadingText); - // await streamMessage.flush(); - // } else { - // streamMessage.setStatus(roundStatus(round, firstRoundStatus)); - // await streamMessage.flush(); - // } - - console.log("MAX_CONTENT_LENGTH", context); + console.log("CONTENT_LENGTH", context); const request: ChatRequest = { model: model, diff --git a/src/callback_commands/user-settings.ts b/src/callback_commands/user-settings.ts index dc19fe0..4c5c672 100644 --- a/src/callback_commands/user-settings.ts +++ b/src/callback_commands/user-settings.ts @@ -71,7 +71,7 @@ export class UserSettingsCallback extends CallbackCommand { if (parsed.screen === "contextSize" && parsed.contextSizeChoice) { const choice = normalizeAiContextSizeChoice(parsed.contextSizeChoice); - if (choice) { + if (choice || choice === -1) { const result = await setUserAiContextSizeChoice(query.from.id, choice); settings = result.settings; } diff --git a/src/commands/gemini-chat.ts b/src/commands/gemini-chat.ts index 3a1b36d..27b6316 100644 --- a/src/commands/gemini-chat.ts +++ b/src/commands/gemini-chat.ts @@ -16,10 +16,6 @@ export class GeminiChat extends ChatCommand { description = Environment.commandDescriptions.geminiChat; async execute(msg: Message, match?: RegExpExecArray): Promise { - return this.executeGemini(msg, match?.[3] || ""); - } - - async executeGemini(msg: Message, text: string, stream: boolean = true): Promise { - await runUnifiedAi({provider: AiProvider.GEMINI, msg, text, stream}); + await runUnifiedAi({provider: AiProvider.GEMINI, msg: msg, text: match?.[3] ?? "", stream: true}); } } diff --git a/src/commands/info.ts b/src/commands/info.ts index 05c6111..437cda8 100644 --- a/src/commands/info.ts +++ b/src/commands/info.ts @@ -2,13 +2,13 @@ import {ChatCommand} from "../base/chat-command"; import {Message} from "typescript-telegram-bot-api"; import {callbackCommands, commands} from "../index"; import {Environment} from "../common/environment"; -import {getCurrentModel, logError, replyToMessage} from "../util/utils"; -import {AiModelCapabilities} from "../model/ai-model-capabilities"; +import {logError, replyToMessage} from "../util/utils"; import {AiProvider} from "../model/ai-provider"; import {Command} from "../base/command"; -import {formatRuntimeModelInfo, getRuntimeCapabilities} from "../ai/provider-model-runtime"; import {getProviderTools} from "../ai/tool-mappers"; import {prepareTelegramMarkdownV2} from "../util/markdown-v2-renderer"; +import {resolveEffectiveAiProviderForUser} from "../common/user-ai-settings"; +import {getFormattedCapabilities} from "../ai/provider-model-runtime"; export class Info extends Command { command = ["info", "v"]; @@ -17,30 +17,10 @@ export class Info extends Command { description = Environment.commandDescriptions.info; async execute(msg: Message): Promise { - const aiProvider = Environment.DEFAULT_AI_PROVIDER; - const aiModel = getCurrentModel(); - if (!aiModel) return; - let aiModelCapabilities: AiModelCapabilities | null = null; - - try { - aiModelCapabilities = await getRuntimeCapabilities(aiProvider, aiModel); - } catch (e) { - logError(e); - await replyToMessage({message: msg, text: Environment.getErrorText(e)}).catch(logError); - return; - } - - const supportedProvidersLength = Object.keys(AiProvider).filter(key => isNaN(Number(key))).length; - - const getAiInfo = async () => { - return Environment.getInfoAiBlockText( - supportedProvidersLength, - await formatRuntimeModelInfo(aiProvider, aiModel, aiModelCapabilities), - ); - }; + if (!msg.from) return; const getToolsInfo = async () => { - const tools = getProviderTools(aiProvider); + const tools = getProviderTools(provider); return Environment.getInfoToolsBlockText(tools.map(t => t.function.name)); }; @@ -61,13 +41,27 @@ export class Info extends Command { }); }; + const provider = await resolveEffectiveAiProviderForUser(msg.from.id); + // const aiProvidersLength = Object.keys(AiProvider).filter(key => isNaN(Number(key))).length; + const aiProviders = Object.keys(AiProvider).map(p => p.toLowerCase()); const finalText = [ - await getAiInfo(), + `\`\`\`${Environment.runtimeProviderLabelText}`, + `${Environment.infoSupportedProvidersLabelText}: ${aiProviders.join(", ")}`, + `${Environment.runtimeProviderCurrentLabelText}: ${provider.toLowerCase()}`, + "```", + "", + + `\`\`\`${Environment.runtimeCapabilitiesLabelText}`, + (await getFormattedCapabilities(provider)).join("\n"), + "```", + "", + await getToolsInfo(), await getCommandsInfo() ].join("\n"); + await replyToMessage({ message: msg, text: prepareTelegramMarkdownV2(finalText, {mode: "final"}), diff --git a/src/commands/mistral-chat.ts b/src/commands/mistral-chat.ts index fa0cf99..101fb09 100644 --- a/src/commands/mistral-chat.ts +++ b/src/commands/mistral-chat.ts @@ -16,10 +16,6 @@ export class MistralChat extends ChatCommand { description = Environment.commandDescriptions.mistralChat; async execute(msg: Message, match?: RegExpExecArray): Promise { - return this.executeMistral(msg, match?.[3] || ""); - } - - async executeMistral(msg: Message, text: string, stream: boolean = true): Promise { - await runUnifiedAi({provider: AiProvider.MISTRAL, msg, text, stream}); + await runUnifiedAi({provider: AiProvider.MISTRAL, msg: msg, text: match?.[3] ?? "", stream: true}); } } diff --git a/src/commands/ollama-chat.ts b/src/commands/ollama-chat.ts index 94b88e6..5e1b807 100644 --- a/src/commands/ollama-chat.ts +++ b/src/commands/ollama-chat.ts @@ -16,10 +16,12 @@ export class OllamaChat extends ChatCommand { description = Environment.commandDescriptions.ollamaChat; async execute(msg: Message, match?: RegExpExecArray): Promise { - return this.executeOllama(msg, match?.[3] || "", match?.[1]?.toLowerCase()?.startsWith("think")); - } - - async executeOllama(msg: Message, text: string, think: boolean = false): Promise { - await runUnifiedAi({provider: AiProvider.OLLAMA, msg, text, stream: true, think: think}); + await runUnifiedAi({ + provider: AiProvider.OLLAMA, + msg: msg, + text: match?.[3] ?? "", + stream: true, + think: match?.[1]?.toLowerCase()?.startsWith("think") + }); } } diff --git a/src/commands/openai-chat.ts b/src/commands/openai-chat.ts index a30fa99..24aebde 100644 --- a/src/commands/openai-chat.ts +++ b/src/commands/openai-chat.ts @@ -16,10 +16,6 @@ export class OpenAIChat extends ChatCommand { description = Environment.commandDescriptions.openAiChat; async execute(msg: Message, match?: RegExpExecArray): Promise { - return this.executeOpenAI(msg, match?.[3] || ""); - } - - async executeOpenAI(msg: Message, text: string, stream: boolean = true): Promise { - await runUnifiedAi({provider: AiProvider.OPENAI, msg, text, stream}); + await runUnifiedAi({provider: AiProvider.OPENAI, msg: msg, text: match?.[3] ?? "", stream: true}); } } diff --git a/src/common/environment.ts b/src/common/environment.ts index 60f301b..4531641 100644 --- a/src/common/environment.ts +++ b/src/common/environment.ts @@ -330,147 +330,573 @@ export class Environment { return Localization.textArray(key, params, fallback); } - static get errorText() { return this.text("errorText", "⚠️ An error occurred."); } - static get waitThinkText() { return this.text("waitThinkText", "⏳ Let me think..."); } - static get analyzingPictureText() { return this.text("analyzingPictureText", "🔍 Analyzing the image..."); } - static get analyzingPicturesText() { return this.text("analyzingPicturesText", "🔍 Analyzing the images..."); } - static get reasoningText() { return this.text("reasoningText", "🤔 Reasoning..."); } - static get transcribingAudioText() { return this.text("transcribingAudioText", "🦻 Transcribing audio..."); } - static get genImageText() { return this.text("genImageText", "👨‍🎨 Generating an image..."); } + static get errorText() { + return this.text("errorText", "⚠️ An error occurred."); + } - static get cancelText() { return this.text("cancelText", "❌ Cancel"); } - static get regenerateText() { return this.text("regenerateText", "🔄 Regenerate"); } - static get aiCancelCallbackText() { return this.text("aiCancelCallbackText", "Cancel AI generation"); } - static get aiRegenerateCallbackText() { return this.text("aiRegenerateCallbackText", "Regenerate AI response"); } - static get userSettingsCallbackText() { return this.text("userSettingsCallbackText", "User settings"); } - static get noAccessText() { return this.text("noAccessText", "No access"); } - static get notBotCreatorText() { return this.text("notBotCreatorText", "You are not the bot creator."); } - static get notBotAdministratorText() { return this.text("notBotAdministratorText", "You are not a bot administrator."); } - static get notAChatText() { return this.text("notAChatText", "This is not a chat."); } - static get notChatAdministratorText() { return this.text("notChatAdministratorText", "You are not a chat administrator."); } - static get botNotChatAdministratorText() { return this.text("botNotChatAdministratorText", "The bot is not a chat administrator."); } - static get replyRequiredText() { return this.text("replyRequiredText", "A reply to a message is required."); } - static get onlyOriginalAuthorText() { return this.text("onlyOriginalAuthorText", "Only the author of the original message can perform this action."); } - static get dockerContainerLabelText() { return this.text("dockerContainerLabelText", "Docker container"); } - static get processLabelText() { return this.text("processLabelText", "Process"); } - static get systemLabelText() { return this.text("systemLabelText", "System"); } - static get systemInfoOsLabelText() { return this.text("systemInfoOsLabelText", "OS"); } - static get systemInfoRuntimeLabelText() { return this.text("systemInfoRuntimeLabelText", "RUNTIME"); } - static get systemInfoDockerLabelText() { return this.text("systemInfoDockerLabelText", "DOCKER"); } - static get systemInfoCpuLabelText() { return this.text("systemInfoCpuLabelText", "CPU"); } - static get systemInfoRamLabelText() { return this.text("systemInfoRamLabelText", "RAM"); } - static get systemInfoCpuCoresText() { return this.text("systemInfoCpuCoresText", "cores"); } - static get systemInfoCpuThreadsText() { return this.text("systemInfoCpuThreadsText", "threads"); } - static get idChatLabelText() { return this.text("idChatLabelText", "chat id"); } - static get idFromLabelText() { return this.text("idFromLabelText", "from id"); } - static get idReplyLabelText() { return this.text("idReplyLabelText", "reply id"); } - static get runtimeProviderLabelText() { return this.text("runtimeProviderLabelText", "provider"); } - static get runtimeModelLabelText() { return this.text("runtimeModelLabelText", "model"); } - static get runtimeCapabilitiesLabelText() { return this.text("runtimeCapabilitiesLabelText", "capabilities"); } - static get runtimeExternalLabelText() { return this.text("runtimeExternalLabelText", "external"); } - static get runtimeCapabilityVisionText() { return this.text("runtimeCapabilityVisionText", "vision / image input"); } - static get runtimeCapabilityOcrText() { return this.text("runtimeCapabilityOcrText", "ocr"); } - static get runtimeCapabilityThinkingText() { return this.text("runtimeCapabilityThinkingText", "thinking / reasoning"); } - static get runtimeCapabilityExtendedThinkingText() { return this.text("runtimeCapabilityExtendedThinkingText", "leveled thinking / reasoning"); } - static get runtimeCapabilityToolsText() { return this.text("runtimeCapabilityToolsText", "tools / function calling"); } - static get runtimeCapabilityAudioText() { return this.text("runtimeCapabilityAudioText", "audio input"); } - static get runtimeCapabilitySpeechToTextText() { return this.text("runtimeCapabilitySpeechToTextText", "speech-to-text"); } - static get runtimeCapabilityTextToSpeechText() { return this.text("runtimeCapabilityTextToSpeechText", "text-to-speech"); } - static get runtimeCapabilityDocumentsText() { return this.text("runtimeCapabilityDocumentsText", "documents / rag"); } - static get runtimeCapabilityOutputImagesText() { return this.text("runtimeCapabilityOutputImagesText", "image gen / image output"); } - static get infoAiBlockLabelText() { return this.text("infoAiBlockLabelText", "AI"); } - static get infoSupportedProvidersLabelText() { return this.text("infoSupportedProvidersLabelText", "supported providers"); } - static get infoToolsBlockLabelText() { return this.text("infoToolsBlockLabelText", "tools"); } - static get infoCountLabelText() { return this.text("infoCountLabelText", "count"); } - static get infoCommandsBlockLabelText() { return this.text("infoCommandsBlockLabelText", "commands"); } - static get infoPublicLabelText() { return this.text("infoPublicLabelText", "public"); } - static get infoPrivateLabelText() { return this.text("infoPrivateLabelText", "private"); } - static get infoChatLabelText() { return this.text("infoChatLabelText", "chat"); } - static get infoCallbackLabelText() { return this.text("infoCallbackLabelText", "callback"); } - static get commandsHeaderText() { return this.text("commandsHeaderText", "Commands:\n\n"); } - static get sentCommandsInDmText() { return this.text("sentCommandsInDmText", "Sent commands in DM 😎"); } - static get couldNotSendCommandsInDmText() { return this.text("couldNotSendCommandsInDmText", "Could not send commands in DM ☹️\nSending them here instead"); } - static get administratorsHeaderText() { return this.text("administratorsHeaderText", "*Administrators*:\n\n"); } - static get noUserInfoText() { return this.text("noUserInfoText", "No user information"); } - static get useLeaveCommandText() { return this.text("useLeaveCommandText", "Use /leave"); } - static get databaseBackupCaption() { return this.text("databaseBackupCaption", "Database backup"); } - static get databaseBackupSentText() { return this.text("databaseBackupSentText", "Successfully sent to the creator in DM!"); } - static get noChoicesText() { return this.text("noChoicesText", "Nothing to choose from"); } - static get qrCodeMissingTextText() { return this.text("qrCodeMissingTextText", "No text found for QR code generation."); } - static get quoteMissingTextText() { return this.text("quoteMissingTextText", "Could not find text in the message 😢"); } - static get quoteBuildFailedText() { return this.text("quoteBuildFailedText", "Could not build the quote 😢"); } - static get speechToTextInstructionText() { return this.text("speechToTextInstructionText", "Send audio/voice/video-note or reply with /stt to a message containing audio."); } - static get speechToTextEmptyResultText() { return this.text("speechToTextEmptyResultText", "Speech-to-text did not return transcription text."); } - static get textToSpeechInstructionText() { return this.text("textToSpeechInstructionText", "Send text after the command or reply with /tts to a message containing text."); } - static get titleMissingText() { return this.text("titleMissingText", "Could not find a title..."); } - static get betterFallbackText() { return this.text("betterFallbackText", "Better"); } - static get pongText() { return this.text("pongText", "pong"); } - static get variableNotDefinedText() { return this.text("variableNotDefinedText", "variable is not defined"); } - static get evaluationVariableNotDefinedText() { return this.text("evaluationVariableNotDefinedText", "Variable not defined"); } - static get defaultTestAnswerText() { return this.text("defaultTestAnswerText", "a"); } - static get prefixFallbackText() { return this.text("prefixFallbackText", "?"); } - static get searchResultsHeaderText() { return this.text("searchResultsHeaderText", "Results:\n\n"); } - static get modelListHeaderText() { return this.text("modelListHeaderText", "Available models:\n\n"); } - static get modelListLoadFailedText() { return this.text("modelListLoadFailedText", "Could not load the model list"); } - static get noCurrentModelText() { return this.text("noCurrentModelText", "Model is not set. Use one of the listed values."); } - static get unsupportedAttachmentText() { return this.text("unsupportedAttachmentText", "This attachment type is not supported."); } - static get attachmentMissingFromCacheText() { return this.text("attachmentMissingFromCacheText", "Attachment file is missing from cache."); } - static get couldNotIdentifyUserForSpeechToTextText() { return this.text("couldNotIdentifyUserForSpeechToTextText", "Could not identify the user for speech-to-text."); } - static get missingTranscriptionFileText() { return this.text("missingTranscriptionFileText", "Unable to prepare the audio file for transcription."); } - static get transcriptionFailedText() { return this.text("transcriptionFailedText", "Could not transcribe the audio."); } - static get imageGenUnsupportedFilesText() { return this.text("imageGenUnsupportedFilesText", "Image generation does not support files in this mode."); } - static get unsupportedDocumentProviderText() { return this.text("unsupportedDocumentProviderText", "This provider does not support attached documents."); } - static get mistralPdfOnlyText() { return this.text("mistralPdfOnlyText", "Mistral currently supports only PDF documents."); } - static get mistralDocumentUploadFailedText() { return this.text("mistralDocumentUploadFailedText", "Could not upload the document to Mistral."); } - static get documentContentLabelText() { return this.text("documentContentLabelText", "Document content"); } - static get mistralLibraryIdMissingText() { return this.text("mistralLibraryIdMissingText", "Mistral did not return a temporary document library id."); } - static get documentsUnifiedRunnerUnsupportedText() { return this.text("documentsUnifiedRunnerUnsupportedText", "Documents in the unified runner are currently handled only by Ollama RAG and Mistral."); } - static get zipCentralDirectoryNotFoundText() { return this.text("zipCentralDirectoryNotFoundText", "ZIP archive is corrupted: central directory was not found."); } - static get zipInvalidCentralDirectoryText() { return this.text("zipInvalidCentralDirectoryText", "ZIP archive is corrupted: invalid central directory."); } - static get tarFileTooLargeText() { return this.text("tarFileTooLargeText", "TAR contains a file that is too large."); } - static get tarInvalidEntrySizeText() { return this.text("tarInvalidEntrySizeText", "TAR archive is corrupted: invalid entry size."); } - static get tarEntryExceedsBoundsText() { return this.text("tarEntryExceedsBoundsText", "TAR archive is corrupted: entry exceeds file bounds."); } - static get docxDocumentXmlMissingText() { return this.text("docxDocumentXmlMissingText", "DOCX does not contain word/document.xml."); } - static get localRagEmbeddingModelRequiredText() { return this.text("localRagEmbeddingModelRequiredText", "Local RAG requires OLLAMA_EMBEDDING_MODEL, for example nomic-embed-text."); } - static get localRagChunksBuildFailedText() { return this.text("localRagChunksBuildFailedText", "Could not build chunks for local RAG."); } - static get localRagNoSuitableFragmentsText() { return this.text("localRagNoSuitableFragmentsText", "Local RAG did not find suitable document fragments."); } - static get unsupportedAiProviderText() { return this.text("unsupportedAiProviderText", "Unsupported AI provider."); } - static get noSupportedTranscriptionProviderText() { return this.text("noSupportedTranscriptionProviderText", "No supported speech-to-text provider is configured."); } - static get noSupportedTextToSpeechProviderText() { return this.text("noSupportedTextToSpeechProviderText", "No supported text-to-speech provider is configured."); } - static get noSpeechToTextProviderForAccessText() { return this.text("noSpeechToTextProviderForAccessText", "No speech-to-text providers are configured for your access level."); } - static get noTextToSpeechProviderForAccessText() { return this.text("noTextToSpeechProviderForAccessText", "No text-to-speech providers are configured for your access level."); } - static get geminiSpeechToTextUnsupportedText() { return this.text("geminiSpeechToTextUnsupportedText", "Gemini does not support speech-to-text right now."); } - static get geminiTextToSpeechUnsupportedText() { return this.text("geminiTextToSpeechUnsupportedText", "Gemini does not support text-to-speech right now."); } - static get ollamaTextToSpeechUnsupportedText() { return this.text("ollamaTextToSpeechUnsupportedText", "Ollama does not support text-to-speech right now."); } - static get ollamaSpeechToTextModelRequiredText() { return this.text("ollamaSpeechToTextModelRequiredText", "Ollama speech-to-text requires OLLAMA_AUDIO_MODEL=gemma4:e2b or OLLAMA_AUDIO_MODEL=gemma4:e4b."); } - static get noTextToSynthesizeText() { return this.text("noTextToSynthesizeText", "No text to synthesize."); } - static get mistralTtsNoAudioDataText() { return this.text("mistralTtsNoAudioDataText", "Mistral TTS did not return audioData."); } - static get speechFileTooLargeText() { return this.text("speechFileTooLargeText", "The speech file is larger than 50 MB and cannot be sent."); } - static get userSettingsTitle() { return this.text("userSettingsTitle", "User Settings"); } - static get userSettingsAiProviderSelectionTitle() { return this.text("userSettingsAiProviderSelectionTitle", "AI Provider Selection"); } - static get userSettingsInterfaceLanguageSelectionTitle() { return this.text("userSettingsInterfaceLanguageSelectionTitle", "Interface Language Selection"); } - static get userSettingsResponseLanguageSelectionTitle() { return this.text("userSettingsResponseLanguageSelectionTitle", "Response Language Selection"); } - static get userSettingsContextSizeSelectionTitle() { return this.text("userSettingsContextSizeSelectionTitle", "Context Size Selection"); } - static get userSettingsVoiceModeSelectionTitle() { return this.text("userSettingsVoiceModeSelectionTitle", "Voice Message Mode Selection"); } - static get userSettingsTierLabel() { return this.text("userSettingsTierLabel", "Tier"); } - static get userSettingsAiProviderLabel() { return this.text("userSettingsAiProviderLabel", "AI provider"); } - static get userSettingsInterfaceLanguageLabel() { return this.text("userSettingsInterfaceLanguageLabel", "Interface language"); } - static get userSettingsResponseLanguageLabel() { return this.text("userSettingsResponseLanguageLabel", "LLM response language"); } - static get userSettingsContextSizeLabel() { return this.text("userSettingsContextSizeLabel", "Context size"); } - static get userSettingsVoiceModeLabel() { return this.text("userSettingsVoiceModeLabel", "Voice messages"); } - static get userSettingsBackButtonText() { return this.text("userSettingsBackButtonText", "Back"); } - static get userSettingsAiProviderButtonPrefix() { return this.text("userSettingsAiProviderButtonPrefix", "AI provider"); } - static get userSettingsInterfaceLanguageButtonPrefix() { return this.text("userSettingsInterfaceLanguageButtonPrefix", "Interface language"); } - static get userSettingsResponseLanguageButtonPrefix() { return this.text("userSettingsResponseLanguageButtonPrefix", "Response language"); } - static get userSettingsContextSizeButtonPrefix() { return this.text("userSettingsContextSizeButtonPrefix", "Context size"); } - static get userSettingsVoiceModeButtonPrefix() { return this.text("userSettingsVoiceModeButtonPrefix", "Voice messages"); } - static get userSettingsCreatorTierText() { return this.text("userSettingsCreatorTierText", "Creator"); } - static get userSettingsAdminTierText() { return this.text("userSettingsAdminTierText", "Admin"); } - static get userSettingsUserTierText() { return this.text("userSettingsUserTierText", "User"); } - static get userSettingsSelectedPrefix() { return this.text("userSettingsSelectedPrefix", "✓ "); } - static get userSettingsContextSizeDefaultText() { return this.text("userSettingsContextSizeDefaultText", "Default"); } - static get userSettingsVoiceModeExecuteText() { return this.text("userSettingsVoiceModeExecuteText", "Run through AI"); } - static get userSettingsVoiceModeTranscriptText() { return this.text("userSettingsVoiceModeTranscriptText", "Show transcript only"); } + static get waitThinkText() { + return this.text("waitThinkText", "⏳ Let me think..."); + } + + static get analyzingPictureText() { + return this.text("analyzingPictureText", "🔍 Analyzing the image..."); + } + + static get analyzingPicturesText() { + return this.text("analyzingPicturesText", "🔍 Analyzing the images..."); + } + + static get reasoningText() { + return this.text("reasoningText", "🤔 Reasoning..."); + } + + static get transcribingAudioText() { + return this.text("transcribingAudioText", "🦻 Transcribing audio..."); + } + + static get genImageText() { + return this.text("genImageText", "👨‍🎨 Generating an image..."); + } + + static get cancelText() { + return this.text("cancelText", "❌ Cancel"); + } + + static get regenerateText() { + return this.text("regenerateText", "🔄 Regenerate"); + } + + static get aiCancelCallbackText() { + return this.text("aiCancelCallbackText", "Cancel AI generation"); + } + + static get aiRegenerateCallbackText() { + return this.text("aiRegenerateCallbackText", "Regenerate AI response"); + } + + static get userSettingsCallbackText() { + return this.text("userSettingsCallbackText", "User settings"); + } + + static get noAccessText() { + return this.text("noAccessText", "No access"); + } + + static get notBotCreatorText() { + return this.text("notBotCreatorText", "You are not the bot creator."); + } + + static get notBotAdministratorText() { + return this.text("notBotAdministratorText", "You are not a bot administrator."); + } + + static get notAChatText() { + return this.text("notAChatText", "This is not a chat."); + } + + static get notChatAdministratorText() { + return this.text("notChatAdministratorText", "You are not a chat administrator."); + } + + static get botNotChatAdministratorText() { + return this.text("botNotChatAdministratorText", "The bot is not a chat administrator."); + } + + static get replyRequiredText() { + return this.text("replyRequiredText", "A reply to a message is required."); + } + + static get onlyOriginalAuthorText() { + return this.text("onlyOriginalAuthorText", "Only the author of the original message can perform this action."); + } + + static get dockerContainerLabelText() { + return this.text("dockerContainerLabelText", "Docker container"); + } + + static get processLabelText() { + return this.text("processLabelText", "Process"); + } + + static get systemLabelText() { + return this.text("systemLabelText", "System"); + } + + static get systemInfoOsLabelText() { + return this.text("systemInfoOsLabelText", "OS"); + } + + static get systemInfoRuntimeLabelText() { + return this.text("systemInfoRuntimeLabelText", "RUNTIME"); + } + + static get systemInfoDockerLabelText() { + return this.text("systemInfoDockerLabelText", "DOCKER"); + } + + static get systemInfoCpuLabelText() { + return this.text("systemInfoCpuLabelText", "CPU"); + } + + static get systemInfoRamLabelText() { + return this.text("systemInfoRamLabelText", "RAM"); + } + + static get systemInfoCpuCoresText() { + return this.text("systemInfoCpuCoresText", "cores"); + } + + static get systemInfoCpuThreadsText() { + return this.text("systemInfoCpuThreadsText", "threads"); + } + + static get idChatLabelText() { + return this.text("idChatLabelText", "chat id"); + } + + static get idFromLabelText() { + return this.text("idFromLabelText", "from id"); + } + + static get idReplyLabelText() { + return this.text("idReplyLabelText", "reply id"); + } + + static get runtimeProviderLabelText() { + return this.text("runtimeProviderLabelText", "provider"); + } + + static get runtimeProviderCurrentLabelText() { + return this.text("runtimeProviderCurrentLabelText", "current"); + } + + static get runtimeModelLabelText() { + return this.text("runtimeModelLabelText", "model"); + } + + static get runtimeCapabilitiesLabelText() { + return this.text("runtimeCapabilitiesLabelText", "capabilities"); + } + + static get runtimeExternalLabelText() { + return this.text("runtimeExternalLabelText", "external"); + } + + static get runtimeCapabilityChatText() { + return this.text("runtimeCapabilityChatText", "chat"); + } + + static get runtimeCapabilityVisionText() { + return this.text("runtimeCapabilityVisionText", "vision / image input"); + } + + static get runtimeCapabilityOcrText() { + return this.text("runtimeCapabilityOcrText", "ocr"); + } + + static get runtimeCapabilityThinkingText() { + return this.text("runtimeCapabilityThinkingText", "thinking / reasoning"); + } + + static get runtimeCapabilityExtendedThinkingText() { + return this.text("runtimeCapabilityExtendedThinkingText", "leveled thinking / reasoning"); + } + + static get runtimeCapabilityToolsText() { + return this.text("runtimeCapabilityToolsText", "tools / function calling"); + } + + static get runtimeCapabilityAudioText() { + return this.text("runtimeCapabilityAudioText", "audio input"); + } + + static get runtimeCapabilitySpeechToTextText() { + return this.text("runtimeCapabilitySpeechToTextText", "speech-to-text"); + } + + static get runtimeCapabilityTextToSpeechText() { + return this.text("runtimeCapabilityTextToSpeechText", "text-to-speech"); + } + + static get runtimeCapabilityDocumentsText() { + return this.text("runtimeCapabilityDocumentsText", "documents / rag"); + } + + static get runtimeCapabilityOutputImagesText() { + return this.text("runtimeCapabilityOutputImagesText", "image gen / image output"); + } + + static get infoAiBlockLabelText() { + return this.text("infoAiBlockLabelText", "AI"); + } + + static get infoSupportedProvidersLabelText() { + return this.text("infoSupportedProvidersLabelText", "providers"); + } + + static get infoToolsBlockLabelText() { + return this.text("infoToolsBlockLabelText", "tools"); + } + + static get infoCommandsBlockLabelText() { + return this.text("infoCommandsBlockLabelText", "commands"); + } + + static get infoPublicLabelText() { + return this.text("infoPublicLabelText", "public"); + } + + static get infoPrivateLabelText() { + return this.text("infoPrivateLabelText", "private"); + } + + static get infoChatLabelText() { + return this.text("infoChatLabelText", "chat"); + } + + static get infoCallbackLabelText() { + return this.text("infoCallbackLabelText", "callback"); + } + + static get commandsHeaderText() { + return this.text("commandsHeaderText", "Commands:\n\n"); + } + + static get sentCommandsInDmText() { + return this.text("sentCommandsInDmText", "Sent commands in DM 😎"); + } + + static get couldNotSendCommandsInDmText() { + return this.text("couldNotSendCommandsInDmText", "Could not send commands in DM ☹️\nSending them here instead"); + } + + static get administratorsHeaderText() { + return this.text("administratorsHeaderText", "*Administrators*:\n\n"); + } + + static get noUserInfoText() { + return this.text("noUserInfoText", "No user information"); + } + + static get useLeaveCommandText() { + return this.text("useLeaveCommandText", "Use /leave"); + } + + static get databaseBackupCaption() { + return this.text("databaseBackupCaption", "Database backup"); + } + + static get databaseBackupSentText() { + return this.text("databaseBackupSentText", "Successfully sent to the creator in DM!"); + } + + static get noChoicesText() { + return this.text("noChoicesText", "Nothing to choose from"); + } + + static get qrCodeMissingTextText() { + return this.text("qrCodeMissingTextText", "No text found for QR code generation."); + } + + static get quoteMissingTextText() { + return this.text("quoteMissingTextText", "Could not find text in the message 😢"); + } + + static get quoteBuildFailedText() { + return this.text("quoteBuildFailedText", "Could not build the quote 😢"); + } + + static get speechToTextInstructionText() { + return this.text("speechToTextInstructionText", "Send audio/voice/video-note or reply with /stt to a message containing audio."); + } + + static get speechToTextEmptyResultText() { + return this.text("speechToTextEmptyResultText", "Speech-to-text did not return transcription text."); + } + + static get textToSpeechInstructionText() { + return this.text("textToSpeechInstructionText", "Send text after the command or reply with /tts to a message containing text."); + } + + static get titleMissingText() { + return this.text("titleMissingText", "Could not find a title..."); + } + + static get betterFallbackText() { + return this.text("betterFallbackText", "Better"); + } + + static get pongText() { + return this.text("pongText", "pong"); + } + + static get variableNotDefinedText() { + return this.text("variableNotDefinedText", "variable is not defined"); + } + + static get evaluationVariableNotDefinedText() { + return this.text("evaluationVariableNotDefinedText", "Variable not defined"); + } + + static get defaultTestAnswerText() { + return this.text("defaultTestAnswerText", "a"); + } + + static get prefixFallbackText() { + return this.text("prefixFallbackText", "?"); + } + + static get searchResultsHeaderText() { + return this.text("searchResultsHeaderText", "Results:\n\n"); + } + + static get modelListHeaderText() { + return this.text("modelListHeaderText", "Available models:\n\n"); + } + + static get modelListLoadFailedText() { + return this.text("modelListLoadFailedText", "Could not load the model list"); + } + + static get noCurrentModelText() { + return this.text("noCurrentModelText", "Model is not set. Use one of the listed values."); + } + + static get unsupportedAttachmentText() { + return this.text("unsupportedAttachmentText", "This attachment type is not supported."); + } + + static get attachmentMissingFromCacheText() { + return this.text("attachmentMissingFromCacheText", "Attachment file is missing from cache."); + } + + static get couldNotIdentifyUserForSpeechToTextText() { + return this.text("couldNotIdentifyUserForSpeechToTextText", "Could not identify the user for speech-to-text."); + } + + static get missingTranscriptionFileText() { + return this.text("missingTranscriptionFileText", "Unable to prepare the audio file for transcription."); + } + + static get transcriptionFailedText() { + return this.text("transcriptionFailedText", "Could not transcribe the audio."); + } + + static get imageGenUnsupportedFilesText() { + return this.text("imageGenUnsupportedFilesText", "Image generation does not support files in this mode."); + } + + static get unsupportedDocumentProviderText() { + return this.text("unsupportedDocumentProviderText", "This provider does not support attached documents."); + } + + static get mistralPdfOnlyText() { + return this.text("mistralPdfOnlyText", "Mistral currently supports only PDF documents."); + } + + static get mistralDocumentUploadFailedText() { + return this.text("mistralDocumentUploadFailedText", "Could not upload the document to Mistral."); + } + + static get documentContentLabelText() { + return this.text("documentContentLabelText", "Document content"); + } + + static get mistralLibraryIdMissingText() { + return this.text("mistralLibraryIdMissingText", "Mistral did not return a temporary document library id."); + } + + static get documentsUnifiedRunnerUnsupportedText() { + return this.text("documentsUnifiedRunnerUnsupportedText", "Documents in the unified runner are currently handled only by Ollama RAG and Mistral."); + } + + static get zipCentralDirectoryNotFoundText() { + return this.text("zipCentralDirectoryNotFoundText", "ZIP archive is corrupted: central directory was not found."); + } + + static get zipInvalidCentralDirectoryText() { + return this.text("zipInvalidCentralDirectoryText", "ZIP archive is corrupted: invalid central directory."); + } + + static get tarFileTooLargeText() { + return this.text("tarFileTooLargeText", "TAR contains a file that is too large."); + } + + static get tarInvalidEntrySizeText() { + return this.text("tarInvalidEntrySizeText", "TAR archive is corrupted: invalid entry size."); + } + + static get tarEntryExceedsBoundsText() { + return this.text("tarEntryExceedsBoundsText", "TAR archive is corrupted: entry exceeds file bounds."); + } + + static get docxDocumentXmlMissingText() { + return this.text("docxDocumentXmlMissingText", "DOCX does not contain word/document.xml."); + } + + static get localRagEmbeddingModelRequiredText() { + return this.text("localRagEmbeddingModelRequiredText", "Local RAG requires OLLAMA_EMBEDDING_MODEL, for example nomic-embed-text."); + } + + static get localRagChunksBuildFailedText() { + return this.text("localRagChunksBuildFailedText", "Could not build chunks for local RAG."); + } + + static get localRagNoSuitableFragmentsText() { + return this.text("localRagNoSuitableFragmentsText", "Local RAG did not find suitable document fragments."); + } + + static get unsupportedAiProviderText() { + return this.text("unsupportedAiProviderText", "Unsupported AI provider."); + } + + static get noSupportedTranscriptionProviderText() { + return this.text("noSupportedTranscriptionProviderText", "No supported speech-to-text provider is configured."); + } + + static get noSupportedTextToSpeechProviderText() { + return this.text("noSupportedTextToSpeechProviderText", "No supported text-to-speech provider is configured."); + } + + static get noSpeechToTextProviderForAccessText() { + return this.text("noSpeechToTextProviderForAccessText", "No speech-to-text providers are configured for your access level."); + } + + static get noTextToSpeechProviderForAccessText() { + return this.text("noTextToSpeechProviderForAccessText", "No text-to-speech providers are configured for your access level."); + } + + static get geminiSpeechToTextUnsupportedText() { + return this.text("geminiSpeechToTextUnsupportedText", "Gemini does not support speech-to-text right now."); + } + + static get geminiTextToSpeechUnsupportedText() { + return this.text("geminiTextToSpeechUnsupportedText", "Gemini does not support text-to-speech right now."); + } + + static get ollamaTextToSpeechUnsupportedText() { + return this.text("ollamaTextToSpeechUnsupportedText", "Ollama does not support text-to-speech right now."); + } + + static get ollamaSpeechToTextModelRequiredText() { + return this.text("ollamaSpeechToTextModelRequiredText", "Ollama speech-to-text requires OLLAMA_AUDIO_MODEL=gemma4:e2b or OLLAMA_AUDIO_MODEL=gemma4:e4b."); + } + + static get noTextToSynthesizeText() { + return this.text("noTextToSynthesizeText", "No text to synthesize."); + } + + static get mistralTtsNoAudioDataText() { + return this.text("mistralTtsNoAudioDataText", "Mistral TTS did not return audioData."); + } + + static get speechFileTooLargeText() { + return this.text("speechFileTooLargeText", "The speech file is larger than 50 MB and cannot be sent."); + } + + static get userSettingsTitle() { + return this.text("userSettingsTitle", "User Settings"); + } + + static get userSettingsAiProviderSelectionTitle() { + return this.text("userSettingsAiProviderSelectionTitle", "AI Provider Selection"); + } + + static get userSettingsInterfaceLanguageSelectionTitle() { + return this.text("userSettingsInterfaceLanguageSelectionTitle", "Interface Language Selection"); + } + + static get userSettingsResponseLanguageSelectionTitle() { + return this.text("userSettingsResponseLanguageSelectionTitle", "Response Language Selection"); + } + + static get userSettingsContextSizeSelectionTitle() { + return this.text("userSettingsContextSizeSelectionTitle", "Context Size Selection"); + } + + static get userSettingsVoiceModeSelectionTitle() { + return this.text("userSettingsVoiceModeSelectionTitle", "Voice Message Mode Selection"); + } + + static get userSettingsTierLabel() { + return this.text("userSettingsTierLabel", "Tier"); + } + + static get userSettingsAiProviderLabel() { + return this.text("userSettingsAiProviderLabel", "AI provider"); + } + + static get userSettingsInterfaceLanguageLabel() { + return this.text("userSettingsInterfaceLanguageLabel", "Interface language"); + } + + static get userSettingsResponseLanguageLabel() { + return this.text("userSettingsResponseLanguageLabel", "LLM response language"); + } + + static get userSettingsContextSizeLabel() { + return this.text("userSettingsContextSizeLabel", "Context size"); + } + + static get userSettingsVoiceModeLabel() { + return this.text("userSettingsVoiceModeLabel", "Voice messages"); + } + + static get userSettingsBackButtonText() { + return this.text("userSettingsBackButtonText", "Back"); + } + + static get userSettingsAiProviderButtonPrefix() { + return this.text("userSettingsAiProviderButtonPrefix", "AI provider"); + } + + static get userSettingsInterfaceLanguageButtonPrefix() { + return this.text("userSettingsInterfaceLanguageButtonPrefix", "Interface language"); + } + + static get userSettingsResponseLanguageButtonPrefix() { + return this.text("userSettingsResponseLanguageButtonPrefix", "Response language"); + } + + static get userSettingsContextSizeButtonPrefix() { + return this.text("userSettingsContextSizeButtonPrefix", "Context size"); + } + + static get userSettingsVoiceModeButtonPrefix() { + return this.text("userSettingsVoiceModeButtonPrefix", "Voice messages"); + } + + static get userSettingsCreatorTierText() { + return this.text("userSettingsCreatorTierText", "Creator"); + } + + static get userSettingsAdminTierText() { + return this.text("userSettingsAdminTierText", "Admin"); + } + + static get userSettingsUserTierText() { + return this.text("userSettingsUserTierText", "User"); + } + + static get userSettingsSelectedPrefix() { + return this.text("userSettingsSelectedPrefix", "✓ "); + } + + static get userSettingsContextSizeDefaultText() { + return this.text("userSettingsContextSizeDefaultText", "Default"); + } + + static get userSettingsContextSizeMaxText() { + return this.text("userSettingsContextSizeMaxText", "Max"); + } + + static get userSettingsVoiceModeExecuteText() { + return this.text("userSettingsVoiceModeExecuteText", "Run through AI"); + } + + static get userSettingsVoiceModeTranscriptText() { + return this.text("userSettingsVoiceModeTranscriptText", "Show transcript only"); + } static commandTitles = { ae: "/ae", @@ -605,9 +1031,17 @@ export class Environment { return this.text("getCancelledText", "{provider}\n❌ Generation cancelled.", {provider}); } - static get startingImageGenText() { return this.text("startingImageGenText", "🌈 Starting image generation..."); } - static get imageGenText() { return this.text("imageGenText", "🌈 Generating image..."); } - static get finalizingImageGenText() { return this.text("finalizingImageGenText", "🌈 Finalizing image generation..."); } + static get startingImageGenText() { + return this.text("startingImageGenText", "🌈 Starting image generation..."); + } + + static get imageGenText() { + return this.text("imageGenText", "🌈 Generating image..."); + } + + static get finalizingImageGenText() { + return this.text("finalizingImageGenText", "🌈 Finalizing image generation..."); + } static getPartialImageGenText(iteration: number, total: number): string { return this.text("getPartialImageGenText", "🌈 Generating image ({iteration}/{total})...", {iteration, total}); @@ -694,20 +1128,9 @@ export class Environment { ].join("\n"); } - static getInfoAiBlockText(supportedProvidersLength: number, modelInfo: string): string { - return [ - `\`\`\`${this.infoAiBlockLabelText}`, - `${this.infoSupportedProvidersLabelText}: ${supportedProvidersLength}`, - "", - modelInfo, - "```", - ].join("\n"); - } - static getInfoToolsBlockText(toolNames: string[]): string { return [ `\`\`\`${this.infoToolsBlockLabelText}`, - `${this.infoCountLabelText}: ${toolNames.length}`, toolNames.map(name => `- ${name}`).join("\n"), "```", ].join("\n"); @@ -780,7 +1203,10 @@ export class Environment { } static getTelegramFileTooLargeText(fileName: string, maxSizeMb: number): string { - return this.text("getTelegramFileTooLargeText", "File {fileName} is larger than {maxSizeMb} MB and cannot be sent.", {fileName, maxSizeMb}); + return this.text("getTelegramFileTooLargeText", "File {fileName} is larger than {maxSizeMb} MB and cannot be sent.", { + fileName, + maxSizeMb + }); } static getUserIsNowAdminText(name: string): string { @@ -799,23 +1225,73 @@ export class Environment { return this.text("getUserWasNotAdminText", "{name} was not an admin 🤔", {name}); } - static get botCannotMakeItselfAdminText() { return this.text("botCannotMakeItselfAdminText", "The bot cannot make itself an admin"); } - static get botCreatorAlreadyAdminText() { return this.text("botCreatorAlreadyAdminText", "The bot creator is already an admin"); } - static get botCannotRemoveItselfFromAdminsText() { return this.text("botCannotRemoveItselfFromAdminsText", "The bot cannot remove itself from admins"); } - static get botCreatorCannotStopBeingAdminText() { return this.text("botCreatorCannotStopBeingAdminText", "The bot creator cannot stop being an admin"); } - static get botWillNotBanCreatorText() { return this.text("botWillNotBanCreatorText", "The bot will not ban its creator."); } - static get botWillNotBanAdminsText() { return this.text("botWillNotBanAdminsText", "The bot will not ban its administrators."); } - static get botIsNotBannedByItselfText() { return this.text("botIsNotBannedByItselfText", "The bot is not banned by itself anyway."); } - static get botCreatorNeverBannedText() { return this.text("botCreatorNeverBannedText", "The bot creator is not banned and never will be."); } - static get botAdminsNotBannedText() { return this.text("botAdminsNotBannedText", "Bot administrators are not banned anyway."); } - static get botWillNotIgnoreItselfText() { return this.text("botWillNotIgnoreItselfText", "The bot will not ignore itself."); } - static get botWillNotIgnoreCreatorText() { return this.text("botWillNotIgnoreCreatorText", "The bot will not ignore its creator."); } - static get botWillNotIgnoreAdminsText() { return this.text("botWillNotIgnoreAdminsText", "The bot will not ignore its administrators."); } - static get botIsNotIgnoredByItselfText() { return this.text("botIsNotIgnoredByItselfText", "The bot is not ignored by itself anyway."); } - static get botCreatorNotIgnoredText() { return this.text("botCreatorNotIgnoredText", "The bot creator is not ignored and never will be."); } - static get botAdminsNotIgnoredText() { return this.text("botAdminsNotIgnoredText", "Bot administrators are not ignored anyway."); } - static get botAlreadyAlwaysListensToItselfText() { return this.text("botAlreadyAlwaysListensToItselfText", "The bot already always listens to itself"); } - static get botAlwaysListensToCreatorText() { return this.text("botAlwaysListensToCreatorText", "The bot always listens to its creator"); } + static get botCannotMakeItselfAdminText() { + return this.text("botCannotMakeItselfAdminText", "The bot cannot make itself an admin"); + } + + static get botCreatorAlreadyAdminText() { + return this.text("botCreatorAlreadyAdminText", "The bot creator is already an admin"); + } + + static get botCannotRemoveItselfFromAdminsText() { + return this.text("botCannotRemoveItselfFromAdminsText", "The bot cannot remove itself from admins"); + } + + static get botCreatorCannotStopBeingAdminText() { + return this.text("botCreatorCannotStopBeingAdminText", "The bot creator cannot stop being an admin"); + } + + static get botWillNotBanCreatorText() { + return this.text("botWillNotBanCreatorText", "The bot will not ban its creator."); + } + + static get botWillNotBanAdminsText() { + return this.text("botWillNotBanAdminsText", "The bot will not ban its administrators."); + } + + static get botIsNotBannedByItselfText() { + return this.text("botIsNotBannedByItselfText", "The bot is not banned by itself anyway."); + } + + static get botCreatorNeverBannedText() { + return this.text("botCreatorNeverBannedText", "The bot creator is not banned and never will be."); + } + + static get botAdminsNotBannedText() { + return this.text("botAdminsNotBannedText", "Bot administrators are not banned anyway."); + } + + static get botWillNotIgnoreItselfText() { + return this.text("botWillNotIgnoreItselfText", "The bot will not ignore itself."); + } + + static get botWillNotIgnoreCreatorText() { + return this.text("botWillNotIgnoreCreatorText", "The bot will not ignore its creator."); + } + + static get botWillNotIgnoreAdminsText() { + return this.text("botWillNotIgnoreAdminsText", "The bot will not ignore its administrators."); + } + + static get botIsNotIgnoredByItselfText() { + return this.text("botIsNotIgnoredByItselfText", "The bot is not ignored by itself anyway."); + } + + static get botCreatorNotIgnoredText() { + return this.text("botCreatorNotIgnoredText", "The bot creator is not ignored and never will be."); + } + + static get botAdminsNotIgnoredText() { + return this.text("botAdminsNotIgnoredText", "Bot administrators are not ignored anyway."); + } + + static get botAlreadyAlwaysListensToItselfText() { + return this.text("botAlreadyAlwaysListensToItselfText", "The bot already always listens to itself"); + } + + static get botAlwaysListensToCreatorText() { + return this.text("botAlwaysListensToCreatorText", "The bot always listens to its creator"); + } static getUserBannedText(name: string): string { return this.text("getUserBannedText", "{name} banned 🚫", {name}); @@ -865,13 +1341,27 @@ export class Environment { return this.text("getCoinResultText", "It landed on *{result}*", {result}); } - static get coinHeadsText() { return this.text("coinHeadsText", "Heads"); } - static get coinTailsText() { return this.text("coinTailsText", "Tails"); } - static get distortReplyInstructionText() { return this.text("distortReplyInstructionText", "Reply with /distort to a message containing an image (photo, document, or sticker).\nExample: /distort 16 80"); } - static get distortMissingImageText() { return this.text("distortMissingImageText", "I do not see an image in the reply. Send a photo or image file."); } + static get coinHeadsText() { + return this.text("coinHeadsText", "Heads"); + } + + static get coinTailsText() { + return this.text("coinTailsText", "Tails"); + } + + static get distortReplyInstructionText() { + return this.text("distortReplyInstructionText", "Reply with /distort to a message containing an image (photo, document, or sticker).\nExample: /distort 16 80"); + } + + static get distortMissingImageText() { + return this.text("distortMissingImageText", "I do not see an image in the reply. Send a photo or image file."); + } static getDistortionReadyCaption(amp: number, wavelength: number): string { - return this.text("getDistortionReadyCaption", "Distortion ready ✅ (amp={amp}, wavelength={wavelength})", {amp, wavelength}); + return this.text("getDistortionReadyCaption", "Distortion ready ✅ (amp={amp}, wavelength={wavelength})", { + amp, + wavelength + }); } static getDistortFailedText(error: unknown): string { @@ -929,7 +1419,10 @@ export class Environment { } static getMistralDocumentProcessingFailedText(fileName: string, status: string): string { - return this.text("getMistralDocumentProcessingFailedText", "Mistral could not process document {fileName}: {status}", {fileName, status}); + return this.text("getMistralDocumentProcessingFailedText", "Mistral could not process document {fileName}: {status}", { + fileName, + status + }); } static getMistralDocumentProcessingTimedOutText(fileName: string): string { @@ -945,7 +1438,10 @@ export class Environment { } static getZipUnsupportedCompressionMethodText(method: number, entryName: string): string { - return this.text("getZipUnsupportedCompressionMethodText", "ZIP archive uses unsupported compression method {method} for {entryName}.", {method, entryName}); + return this.text("getZipUnsupportedCompressionMethodText", "ZIP archive uses unsupported compression method {method} for {entryName}.", { + method, + entryName + }); } static getGzipUncompressedLimitText(maxBytes: number): string { @@ -1015,7 +1511,11 @@ export class Environment { reason: error instanceof Error ? error.message : String(error), }); } - static get shutdownFallbackText() { return this.text("shutdownFallbackText", "..."); } + + static get shutdownFallbackText() { + return this.text("shutdownFallbackText", "..."); + } + static get shutdownSequenceTexts() { return this.textArray("shutdownSequenceTexts", [ "well then, everyone", @@ -1024,21 +1524,50 @@ export class Environment { "all the best", ]); } - static get shutdownDoneText() { return this.text("shutdownDoneText", "*R.I.P*"); } + + static get shutdownDoneText() { + return this.text("shutdownDoneText", "*R.I.P*"); + } static getWhenPrefixText(): string { return this.text("getWhenPrefixText", "in "); } - static get whenNowText() { return this.text("whenNowText", "right now"); } - static get whenNeverText() { return this.text("whenNeverText", "never"); } - static get whenYearUnitText() { return this.text("whenYearUnitText", "year"); } - static get whenDayUnitText() { return this.text("whenDayUnitText", "day"); } - static get whenWeekUnitText() { return this.text("whenWeekUnitText", "week"); } - static get whenMonthUnitText() { return this.text("whenMonthUnitText", "month"); } - static get whenHourUnitText() { return this.text("whenHourUnitText", "hour"); } - static get whenMinuteUnitText() { return this.text("whenMinuteUnitText", "minute"); } - static get whenSecondUnitText() { return this.text("whenSecondUnitText", "second"); } + static get whenNowText() { + return this.text("whenNowText", "right now"); + } + + static get whenNeverText() { + return this.text("whenNeverText", "never"); + } + + static get whenYearUnitText() { + return this.text("whenYearUnitText", "year"); + } + + static get whenDayUnitText() { + return this.text("whenDayUnitText", "day"); + } + + static get whenWeekUnitText() { + return this.text("whenWeekUnitText", "week"); + } + + static get whenMonthUnitText() { + return this.text("whenMonthUnitText", "month"); + } + + static get whenHourUnitText() { + return this.text("whenHourUnitText", "hour"); + } + + static get whenMinuteUnitText() { + return this.text("whenMinuteUnitText", "minute"); + } + + static get whenSecondUnitText() { + return this.text("whenSecondUnitText", "second"); + } static getWhenDurationText(value: number, unit: string): string { const pluralUnit = value === 1 ? unit : this.text("getWhenPluralUnitText", "{unit}s", {unit}); diff --git a/src/common/user-ai-settings.ts b/src/common/user-ai-settings.ts index bf9af9a..781623c 100644 --- a/src/common/user-ai-settings.ts +++ b/src/common/user-ai-settings.ts @@ -3,21 +3,18 @@ import {UserStore} from "./user-store"; import {AiProvider} from "../model/ai-provider"; import {StoredUser} from "../model/stored-user"; import {resolveAiRuntimeTarget} from "../ai/ai-runtime-target"; -import { - DEFAULT_LANGUAGE_CHOICE, - LanguageChoice, - Localization, -} from "./localization"; +import {DEFAULT_LANGUAGE_CHOICE, LanguageChoice, Localization,} from "./localization"; export const DEFAULT_AI_PROVIDER_CHOICE = "DEFAULT"; export const DEFAULT_AI_CONTEXT_SIZE_CHOICE = "DEFAULT"; -export const USER_AI_CONTEXT_SIZE_PRESETS = [64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 256000] as const; -export const MIN_USER_AI_CONTEXT_SIZE = 64; +export const AI_CONTEXT_SIZE_MAX_CHOICE = "MAX"; +export const USER_AI_CONTEXT_SIZE_PRESETS = [1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144] as const; +export const MIN_USER_AI_CONTEXT_SIZE = 1024; export const MAX_USER_AI_CONTEXT_SIZE = 1_000_000; export const AI_VOICE_MODE_EXECUTE = "execute"; export const AI_VOICE_MODE_TRANSCRIPT = "transcript"; export type UserAiProviderChoice = AiProvider | typeof DEFAULT_AI_PROVIDER_CHOICE; -export type UserAiContextSizeChoice = number | typeof DEFAULT_AI_CONTEXT_SIZE_CHOICE; +export type UserAiContextSizeChoice = number | typeof DEFAULT_AI_CONTEXT_SIZE_CHOICE | typeof AI_CONTEXT_SIZE_MAX_CHOICE; export type UserAiVoiceMode = typeof AI_VOICE_MODE_EXECUTE | typeof AI_VOICE_MODE_TRANSCRIPT; export type UserInterfaceLanguage = LanguageChoice; export type UserAiResponseLanguage = LanguageChoice; @@ -65,7 +62,7 @@ export function getUserLanguageChoices(): string[] { } export function getUserAiContextSizeChoices(): UserAiContextSizeChoice[] { - return [DEFAULT_AI_CONTEXT_SIZE_CHOICE, ...USER_AI_CONTEXT_SIZE_PRESETS]; + return [DEFAULT_AI_CONTEXT_SIZE_CHOICE, ...USER_AI_CONTEXT_SIZE_PRESETS, AI_CONTEXT_SIZE_MAX_CHOICE]; } export function getUserAiVoiceModes(): UserAiVoiceMode[] { @@ -129,11 +126,19 @@ export function normalizeAiContextSizeChoice(value: string | number | undefined return DEFAULT_AI_CONTEXT_SIZE_CHOICE; } + if (normalized === AI_CONTEXT_SIZE_MAX_CHOICE || lower === "max") { + return AI_CONTEXT_SIZE_MAX_CHOICE; + } + numericValue = Number(normalized); } else { numericValue = value; } + if (numericValue === -1) { + return AI_CONTEXT_SIZE_MAX_CHOICE; + } + if (!Number.isSafeInteger(numericValue) || numericValue < MIN_USER_AI_CONTEXT_SIZE || numericValue > MAX_USER_AI_CONTEXT_SIZE) { return undefined; } @@ -195,6 +200,10 @@ export function getContextSizeLabel(choice: UserAiContextSizeChoice): string { return Environment.userSettingsContextSizeDefaultText; } + if (choice === AI_CONTEXT_SIZE_MAX_CHOICE) { + return Environment.userSettingsContextSizeMaxText; + } + return Environment.getUserSettingsContextSizeText(choice); } @@ -248,7 +257,7 @@ function shouldUpdateVoiceMode(user: StoredUser | null, mode: UserAiVoiceMode): } function contextSizeChoiceToStored(choice: UserAiContextSizeChoice): number | undefined { - return choice === DEFAULT_AI_CONTEXT_SIZE_CHOICE ? undefined : choice; + return choice === DEFAULT_AI_CONTEXT_SIZE_CHOICE ? undefined : choice === AI_CONTEXT_SIZE_MAX_CHOICE ? -1 : choice; } export async function ensureValidUserAiSettings(userId: number): Promise { @@ -383,7 +392,7 @@ export async function setUserAiContextSizeChoice( const settings = await ensureValidUserAiSettings(userId); const normalized = normalizeAiContextSizeChoice(choice); - if (!normalized) { + if (!normalized && normalized !== -1) { return {ok: false, settings}; } diff --git a/src/model/ai-model-capabilities.ts b/src/model/ai-model-capabilities.ts index 4b8aadd..d800240 100644 --- a/src/model/ai-model-capabilities.ts +++ b/src/model/ai-model-capabilities.ts @@ -1,6 +1,7 @@ import {AiCapabilityInfo} from "./ai-capability-info"; export class AiModelCapabilities { + chat: AiCapabilityInfo | undefined; vision: AiCapabilityInfo | undefined; ocr: AiCapabilityInfo | undefined; thinking: AiCapabilityInfo | undefined; diff --git a/src/util/utils.ts b/src/util/utils.ts index 9ac85cc..2da2082 100644 --- a/src/util/utils.ts +++ b/src/util/utils.ts @@ -90,7 +90,8 @@ export function searchChatCommand( botUsername: string | undefined = botUser.username ): Command | null { for (const command of commands) { - const match = command.finalRegexp.exec(text); + const finalRegexp = command.finalRegexp; + const match = finalRegexp.exec(text); if (!match) continue; const mentioned = match[2]?.toLowerCase(); @@ -2089,19 +2090,6 @@ export function photoPathByUniqueId(uniqueId: string): string { return path.join(photoDir, uniqueId + ".jpg"); } -export function getCurrentModel(): string | undefined { - switch (Environment.DEFAULT_AI_PROVIDER) { - case AiProvider.OLLAMA: - return Environment.OLLAMA_CHAT_MODEL; - case AiProvider.GEMINI: - return Environment.GEMINI_MODEL; - case AiProvider.MISTRAL: - return Environment.MISTRAL_MODEL; - case AiProvider.OPENAI: - return Environment.OPENAI_MODEL; - } -} - export async function processMyChatMember(u: ChatMemberUpdated): Promise { console.log("my_chat_member", u); }