17 KiB
AGENTS.md — NexusAI
Карта проекта для ИИ-агентов и разработчиков. Читай этот файл первым, чтобы не перечитывать весь код. Обновляй раздел «Последние изменения» и карту при значимых правках.
1. Что это
NexusAI — универсальный MCP-сервис для агентов. Даёт один набор инструментов для:
- TTS (text-to-speech), STT (speech-to-text, локально),
- генерации изображений / видео / музыки,
- веб-поиска (search-augmented модели),
- vision (описание изображения chat-моделью),
- служебных операций: список моделей, оценка стоимости, загрузка файла, статистика.
Плюс веб-админка (Fastify) для галереи контента, учёта трат и управления провайдерами/ключами.
Первый и основной провайдер — NordRouter (https://nordrouter.com, OpenAI-совместимый).
Архитектура провайдер-агностична: адаптеры добавляются в registry, слой инструментов не меняется.
2. Технологии
- TypeScript, monorepo через npm workspaces (CommonJS,
tsc). - MCP:
@modelcontextprotocol/sdk(stdio-транспорт). - HTTP:
undici(fetch + ProxyAgent + FormData). - БД:
better-sqlite3(журнал + провайдеры). - Админка:
fastify+@fastify/static, фронт — ванильный HTML/CSS/JS (без сборки). - Схемы:
zod(+zod-to-json-schemaдля MCP inputSchema). - STT: Python sidecar
faster-whisper, запускается черезuv.
3. Карта проекта
NexusAI/
├── AGENTS.md # ← этот файл
├── README.md # пользовательская документация
├── TEST_RESUME.md # лог ручных тестов (search/STT/TTS/image/video/vision)
├── .env.example # все переменные окружения с описанием
├── package.json # workspaces + скрипты build/start/admin
├── sidecar/
│ └── stt_server.py # faster-whisper sidecar (JSON in argv → JSON stdout)
└── packages/
├── core/ # @nexusai/core — общая логика, БД, провайдеры
├── mcp-server/ # @nexusai/mcp-server — MCP stdio-сервер (инструменты)
└── admin/ # @nexusai/admin — веб-дашборд
3.1 packages/core (@nexusai/core)
| Файл | Назначение |
|---|---|
src/config.ts |
loadConfig() — читает все NEXUS_* / NORDROUTER_* env → NexusConfig. |
src/index.ts |
Реэкспорт всего публичного API core. |
src/db/journal.ts |
Journal — SQLite журнал генераций: record/getById/list/deleteById/stats. |
src/db/providers-store.ts |
ProvidersStore — провайдеры + зашифрованные ключи (AES-256-GCM). Отдаёт ProviderView с маской. |
src/security/crypto.ts |
KeyCipher (шифрование ключей), maskSecret. enabled = задан ли мастер-ключ. |
src/security/resolver.ts |
KeyResolver — эффективные настройки провайдера. Приоритет ключа: ENV > DB. Грациозный фолбэк, если DB-ключ нечитаем. |
src/media/storage.ts |
MediaStorage — скачивание URL/base64 на диск, mimeToExt/extToMime, cleanup(ttlDays). |
src/media/content.ts |
buildEmbeddedContent() — упаковка файла в MCP-контент (image/audio) с лимитом. |
src/providers/types.ts |
Базовые типы: Capability, MediaProvider, SearchProvider, SttProvider, ModelInfo и пр. |
src/providers/registry.ts |
ProviderRegistry — карта name+capability → адаптер. |
src/providers/model-catalog.ts |
ModelCatalog — фоновая синхронизация каталога моделей + fuzzy-подбор модели (LCS + токены, дешёвая при равенстве), ModelResolutionError со списком моделей. |
src/providers/nordrouter/client.ts |
NordRouterClient — низкоуровневый HTTP (Bearer, rate-limit). |
src/providers/nordrouter/media.ts |
NordRouterMediaProvider — /media/generate + polling, estimate, upload (data-URL, MIME по расширению), listModels, checkKey. Capabilities: tts,image,video,music. |
src/providers/nordrouter/search.ts |
NordRouterSearchProvider — веб-поиск через /v1/chat/completions (perplexity/sonar-pro), citations из annotations. |
src/providers/nordrouter/index.ts |
createNordRouterProviders() — фабрика media+search. |
src/providers/local-stt/whisper.ts |
LocalWhisperProvider — спавнит sidecar, materialize аудио (path/url/base64). |
src/shared/http.ts |
HttpClient (retry, rate-limit, proxy, FormData), RateLimiter. |
src/shared/errors.ts |
NexusError + codeFromHttpStatus. |
src/shared/logger.ts |
createLogger — пишет только в stderr (stdout занят MCP). |
src/shared/polling.ts |
pollUntilDone, sleep. |
3.2 packages/mcp-server (@nexusai/mcp-server)
| Файл | Назначение |
|---|---|
src/index.ts |
Точка входа: поднимает MCP Server на stdio, регистрирует ListTools/CallTool. |
src/context.ts |
buildContext() — собирает AppContext: registry, storage, journal, providersStore, catalogs (Map<provider, ModelCatalog> с авто-синком), defaultProvider. |
src/schemas.ts |
Zod-схемы входа для всех инструментов. |
src/tools.ts |
TOOLS[] — описания + JSON Schema (генерится из zod). |
src/handlers.ts |
Handlers — реализация всех инструментов, запись в журнал, резолв модели через catalog, запись STT-транскрипта в .md. |
3.3 packages/admin (@nexusai/admin)
| Файл | Назначение |
|---|---|
src/server.ts |
Fastify: раздаёт /media/:file (с корректным Content-Type, incl. text/markdown), статику public/, API. |
src/context.ts |
buildAdminContext() — journal + providersStore + cipher (без registry). |
src/routes.ts |
REST API (см. §6). KNOWN_TYPES = ['nordrouter','openai']. |
public/index.html |
UI: вкладки, темы, галерея, модалка, автообновление. Ванильный CSS через --vars. |
public/app.js |
Логика UI: fetch API, рендер плиток, модалка, аудиоплеер, авто-refresh. |
4. MCP-инструменты (11)
| Инструмент | Что делает | Ключевые аргументы |
|---|---|---|
nexus_tts |
Синтез речи | model (default audio/elevenlabs-tts), input.text/voice/... |
nexus_image |
Генерация/редактирование картинок | model (default image/nano-banana-2), input.prompt/image/... |
nexus_video |
t2v / i2v (может занять минуты) | model (default video/kling-2.6-t2v), input.prompt/image/... |
nexus_music |
Генерация музыки | model (default music/suno-v5), input... |
nexus_stt |
Распознавание речи (локально) | input.audioPath/audioUrl/audioBase64/language/model |
nexus_web_search |
Веб-поиск с источниками | query, recency, maxResults, model |
nexus_vision |
Описание изображения chat-моделью | image (url/path/base64), prompt, model (default google/gemini-3.1-flash-lite) |
nexus_media_models |
Список моделей провайдера | type, search |
nexus_media_estimate |
Оценка стоимости до генерации | model, input |
nexus_media_upload |
Загрузка файла к провайдеру → URL | path, name, mimeType |
nexus_usage_stats |
Статистика из журнала | sinceDays, list, limit |
Медиа-пайплайн (Handlers.media): резолв модели через ModelCatalog → provider.generate (polling) → скачивание файла в media-dir → запись в журнал → возврат JSON (+ note при подмене модели).
Fuzzy-резолв модели: кривое имя → ближайшая (при равенстве — дешевле); нет совпадений → BAD_REQUEST с available (список категории, дешёвые первыми) и подсказкой попросить пользователя уточнить.
5. Возможности (capabilities)
Capability = 'tts' | 'stt' | 'image' | 'video' | 'music' | 'search'
(в журнале также встречается vision).
Маппинг NordRouter type → capability (в model-catalog.ts): audio→tts, image→image, video→video, music→music.
6. Admin REST API
| Метод | Путь | Назначение |
|---|---|---|
| GET | /api/providers |
Список провайдеров (маска ключа), knownTypes, cryptoEnabled. |
| POST | /api/providers |
Upsert провайдера (ключ шифруется). |
| POST | /api/providers/:name/enabled |
Вкл/выкл. |
| POST | /api/providers/:name/default |
Сделать default. |
| DELETE | /api/providers/:name |
Удалить. |
| POST | /api/providers/:name/check |
Проверка ключа реальным вызовом. |
| GET | /api/stats?sinceDays= |
Статистика трат. |
| GET | /api/generations?limit=&offset=&capability= |
Галерея (добавляет mediaUrl). |
| GET | /api/generations/:id |
Одна запись. |
| DELETE | /api/generations/:id |
Удалить запись + файл (только внутри media-dir). |
| GET | /media/:file |
Раздача файла (Content-Type по расширению). |
7. Переменные окружения (главное)
# NordRouter
NORDROUTER_API_KEY=sk-...
NORDROUTER_BASE_URL=https://nordrouter.com
# Общие
NEXUS_DEFAULT_PROVIDER=nordrouter
NEXUS_MEDIA_DIR=./media-output # медиа + SQLite
NEXUS_DB_PATH=<media>/nexus.sqlite # опц.
NEXUS_MASTER_KEY=<hex32|passphrase> # шифрование ключей в БД (иначе только ENV-ключи)
NEXUS_EMBED_MAX_BYTES=10485760
NEXUS_LOG_LEVEL=info # логи → stderr
# STT
NEXUS_STT_PYTHON=uv
NEXUS_STT_MODEL=small # tiny|base|small|medium|large-v3 ИЛИ путь к CT2-модели
NEXUS_STT_DEVICE=auto ; NEXUS_STT_COMPUTE=auto
NEXUS_STT_LOCAL_ONLY=1 # оффлайн (без скачивания)
HF_ENDPOINT=https://hf-mirror.com # зеркало HF в закрытых сетях
# Admin
NEXUS_ADMIN_HOST=127.0.0.1 ; NEXUS_ADMIN_PORT=4123
# Proxy (опц.)
NEXUS_HTTP_PROXY=http://proxy:8080
Приоритет ключа: <NAME>_API_KEY (env) > зашифрованная запись в БД.
8. Команды
npm install
npm run build # core → mcp-server → admin (по отдельности: build:core|build:mcp|build:admin)
npm run start # запустить MCP-сервер (stdio)
npm run admin # запустить админку (http://127.0.0.1:4123)
MCP работает по stdio → никогда не писать в stdout кроме протокола; все логи идут в stderr.
Локальный запуск админки в этой среде (фоновые процессы реапятся)
tmux new-session -d -s nexusadmin -c /home/user/moder/mcp/NexusAI \
"export NEXUS_MEDIA_DIR=/tmp/opencode/nexus-media NEXUS_MASTER_KEY=... NEXUS_ADMIN_PORT=4123; \
exec node packages/admin/dist/server.js >/tmp/opencode/admin_live.log 2>&1"
Тестирование инструментов без полного MCP-хендшейка
Импортировать buildContext + Handlers из dist/ и звать handlers.handle('nexus_*', args).
⚠️ Скрипт должен лежать внутри packages/mcp-server/ (иначе не резолвятся node_modules workspace).
Env для теста: NEXUS_MEDIA_DIR, NEXUS_MASTER_KEY (тот же, что при сохранении ключа), STT-переменные.
9. Важные особенности / подводные камни
- stdout только для MCP. Логи — в stderr (
createLogger). - Ключ мастер-шифрования обязателен для чтения ключей из БД; иначе только ENV-ключи. Без ключа сервис не падает (грациозный фолбэк).
- NordRouter upload ждёт
data:<mime>;base64,...в полеdata. MIME определяется по расширению — иначеHTTP 415. - Kling i2v не принимает прямую ссылку на сгенерированный контент — нужен URL из
nexus_media_upload(?raw=1). - Видео-модели часто требуют полный набор полей (
duration,aspect_ratio,resolution, ...), иначеHTTP 400. - STT не качает модель в закрытой сети (HF недоступен). Варианты:
HF_ENDPOINT, локальная модель +NEXUS_STT_LOCAL_ONLY=1(модельfaster-whisper-*можно взять с ModelScope). - STT-транскрипт сохраняется как
.mdв media-dir и виден в галерее/модалке (не путать с исходным аудио). - Стоимость видео может превышать
estUsd(например, Kling 2.6 i2v: est$0.34→ факт$0.69). - Некоторые модели бывают временно недоступны («Генерация временно недоступна — средства возвращены»).
- ID моделей NordRouter для chat/vision:
google/gemini-3.1-flash-lite,openai/gpt-5.x(НЕgpt-4o).
10. Последние изменения
Свежие сверху. Держи актуальным.
8bd2e89— STT пишет транскрипт в.md(виден в галерее/модалке);ModelCatalog(фоновый синк + fuzzy-резолв с дешёвым фолбэком и подсказками); UI: аудиоплеер (кнопка в карточке + плеер в модалке), просмотр текстовых.md, автообновление активной вкладки без перезагрузки;/mediaотдаёт корректный Content-Type.b2f57cc— новый инструментnexus_vision; фиксnexus_media_upload(MIME по расширению, чинит 415);HttpClientподдерживает FormData.c65f910— STT больше не пишет путь исходного аудио какfilePath(убрана битая ссылка в галерее).1bc3746— Admin UX: светлая/тёмная тема, пагинация галереи, модалка + удаление, тип провайдераopenai.6ff2b27— фикс краша MCP при наличии ключа в БД безNEXUS_MASTER_KEY.679e564— Admin (Phase 2): дашборд трат, галерея, управление провайдерами.
11. Куда добавлять новое
- Новый провайдер → адаптер в
core/src/providers/<name>/, регистрация вmcp-server/src/context.ts, тип вadminKNOWN_TYPESпри необходимости. - Новый инструмент → схема в
schemas.ts, ветка вHandlers.handle, описание вtools.ts. - Новое поле журнала → миграция в
Journal.init, обновитьGenerationRecordи SELECT-колонки вgetById/list. - UI →
public/index.html(разметка/CSS) +public/app.js(логика). Сборки нет — правки применяются сразу.