Files
suno-mcp-server/NexusAI/AGENTS.md
T

239 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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. Переменные окружения (главное)
```bash
# 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. Команды
```bash
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.
### Локальный запуск админки в этой среде (фоновые процессы реапятся)
```bash
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`, тип в `admin` `KNOWN_TYPES` при необходимости.
- **Новый инструмент** → схема в `schemas.ts`, ветка в `Handlers.handle`, описание в `tools.ts`.
- **Новое поле журнала** → миграция в `Journal.init`, обновить `GenerationRecord` и SELECT-колонки в `getById/list`.
- **UI** → `public/index.html` (разметка/CSS) + `public/app.js` (логика). Сборки нет — правки применяются сразу.