feat: оновлення інфраструктури з Node #2 та нові сервіси

This commit is contained in:
Apple
2025-11-21 00:35:03 -08:00
parent 36c35cab57
commit 31f3602047
5 changed files with 179 additions and 18 deletions

View File

@@ -6,20 +6,36 @@
---
## 📍 Production Servers
## 📍 Network Nodes
### GEX44 Server #2844465 (Hetzner)
### Node #1: Production Server (Hetzner GEX44 #2844465)
- **Node ID:** `node-1-hetzner-gex44`
- **IP Address:** `144.76.224.179`
- **SSH Access:** `ssh root@144.76.224.179`
- **Location:** Hetzner Cloud
- **Project Root:** `/opt/microdao-daarion` (or `/opt/dagi-router`)
- **Location:** Hetzner Cloud (Germany)
- **Project Root:** `/opt/microdao-daarion`
- **Docker Network:** `dagi-network`
- **Role:** Production Router + Gateway + All Services
- **Uptime:** 24/7
**Domains:**
- `gateway.daarion.city``144.76.224.179` (Gateway + Nginx)
- `api.daarion.city` → TBD (API Gateway)
- `daarion.city` → TBD (Main website)
### Node #2: Development Node (MacBook Pro M4 Max)
- **Node ID:** `node-2-macbook-m4max`
- **Local IP:** `192.168.1.244`
- **SSH Access:** `ssh apple@192.168.1.244` (if enabled)
- **Location:** Local Network (Ivan's Office)
- **Project Root:** `/Users/apple/github-projects/microdao-daarion`
- **Role:** Development + Testing + Backup Router
- **Specs:** M4 Max (16 cores), 64GB RAM, 2TB SSD, 40-core GPU
- **Uptime:** On-demand (battery-powered)
**See full specs:** [NODE-2-MACBOOK-SPECS.md](./NODE-2-MACBOOK-SPECS.md)
**Current state:** [NODE-2-CURRENT-STATE.md](./NODE-2-CURRENT-STATE.md) — What's running now
---
## 🐙 GitHub Repositories

View File

@@ -6,12 +6,12 @@
"source": [
"# 🚀 Infrastructure Quick Reference — DAARION & MicroDAO\n",
"\n",
"**Версія:** 1.1.0 \n",
"**Версія:** 1.2.0 \n",
"**Останнє оновлення:** 2025-01-17 \n",
"\n",
"Цей notebook містить швидкий довідник по серверах, репозиторіях та endpoints для DAGI Stack.\n",
"\n",
"**NEW:** Vision Encoder + Qdrant vector database (OpenCLIP ViT-L/14)"
"**NEW:** Vision Encoder + Qdrant + Node #2 (MacBook Pro M4 Max)"
]
},
{
@@ -48,6 +48,75 @@
" print(f\"{name.upper():<20} {service['port']:<7} {service['container']:<30} {health}{gpu}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 🖥️ Network Nodes\n",
"\n",
"### Node #1: Production Server (Hetzner)\n",
"- **Node ID:** node-1-hetzner-gex44\n",
"- **IP:** 144.76.224.179\n",
"- **Role:** Production Router + Gateway + All Services (24/7)\n",
"- **Location:** Hetzner Cloud (Germany)\n",
"\n",
"### Node #2: Development Node (MacBook Pro M4 Max)\n",
"- **Node ID:** node-2-macbook-m4max\n",
"- **Local IP:** 192.168.1.244\n",
"- **Role:** Development + Testing + Backup Router\n",
"- **Specs:** M4 Max (16 cores), 64GB RAM, 2TB SSD, 40-core GPU\n",
"- **Location:** Local Network (Ivan's Office)\n",
"- **Docs:** [NODE-2-MACBOOK-SPECS.md](../NODE-2-MACBOOK-SPECS.md)\n",
"\n",
"---"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Network Nodes Configuration\n",
"NODES = {\n",
" \"node-1\": {\n",
" \"name\": \"Hetzner GEX44\",\n",
" \"ip\": \"144.76.224.179\",\n",
" \"local_ip\": None,\n",
" \"role\": \"production\",\n",
" \"uptime\": \"24/7\",\n",
" \"ssh\": \"root@144.76.224.179\",\n",
" \"domain\": \"gateway.daarion.city\",\n",
" \"services\": \"All (17 services)\",\n",
" \"specs\": \"See SYSTEM-INVENTORY.md\"\n",
" },\n",
" \"node-2\": {\n",
" \"name\": \"MacBook Pro M4 Max\",\n",
" \"ip\": None,\n",
" \"local_ip\": \"192.168.1.244\",\n",
" \"role\": \"development\",\n",
" \"uptime\": \"on-demand\",\n",
" \"ssh\": \"apple@192.168.1.244\",\n",
" \"domain\": None,\n",
" \"services\": \"Core only (Router, DevTools, Memory, Ollama)\",\n",
" \"specs\": \"M4 Max, 16 cores, 64GB RAM, 2TB SSD, 40-core GPU\"\n",
" }\n",
"}\n",
"\n",
"print(\"DAGI Stack Network Nodes:\")\n",
"print(\"=\"*80)\n",
"for node_id, node in NODES.items():\n",
" print(f\"\\n{node_id.upper()}: {node['name']}\")\n",
" print(f\" Role: {node['role']}\")\n",
" print(f\" IP: {node['ip'] or node['local_ip']}\")\n",
" print(f\" SSH: {node['ssh']}\")\n",
" print(f\" Uptime: {node['uptime']}\")\n",
" print(f\" Services: {node['services']}\")\n",
" if node['domain']:\n",
" print(f\" Domain: https://{node['domain']}\")\n",
" print(f\" Specs: {node['specs']}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
@@ -157,7 +226,8 @@
" \"Deployment\": \"../DEPLOY-NOW.md\",\n",
" \"Helion Status\": \"../STATUS-HELION.md\",\n",
" \"Architecture Index\": \"../docs/cursor/README.md\",\n",
" \"API Reference\": \"../docs/api.md\"\n",
" \"API Reference\": \"../docs/api.md\",\n",
" \"Node #2 Specs\": \"../NODE-2-MACBOOK-SPECS.md\"\n",
"}\n",
"\n",
"print(\"Documentation Quick Links:\")\n",
@@ -180,11 +250,14 @@
"- ✅ **768-dim embeddings** for multimodal RAG\n",
"- ✅ Created VISION-ENCODER-STATUS.md with full implementation details\n",
"- ✅ Added test-vision-encoder.sh smoke tests\n",
"- ✅ **Added Node #2** (MacBook Pro M4 Max) as development node\n",
"- ✅ Created NODE-2-MACBOOK-SPECS.md with complete specifications\n",
"\n",
"### Services Count: 17 (from 15)\n",
"- Total Services: 17\n",
"- GPU Services: 1 (Vision Encoder)\n",
"- Vector Databases: 1 (Qdrant)\n",
"### Network Architecture\n",
"- **Nodes:** 2 (1 production + 1 development)\n",
"- **Total Services:** 17\n",
"- **GPU Services:** 1 (Vision Encoder) + Node #2 (40-core Apple GPU)\n",
"- **Vector Databases:** 1 (Qdrant)\n",
"\n",
"---\n",
"\n",

View File

@@ -5,7 +5,7 @@ Provides HTTP endpoints for routing requests
"""
from fastapi import APIRouter, HTTPException, status
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, ConfigDict
from typing import Optional, Dict, Any
import logging
@@ -21,6 +21,8 @@ logger = logging.getLogger(__name__)
class IncomingRequest(BaseModel):
"""HTTP request format"""
model_config = ConfigDict(extra='allow') # Дозволити додаткові поля для сумісності
mode: Optional[str] = Field(None, description="Request mode (e.g., 'chat', 'crew')")
agent: Optional[str] = Field(None, description="Agent ID (e.g., 'devtools')")
message: Optional[str] = Field(None, description="User message")
@@ -29,6 +31,7 @@ class IncomingRequest(BaseModel):
session_id: Optional[str] = Field(None, description="Session identifier")
user_id: Optional[str] = Field(None, description="User identifier")
payload: Dict[str, Any] = Field(default_factory=dict, description="Additional payload data")
context: Optional[Dict[str, Any]] = Field(None, description="Legacy: context on top level (will be migrated to payload.context)")
class RouterAPIResponse(BaseModel):
@@ -74,6 +77,32 @@ def build_router_http(app_core: RouterApp) -> APIRouter:
- Other metadata
"""
logger.info(f"Incoming request: agent={req.agent}, mode={req.mode}")
logger.info(f"Raw payload type: {type(req.payload)}, keys: {list(req.payload.keys()) if req.payload else []}")
logger.info(f"Raw context: {req.context}")
# Нормалізувати payload: якщо є "context" на верхньому рівні (старий формат),
# перемістити його в payload.context для уніфікованої обробки
normalized_payload = req.payload.copy() if req.payload else {}
# Перевірити, чи є context на верхньому рівні (legacy формат від gateway-bot)
# Це потрібно для сумісності з DAARWIZZ
if req.context:
# Якщо context на верхньому рівні, перемістити в payload.context
if "context" not in normalized_payload:
normalized_payload["context"] = {}
# Мержити context з верхнього рівня в payload.context
if isinstance(req.context, dict):
normalized_payload["context"].update(req.context)
logger.info(f"✅ Migrated top-level context to payload.context for agent={req.agent}, keys={list(req.context.keys())}")
logger.info(f"✅ Normalized payload keys: {list(normalized_payload.keys())}")
if normalized_payload and "context" in normalized_payload:
logger.info(f"✅ Context keys: {list(normalized_payload['context'].keys()) if isinstance(normalized_payload.get('context'), dict) else []}")
if isinstance(normalized_payload.get('context'), dict) and "system_prompt" in normalized_payload['context']:
sp = normalized_payload['context']['system_prompt']
sp_len = len(sp) if sp else 0
logger.info(f"✅ System prompt found in context: {sp_len} chars")
logger.info(f"✅ System prompt preview: {sp[:100] if sp else 'None'}...")
# Convert to internal RouterRequest
rreq = RouterRequest(
@@ -84,7 +113,7 @@ def build_router_http(app_core: RouterApp) -> APIRouter:
session_id=req.session_id,
user_id=req.user_id,
message=req.message,
payload=req.payload,
payload=normalized_payload,
)
# Handle request

View File

@@ -158,15 +158,34 @@ class LLMProvider(Provider):
def _get_system_prompt(self, req: RouterRequest) -> Optional[str]:
"""Get system prompt based on agent or context"""
# 1. Check if context.system_prompt provided (e.g., from Gateway)
logger.info(f"[DEBUG] _get_system_prompt called for agent={req.agent}")
logger.info(f"[DEBUG] req.payload type: {type(req.payload)}, keys: {list(req.payload.keys()) if req.payload else []}")
context = req.payload.get("context") or {}
logger.info(f"[DEBUG] payload keys: {list(req.payload.keys())}")
logger.info(f"[DEBUG] context keys: {list(context.keys())}")
if "system_prompt" in context:
logger.info(f"[DEBUG] context type: {type(context)}, keys: {list(context.keys()) if isinstance(context, dict) else 'not a dict'}")
if isinstance(context, dict) and "system_prompt" in context:
prompt = context["system_prompt"]
logger.info(f"[DEBUG] Using context.system_prompt: {len(prompt)} chars, agent={req.agent}")
logger.info(f"[DEBUG] Using context.system_prompt: {len(prompt)} chars, agent={req.agent}")
logger.info(f"[DEBUG] System prompt type: {type(prompt)}")
logger.info(f"[DEBUG] System prompt preview (first 200): {str(prompt)[:200]}...")
logger.info(f"[DEBUG] System prompt full length check: {len(str(prompt))} chars")
# Переконаємось, що це рядок
if not isinstance(prompt, str):
logger.warning(f"[DEBUG] ⚠️ System prompt is not a string! Type: {type(prompt)}, value: {prompt}")
prompt = str(prompt) if prompt else None
return prompt
else:
logger.info(f"[DEBUG] ⚠️ No system_prompt in context for agent={req.agent}")
# 2. Agent-specific system prompts (fallback, якщо не передано в context)
if req.agent == "helion":
return (
"Ти - Helion, AI-агент платформи Energy Union екосистеми DAARION.city. "
"Допомагай користувачам з технологіями EcoMiner/BioMiner, токеномікою та DAO governance. "
"Твої основні функції: консультації з енергетичними технологіями, пояснення токеноміки Energy Union, "
"допомога з onboarding в DAO, відповіді на питання про EcoMiner/BioMiner устаткування. "
"Стиль спілкування: професійний, технічний, але зрозумілий, точний у цифрах та даних."
)
# 2. Agent-specific system prompts
if req.agent == "daarwizz":
return (
"Ти — DAARWIZZ, офіційний AI-агент екосистеми DAARION.city. "

View File

@@ -89,6 +89,30 @@ agents:
You are a multi-agent orchestrator for DAARION.city microDAO ecosystem.
You coordinate complex workflows involving multiple specialized agents.
greenfood:
description: "GREENFOOD Assistant - ERP orchestrator for craft food producers"
default_llm: local_qwen3_8b
system_prompt: |
Ти — GREENFOOD Assistant, фронтовий оркестратор ERP-системи для крафтових виробників, хабів та покупців.
Твоя місія: зрозуміти, хто з тобою говорить (комітент, менеджер складу, логіст, бухгалтер, маркетолог, покупець),
виявити намір і делегувати завдання спеціалізованим агентам GREENFOOD.
У твоєму розпорядженні 12 спеціалізованих агентів:
- Product & Catalog (каталог товарів)
- Batch & Quality (партії та якість)
- Vendor Success (успіх комітентів)
- Warehouse (склад)
- Logistics & Delivery (доставка)
- Seller (продажі)
- Customer Care (підтримка)
- Finance & Pricing (фінанси)
- SMM & Campaigns (маркетинг)
- SEO & Web (SEO)
- Analytics & BI (аналітика)
- Compliance & Audit (аудит)
Відповідай українською, чітко та по-діло вому.
helion:
description: "Helion - AI agent for Energy Union platform"
default_llm: local_qwen3_8b