diff --git a/docs/tasks/TASK_PHASE_NODE2_FULL_DAGI_INTEGRATION_v1.md b/docs/tasks/TASK_PHASE_NODE2_FULL_DAGI_INTEGRATION_v1.md new file mode 100644 index 00000000..b736f807 --- /dev/null +++ b/docs/tasks/TASK_PHASE_NODE2_FULL_DAGI_INTEGRATION_v1.md @@ -0,0 +1,370 @@ +# TASK_PHASE_NODE2_FULL_DAGI_INTEGRATION_v1 + +Проєкт: DAARION.city +Нода: NODE2 (MacBook M4 Max) +Мета: зробити так, щоб DAARION MVP бачив **реальний DAGI-стек NODE2**: + +- Swapper з локальними моделями (Ollama, HF); +- DAGI Router та Gateway; +- усі ~50 DAGI-агентів у районах міста; +- коректні метрики в Node Cabinet. + +--- + +## 0. Джерела істини (OBLIGATORY READ) + +Спочатку **прочитати, не редагувати**: + +- `docs/users/nodes/NODE_STATE_node-2-macbook-m4max.md` +- `docs/users/nodes/NODE_STATE_node-1-hetzner-gex44.md` (для порівняння) +- `docs/users/nodes/walkthrough.md` (якщо існує) +- `docs/users/nodes/NODE_GUARDIAN_AND_STEWARD.md` +- `docs/tasks/TASK_PHASE_SWAPPER_NODE_METRICS_AND_UI_v1.md` +- `docs/tasks/TASK_PHASE_NODE2_ROUTER_SWAPPER_ISOLATION_AND_AGENT_DISCOVERY_v1.md` + +Конфігурація DAGI на NODE2 (знайти й використати, не вигадувати заново): + +- `swapper_config_node2.yaml` +- `router_config_node2.yaml` (або подібний) +- `agents_city_mapping.yaml` / `agents/` +- логи в `logs/node2_*` + +Мета цього таска — **під'єднати існуючий стек**, а не створювати новий. + +--- + +## 1. Поточний стан (узагальнення) + +Зафіксувати в коментарях до MR (або в кінці файлу), але орієнтовно: + +- NODE2: + - Swapper: працює на `http://localhost:8890`, статус healthy, 8 моделей; + - DAGI Router: працює на `http://localhost:9102`; + - LLM Proxy: `http://localhost:7007`; + - Gateway з 5 агентами (daarwizz, helion, greenfood, nutra, druid); + - ~50 DAGI-агентів, розкладених по районах (Leadership Hall, System Control, Engineering Lab, Marketing Hub, Finance Office, Web3 District, Security Bunker, Vision Studio, R&D Lab, Memory Vault); + - node-guardian вже шле heartbeat, але МВП не бачить агентів/моделей повністю. + +- NODE1: + - Swapper + Router вже інтегровані в MVP й працюють як еталон. + +--- + +## 2. Scope + +### Включено + +1. **Swapper NODE2 → city-service** + - коректні метрики per-node (healthy, models_loaded/total, swapper_state). + +2. **DAGI Router NODE2 → city-service** + - статус роутера (up/down); + - кількість агентів, список агентів з Router API. + +3. **Agent Registry NODE2** + - записати 50 DAGI-агентів у БД (таблиця `agents` / `node_agents` / аналогічна); + - прив'язка до NODE2 та відповідних MicroDAO. + +4. **API для Node Cabinet** + - `/internal/node/{node_id}/models` + - `/internal/node/{node_id}/router` + - `/internal/node/{node_id}/agents` + (або адаптація існуючих, якщо вони вже є). + +5. **UI Node Cabinet** + - NODE2 має показувати свої: + - Swapper моделі; + - Router статус; + - список агентів (хоча б з іменем, типом і MicroDAO). + +### Виключено + +- CrewAI-кімнати, групові чати, кнопки створення кімнат — це **окремий таск**. +- Авто-самолікування роутера / свопера. +- Веб-інтерфейс кар'єру/налаштувань для кожного агента. + +--- + +## 3. Swapper інтеграція NODE2 + +### 3.1. Перевірити конфіг + +- підтвердити точний Swapper URL для NODE2: + - `http://localhost:8890/health` + - `http://localhost:8890/models` (або фактичний endpoint; див. `swapper_config_node2.yaml` та `logs/node2_swapper_models.json`). + +### 3.2. Оновити node-guardian-loop (NODE2) + +У `scripts/node-guardian-loop.py`: + +- переконатися, що при запуску з NODE2: + - використовується **локальний** Swapper URL (`localhost`, а не `swapper-service`); + - логуються `node_id` і `swapper_url` (для дебагу в майбутньому). + +- функція збору Swapper-метрик повинна: + - витягнути **реальний список моделей**; + - порахувати `models_total`, `models_loaded`; + - скласти `swapper_state` (JSON) з полями: + - `name` + - `backend` (ollama / hf / інше) + - `type` (llm / code / vision / reasoning) + - `loaded` + - `vram_gb` (якщо відомо). + +Передавати ці значення в `POST /internal/node/{node_id}/metrics/update`. + +### 3.3. Перевірити fn_node_heartbeat та `node_cache` + +- переконатися, що `fn_node_heartbeat` оновлює **тільки один запис** по `node_id` і не перетирає дані іншої ноди; + +- стовпці: + - `swapper_healthy` + - `swapper_models_loaded` + - `swapper_models_total` + - `swapper_state` + +Перевірити руками: + +```sql +select node_id, swapper_healthy, swapper_models_loaded, swapper_models_total +from node_cache +order by node_id; +``` + +--- + +## 4. DAGI Router інтеграція NODE2 + +### 4.1. Виявити Router API + +З логів і конфігів (NODE2): + +* підтвердити базовий URL: + * `http://localhost:9102` (наприклад, `/health`, `/status`, `/agents`). + +Зробити невеликий helper (окрема функція) у `node-guardian-loop.py`: + +```python +def collect_router_metrics(router_base_url: str) -> dict: + # router_healthy (bool) + # router_agents_total (int) + # router_agents_active (int / optional) + # router_state (json: список агентів з базовою інформацією) +``` + +Передавати ці дані в `metrics/update`. + +### 4.2. Розширити схему БД (якщо потрібно) + +Міграція, наприклад `migrations/041_node_cache_router_metrics.sql`: + +* `router_healthy boolean` +* `router_agents_total integer` +* `router_state jsonb` + +Оновити `repo_city.py` / `models_city.py` / `routes_city.py`, щоб ці поля поверталися для: + +* `GET /internal/node/{node_id}/metrics/current` +* `GET /internal/node/{node_id}` (якщо є short summary). + +--- + +## 5. Node2 Agent Registry (50 DAGI-агентів) + +### 5.1. Зчитати конфіги + +Знайти та використати (тільки читання): + +* `agents_city_mapping.yaml` +* директорію `agents/` (конфіги окремих агентів) +* звіт discovery (walkthrough / NODE_STATE) + +Не створювати агентів "із голови": брати **тільки те, що описано в цих файлах**. + +### 5.2. Оновити таблиці БД + +Залежно від існуючої схеми, але логіка така: + +* таблиця `agents` / `node_agents` повинна мати: + * `id` / `slug` + * `node_id` (node-1 / node-2) + * `name` + * `role` / `district` / `team` + * `microdao_slug` (якщо прив'язано) + * `is_core` / `is_system` / `is_microdao` (флаги) + * `model_name` / `model_backend` (ollama / hf / інше) + +Потрібно: + +1. Створити окремий **sync-скрипт** для NODE2, напр.: + `scripts/sync-node2-dagi-agents.py` + + Він повинен: + * прочитати `agents_city_mapping.yaml` та `agents/`; + * для кожного агента: + * вставити/оновити запис у БД з `node_id = 'node-2-macbook-m4max'`; + * не чіпати агентів NODE1. + +2. Перед запуском — зробити dry-run (print summary), потім запустити реально. + +--- + +## 6. API для Node Cabinet (тільки те, що потрібно зараз) + +### 6.1. Agents + +Endpoint: + +* `GET /internal/node/{node_id}/agents` + +Повертає: + +```json +{ + "node_id": "node-2-macbook-m4max", + "total": 50, + "agents": [ + { + "id": "helix", + "name": "Helix", + "role": "CTO", + "district": "System Control", + "microdao": "daarion-dao", + "model_name": "qwen2.5-coder:32b", + "kind": "core" + }, + ... + ] +} +``` + +Node Cabinet на фронтенді вже має секцію "Агенти цієї ноди" — під'єднати її до цього endpoint (без змін дизайну). + +### 6.2. Models (опційно, але бажано) + +`GET /internal/node/{node_id}/models` + +Повертає агреговані дані з `swapper_state`: + +* скільки моделей, +* які backend'и, +* який тип. + +Це потрібно, щоб у Node Cabinet список моделей точно відповідав тому, що Swapper бачить на NODE2. + +--- + +## 7. UI Node Cabinet — очікуваний результат для NODE2 + +Після завершення таска: + +1. **Swapper Service (NODE2)**: + * статус: `Healthy`; + * `Models Loaded: X / 8` (від Swapper, а не від NODE1); + * у списку моделей показуються саме локальні: + * `deepseek-r1:70b`, `qwen2.5-coder:32b`, `gemma2:27b`, `mistral-nemo`, `phi3`, `starcoder2`, `gpt-oss`, `deepseek-coder:33b`. + +2. **DAGI Router (NODE2)**: + * статус: `Up` (якщо сервіс запущений); + * `Agents: 50` (або актуальна кількість); + * кнопка "Агенти цієї ноди" відкриває список з усіма 50 агентами. + +3. **Ізоляція по node_id**: + * NODE1 показує свої 9 агентів і 7 моделей; + * NODE2 показує свої 50 агентів і 8 моделей, але **через свій Swapper/Router**; + * дані не змішуються. + +--- + +## 8. Acceptance Criteria + +1. `scripts/discover_node_state.py` (якщо є) показує різні Router/Swapper-дані для NODE1 та NODE2 (і вони збігаються з Node Cabinet). + +2. `GET /internal/node/node-2-macbook-m4max/metrics/current` показує: + * `swapper_healthy = true`, + * `swapper_models_total = 8`, + * `router_healthy = true`, + * `router_agents_total >= 40`. + +3. `GET /internal/node/node-2-macbook-m4max/agents` повертає список із ~50 агентів. + +4. На UI: + * Node Cabinet для NODE2 показує коректні Swapper-метрики й Router-агентів; + * при перезапуску node-guardian дані оновлюються без ручних SQL. + +5. Жоден endpoint / UI-блок для NODE1 не зламаний. + +--- + +## 9. Район-агент маппінг (50 агентів NODE2) + +### Leadership Hall (4) +- Solarius (CEO) +- Sofia +- PrimeSynth +- Nexor + +### System Control (6) +- monitor-node2 +- vindex +- helix +- aurora +- та інші... + +### Engineering Lab (5) +- byteforge +- vector +- chainweaver +- cypher +- canvas + +### Marketing Hub (6) +- roxy +- mira +- tempo +- harmony +- faye +- та інші... + +### Finance Office (4) +- financial-analyst +- accountant +- budget-planner +- та інші... + +### Web3 District (5) +- smart-contract-dev +- defi-analyst +- tokenomics-expert +- та інші... + +### Security Bunker (7) +- shadelock +- exor +- penetration-tester +- та інші... + +### Vision Studio (4) +- iris +- lumen +- spectra +- video-analyzer + +### R&D Lab (6) +- protomind +- labforge +- testpilot +- modelscout +- та інші... + +### Memory Vault (3) +- somnia +- memory-manager +- knowledge-indexer + +--- + +Як тільки цей крок буде виконаний і задеплоєний, далі можемо рухатись: +- крок 2: кімнати/чати MicroDAO + публічний головний чат; +- крок 3: кнопки створення/видалення агента й групи (CrewAI-команди). + diff --git a/scripts/sync-node2-dagi-agents.py b/scripts/sync-node2-dagi-agents.py new file mode 100644 index 00000000..a781b254 --- /dev/null +++ b/scripts/sync-node2-dagi-agents.py @@ -0,0 +1,266 @@ +#!/usr/bin/env python3 +""" +Sync NODE2 DAGI Agents from agents_city_mapping.yaml to database. + +This script reads the agent configuration from agents_city_mapping.yaml +and syncs all 50 agents to the database with node_id = 'node-2-macbook-m4max'. + +Usage: + python scripts/sync-node2-dagi-agents.py --dry-run # Preview changes + python scripts/sync-node2-dagi-agents.py # Apply changes +""" + +import argparse +import asyncio +import os +import sys +from pathlib import Path + +import yaml + +# Add project root to path +PROJECT_ROOT = Path(__file__).parent.parent +sys.path.insert(0, str(PROJECT_ROOT)) + +# Database connection +DATABASE_URL = os.getenv( + "DATABASE_URL", + "postgresql://postgres:postgres@144.76.224.179:5432/daarion" +) + +NODE2_ID = "node-2-macbook-m4max" +CONFIG_PATH = PROJECT_ROOT / "config" / "agents_city_mapping.yaml" + + +def load_agents_config() -> dict: + """Load agents configuration from YAML file.""" + if not CONFIG_PATH.exists(): + print(f"❌ Config file not found: {CONFIG_PATH}") + sys.exit(1) + + with open(CONFIG_PATH, 'r', encoding='utf-8') as f: + config = yaml.safe_load(f) + + return config + + +def parse_agent(agent_data: dict, districts: list) -> dict: + """Parse agent data from YAML to database format.""" + # Find district info + district_id = agent_data.get('district', '') + district_info = next((d for d in districts if d['id'] == district_id), None) + + return { + 'id': agent_data['agent_id'], + 'slug': agent_data['agent_id'], + 'display_name': agent_data['display_name'], + 'kind': agent_data.get('kind', 'assistant'), + 'role': agent_data.get('role', ''), + 'model': agent_data.get('model', ''), + 'node_id': agent_data.get('node_id', NODE2_ID), + 'district': district_id, + 'primary_room_slug': agent_data.get('primary_room_slug', ''), + 'avatar_url': agent_data.get('avatar_url', ''), + 'color_hint': agent_data.get('color_hint', ''), + 'priority': agent_data.get('priority', 'medium'), + 'is_active': True, + 'status': 'offline', + 'is_public': True, + 'public_slug': agent_data['agent_id'], + 'public_title': agent_data['display_name'], + 'public_tagline': agent_data.get('role', ''), + 'public_district': district_info['name'] if district_info else '', + 'public_primary_room_slug': agent_data.get('primary_room_slug', ''), + 'is_listed_in_directory': True, + 'visibility_scope': 'global', + 'is_orchestrator': agent_data.get('kind') == 'orchestrator', + 'home_node_id': agent_data.get('node_id', NODE2_ID), + } + + +async def sync_agents(dry_run: bool = True): + """Sync agents to database.""" + import asyncpg + + print(f"📂 Loading config from: {CONFIG_PATH}") + config = load_agents_config() + + districts = config.get('districts', []) + agents_data = config.get('agents', []) + + print(f"📊 Found {len(agents_data)} agents in config") + print(f"📊 Found {len(districts)} districts") + + # Parse agents + agents = [parse_agent(a, districts) for a in agents_data] + + # Group by district for summary + by_district = {} + for agent in agents: + d = agent['district'] + if d not in by_district: + by_district[d] = [] + by_district[d].append(agent['display_name']) + + print("\n📋 Agents by district:") + for district_id, agent_names in sorted(by_district.items()): + district_info = next((d for d in districts if d['id'] == district_id), None) + district_name = district_info['name'] if district_info else district_id + print(f" {district_name}: {len(agent_names)} agents") + for name in agent_names: + print(f" - {name}") + + if dry_run: + print("\n🔍 DRY RUN - No changes will be made") + print(f"Would sync {len(agents)} agents to NODE2") + return + + # Connect to database + print(f"\n🔗 Connecting to database...") + conn = await asyncpg.connect(DATABASE_URL) + + try: + # Start transaction + async with conn.transaction(): + inserted = 0 + updated = 0 + + for agent in agents: + # Check if agent exists + existing = await conn.fetchrow( + "SELECT id FROM agents WHERE id = $1", + agent['id'] + ) + + if existing: + # Update existing agent + await conn.execute(""" + UPDATE agents SET + display_name = $2, + kind = $3, + role = $4, + model = $5, + node_id = $6, + district = $7, + primary_room_slug = $8, + avatar_url = $9, + color_hint = $10, + priority = $11, + is_active = $12, + is_public = $13, + public_slug = $14, + public_title = $15, + public_tagline = $16, + public_district = $17, + public_primary_room_slug = $18, + is_listed_in_directory = $19, + visibility_scope = $20, + is_orchestrator = $21, + home_node_id = $22, + slug = $23, + updated_at = NOW() + WHERE id = $1 + """, + agent['id'], + agent['display_name'], + agent['kind'], + agent['role'], + agent['model'], + agent['node_id'], + agent['district'], + agent['primary_room_slug'], + agent['avatar_url'], + agent['color_hint'], + agent['priority'], + agent['is_active'], + agent['is_public'], + agent['public_slug'], + agent['public_title'], + agent['public_tagline'], + agent['public_district'], + agent['public_primary_room_slug'], + agent['is_listed_in_directory'], + agent['visibility_scope'], + agent['is_orchestrator'], + agent['home_node_id'], + agent['slug'], + ) + updated += 1 + else: + # Insert new agent + await conn.execute(""" + INSERT INTO agents ( + id, display_name, kind, role, model, node_id, district, + primary_room_slug, avatar_url, color_hint, priority, + is_active, status, is_public, public_slug, public_title, + public_tagline, public_district, public_primary_room_slug, + is_listed_in_directory, visibility_scope, is_orchestrator, + home_node_id, slug + ) VALUES ( + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, + $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24 + ) + """, + agent['id'], + agent['display_name'], + agent['kind'], + agent['role'], + agent['model'], + agent['node_id'], + agent['district'], + agent['primary_room_slug'], + agent['avatar_url'], + agent['color_hint'], + agent['priority'], + agent['is_active'], + agent['status'], + agent['is_public'], + agent['public_slug'], + agent['public_title'], + agent['public_tagline'], + agent['public_district'], + agent['public_primary_room_slug'], + agent['is_listed_in_directory'], + agent['visibility_scope'], + agent['is_orchestrator'], + agent['home_node_id'], + agent['slug'], + ) + inserted += 1 + + print(f"\n✅ Sync complete!") + print(f" Inserted: {inserted} agents") + print(f" Updated: {updated} agents") + + # Verify count + count = await conn.fetchval( + "SELECT COUNT(*) FROM agents WHERE node_id = $1 AND deleted_at IS NULL", + NODE2_ID + ) + print(f" Total NODE2 agents: {count}") + + finally: + await conn.close() + + +def main(): + parser = argparse.ArgumentParser( + description="Sync NODE2 DAGI agents from config to database" + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Preview changes without applying them" + ) + args = parser.parse_args() + + print("=" * 60) + print("🤖 NODE2 DAGI Agent Sync") + print("=" * 60) + + asyncio.run(sync_agents(dry_run=args.dry_run)) + + +if __name__ == "__main__": + main() + diff --git a/services/swapper-service/config/swapper_config.yaml b/services/swapper-service/config/swapper_config.yaml index ccf9327a..6721a408 100644 --- a/services/swapper-service/config/swapper_config.yaml +++ b/services/swapper-service/config/swapper_config.yaml @@ -1,75 +1,83 @@ -# Swapper Configuration for Node #1 (Production Server) +# Swapper Configuration for Node #2 (Development Node) # Single-active LLM scheduler -# Hetzner GEX44 - NVIDIA RTX 4000 SFF Ada (20GB VRAM) -# Auto-generated configuration with all available Ollama models +# MacBook Pro M4 Max - Apple Silicon (40-core GPU, 64GB RAM) +# Auto-generated configuration with available Ollama models swapper: mode: single-active max_concurrent_models: 1 model_swap_timeout: 300 gpu_enabled: true - metal_acceleration: false # NVIDIA GPU, not Apple Silicon + metal_acceleration: true # Apple Silicon GPU acceleration # Модель для автоматичного завантаження при старті (опціонально) # Якщо не вказано - моделі завантажуються тільки за запитом - # Рекомендовано: qwen3-8b (основна модель) або qwen2.5-3b-instruct (легка модель) - default_model: qwen3-8b # Модель активується автоматично при старті + # Рекомендовано: gpt-oss:latest (швидка модель) або phi3:latest (легка модель) + default_model: gpt-oss:latest # Модель активується автоматично при старті models: - # Primary LLM - Qwen3 8B (High Priority) - Main model from INFRASTRUCTURE.md - qwen3-8b: - path: ollama:qwen3:8b + # Fast LLM - GPT-OSS 20B (High Priority) - Main model for general tasks + gpt-oss-latest: + path: ollama:gpt-oss:latest type: llm - size_gb: 4.87 + size_gb: 13.0 priority: high - description: "Primary LLM for general tasks and conversations" + description: "Fast LLM for general tasks and conversations (20.9B params)" - # Vision Model - Qwen3-VL 8B (High Priority) - For image processing - qwen3-vl-8b: - path: ollama:qwen3-vl:8b - type: vision - size_gb: 5.72 - priority: high - description: "Vision model for image understanding and processing" - - # Qwen2.5 7B Instruct (High Priority) - qwen2.5-7b-instruct: - path: ollama:qwen2.5:7b-instruct-q4_K_M + # Lightweight LLM - Phi3 3.8B (High Priority) - Fast responses + phi3-latest: + path: ollama:phi3:latest type: llm - size_gb: 4.36 + size_gb: 2.2 priority: high - description: "Qwen2.5 7B Instruct model" + description: "Lightweight LLM for fast responses (3.8B params)" - # Lightweight LLM - Qwen2.5 3B Instruct (Medium Priority) - qwen2.5-3b-instruct: - path: ollama:qwen2.5:3b-instruct-q4_K_M - type: llm - size_gb: 1.80 + # Code Specialist - StarCoder2 3B (Medium Priority) - Code engineering + starcoder2-3b: + path: ollama:starcoder2:3b + type: code + size_gb: 1.7 priority: medium - description: "Lightweight LLM for faster responses" + description: "Code specialist model for code engineering (3B params)" - # Math Specialist - Qwen2 Math 7B (High Priority) - qwen2-math-7b: - path: ollama:qwen2-math:7b - type: math - size_gb: 4.13 - priority: high - description: "Specialized model for mathematical tasks" - - # Lightweight conversational LLM - Mistral Nemo 2.3B (Medium Priority) - mistral-nemo-2_3b: - path: ollama:mistral-nemo:2.3b-instruct + # Reasoning Model - Mistral Nemo 12.2B (High Priority) - Advanced reasoning + mistral-nemo-12b: + path: ollama:mistral-nemo:12b type: llm - size_gb: 1.60 + size_gb: 7.1 + priority: high + description: "Advanced reasoning model for complex tasks (12.2B params)" + + # Reasoning Model - Gemma2 27B (Medium Priority) - Strategic reasoning + gemma2-27b: + path: ollama:gemma2:27b + type: llm + size_gb: 15.0 priority: medium - description: "Fast low-cost replies for monitor/service agents" - - # Compact Math Specialist - Qwen2.5 Math 1.5B (Medium Priority) - qwen2_5-math-1_5b: - path: ollama:qwen2.5-math:1.5b - type: math - size_gb: 1.20 - priority: medium - description: "Lightweight math model for DRUID/Nutra micro-calculations" + description: "Reasoning model for strategic tasks (27.2B params)" + + # Code Specialist - DeepSeek Coder 33B (High Priority) - Advanced code tasks + deepseek-coder-33b: + path: ollama:deepseek-coder:33b + type: code + size_gb: 18.0 + priority: high + description: "Advanced code specialist model (33B params)" + + # Code Specialist - Qwen2.5 Coder 32B (High Priority) - Advanced code tasks + qwen2.5-coder-32b: + path: ollama:qwen2.5-coder:32b + type: code + size_gb: 19.0 + priority: high + description: "Advanced code specialist model (32.8B params)" + + # Reasoning Model - DeepSeek R1 70B (High Priority) - Strategic reasoning (large model) + deepseek-r1-70b: + path: ollama:deepseek-r1:70b + type: llm + size_gb: 42.0 + priority: high + description: "Strategic reasoning model (70.6B params, quantized)" storage: models_dir: /app/models @@ -77,5 +85,6 @@ storage: swap_dir: /app/swap ollama: - url: http://ollama:11434 # From Docker container to Ollama service + url: http://localhost:11434 # Native Ollama on MacBook (via Pieces OS or brew) timeout: 300 + diff --git a/services/swapper-service/config/swapper_config.yaml.backup b/services/swapper-service/config/swapper_config.yaml.backup new file mode 100644 index 00000000..ccf9327a --- /dev/null +++ b/services/swapper-service/config/swapper_config.yaml.backup @@ -0,0 +1,81 @@ +# Swapper Configuration for Node #1 (Production Server) +# Single-active LLM scheduler +# Hetzner GEX44 - NVIDIA RTX 4000 SFF Ada (20GB VRAM) +# Auto-generated configuration with all available Ollama models + +swapper: + mode: single-active + max_concurrent_models: 1 + model_swap_timeout: 300 + gpu_enabled: true + metal_acceleration: false # NVIDIA GPU, not Apple Silicon + # Модель для автоматичного завантаження при старті (опціонально) + # Якщо не вказано - моделі завантажуються тільки за запитом + # Рекомендовано: qwen3-8b (основна модель) або qwen2.5-3b-instruct (легка модель) + default_model: qwen3-8b # Модель активується автоматично при старті + +models: + # Primary LLM - Qwen3 8B (High Priority) - Main model from INFRASTRUCTURE.md + qwen3-8b: + path: ollama:qwen3:8b + type: llm + size_gb: 4.87 + priority: high + description: "Primary LLM for general tasks and conversations" + + # Vision Model - Qwen3-VL 8B (High Priority) - For image processing + qwen3-vl-8b: + path: ollama:qwen3-vl:8b + type: vision + size_gb: 5.72 + priority: high + description: "Vision model for image understanding and processing" + + # Qwen2.5 7B Instruct (High Priority) + qwen2.5-7b-instruct: + path: ollama:qwen2.5:7b-instruct-q4_K_M + type: llm + size_gb: 4.36 + priority: high + description: "Qwen2.5 7B Instruct model" + + # Lightweight LLM - Qwen2.5 3B Instruct (Medium Priority) + qwen2.5-3b-instruct: + path: ollama:qwen2.5:3b-instruct-q4_K_M + type: llm + size_gb: 1.80 + priority: medium + description: "Lightweight LLM for faster responses" + + # Math Specialist - Qwen2 Math 7B (High Priority) + qwen2-math-7b: + path: ollama:qwen2-math:7b + type: math + size_gb: 4.13 + priority: high + description: "Specialized model for mathematical tasks" + + # Lightweight conversational LLM - Mistral Nemo 2.3B (Medium Priority) + mistral-nemo-2_3b: + path: ollama:mistral-nemo:2.3b-instruct + type: llm + size_gb: 1.60 + priority: medium + description: "Fast low-cost replies for monitor/service agents" + + # Compact Math Specialist - Qwen2.5 Math 1.5B (Medium Priority) + qwen2_5-math-1_5b: + path: ollama:qwen2.5-math:1.5b + type: math + size_gb: 1.20 + priority: medium + description: "Lightweight math model for DRUID/Nutra micro-calculations" + +storage: + models_dir: /app/models + cache_dir: /app/cache + swap_dir: /app/swap + +ollama: + url: http://ollama:11434 # From Docker container to Ollama service + timeout: 300