diff --git a/NexusAI/AGENTS.md b/NexusAI/AGENTS.md new file mode 100644 index 0000000..e37b39d --- /dev/null +++ b/NexusAI/AGENTS.md @@ -0,0 +1,238 @@ +# 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 с авто-синком), 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=/nexus.sqlite # опц. +NEXUS_MASTER_KEY= # шифрование ключей в БД (иначе только 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 +``` + +Приоритет ключа: `_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:;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//`, регистрация в `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` (логика). Сборки нет — правки применяются сразу.