- Light/dark theme toggle persisted in localStorage - Gallery pagination: page size 50/100/150/200 with prev/next - Fullscreen modal viewer (image/video/audio) with metadata and delete - Delete content: DELETE /api/generations/:id removes file (media-dir guarded) and journal record; added Journal.deleteById - Provider types now include openai - HTML output escaping in UI
185 lines
9.8 KiB
Markdown
185 lines
9.8 KiB
Markdown
# NexusAI — универсальный MCP-сервис для агентов
|
||
|
||
Единый MCP-сервер (TypeScript, stdio), дающий агентам провайдер-абстрагированный доступ к:
|
||
|
||
- **TTS** — озвучка текста
|
||
- **STT** — распознавание речи (локально, faster-whisper)
|
||
- **Изображения** — генерация и правка
|
||
- **Видео** — text→video / image→video
|
||
- **Музыка** — генерация треков
|
||
- **Веб-поиск** — ответ с цитатами (search-augmented модели)
|
||
|
||
Первый провайдер — **NordRouter**. Архитектура расширяемая: RouterAI, GPTunnel, OpenRouter и облачные STT добавляются как новые адаптеры без изменения MCP-инструментов. Каждая операция логируется в SQLite (провайдер, модель, стоимость, статус, файл) — основа для будущей веб-админки.
|
||
|
||
## Структура
|
||
|
||
```
|
||
NexusAI/
|
||
├── packages/
|
||
│ ├── core/ # провайдеры, media-storage, journal, ключи, типы, registry
|
||
│ ├── mcp-server/ # stdio MCP-сервер (10 инструментов)
|
||
│ └── admin/ # веб-админка (Fastify + лёгкий фронт)
|
||
├── sidecar/ # faster-whisper STT (PEP 723, запуск через uv)
|
||
├── .env.example
|
||
└── .opencode/opencode.json
|
||
```
|
||
|
||
## Установка
|
||
|
||
```bash
|
||
cd NexusAI
|
||
npm install
|
||
npm run build
|
||
```
|
||
|
||
> `better-sqlite3` собирается нативно. Если сборка прервалась: `npm rebuild better-sqlite3`.
|
||
|
||
Для STT нужен [`uv`](https://docs.astral.sh/uv/) и `ffmpeg`. Зависимости whisper `uv` ставит автоматически при первом запуске.
|
||
|
||
## Подключение к opencode
|
||
|
||
`.opencode/opencode.json`:
|
||
|
||
```json
|
||
{
|
||
"mcp": {
|
||
"nexusai": {
|
||
"type": "local",
|
||
"command": ["node", "/абс/путь/NexusAI/packages/mcp-server/dist/index.js"],
|
||
"env": {
|
||
"NORDROUTER_API_KEY": "sk-...",
|
||
"NEXUS_MEDIA_DIR": "/абс/путь/NexusAI/media-output",
|
||
"NEXUS_STT_MODEL": "small"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
Переменные окружения — см. `.env.example`.
|
||
|
||
## Инструменты
|
||
|
||
| Инструмент | Назначение |
|
||
|---|---|
|
||
| `nexus_tts` | текст → речь (`audio/elevenlabs-tts`, `audio/gemini-3.1-flash-tts`, …) |
|
||
| `nexus_image` | генерация/правка изображений (`image/*`) |
|
||
| `nexus_video` | видео из текста/фото (`video/*`) |
|
||
| `nexus_music` | музыка (`music/*`) |
|
||
| `nexus_stt` | речь → текст (локально) |
|
||
| `nexus_web_search` | веб-поиск с цитатами (`perplexity/sonar*`, `search/*`) |
|
||
| `nexus_media_models` | список моделей провайдера (id, тип, цена) |
|
||
| `nexus_media_estimate` | оценка стоимости до запуска |
|
||
| `nexus_media_upload` | загрузка исходника (фото/аудио/видео) |
|
||
| `nexus_usage_stats` | траты и история из журнала |
|
||
|
||
### Единый формат вызова
|
||
|
||
```json
|
||
{
|
||
"provider": "nordrouter",
|
||
"model": "audio/elevenlabs-tts",
|
||
"input": { "text": "Привет, мир", "voice": "James · Husky, Engaging and Bold" },
|
||
"output": { "save": true, "embed": false }
|
||
}
|
||
```
|
||
|
||
- `provider` опционален (default из `NEXUS_DEFAULT_PROVIDER`).
|
||
- `input` пропускает любые модель-специфичные поля (`aspect_ratio`, `resolution`, `duration`, `sound`, …).
|
||
- `output.save` — сохранять файл локально (по умолчанию да).
|
||
- `output.embed` — встроить image/audio прямо в ответ MCP (с лимитом размера).
|
||
|
||
### Единый формат ответа
|
||
|
||
```json
|
||
{
|
||
"provider": "nordrouter",
|
||
"capability": "tts",
|
||
"model": "audio/elevenlabs-tts",
|
||
"status": "done",
|
||
"file_path": "/.../media-output/audio-...wav",
|
||
"mime_type": "audio/wav",
|
||
"result_url": "https://nordrouter.com/media/file/...",
|
||
"result_url_2": null,
|
||
"cost_usd": 0.0049
|
||
}
|
||
```
|
||
|
||
`result_url_2` заполняется для музыкальных моделей, отдающих два трека (Suno).
|
||
|
||
## Как это работает
|
||
|
||
- **Media** асинхронно: `POST /media/generate` → polling `GET /media/job/:id` → скачивание `result_url` в `NEXUS_MEDIA_DIR`. Таймаут зависит от типа (картинки — быстро, видео/музыка — до минут).
|
||
- **Web search** через OpenAI-совместимый `/v1/chat/completions` с sonar/`search/*`; цитаты берутся из `message.annotations[].url_citation`.
|
||
- **STT** локально: TS запускает `uv run sidecar/stt_server.py`; faster-whisper декодирует аудио (mp3/wav/ogg/m4a/webm) и возвращает `{text, language, segments}`.
|
||
- **Журнал** SQLite (`nexus.sqlite` в media-dir) пишет каждую операцию; `nexus_usage_stats` агрегирует траты.
|
||
- MCP работает по stdio — **все логи идут в stderr**.
|
||
|
||
## Ключи провайдеров
|
||
|
||
Ключи можно задавать двумя способами, с приоритетом **env → БД**:
|
||
|
||
1. **Env (приоритет)** — `NORDROUTER_API_KEY` (и `<NAME>_API_KEY` для других провайдеров). Просто для CI и локального запуска.
|
||
2. **Зашифрованная БД** — управляется из будущей админки. Ключи хранятся в SQLite, зашифрованные **AES-256-GCM** на мастер-ключе `NEXUS_MASTER_KEY` (сам мастер-ключ — только в env, в БД его нет).
|
||
|
||
Свойства слоя:
|
||
- В git/логи/БД сырой ключ не попадает; в UI показывается только маска `sk-…last4`.
|
||
- Бэкап БД не раскрывает ключи (нужен мастер-ключ).
|
||
- Ротация `NEXUS_MASTER_KEY` инвалидирует зашифрованные ключи — их нужно ввести заново.
|
||
- Без `NEXUS_MASTER_KEY` работают только env-ключи (сервер предупреждает в stderr).
|
||
|
||
Программное управление (пока до админки) — через `@nexusai/core`:
|
||
|
||
```ts
|
||
import { KeyCipher, ProvidersStore } from '@nexusai/core';
|
||
const cipher = new KeyCipher(process.env.NEXUS_MASTER_KEY);
|
||
const store = new ProvidersStore('./media-output/nexus.sqlite', cipher);
|
||
store.upsert({ name: 'nordrouter', type: 'nordrouter', apiKey: 'sk-...', isDefault: true });
|
||
store.setEnabled('nordrouter', true);
|
||
store.listViews(); // masked view for UI
|
||
```
|
||
|
||
Генерация мастер-ключа: `openssl rand -hex 32`.
|
||
|
||
## STT в ограниченных сетях
|
||
|
||
faster-whisper скачивает модель с HuggingFace. Если `huggingface.co` недоступен:
|
||
|
||
- задать зеркало: `HF_ENDPOINT=https://hf-mirror.com`
|
||
- либо предзагрузить модель и указать путь: `NEXUS_STT_MODEL=/abs/model-dir` + `NEXUS_STT_LOCAL_ONLY=1`
|
||
- либо кэш-директорию: `NEXUS_STT_DOWNLOAD_ROOT=./models`
|
||
|
||
## Веб-админка
|
||
|
||
Отдельный пакет `@nexusai/admin` (Fastify + одностраничный фронт) на общем `core`. Читает тот же SQLite и media-каталог.
|
||
|
||
```bash
|
||
npm run build
|
||
NEXUS_MASTER_KEY=$(openssl rand -hex 32) \
|
||
NEXUS_MEDIA_DIR=./media-output \
|
||
npm run admin
|
||
# → http://127.0.0.1:4123
|
||
```
|
||
|
||
Возможности:
|
||
- **Тема** — переключатель светлая/тёмная (сохраняется в localStorage).
|
||
- **Потребление** — дашборд трат: суммарно, по провайдерам, по возможностям, по дням (из журнала).
|
||
- **Галерея** — просмотр сгенерированного контента (изображения/аудио/видео), фильтр по возможности, пагинация 50/100/150/200, модальное окно на весь экран, удаление контента (файл + запись).
|
||
- **Провайдеры** — добавить/включить/выключить, назначить default, ввести ключ (шифруется), **проверить ключ** реальным вызовом, типы `nordrouter` и `openai`. Ключи показываются только маской.
|
||
|
||
Переменные: `NEXUS_ADMIN_HOST` (default `127.0.0.1`), `NEXUS_ADMIN_PORT` (default `4123`), `NEXUS_MASTER_KEY` (для ключей).
|
||
|
||
> Важно: изменения провайдеров/ключей в админке подхватываются MCP-сервером **при рестарте** (hot-reload — в планах). По умолчанию админка слушает только localhost.
|
||
|
||
## Расширение: новый провайдер
|
||
|
||
1. Реализовать `MediaProvider` / `SearchProvider` / `SttProvider` из `@nexusai/core`.
|
||
2. Зарегистрировать в `packages/mcp-server/src/context.ts`.
|
||
|
||
Инструменты и схемы менять не нужно — они провайдер-агностичны.
|
||
|
||
## Roadmap
|
||
|
||
- Фаза 2: веб-админка (отдельный пакет `admin`, Hono/Fastify + лёгкий фронт) на общем `core`: галерея контента, дашборд трат, управление провайдерами.
|
||
- Доп. провайдеры: RouterAI, GPTunnel, OpenRouter; облачный STT.
|