DeepSeek напрямую как провайдер по умолчанию, проверка ключа модели при старте
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
13
.env.example
13
.env.example
@@ -4,12 +4,15 @@ BOT_TOKEN=
|
|||||||
# Telegram ID администраторов через запятую (узнать свой: написать боту /whoami в личку)
|
# Telegram ID администраторов через запятую (узнать свой: написать боту /whoami в личку)
|
||||||
ADMIN_IDS=
|
ADMIN_IDS=
|
||||||
|
|
||||||
# Модель в формате Mastra model router: openrouter/<vendor>/<model>
|
# Провайдер модели — задай ключ одного из них.
|
||||||
AI_MODEL=openrouter/deepseek/deepseek-v4-flash
|
# DeepSeek напрямую (если ключ задан, по умолчанию берётся deepseek/deepseek-flash):
|
||||||
OPENROUTER_API_KEY=
|
DEEPSEEK_API_KEY=
|
||||||
|
# Или OpenRouter (по умолчанию openrouter/deepseek/deepseek-v4-flash):
|
||||||
|
# OPENROUTER_API_KEY=
|
||||||
|
|
||||||
# Напрямую через DeepSeek: AI_MODEL=deepseek/deepseek-chat и DEEPSEEK_API_KEY=
|
# Необязательно: конкретная модель в формате Mastra model router <провайдер>/<модель>,
|
||||||
# DEEPSEEK_API_KEY=
|
# например deepseek/deepseek-v4-pro или openrouter/deepseek/deepseek-v4-pro
|
||||||
|
# AI_MODEL=
|
||||||
|
|
||||||
# Путь к SQLite-базе и сколько сообщений на чат хранить
|
# Путь к SQLite-базе и сколько сообщений на чат хранить
|
||||||
DB_PATH=data/bot.db
|
DB_PATH=data/bot.db
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Boltun AI
|
# 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).
|
||||||
|
|
||||||
## Команды
|
## Команды
|
||||||
|
|
||||||
|
|||||||
11
README.md
11
README.md
@@ -1,6 +1,6 @@
|
|||||||
# Boltun AI
|
# 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
|
```bash
|
||||||
nvm use # Node 24
|
nvm use # Node 24
|
||||||
pnpm install
|
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
|
pnpm dev
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -39,8 +39,11 @@ pnpm dev
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `BOT_TOKEN` | да | токен от @BotFather |
|
| `BOT_TOKEN` | да | токен от @BotFather |
|
||||||
| `ADMIN_IDS` | да | Telegram ID админов через запятую |
|
| `ADMIN_IDS` | да | Telegram ID админов через запятую |
|
||||||
| `OPENROUTER_API_KEY` | да* | ключ OpenRouter (*или ключ того провайдера, что в `AI_MODEL`) |
|
| `DEEPSEEK_API_KEY` | да* | ключ DeepSeek — модель идёт напрямую в api.deepseek.com |
|
||||||
| `AI_MODEL` | нет | модель Mastra model router, по умолчанию `openrouter/deepseek/deepseek-v4-flash`; напрямую через DeepSeek — `deepseek/deepseek-chat` + `DEEPSEEK_API_KEY` |
|
| `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`) |
|
| `DB_PATH` | нет | путь к SQLite, по умолчанию `data/bot.db` (в Docker — `/app/data/bot.db`) |
|
||||||
| `HISTORY_LIMIT` | нет | сколько сообщений на чат хранить, по умолчанию 2000 |
|
| `HISTORY_LIMIT` | нет | сколько сообщений на чат хранить, по умолчанию 2000 |
|
||||||
|
|
||||||
|
|||||||
@@ -34,13 +34,41 @@ const parseAdminIds = (raw: string): Set<number> => {
|
|||||||
return new Set(ids);
|
return new Set(ids);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Ключ, который Mastra берёт из env для провайдера модели (`<провайдер>/<модель>`). */
|
||||||
|
const PROVIDER_API_KEYS: Record<string, string> = {
|
||||||
|
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`, которому остальные переменные не нужны. */
|
/** Путь к SQLite; нужен и боту, и скрипту `seed`, которому остальные переменные не нужны. */
|
||||||
export const DB_PATH = process.env.DB_PATH?.trim() || "data/bot.db";
|
export const DB_PATH = process.env.DB_PATH?.trim() || "data/bot.db";
|
||||||
|
|
||||||
export const loadBotConfig = () => ({
|
export const loadBotConfig = () => ({
|
||||||
botToken: required("BOT_TOKEN"),
|
botToken: required("BOT_TOKEN"),
|
||||||
adminIds: parseAdminIds(required("ADMIN_IDS")),
|
adminIds: parseAdminIds(required("ADMIN_IDS")),
|
||||||
aiModel: process.env.AI_MODEL?.trim() || "openrouter/deepseek/deepseek-v4-flash",
|
aiModel: parseModel(),
|
||||||
/** Сколько последних сообщений каждого чата хранить в базе. */
|
/** Сколько последних сообщений каждого чата хранить в базе. */
|
||||||
historyLimit: positiveInt("HISTORY_LIMIT", 2000),
|
historyLimit: positiveInt("HISTORY_LIMIT", 2000),
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user