From 34a8d426d2c83c8bbe7cdc0a777ce658508aca4c Mon Sep 17 00:00:00 2001 From: yunogasai Date: Thu, 24 Sep 2026 19:02:41 +0200 Subject: [PATCH] =?UTF-8?q?DeepSeek=20=D0=BD=D0=B0=D0=BF=D1=80=D1=8F=D0=BC?= =?UTF-8?q?=D1=83=D1=8E=20=D0=BA=D0=B0=D0=BA=20=D0=BF=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D0=B9=D0=B4=D0=B5=D1=80=20=D0=BF=D0=BE=20=D1=83=D0=BC?= =?UTF-8?q?=D0=BE=D0=BB=D1=87=D0=B0=D0=BD=D0=B8=D1=8E,=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B0=20=D0=BA=D0=BB=D1=8E=D1=87?= =?UTF-8?q?=D0=B0=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D0=B8=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=20=D1=81=D1=82=D0=B0=D1=80=D1=82=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .env.example | 13 ++++++++----- CLAUDE.md | 2 +- README.md | 11 +++++++---- src/config.ts | 30 +++++++++++++++++++++++++++++- 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/.env.example b/.env.example index 302c1c8..777c5ff 100644 --- a/.env.example +++ b/.env.example @@ -4,12 +4,15 @@ BOT_TOKEN= # Telegram ID администраторов через запятую (узнать свой: написать боту /whoami в личку) ADMIN_IDS= -# Модель в формате Mastra model router: openrouter// -AI_MODEL=openrouter/deepseek/deepseek-v4-flash -OPENROUTER_API_KEY= +# Провайдер модели — задай ключ одного из них. +# DeepSeek напрямую (если ключ задан, по умолчанию берётся deepseek/deepseek-flash): +DEEPSEEK_API_KEY= +# Или OpenRouter (по умолчанию openrouter/deepseek/deepseek-v4-flash): +# OPENROUTER_API_KEY= -# Напрямую через DeepSeek: AI_MODEL=deepseek/deepseek-chat и DEEPSEEK_API_KEY= -# DEEPSEEK_API_KEY= +# Необязательно: конкретная модель в формате Mastra model router <провайдер>/<модель>, +# например deepseek/deepseek-v4-pro или openrouter/deepseek/deepseek-v4-pro +# AI_MODEL= # Путь к SQLite-базе и сколько сообщений на чат хранить DB_PATH=data/bot.db diff --git a/CLAUDE.md b/CLAUDE.md index 4f5d1fb..7e2898a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # Boltun AI -Telegram-бот на Node.js + TypeScript + grammY, AI через Mastra (модели через OpenRouter, например DeepSeek). Хранилище — SQLite (better-sqlite3). +Telegram-бот на Node.js + TypeScript + grammY, AI через Mastra (DeepSeek напрямую или через OpenRouter). Хранилище — SQLite (better-sqlite3). ## Команды diff --git a/README.md b/README.md index 19da292..3885407 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Boltun AI -Telegram-бот для групповых чатов с AI-личностями. Стек: Node.js, TypeScript, [grammY](https://grammy.dev), [Mastra](https://mastra.ai) (модели через OpenRouter / DeepSeek), SQLite. +Telegram-бот для групповых чатов с AI-личностями. Стек: Node.js, TypeScript, [grammY](https://grammy.dev), [Mastra](https://mastra.ai) (DeepSeek напрямую или через OpenRouter), SQLite. ## Возможности @@ -27,7 +27,7 @@ Telegram-бот для групповых чатов с AI-личностями. ```bash nvm use # Node 24 pnpm install -cp .env.example .env # заполнить BOT_TOKEN, ADMIN_IDS, OPENROUTER_API_KEY +cp .env.example .env # заполнить BOT_TOKEN, ADMIN_IDS и DEEPSEEK_API_KEY (или OPENROUTER_API_KEY) pnpm dev ``` @@ -39,8 +39,11 @@ pnpm dev | --- | --- | --- | | `BOT_TOKEN` | да | токен от @BotFather | | `ADMIN_IDS` | да | Telegram ID админов через запятую | -| `OPENROUTER_API_KEY` | да* | ключ OpenRouter (*или ключ того провайдера, что в `AI_MODEL`) | -| `AI_MODEL` | нет | модель Mastra model router, по умолчанию `openrouter/deepseek/deepseek-v4-flash`; напрямую через DeepSeek — `deepseek/deepseek-chat` + `DEEPSEEK_API_KEY` | +| `DEEPSEEK_API_KEY` | да* | ключ DeepSeek — модель идёт напрямую в api.deepseek.com | +| `OPENROUTER_API_KEY` | да* | ключ OpenRouter — альтернатива DeepSeek напрямую | +| `AI_MODEL` | нет | модель Mastra model router `<провайдер>/<модель>`. По умолчанию `deepseek/deepseek-flash`, если задан `DEEPSEEK_API_KEY`, иначе `openrouter/deepseek/deepseek-v4-flash`. Пример посильнее: `deepseek/deepseek-v4-pro` | + +\* нужен ключ того провайдера, чья модель выбрана; бот проверяет это при старте. | `DB_PATH` | нет | путь к SQLite, по умолчанию `data/bot.db` (в Docker — `/app/data/bot.db`) | | `HISTORY_LIMIT` | нет | сколько сообщений на чат хранить, по умолчанию 2000 | diff --git a/src/config.ts b/src/config.ts index 8ba15bd..38ca66e 100644 --- a/src/config.ts +++ b/src/config.ts @@ -34,13 +34,41 @@ const parseAdminIds = (raw: string): Set => { return new Set(ids); }; +/** Ключ, который Mastra берёт из env для провайдера модели (`<провайдер>/<модель>`). */ +const PROVIDER_API_KEYS: Record = { + deepseek: "DEEPSEEK_API_KEY", + openrouter: "OPENROUTER_API_KEY", +}; + +/** Есть ключ DeepSeek — ходим в DeepSeek напрямую, иначе через OpenRouter. */ +const defaultModel = (): string => + process.env.DEEPSEEK_API_KEY?.trim() ? "deepseek/deepseek-flash" : "openrouter/deepseek/deepseek-v4-flash"; + +/** Модель из AI_MODEL (или по умолчанию) + проверка, что ключ её провайдера задан. */ +const parseModel = (): string => { + const hasAnyKey = Object.values(PROVIDER_API_KEYS).some((name) => process.env[name]?.trim()); + + if (!process.env.AI_MODEL?.trim() && !hasAnyKey) { + throw new Error("Задай ключ модели: DEEPSEEK_API_KEY (DeepSeek напрямую) или OPENROUTER_API_KEY"); + } + + const model = process.env.AI_MODEL?.trim() || defaultModel(); + + const [provider = ""] = model.split("/"); + const keyName = PROVIDER_API_KEYS[provider]; + + if (keyName) required(keyName); + + return model; +}; + /** Путь к SQLite; нужен и боту, и скрипту `seed`, которому остальные переменные не нужны. */ export const DB_PATH = process.env.DB_PATH?.trim() || "data/bot.db"; export const loadBotConfig = () => ({ botToken: required("BOT_TOKEN"), adminIds: parseAdminIds(required("ADMIN_IDS")), - aiModel: process.env.AI_MODEL?.trim() || "openrouter/deepseek/deepseek-v4-flash", + aiModel: parseModel(), /** Сколько последних сообщений каждого чата хранить в базе. */ historyLimit: positiveInt("HISTORY_LIMIT", 2000), });