# 🔍 Як працює НОДА2 - Детальний розбір **Дата:** 2026-01-12 **Статус:** ✅ Повний аналіз роботи НОДА2 --- ## 🏗️ Архітектура НОДА2 ### Основні компоненти ``` ┌─────────────────────────────────────────────────────────┐ │ НОДА2 (MacBook M4 Max) │ ├─────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ Router │──────│ Swapper │ │ │ │ (9102) │ │ (8890) │ │ │ └──────┬────────┘ └──────┬──────┘ │ │ │ │ │ │ │ │ │ │ │ ┌────────▼────────┐ │ │ │ │ Ollama │ │ │ │ │ (host:11434) │ │ │ │ └─────────────────┘ │ │ │ │ │ └──────────────┬─────────────────┐ │ │ │ │ │ │ ┌─────▼─────┐ ┌─────▼─────┐ │ │ │ NATS │ │ Gateway │ │ │ │ (4222) │ │ (9300) │ │ │ └────────────┘ └────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ ``` --- ## 🔄 Як працює Router ### Основні функції Router 1. **Маршрутизація запитів:** - Отримує запити на порту 9102 - Аналізує запит та визначає провайдера - Маршрутизує до відповідного сервісу 2. **Інтеграція з Swapper:** - Перевіряє доступність Swapper через `/health` - Завантажує моделі через `/models/{name}/load` - Отримує список моделей через `/models` - **Проблема:** Намагається викликати `/v1/chat/completions` (не існує) 3. **Провайдери (17 штук):** - LLM провайдери (9 штук) - DevTools провайдери (5 штук) - Інші провайдери (3 штуки) ### Конфігурація Router **Змінні середовища:** ```bash SWAPPER_URL=http://192.168.1.33:8890 # IP адреса хоста NATS_URL=nats://nats:4222 STT_URL=http://192.168.1.33:8895 VISION_URL=http://192.168.1.33:11434 OCR_URL=http://192.168.1.33:8896 ``` **Проблема:** Використовує IP замість Docker service name **Рекомендація:** Змінити на `http://swapper-service:8890` --- ## 🔄 Як працює Swapper ### Основні функції Swapper 1. **Динамічне завантаження моделей:** - Завантажує моделі з Ollama за запитом - Вивантажує моделі для звільнення пам'яті - Управляє станом моделей (loaded/unloaded) 2. **Single-active режим:** - Тільки одна модель активна одночасно - Автоматичне вивантаження при завантаженні нової - Timeout: 30 секунд для swap 3. **Інтеграція з Ollama:** - Доступ через `host.docker.internal:11434` - Викликає Ollama API для генерації - Відстежує стан моделей ### API Endpoints Swapper **Основні:** - `GET /health` - перевірка здоров'я - `GET /status` - статус сервісу - `GET /models` - список моделей - `GET /models/{name}` - інформація про модель - `POST /models/{name}/load` - завантажити модель - `POST /models/{name}/unload` - вивантажити модель **Cabinet API:** - `GET /api/cabinet/swapper/status` - статус для кабінету - `GET /api/cabinet/swapper/models` - моделі для кабінету - `GET /api/cabinet/swapper/metrics/summary` - метрики **⚠️ Відсутній:** `/v1/chat/completions` (Router намагається викликати) --- ## 🔗 Інтеграція Router ↔ Swapper ### Поточна реалізація **З коду Router (`main.py`):** 1. **Перевірка Swapper:** ```python health_resp = await http_client.get(f"{SWAPPER_URL}/health") ``` 2. **Завантаження моделі:** ```python load_resp = await http_client.post( f"{SWAPPER_URL}/load", json={"model": model_name} ) ``` 3. **Генерація (проблема):** ```python # Router намагається викликати: generate_resp = await http_client.post( f"{VISION_URL}/api/generate", # Прямо до Ollama! ... ) ``` 4. **Отримання моделей:** ```python resp = await http_client.get(f"{SWAPPER_URL}/models") ``` ### Проблема інтеграції **Що не працює:** - Router намагається викликати `/v1/chat/completions` на Swapper - Swapper не має такого endpoint - Router використовує `VISION_URL` для генерації (прямо до Ollama) **Що працює:** - ✅ Health check - ✅ Завантаження моделей - ✅ Отримання списку моделей - ❌ Генерація через Swapper (Router йде напряму до Ollama) --- ## 🔄 Потік запиту (поточна реалізація) ### Варіант 1: Router → Ollama (прямий) ``` Клієнт ↓ POST /route Router (9102) ↓ [визначає модель] ↓ POST /api/generate Ollama (host:11434) ↓ [генерує] Router ↓ Клієнт ``` **Проблема:** Обходить Swapper, не використовує динамічне завантаження ### Варіант 2: Router → Swapper → Ollama (ідеальний) ``` Клієнт ↓ POST /route Router (9102) ↓ [визначає модель] ↓ POST /models/{name}/load Swapper (8890) ↓ [перевіряє модель] ↓ [завантажує якщо потрібно] Ollama (host:11434) ↓ [генерує] Swapper ↓ Router ↓ Клієнт ``` **Проблема:** Swapper не має `/generate` або `/v1/chat/completions` endpoint --- ## 📊 Поточний стан ### ✅ Що працює 1. **Router:** - ✅ Запущений, healthy - ✅ Підключений до NATS - ✅ Може викликати Swapper для завантаження моделей - ✅ Може отримувати список моделей з Swapper 2. **Swapper:** - ✅ Запущений, healthy - ✅ Активна модель `gpt-oss-latest` - ✅ Доступ до Ollama працює - ✅ Може завантажувати/вивантажувати моделі 3. **Ollama:** - ✅ Працює на хості - ✅ 9 моделей доступні - ✅ Модель `gpt-oss:latest` завантажена ### ⚠️ Що не працює повністю 1. **Генерація через Swapper:** - Router намагається викликати `/v1/chat/completions` - Swapper не має такого endpoint - Router йде напряму до Ollama 2. **SWAPPER_URL:** - Використовує IP адресу замість service name - Може не працювати при зміні IP 3. **NATS Unhealthy:** - Може впливати на messaging --- ## 🔧 Рекомендації для виправлення ### 1. Додати endpoint в Swapper **Потрібно додати в Swapper:** ```python @app.post("/v1/chat/completions") async def chat_completions(request: ChatRequest): # Завантажити модель якщо потрібно # Викликати Ollama # Повернути відповідь ``` ### 2. Виправити SWAPPER_URL в Router **Змінити:** ```yaml # Замість: SWAPPER_URL=http://192.168.1.33:8890 # Використати: SWAPPER_URL=http://swapper-service:8890 ``` ### 3. Оновити Router для використання Swapper **Змінити генерацію:** ```python # Замість прямого виклику Ollama: generate_resp = await http_client.post(f"{VISION_URL}/api/generate", ...) # Викликати Swapper: generate_resp = await http_client.post( f"{SWAPPER_URL}/v1/chat/completions", ... ) ``` --- ## 📝 Висновок **НОДА2 працює наступним чином:** 1. ✅ **Router** - маршрутизує запити, має 17 провайдерів 2. ✅ **Swapper** - управляє моделями, активна модель `gpt-oss-latest` 3. ✅ **Ollama** - локальний LLM runtime, 9 моделей доступні 4. ⚠️ **Інтеграція** - частково працює, потрібні доопрацювання **Поточна реалізація:** - Router може завантажувати моделі через Swapper - Router йде напряму до Ollama для генерації - Swapper не використовується для генерації **Ідеальна реалізація:** - Router → Swapper → Ollama (повний потік) - Swapper керує всіма LLM операціями - Динамічне завантаження моделей за потреби --- **Оновлено:** 2026-01-12 **Статус:** ✅ Аналіз завершено, виявлено проблеми інтеграції