feat: STT transcripts, model catalog+fuzzy resolve, UI audio player & auto-refresh
Core: - ModelCatalog: periodic background sync of provider models + fuzzy resolve. Misspelled model ids map to the nearest match (cheapest on ties); no match raises ModelResolutionError with cheapest-first suggestions for the agent. mcp-server: - nexus_stt now writes the transcript to a Markdown file in the media dir so it appears in the gallery and is readable in the modal. - media handler resolves model via catalog before generating; returns a note when substituting, or a BAD_REQUEST with available models when nothing fits. - context wires a ModelCatalog per media provider with auto-sync. admin: - /media serves correct Content-Type (incl. text/markdown for transcripts). - Gallery: audio play button on cards, dedicated audio player in modal, text/transcript tiles that fetch and render the .md in the modal. - Header: auto-refresh toggle (5/10/30s) that refreshes the active tab without a full page reload; pauses while a modal is open.
This commit is contained in:
@@ -0,0 +1,372 @@
|
||||
# NexusAI — полное резюме тестов
|
||||
|
||||
**Дата:** 2026-08-18
|
||||
**Репозиторий:** `/home/user/moder/mcp/NexusAI`
|
||||
**Ветка:** `main`
|
||||
**Админка:** http://127.0.0.1:4123
|
||||
|
||||
---
|
||||
|
||||
## 1. Общая архитектура
|
||||
|
||||
Проект — универсальный MCP-сервис для агентов:
|
||||
|
||||
- `packages/core` — журнал SQLite, шифрование ключей, медиа-хранилище, провайдеры.
|
||||
- `packages/mcp-server` — MCP stdio-сервер с инструментами `nexus_*`.
|
||||
- `packages/admin` — Fastify web UI (дашборд, галерея, управление провайдерами).
|
||||
- `sidecar/stt_server.py` — локальный faster-whisper sidecar.
|
||||
|
||||
Провайдер по умолчанию — **NordRouter** (`https://nordrouter.com`).
|
||||
Ключи хранятся в SQLite, зашифрованные мастер-ключом `NEXUS_MASTER_KEY`.
|
||||
|
||||
### Переменные окружения, использованные в тестах
|
||||
|
||||
```bash
|
||||
NEXUS_MEDIA_DIR=/tmp/opencode/nexus-media
|
||||
NEXUS_MASTER_KEY=fixed-test-master-key-1234567890
|
||||
NEXUS_ADMIN_PORT=4123
|
||||
NEXUS_STT_MODEL=/tmp/opencode/whisper-tiny
|
||||
NEXUS_STT_LOCAL_ONLY=1
|
||||
NEXUS_STT_DEVICE=cpu
|
||||
HF_ENDPOINT=https://hf-mirror.com
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Запуск админки
|
||||
|
||||
Админ-сервер поднимался в detached `tmux`-сессии, потому что фоновые процессы в bash-инструменте реапятся.
|
||||
|
||||
```bash
|
||||
tmux kill-session -t nexusadmin 2>/dev/null; sleep 1
|
||||
tmux new-session -d -s nexusadmin -c /home/user/moder/mcp/NexusAI \
|
||||
"export NEXUS_MEDIA_DIR=/tmp/opencode/nexus-media NEXUS_MASTER_KEY=fixed-test-master-key-1234567890 NEXUS_ADMIN_PORT=4123; exec node packages/admin/dist/server.js >/tmp/opencode/admin_live.log 2>&1"
|
||||
```
|
||||
|
||||
**Проверка:**
|
||||
|
||||
```text
|
||||
HTTP:200
|
||||
[2026-08-18T02:59:15.659Z] INFO (admin:api) routes registered
|
||||
[2026-08-18T02:59:15.688Z] INFO (admin) NexusAI admin running at http://127.0.0.1:4123
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Тест: веб-поиск «последние новости Барнаула»
|
||||
|
||||
### Скрипт
|
||||
|
||||
Временный: `packages/mcp-server/_search_test.cjs`
|
||||
|
||||
### Вызов
|
||||
|
||||
```javascript
|
||||
const { buildContext } = require('./dist/context.js');
|
||||
const ctx = buildContext();
|
||||
const p = ctx.registry.getSearch('nordrouter');
|
||||
const res = await p.search({ query: 'последние новости Барнаула', recency: 'day', maxResults: 5 });
|
||||
```
|
||||
|
||||
### Статус
|
||||
|
||||
✅ Успех.
|
||||
|
||||
### Сырой ответ ( answer )
|
||||
|
||||
```text
|
||||
Вот самые заметные новости Барнаула за последние сутки по доступным заголовкам и лентам:
|
||||
|
||||
- В Барнауле поддерживают подготовку к отопительному сезону...
|
||||
- В городе идут дожди...
|
||||
- В Барнауле можно бесплатно оформить медкарты...
|
||||
- Показательные выступления служебных собак...
|
||||
- На этой неделе в Барнауле можно сделать флюорографию...
|
||||
- ...
|
||||
```
|
||||
|
||||
### Citations
|
||||
|
||||
1. https://brl.mk.ru/news/
|
||||
2. https://ria.ru/location_Barnaul/
|
||||
3. https://www.alt.kp.ru/
|
||||
4. https://tolknews.ru/
|
||||
5. https://barnaul.org/news/
|
||||
|
||||
### Примечание
|
||||
|
||||
- Вызов был напрямую через адаптер, не через MCP-инструмент, поэтому в журнал не записался.
|
||||
- `costUsd` вернулся `undefined`: NordRouter не возвращает `usage.total_cost` для `perplexity/sonar-pro`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Тест: STT → TTS
|
||||
|
||||
### 4.1 Подготовка STT-модели
|
||||
|
||||
HuggingFace был недоступен (`Broken pipe`), поэтому модель `faster-whisper-tiny` скачана с зеркала ModelScope:
|
||||
|
||||
```bash
|
||||
mkdir -p /tmp/opencode/whisper-tiny
|
||||
cd /tmp/opencode/whisper-tiny
|
||||
for f in config.json configuration.json model.bin tokenizer.json vocabulary.txt README.md; do
|
||||
curl -sL -o "$f" "https://modelscope.cn/models/systran/faster-whisper-tiny/resolve/master/$f"
|
||||
done
|
||||
```
|
||||
|
||||
### 4.2 Скрипты
|
||||
|
||||
- `packages/mcp-server/_stt_test.cjs` — прямой вызов `local` STT.
|
||||
- `packages/mcp-server/_tts_test.cjs` — прямой вызов NordRouter TTS.
|
||||
|
||||
### 4.3 STT-результат
|
||||
|
||||
**Файл:** `/home/user/Музыка/Денис/audio_2025-08-21_12-21-30.ogg` (30K, Ogg)
|
||||
|
||||
```text
|
||||
--- text ---
|
||||
А то с ука в 30 градусной жарее весь трещал передел свистелблять. Каких там только звуков не было под нагрузкой блять.
|
||||
|
||||
language: ru | duration: 7.76
|
||||
```
|
||||
|
||||
### 4.4 TTS-результат
|
||||
|
||||
Модель: `audio/gemini-3.1-flash-tts`, голос `Zephyr`.
|
||||
|
||||
```text
|
||||
file_path: /tmp/opencode/nexus-media/audio-2026-08-18T03-25-00-156Z-v50yby.wav
|
||||
mime_type: audio/wav
|
||||
result_url: https://nordrouter.com/media/file/f987fdf8-5e2b-42c8-9883-6f18f8764452
|
||||
cost_usd: 0.00825
|
||||
```
|
||||
|
||||
### 4.5 Позже: полный цикл через MCP-инструменты
|
||||
|
||||
Использован `Handlers.handle('nexus_stt', ...)` и `Handlers.handle('nexus_tts', ...)`.
|
||||
Записи появились в журнале и галерее.
|
||||
|
||||
**Фикс:** `nexus_stt` больше не сохраняет путь к исходному аудио как `filePath`, иначе галерея пытается отдать файл из `/home/user/Музыка/...` через `/media/` и получает 404.
|
||||
|
||||
Коммит: `c65f910`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Тест: генерация изображений (GPT Image 2, Seedream 5.0 Pro, Gemini 3.1 Flash)
|
||||
|
||||
### Скрипты
|
||||
|
||||
- `packages/mcp-server/_image_models.cjs` — список image-моделей.
|
||||
- `packages/mcp-server/_image_gen.cjs` — генерация тремя моделями.
|
||||
|
||||
### Промпт
|
||||
|
||||
```text
|
||||
лисица кусает мухамор реалистичное фото изображение сделанное на проффисианальную камеру
|
||||
```
|
||||
|
||||
### Результаты
|
||||
|
||||
| Модель | Статус | Файл | Стоимость |
|
||||
|---|---|---|---|
|
||||
| `image/gpt-image-2` | ✅ done | `/tmp/opencode/nexus-media/image-2026-08-18T04-04-28-304Z-izb5rj.png` | `$0` |
|
||||
| `image/gemini-3.1-flash` | ✅ done | `/tmp/opencode/nexus-media/image-2026-08-18T04-04-48-360Z-3gloqp.png` | `$0.011534` |
|
||||
| `image/seedream-5.0-pro` | ❌ failed | — | средства не списаны |
|
||||
|
||||
### Ошибка Seedream
|
||||
|
||||
```text
|
||||
NexusError: Генерация временно недоступна — попробуйте ещё раз. Средства не списаны.
|
||||
```
|
||||
|
||||
Повторный запрос дал тот же результат.
|
||||
|
||||
### Сырой ответ (Gemini 3.1 Flash)
|
||||
|
||||
Изображение: лиса в зелёном лесу с красным мухомором во рту.
|
||||
|
||||
---
|
||||
|
||||
## 6. Тест: видео из картинки (Seedance 1.5 Pro)
|
||||
|
||||
### Скрипты
|
||||
|
||||
- `packages/mcp-server/_video_models.cjs`
|
||||
- `packages/mcp-server/_video_gen.cjs`
|
||||
|
||||
### Параметры
|
||||
|
||||
- Модель: `video/seedance-1.5-pro`
|
||||
- Входное изображение: `https://nordrouter.com/media/file/4da49a3c-3866-44af-8e6c-d9360f5ebab8`
|
||||
- Промпт: `A red fox gently bites and eats a bright red fly agaric mushroom...`
|
||||
- `duration=5`, `aspect_ratio=16:9`, `resolution=720p`, `generate_audio=true`
|
||||
|
||||
### Результат
|
||||
|
||||
```text
|
||||
status: done
|
||||
file_path: /tmp/opencode/nexus-media/video-2026-08-18T04-18-15-277Z-jb2tk9.mp4
|
||||
mime_type: video/mp4
|
||||
result_url: https://nordrouter.com/media/file/3dc0b9e7-5889-4f7b-b431-9087906380f9
|
||||
cost_usd: 0.21875
|
||||
```
|
||||
|
||||
Видео: 720p, 24 fps, AAC stereo — со звуком.
|
||||
|
||||
---
|
||||
|
||||
## 7. Тест: Kling 2.6 i2v с vision-описанием
|
||||
|
||||
### 7.1 Добавлен инструмент `nexus_vision`
|
||||
|
||||
Для полного цикла через NexusAI добавлен MCP-инструмент `nexus_vision`.
|
||||
|
||||
Изменения:
|
||||
|
||||
- `packages/mcp-server/src/schemas.ts` — `VisionSchema`
|
||||
- `packages/mcp-server/src/handlers.ts` — обработчик `vision`
|
||||
- `packages/mcp-server/src/tools.ts` — регистрация инструмента
|
||||
- `packages/core/src/shared/http.ts` — поддержка `FormData`
|
||||
- `packages/core/src/providers/nordrouter/media.ts` — определение MIME по расширению для upload
|
||||
|
||||
Дефолтная vision-модель: `google/gemini-3.1-flash-lite`.
|
||||
|
||||
### 7.2 Скрипты
|
||||
|
||||
- `packages/mcp-server/_video_models.cjs`
|
||||
- `packages/mcp-server/_vision_model_test.cjs` — подбор рабочей vision-модели
|
||||
- `packages/mcp-server/_upload_image.cjs`, `_upload_debug.cjs`, `_upload_json.cjs`, `_upload_dataurl.cjs` — отладка upload
|
||||
- `packages/mcp-server/_kling_test.cjs`, `_kling_retry.cjs`, `_kling_base64.cjs`, `_kling_neutral.cjs`, `_kling_standard.cjs`, `_kling26.cjs`, `_kling26_uploaded.cjs` — тесты Kling
|
||||
|
||||
### 7.3 Подбор vision-модели
|
||||
|
||||
Начальные попытки (`gpt-4o`, `openai/gpt-4o`, `gemini-3.1-flash`) вернули `unknown model`.
|
||||
Список `/v1/models` показал, что у NordRouter модели вида `google/gemini-3.1-flash-lite`, `openai/gpt-5.4-mini` и т.д.
|
||||
|
||||
Рабочая: `google/gemini-3.1-flash-lite`.
|
||||
|
||||
### 7.4 Vision-описание (сырой ответ)
|
||||
|
||||
```text
|
||||
Вот подробное описание сцены, структурированное как профессиональный промпт для генерации видео:
|
||||
|
||||
### Видео-промпт: «Лиса и мухомор в осеннем лесу»
|
||||
|
||||
**Общая сцена и композиция:**
|
||||
Кинематографичный кадр крупным планом, уровень глаз животного...
|
||||
|
||||
**Детали объектов:**
|
||||
* Лиса: густая пушистая шерсть рыжего цвета...
|
||||
* Гриб: классический мухомор...
|
||||
* Окружение: лесная подстилка, мох, опавшие листья...
|
||||
|
||||
**Освещение и цвета:**
|
||||
Мягкий естественный дневной свет...
|
||||
|
||||
**Технические параметры:**
|
||||
Стиль фотоработы National Geographic, 8k, макро-фотография, гиперреализм...
|
||||
Камера: f/2.8, объектив 85 мм...
|
||||
```
|
||||
|
||||
### 7.5 Проблемы с Kling
|
||||
|
||||
Сначала Kling 2.1/2.6 возвращали:
|
||||
|
||||
```text
|
||||
Не удалось сгенерировать. Средства возвращены на баланс.
|
||||
```
|
||||
|
||||
Причина: Kling i2v не принимал прямую ссылку на изображение, сгенерированное через `/media/generate`.
|
||||
Решение: загрузить исходник через `nexus_media_upload` и получить URL вида `https://nordrouter.com/media/file/up-...?raw=1`.
|
||||
|
||||
Также при upload был баг: MIME определялся как `application/octet-stream`, и NordRouter отвечал `HTTP 415`. Починено определением MIME по расширению файла.
|
||||
|
||||
### 7.6 Результат Kling 2.6 i2v
|
||||
|
||||
```text
|
||||
{
|
||||
"provider": "nordrouter",
|
||||
"capability": "video",
|
||||
"model": "video/kling-2.6-i2v",
|
||||
"status": "done",
|
||||
"file_path": "/tmp/opencode/nexus-media/video-2026-08-18T04-48-21-385Z-r5um7w.mp4",
|
||||
"mime_type": "video/mp4",
|
||||
"result_url": "https://nordrouter.com/media/file/3274f543-b954-4db5-9407-975cfa4de7cd",
|
||||
"cost_usd": 0.6875
|
||||
}
|
||||
```
|
||||
|
||||
- Длительность: 5.04 сек
|
||||
- Разрешение: 1440×1440, 24 fps
|
||||
- Аудио: AAC 44.1 kHz stereo
|
||||
- Размер: 13M
|
||||
|
||||
---
|
||||
|
||||
## 8. Ошибки и фиксы
|
||||
|
||||
| Проблема | Причина | Решение | Коммит |
|
||||
|---|---|---|---|
|
||||
| MCP падает без `NEXUS_MASTER_KEY` | `KeyCipher` пытался расшифровать пустой ключ | graceful fallback | `6ff2b27` |
|
||||
| STT-запись даёт битую ссылку в галерее | `filePath` указывал на исходный аудиофайл | `filePath = null` для STT | `c65f910` |
|
||||
| Upload возвращает 415 | MIME не определялся по расширению | таблица `mimeByExt` | `b2f57cc` |
|
||||
| Нет инструмента для vision | не было MCP-инструмента | добавлен `nexus_vision` | `b2f57cc` |
|
||||
| Kling i2v не принимал сгенерированный URL | требует upload-URL `?raw=1` | использован `nexus_media_upload` | — |
|
||||
|
||||
---
|
||||
|
||||
## 9. Итоговая статистика
|
||||
|
||||
```json
|
||||
{
|
||||
"totalCostUsd": 0.950597,
|
||||
"totalCount": 18,
|
||||
"byProvider": [
|
||||
{ "provider": "nordrouter", "count": 17, "costUsd": 0.950597 },
|
||||
{ "provider": "local", "count": 1, "costUsd": 0 }
|
||||
],
|
||||
"byCapability": [
|
||||
{ "capability": "video", "count": 2, "costUsd": 0.90625 },
|
||||
{ "capability": "tts", "count": 4, "costUsd": 0.022813 },
|
||||
{ "capability": "image", "count": 4, "costUsd": 0.021534 },
|
||||
{ "capability": "vision", "count": 1, "costUsd": 0 },
|
||||
{ "capability": "stt", "count": 1, "costUsd": 0 }
|
||||
],
|
||||
"byDay": [
|
||||
{ "day": "2026-08-18", "count": 14, "costUsd": 0.931534 },
|
||||
{ "day": "2026-08-17", "count": 4, "costUsd": 0.019063 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Коммиты
|
||||
|
||||
```text
|
||||
b2f57cc feat(mcp-server): add nexus_vision tool and fix upload MIME detection
|
||||
c65f910 fix(mcp-server): do not store source audio path as STT gallery file
|
||||
1bc3746 Admin UX: light/dark theme, gallery pagination, content modal + delete, openai type
|
||||
6ff2b27 Fix MCP crash when DB key exists without NEXUS_MASTER_KEY
|
||||
679e564 Add admin web UI (Phase 2): spend dashboard, media gallery, provider management
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Ключевые файлы
|
||||
|
||||
- `/tmp/opencode/nexus-media/image-2026-08-18T04-04-48-360Z-3gloqp.png` — Gemini 3.1 Flash (исходник для видео)
|
||||
- `/tmp/opencode/nexus-media/video-2026-08-18T04-18-15-277Z-jb2tk9.mp4` — Seedance 1.5 Pro i2v (со звуком)
|
||||
- `/tmp/opencode/nexus-media/video-2026-08-18T04-48-21-385Z-r5um7w.mp4` — Kling 2.6 i2v (со звуком)
|
||||
- `/tmp/opencode/whisper-tiny/` — локальная STT-модель
|
||||
- `/tmp/opencode/admin_live.log` — лог админки
|
||||
|
||||
---
|
||||
|
||||
## 12. Выводы
|
||||
|
||||
- ✅ MCP-инструменты работают в полном цикле и пишут в журнал/галерею.
|
||||
- ✅ Web search, STT, TTS, image, video, vision — все интегрированы.
|
||||
- ✅ Поддержка конкретных моделей (`model` в запросе) работает.
|
||||
- ⚠️ Некоторые модели временно недоступны (`seedream-5.0-pro`) или требуют upload (`kling-2.6-i2v`).
|
||||
- ⚠️ Расходы на видео значительно выше оценок: Kling 2.6 i2v `$0.6875` против estimated `$0.3438`.
|
||||
Reference in New Issue
Block a user