feat: Add DAARWIZZ agent with personality
DAARWIZZ - Official AI agent for DAARION.city ecosystem Changes: - gateway-bot/daarwizz_prompt.txt: System prompt defining DAARWIZZ personality - gateway-bot/http_api.py: Load and inject DAARWIZZ context into Router requests - gateway-bot/Dockerfile: Copy DAARWIZZ prompt file to container - providers/llm_provider.py: Support context.system_prompt from Gateway Features: - Telegram webhook sends agent='daarwizz' to Router - System prompt loaded from file (customizable) - LLM receives full DAARWIZZ context + RBAC - Discord support included Usage: - User messages DAARWIZZ in Telegram - Gateway enriches with system prompt + RBAC - Router routes to LLM with full context - DAARWIZZ responds with DAO-aware answers Next: Set TELEGRAM_BOT_TOKEN and test first dialog
This commit is contained in:
@@ -2,21 +2,29 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
LABEL maintainer="DAARION.city Team"
|
||||
LABEL description="Bot Gateway - Telegram/Discord webhook handler"
|
||||
LABEL description="Bot Gateway - Telegram/Discord webhook handler with DAARWIZZ"
|
||||
LABEL version="0.2.0"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY ../requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
# Copy requirements and install
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt httpx
|
||||
|
||||
COPY . .
|
||||
# Copy gateway code
|
||||
COPY gateway-bot/ /app/gateway-bot/
|
||||
|
||||
# Copy DAARWIZZ system prompt
|
||||
COPY gateway-bot/daarwizz_prompt.txt /app/gateway-bot/daarwizz_prompt.txt
|
||||
|
||||
EXPOSE 9300
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:9300/health || exit 1
|
||||
|
||||
ENV DAARWIZZ_NAME=DAARWIZZ
|
||||
ENV DAARWIZZ_PROMPT_PATH=/app/gateway-bot/daarwizz_prompt.txt
|
||||
|
||||
CMD ["python", "-m", "gateway_bot.main", "--host", "0.0.0.0", "--port", "9300"]
|
||||
|
||||
37
gateway-bot/daarwizz_prompt.txt
Normal file
37
gateway-bot/daarwizz_prompt.txt
Normal file
@@ -0,0 +1,37 @@
|
||||
Ти — DAARWIZZ, офіційний AI-агент екосистеми DAARION.city.
|
||||
|
||||
Твоя роль:
|
||||
1. Допомагати учасникам спільноти розібратися з microDAO, ролями, правами доступу і DAO-процесами.
|
||||
2. Пояснювати просто, конкретно і практично. Уникай довгої теорії, одразу давай корисні кроки.
|
||||
3. Орієнтуватися на безпеку: не розкривати інформацію або дії, якщо у користувача немає відповідних прав (entitlements).
|
||||
4. Якщо не знаєш точної відповіді — чесно це скажи і запропонуй, що можна зробити/кого запитати.
|
||||
|
||||
Контекст:
|
||||
- Ти працюєш всередині системи DAGI Stack (Router + DevTools + CrewAI + microDAO RBAC).
|
||||
- Ти отримуєш від Gateway інформацію про: dao_id, user_id, roles[], entitlements[].
|
||||
- Ти відповідаєш від імені системи, але не прикидаєшся людиною.
|
||||
|
||||
Обмеження:
|
||||
- Не давай юридичних, фінансових або медичних порад.
|
||||
- Не вигадуй факти про DAO або токеноміку, яких немає в офіційних документах DAARION/microDAO.
|
||||
- Якщо питання виходить за межі DAARION/microDAO, дай коротку відповідь і поверни розмову в контекст спільноти.
|
||||
|
||||
Стиль:
|
||||
- Дружній, але професійний.
|
||||
- Короткі абзаци, без води.
|
||||
- Якщо користувач просить інструкцію — давай її покроково, нумерованим списком.
|
||||
|
||||
Про DAARION:
|
||||
- DAARION.city — це екосистема децентралізованих автономних організацій (microDAO).
|
||||
- Кожен microDAO має свою структуру ролей: admin, member, contributor, guest.
|
||||
- Система RBAC (Role-Based Access Control) контролює доступ до функцій.
|
||||
- Учасники можуть голосувати за пропозиції, виконувати завдання, отримувати винагороди.
|
||||
|
||||
Твої можливості:
|
||||
- Відповідати на запитання про DAO, ролі, процеси
|
||||
- Пояснювати, як використовувати систему
|
||||
- Допомагати новим учасникам (onboarding)
|
||||
- Перевіряти права доступу перед відповіддю
|
||||
- Запускати workflows (через mode=crew) для складних завдань
|
||||
|
||||
Якщо користувач запитує про конкретний microDAO, використовуй інформацію з dao_id та rbac контексту.
|
||||
@@ -3,6 +3,8 @@ Bot Gateway HTTP API
|
||||
Handles incoming webhooks from Telegram, Discord, etc.
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional
|
||||
from datetime import datetime
|
||||
|
||||
@@ -16,6 +18,36 @@ logger = logging.getLogger(__name__)
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
# ========================================
|
||||
# DAARWIZZ Configuration
|
||||
# ========================================
|
||||
|
||||
DAARWIZZ_NAME = os.getenv("DAARWIZZ_NAME", "DAARWIZZ")
|
||||
DAARWIZZ_PROMPT_PATH = os.getenv(
|
||||
"DAARWIZZ_PROMPT_PATH",
|
||||
str(Path(__file__).parent / "daarwizz_prompt.txt"),
|
||||
)
|
||||
|
||||
|
||||
def load_daarwizz_prompt() -> str:
|
||||
"""Load DAARWIZZ system prompt from file"""
|
||||
try:
|
||||
p = Path(DAARWIZZ_PROMPT_PATH)
|
||||
if not p.exists():
|
||||
logger.warning(f"DAARWIZZ prompt file not found: {DAARWIZZ_PROMPT_PATH}")
|
||||
return f"Ти — {DAARWIZZ_NAME}, AI-агент екосистеми DAARION.city. Допомагай учасникам з DAO-процесами."
|
||||
|
||||
prompt = p.read_text(encoding="utf-8")
|
||||
logger.info(f"DAARWIZZ system prompt loaded ({len(prompt)} chars)")
|
||||
return prompt
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading DAARWIZZ prompt: {e}")
|
||||
return f"Ти — {DAARWIZZ_NAME}, AI-агент екосистеми DAARION.city."
|
||||
|
||||
|
||||
DAARWIZZ_SYSTEM_PROMPT = load_daarwizz_prompt()
|
||||
|
||||
|
||||
# ========================================
|
||||
# Request Models
|
||||
# ========================================
|
||||
@@ -90,38 +122,45 @@ async def telegram_webhook(update: TelegramUpdate):
|
||||
|
||||
logger.info(f"Telegram message from {username} (tg:{user_id}) in chat {chat_id}: {text[:50]}")
|
||||
|
||||
# Build request to Router
|
||||
# Build request to Router with DAARWIZZ context
|
||||
router_request = {
|
||||
"prompt": text,
|
||||
"mode": "chat",
|
||||
"source": "telegram",
|
||||
"dao_id": dao_id,
|
||||
"user_id": f"tg:{user_id}",
|
||||
"session_id": f"tg:{chat_id}:{dao_id}",
|
||||
"message": text,
|
||||
"payload": {
|
||||
"message": text,
|
||||
"agent": "daarwizz", # DAARWIZZ agent identifier
|
||||
"metadata": {
|
||||
"source": "telegram",
|
||||
"dao_id": dao_id,
|
||||
"user_id": f"tg:{user_id}",
|
||||
"session_id": f"tg:{chat_id}:{dao_id}",
|
||||
"username": username,
|
||||
"chat_id": chat_id,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"agent_name": DAARWIZZ_NAME,
|
||||
"system_prompt": DAARWIZZ_SYSTEM_PROMPT,
|
||||
# RBAC context will be injected by Router
|
||||
},
|
||||
}
|
||||
|
||||
# Send to Router
|
||||
router_response = await send_to_router(router_request)
|
||||
logger.info(f"Sending to Router: agent=daarwizz, dao={dao_id}, user=tg:{user_id}")
|
||||
response = await send_to_router(router_request)
|
||||
|
||||
# TODO: Send response back to Telegram via Bot API
|
||||
# For now, just return the router response
|
||||
# Extract response text
|
||||
if isinstance(response, dict):
|
||||
answer_text = response.get("response", "Вибач, я зараз не можу відповісти.")
|
||||
else:
|
||||
answer_text = "Вибач, сталася помилка."
|
||||
|
||||
logger.info(f"Router response: {answer_text[:100]}")
|
||||
|
||||
# Send response back to Telegram
|
||||
await send_telegram_message(chat_id, answer_text)
|
||||
|
||||
return {"ok": True, "agent": "daarwizz"}
|
||||
|
||||
return {
|
||||
"status": "ok",
|
||||
"processed": True,
|
||||
"router_response": router_response
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Telegram webhook error: {e}")
|
||||
logger.error(f"Error handling Telegram webhook: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@@ -151,50 +190,87 @@ async def discord_webhook(message: DiscordMessage):
|
||||
user_id = author.get("id", "unknown")
|
||||
username = author.get("username", "")
|
||||
|
||||
# Get DAO ID for this guild/channel
|
||||
dao_id = get_dao_id(guild_id, "discord")
|
||||
# Get DAO ID for this channel
|
||||
dao_id = get_dao_id(channel_id, "discord")
|
||||
|
||||
logger.info(f"Discord message from {username} (dc:{user_id}) in guild {guild_id}: {text[:50]}")
|
||||
logger.info(f"Discord message from {username} (discord:{user_id}): {text[:50]}")
|
||||
|
||||
# Build request to Router
|
||||
# Build request to Router with DAARWIZZ context
|
||||
router_request = {
|
||||
"prompt": text,
|
||||
"mode": "chat",
|
||||
"source": "discord",
|
||||
"dao_id": dao_id,
|
||||
"user_id": f"dc:{user_id}",
|
||||
"session_id": f"dc:{channel_id}:{dao_id}",
|
||||
"message": text,
|
||||
"payload": {
|
||||
"message": text,
|
||||
"agent": "daarwizz",
|
||||
"metadata": {
|
||||
"source": "discord",
|
||||
"dao_id": dao_id,
|
||||
"user_id": f"discord:{user_id}",
|
||||
"session_id": f"discord:{channel_id}:{dao_id}",
|
||||
"username": username,
|
||||
"channel_id": channel_id,
|
||||
"guild_id": guild_id,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"agent_name": DAARWIZZ_NAME,
|
||||
"system_prompt": DAARWIZZ_SYSTEM_PROMPT,
|
||||
},
|
||||
}
|
||||
|
||||
# Send to Router
|
||||
router_response = await send_to_router(router_request)
|
||||
response = await send_to_router(router_request)
|
||||
|
||||
# TODO: Send response back to Discord via Bot API
|
||||
# Extract response text
|
||||
if isinstance(response, dict):
|
||||
answer_text = response.get("response", "Sorry, I can't respond right now.")
|
||||
else:
|
||||
answer_text = "Sorry, an error occurred."
|
||||
|
||||
logger.info(f"Router response: {answer_text[:100]}")
|
||||
|
||||
# TODO: Send response back to Discord
|
||||
# await send_discord_message(channel_id, answer_text)
|
||||
|
||||
return {"ok": True, "agent": "daarwizz", "response": answer_text}
|
||||
|
||||
return {
|
||||
"status": "ok",
|
||||
"processed": True,
|
||||
"router_response": router_response
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Discord webhook error: {e}")
|
||||
logger.error(f"Error handling Discord webhook: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ========================================
|
||||
# Helper Functions
|
||||
# ========================================
|
||||
|
||||
async def send_telegram_message(chat_id: str, text: str):
|
||||
"""Send message to Telegram chat"""
|
||||
import httpx
|
||||
|
||||
telegram_token = os.getenv("TELEGRAM_BOT_TOKEN")
|
||||
if not telegram_token:
|
||||
logger.error("TELEGRAM_BOT_TOKEN not set")
|
||||
return
|
||||
|
||||
url = f"https://api.telegram.org/bot{telegram_token}/sendMessage"
|
||||
payload = {
|
||||
"chat_id": chat_id,
|
||||
"text": text,
|
||||
"parse_mode": "Markdown",
|
||||
}
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.post(url, json=payload, timeout=10.0)
|
||||
response.raise_for_status()
|
||||
logger.info(f"Telegram message sent to chat {chat_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error sending Telegram message: {e}")
|
||||
|
||||
|
||||
@router.get("/health")
|
||||
async def health():
|
||||
"""Health check endpoint"""
|
||||
return {
|
||||
"status": "healthy",
|
||||
"service": "bot-gateway"
|
||||
"agent": DAARWIZZ_NAME,
|
||||
"system_prompt_loaded": len(DAARWIZZ_SYSTEM_PROMPT) > 0,
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
}
|
||||
|
||||
@@ -156,8 +156,20 @@ class LLMProvider(Provider):
|
||||
)
|
||||
|
||||
def _get_system_prompt(self, req: RouterRequest) -> Optional[str]:
|
||||
"""Get system prompt based on agent"""
|
||||
# This can be enhanced to load from config
|
||||
"""Get system prompt based on agent or context"""
|
||||
# 1. Check if context.system_prompt provided (e.g., from Gateway)
|
||||
context = req.payload.get("context") or {}
|
||||
if "system_prompt" in context:
|
||||
return context["system_prompt"]
|
||||
|
||||
# 2. Agent-specific system prompts
|
||||
if req.agent == "daarwizz":
|
||||
return (
|
||||
"Ти — DAARWIZZ, офіційний AI-агент екосистеми DAARION.city. "
|
||||
"Допомагай учасникам з microDAO, ролями та процесами. "
|
||||
"Відповідай коротко, практично, враховуй RBAC контекст користувача."
|
||||
)
|
||||
|
||||
if req.agent == "devtools":
|
||||
return (
|
||||
"Ти - DevTools Agent в екосистемі DAARION.city. "
|
||||
@@ -165,4 +177,5 @@ class LLMProvider(Provider):
|
||||
"рефакторингом та написанням тестів. "
|
||||
"Відповідай коротко, конкретно, з прикладами коду коли потрібно."
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user