feat(node2): Full DAGI integration - 50 agents synced
- Created sync-node2-dagi-agents.py script to sync agents from agents_city_mapping.yaml - Synced 50 DAGI agents across 10 districts: - Leadership Hall (4): Solarius, Sofia, PrimeSynth, Nexor - System Control (6): Monitor, Strategic Sentinels, Vindex, Helix, Aurora, Arbitron - Engineering Lab (5): ByteForge, Vector, ChainWeaver, Cypher, Canvas - Marketing Hub (6): Roxy, Mira, Tempo, Harmony, Faye, Storytelling - Finance Office (4): Financial Analyst, Accountant, Budget Planner, Tax Advisor - Web3 District (5): Smart Contract Dev, DeFi Analyst, Tokenomics Expert, NFT Specialist, DAO Governance - Security Bunker (7): Shadelock, Exor, Penetration Tester, Security Monitor, Incident Responder, Shadelock Forensics, Exor Forensics - Vision Studio (4): Iris, Lumen, Spectra, Video Analyzer - R&D Lab (6): ProtoMind, LabForge, TestPilot, ModelScout, BreakPoint, GrowCell - Memory Vault (3): Somnia, Memory Manager, Knowledge Indexer - Fixed Swapper config to use swapper_config_node2.yaml with 8 models - Created TASK_PHASE_NODE2_FULL_DAGI_INTEGRATION_v1.md NODE2 now shows: - 50 agents in DAGI Router Card - 8 models in Swapper Service (gpt-oss, phi3, starcoder2, mistral-nemo, gemma2, deepseek-coder, qwen2.5-coder, deepseek-r1) - Full isolation from NODE1
This commit is contained in:
370
docs/tasks/TASK_PHASE_NODE2_FULL_DAGI_INTEGRATION_v1.md
Normal file
370
docs/tasks/TASK_PHASE_NODE2_FULL_DAGI_INTEGRATION_v1.md
Normal file
@@ -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-команди).
|
||||
|
||||
266
scripts/sync-node2-dagi-agents.py
Normal file
266
scripts/sync-node2-dagi-agents.py
Normal file
@@ -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()
|
||||
|
||||
@@ -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
|
||||
priority: medium
|
||||
description: "Fast low-cost replies for monitor/service agents"
|
||||
size_gb: 7.1
|
||||
priority: high
|
||||
description: "Advanced reasoning model for complex tasks (12.2B params)"
|
||||
|
||||
# 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
|
||||
# Reasoning Model - Gemma2 27B (Medium Priority) - Strategic reasoning
|
||||
gemma2-27b:
|
||||
path: ollama:gemma2:27b
|
||||
type: llm
|
||||
size_gb: 15.0
|
||||
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
|
||||
|
||||
|
||||
81
services/swapper-service/config/swapper_config.yaml.backup
Normal file
81
services/swapper-service/config/swapper_config.yaml.backup
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user