# Застосування production-patch AgroMatrix/Stepan на НОДА1 Патч: `agromatrix_stepan_noda1_prod.patch` — тільки зміни для стабілізації Степана/AgroMatrix. Без vision/router/інших агентів. --- ## Передумови - Доступ по SSH на НОДА1 (144.76.224.179). - Репозиторій на сервері: `/opt/microdao-daarion` (або ваш `DEPLOY_ROOT`). - На сервері є каталоги: `gateway-bot/`, `crews/`, `packages/agromatrix-tools/`. --- ## Крок 1 — Завантаження patch на НОДА1 **Варіант A (з локальної машини):** ```bash scp /шлях/до/agromatrix_stepan_noda1_prod.patch USER@144.76.224.179:/opt/microdao-daarion/ ``` **Варіант B (якщо patch уже в репо):** ```bash ssh USER@144.76.224.179 "cd /opt/microdao-daarion && git pull && git show HEAD:agromatrix_stepan_noda1_prod.patch > agromatrix_stepan_noda1_prod.patch" # або просто скопіювати вміст файла в репо на сервер ``` **Варіант C (вміст патчу вставлений вручну):** На сервері створити файл `/opt/microdao-daarion/agromatrix_stepan_noda1_prod.patch` з повним вмістом unified diff. --- ## Крок 2 — Застосування patch (git apply) На НОДА1: ```bash cd /opt/microdao-daarion # Переконатися, що немає незакомічених змін у задіяних файлах (або зробити backup) git status # Сухий прогон (перевірка, що патч застосується без конфліктів) git apply --check agromatrix_stepan_noda1_prod.patch # Застосувати git apply agromatrix_stepan_noda1_prod.patch ``` Якщо `git apply --check` повертає помилку (наприклад, через відмінний базовий коміт), можна спробувати: ```bash git apply --reject --whitespace=fix agromatrix_stepan_noda1_prod.patch # і вручну розв’язати файли *.rej, якщо з’являться. ``` --- ## Крок 3 — Rebuild і запуск gateway ```bash cd /opt/microdao-daarion # Зібрати образ gateway і запустити контейнер docker compose -f docker-compose.node1.yml up -d --build gateway ``` Сервіс у compose називається `gateway`, контейнер — `dagi-gateway-node1`. Якщо використовуєте інший compose-файл або ім’я сервісу, підставте їх. --- ## Крок 4 — Перевірка health ```bash # Локально на сервері curl -s http://localhost:9300/health curl -s http://localhost:9300/ # Ззовні (якщо порт 9300 відкритий) curl -s http://144.76.224.179:9300/health ``` Очікується: HTTP 200 і JSON зі статусом сервісу. --- ## Крок 5 — Перевірка логів (NameError / ImportError) ```bash docker logs dagi-gateway-node1 2>&1 | tail -100 docker logs dagi-gateway-node1 2>&1 | grep -E "NameError|ImportError|Stepan mode|Stepan inproc|Stepan disabled" ``` - Має з’явитися рядок типу `Stepan mode=inproc (AGX_STEPAN_MODE)` після першого звернення до AgroMatrix або при старті (залежно від реалізації логу). - Не повинно бути `NameError: has_recent_interaction` або `ImportError` через crews/agromatrix_tools після коректного монтування та env. Якщо бачите `Stepan disabled` — перевірте `AGX_REPO_ROOT`, наявність томів `crews` і `packages/agromatrix-tools` у compose та що в контейнері є відповідні каталоги. --- ## Крок 6 — Smoketests 1. **Doc follow-up (раніше 500)** У Telegram-чаті агента AgroMatrix: надіслати документ, потім текстове питання по ньому. Очікується: відповідь без HTTP 500 (відповідь з RAG або повідомлення про відсутність відповіді в документі). 2. **Оператор: /whoami** Від користувача з `user_id` у `AGX_OPERATOR_IDS` (або в чаті з `AGX_OPERATOR_CHAT_ID`): надіслати `/whoami`. Очікується: відповідь від Степана (whoami), не звичайний pipeline. 3. **Оператор: звичайний текст без slash** Від того ж оператора: надіслати текст без слеша (наприклад «привіт»). Очікується: обробка через Степана (handle_stepan_message), не fallback у звичайний Router pipeline. 4. **Не-оператор: звичайний текст** Від користувача, який не входить до операторів: звичайне повідомлення. Очікується: обробка стандартним Router pipeline, **не** через Степана. Перед тестами 2–4 переконайтеся, що в env задані `AGX_OPERATOR_IDS` (та за потреби `AGX_OPERATOR_CHAT_ID`). --- ## Rollback Якщо потрібно повернути стан до патчу: ```bash cd /opt/microdao-daarion # Скасувати зміни в робочій копії (повернути файли до останнього коміту) git checkout HEAD -- gateway-bot/http_api.py gateway-bot/services/doc_service.py gateway-bot/app.py gateway-bot/gateway_boot.py crews/agromatrix_crew/operator_commands.py docker-compose.node1.yml rm -f gateway-bot/gateway_boot.py # Перезібрати і перезапустити gateway docker compose -f docker-compose.node1.yml up -d --build gateway ``` Або ж повернути весь репо до попереднього коміту і перезібрати образ: ```bash git reset --hard HEAD docker compose -f docker-compose.node1.yml up -d --build gateway ``` --- ## Env на НОДА1 (без секретів) Переконайтеся, що в середовищі gateway (compose або .env) задані: - `AGX_STEPAN_MODE=inproc` (за замовчуванням) - `AGX_REPO_ROOT=/app` (в контейнері; на хості відповідає вашому `DEPLOY_ROOT` у путях томів) - `AGX_OPERATOR_IDS=***` — список Telegram user_id операторів (через кому) - `AGX_OPERATOR_CHAT_ID=***` — опційно, обмеження чату для операторів - `OPENAI_API_KEY=***` — потрібен для inproc Степана Секрети не виводьте в логи; у документації позначайте як `KEY=***`. --- ## A. Імпорт gateway_boot (fail-closed) У контейнері CMD: `WORKDIR /app/gateway-bot` і `python -m uvicorn app:app`. Тому поточний каталог для імпортів — `/app/gateway-bot`. У `app.py` використовується **`import gateway_boot`** (модуль у тій самій директорії, що й `app.py`), тож Python резолвить `/app/gateway-bot/gateway_boot.py`. Це коректно при volume `${DEPLOY_ROOT}/gateway-bot:/app/gateway-bot:ro`. **Швидкий тест у контейнері (CWD /app/gateway-bot, як у uvicorn):** ```bash ssh root@144.76.224.179 'docker exec dagi-gateway-node1 sh -c "cd /app/gateway-bot && python3 -c \"import gateway_boot; print(\\\"gateway_boot OK\\\")\""' ``` Очікується: `gateway_boot OK`. Якщо тут `ImportError` — `STEPAN_IMPORTS_OK` ніколи не стане `True` і Степан залишиться тихо вимкненим. --- ## B. Volume-монтування crews і packages/agromatrix-tools У `docker-compose.node1.yml` томи змонтовані **в контейнері** так: - `${DEPLOY_ROOT:-.}/crews` → **`/app/crews`** - `${DEPLOY_ROOT:-.}/packages/agromatrix-tools` → **`/app/packages/agromatrix-tools`** На хості це може бути `/opt/microdao-daarion/crews`, але в контейнері завжди `/app/crews` та `/app/packages/agromatrix-tools`. **Перевірка наявності в контейнері:** ```bash ssh root@144.76.224.179 "docker exec dagi-gateway-node1 ls -la /app/crews | head" ssh root@144.76.224.179 "docker exec dagi-gateway-node1 ls -la /app/packages/agromatrix-tools | head" ``` Очікується: список файлів/каталогів (наприклад `agromatrix_crew` у crews, пакет у agromatrix-tools). Якщо `No such file or directory` — томи не змонтовані або compose не оновлено після патчу. --- ## PYTHONPATH у контейнері (crews і agromatrix_tools) Щоб працювало `from crews.agromatrix_crew.run import ...`, у `sys.path` має бути **`/app`** (батько каталога `crews`), а не `/app/crews`. Щоб працювало `import agromatrix_tools`, у `sys.path` має бути **`/app/packages/agromatrix-tools`** (батьківська директорія пакета). У патчі задано: `PYTHONPATH=/app:/app/packages/agromatrix-tools`. При потребі можна додати `/app/gateway-bot`, але uvicorn і так додає робочу директорію при `python -m uvicorn app:app`. **Практичний контроль після деплою:** ```bash ssh root@144.76.224.179 "docker exec dagi-gateway-node1 sh -c 'env | grep -E \"^PYTHONPATH=\" || true'" ssh root@144.76.224.179 "docker exec dagi-gateway-node1 sh -c 'cd /app/gateway-bot && python3 -c \"import sys; print(\\\"\\n\\\".join(sys.path))\"' | head -20" ``` --- ## Читання STEPAN_IMPORTS_OK (без “заморожування”) Щоб прапорець оновлювався після startup, усі місця мають робити **`import gateway_boot`** і читати **`gateway_boot.STEPAN_IMPORTS_OK`** (або `getattr(gateway_boot, "STEPAN_IMPORTS_OK", False)`). **Не використовувати** `from gateway_boot import STEPAN_IMPORTS_OK` — значення “заморозиться” на момент імпорту і не відобразиться після встановлення в `app.py`. **Grep-контроль на сервері:** ```bash ssh root@144.76.224.179 "cd /opt/microdao-daarion && grep -Rn 'from gateway_boot import' gateway-bot || true" ssh root@144.76.224.179 "cd /opt/microdao-daarion && grep -Rn 'import gateway_boot' gateway-bot" ``` Очікується: немає збігів для `from gateway_boot import`; є лише `import gateway_boot` (у app.py та http_api.py). --- ## Compose env: лапки для AGX_OPERATOR_* Якщо значення задані прямо в YAML (а не тільки через `${...}`), краще завжди брати їх у лапки, щоб уникнути проблем парсингу (зокрема від’ємні chat_id): - `AGX_OPERATOR_CHAT_ID: "-1001234567890"` - `AGX_OPERATOR_IDS: "123,456"` При використанні змінних середовища (`AGX_OPERATOR_IDS=${AGX_OPERATOR_IDS:-}`) лапки ставлять у `.env` або в значенні змінної при експорті. --- ## Мінімальний фінальний чек-лист (після git apply і --build) 1. **Патч застосовано чисто** Перед першим застосуванням: `git apply --check agromatrix_stepan_noda1_prod.patch` (код виходу 0 = можна застосовувати). Після застосування можна перевірити наявність змін, наприклад: ```bash ssh root@144.76.224.179 "cd /opt/microdao-daarion && test -f gateway-bot/gateway_boot.py && grep -q 'import gateway_boot' gateway-bot/app.py && echo OK" ``` 2. **Rebuild gateway** ```bash ssh root@144.76.224.179 "cd /opt/microdao-daarion && docker compose -f docker-compose.node1.yml up -d --build gateway" ``` 3. **Stepan реально увімкнувся (логи)** ```bash ssh root@144.76.224.179 "docker logs dagi-gateway-node1 --since 10m 2>&1 | grep -E 'Stepan mode|STEPAN_IMPORTS_OK|Stepan disabled|Stepan inproc|ImportError|ModuleNotFoundError' | tail -30" ``` Очікується: згадка `Stepan mode=inproc` (після першого звернення до AgroMatrix) і **відсутність** "Stepan disabled". У логах після старту має з’явитися рядок **`STEPAN_IMPORTS_OK=True`** (додано в app.py після встановлення прапорця), тоді fail-closed коректно пройшов. 4. **Оператори задані (обов’язково для "людського" режиму)** ```bash ssh root@144.76.224.179 "docker exec dagi-gateway-node1 env | grep -E '^AGX_OPERATOR_IDS=|^AGX_OPERATOR_CHAT_ID=' | sed 's/=.*/=***/'" ``` Очікується: хоча б `AGX_OPERATOR_IDS=***` (значення приховуються). Якщо змінних немає — операторські повідомлення не підуть у Степана. 5. **Опційно: резолв gateway_boot і томи (див. блоки A і B вище).** --- ## Нюанс: AGX_STEPAN_MODE=http Режим `AGX_STEPAN_MODE=http` зараз повертає 501 stub (Степан "офіційно" недоступний). Щоб у проді не випадково вимкнути Степана: - У **docker-compose.node1.yml** у секції gateway залишити явно **`AGX_STEPAN_MODE=inproc`** (або не задавати — тоді default з патчу inproc). - Режим `http` використовувати лише в тестових середовищах, коли з’явиться реалізація клієнта crewai-service. --- ## Фрагмент патчу для перевірки імпортів (app.py + gateway_boot) Нижче — заголовки diff і контекст імпортів у `app.py`, щоб переконатися, що `gateway_boot` імпортується як модуль з тієї самої директорії, де лежить `app.py`, і резолвиться в контейнері. **gateway-bot/app.py (фрагмент після патчу):** ```python import logging import os import sys from pathlib import Path from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from http_api import router as gateway_router from http_api_doc import router as doc_router import gateway_boot # <-- той самий каталог: /app/gateway-bot/gateway_boot.py ``` **gateway-bot/gateway_boot.py (новий файл):** ```python """ Boot-time state for Gateway. Set by app startup; read by http_api. """ STEPAN_IMPORTS_OK = False ``` У контейнері: `WORKDIR /app/gateway-bot`, volume монтує `gateway-bot` у `/app/gateway-bot`, тому `app.py` і `gateway_boot.py` лежать поруч — `import gateway_boot` коректно знаходить модуль. Після startup у `startup_stepan_check()` при успішному імпорті виконується `gateway_boot.STEPAN_IMPORTS_OK = True` і лог `STEPAN_IMPORTS_OK=True`; інакше прапорець залишається `False` (fail-closed). У **http_api** використовується лише `import gateway_boot` і `getattr(gateway_boot, "STEPAN_IMPORTS_OK", False)` — не `from gateway_boot import ...`, щоб значення читалося в runtime після оновлення в app.py. --- ## Підсумок перевірок “готово до прод” Мінімально достатньо три пункти: 1. **Логи після старту** (Stepan увімкнено, без ImportError): ```bash ssh root@144.76.224.179 "docker logs dagi-gateway-node1 --since 10m 2>&1 | grep -E 'Stepan mode|STEPAN_IMPORTS_OK|Stepan disabled|ImportError|ModuleNotFoundError' | tail -120" ``` 2. **Volumes на місці** (`/app/crews`, `/app/packages/agromatrix-tools`): ```bash ssh root@144.76.224.179 "docker exec dagi-gateway-node1 ls -la /app/crews | head" ssh root@144.76.224.179 "docker exec dagi-gateway-node1 ls -la /app/packages/agromatrix-tools | head" ``` 3. **Env операторів і режиму** (масковано): ```bash ssh root@144.76.224.179 "docker exec dagi-gateway-node1 env | grep -E '^AGX_OPERATOR_IDS=|^AGX_OPERATOR_CHAT_ID=|^AGX_STEPAN_MODE=' | sed 's/=.*/=***/'" ``` Якщо ці три пункти зелені — патч відповідає fail-closed моделі і не повинен давати “тихих” падінь gateway при відсутніх crews/agromatrix-tools.