Skip to content

# 🚀 План інтеграції: Vision, Parser, TTS та Grafana

Дата: 2025-11-18
Статус: 📋 В плануванні

## ✅ Поточний стан

### Що вже працює:

- ✅ Голосові повідомлення (STT через Whisper)
- ✅ Фото detection (metadata → NATS)
- ✅ PDF detection (metadata → NATS)
- ✅ Prometheus metrics (Router + Gateway)
- ✅ 3 боти (DAARWIZZ, Helion, GREENFOOD)
- ✅ Helion 502 фікс (timeout 120s)

### Що не інтегровано:

- ⚠️ Vision Encoder (сервіс готовий, але не викликається)
- ⚠️ Parser Service для PDF (сервіс готовий, але не викликається)
- ⚠️ TTS для голосових відповідей
- ⚠️ Grafana дашборди (Grafana працює, дашборди порожні)

## 📋 План імплементації

### 1. Vision Encoder Integration 🖼️ (Пріоритет: 🔴 ВИСОКИЙ)

Мета: Бот може описувати що на фото.

Кроки:

#### 1.1. Оновити `router_handler.py`

Додати обробку `metadata.photo`:

    # В методі _handle_telegram_event():
    if event.metadata and "photo" in event.metadata:
        photo_info = event.metadata["photo"]

        # Викликати Vision Encoder
        vision_result = await self._analyze_photo(
            photo_url=photo_info["file_url"],
            caption=event.text or ""
        )

        # Додати результат Vision до контексту для LLM
        enhanced_text = f"{event.text or ''}\n\n[VISION]: {vision_result}"
        event.text = enhanced_text

#### 1.2. Додати метод `_analyze_photo()`

    async def _analyze_photo(self, photo_url: str, caption: str) -> str:
        """Викликати Vision Encoder Service"""
        try:
            async with httpx.AsyncClient(timeout=60.0) as client:
                response = await client.post(
                    "http://dagi-vision-encoder:9500/analyze",  # TODO: перевірити endpoint
                    json={
                        "image_url": photo_url,
                        "prompt": caption or "Опиши що на цьому зображенні"
                    }
                )
                response.raise_for_status()
                result = response.json()
                return result.get("description", "")
        except Exception as e:
            logger.error(f"❌ Vision Encoder error: {e}")
            return "[Не вдалося проаналізувати зображення]"

#### 1.3. Перевірити Vision Encoder сервіс

    # Перевірити чи працює
    docker ps | grep vision-encoder

    # Перевірити API
    curl -X POST http://localhost:9500/analyze \
      -H "Content-Type: application/json" \
      -d '{"image_url": "https://example.com/image.jpg", "prompt": "Describe this image"}'

Файли для зміни: - `/opt/telegram-infrastructure/telegram-gateway/app/router_handler.py`

Очікуваний результат:

    Ти → 🖼️ [Фото кота] + "Хто це?"
    Бот → На зображенні зображений рудий кіт, який сидить на підвіконні і дивиться у вікно...

### 2. Parser Service Integration 📄 (Пріоритет: 🔴 ВИСОКИЙ)

Мета: Бот може читати PDF і відповідати на питання.

Кроки:

#### 2.1. Оновити `router_handler.py`

Додати обробку `metadata.document`:

    # В методі _handle_telegram_event():
    if event.metadata and "document" in event.metadata:
        doc_info = event.metadata["document"]

        # Викликати Parser Service
        parsed_content = await self._parse_document(
            doc_url=doc_info["file_url"],
            file_name=doc_info["file_name"]
        )

        # Якщо є питання - відповісти на основі parsed_content
        if event.text and event.text != f"[DOCUMENT] {doc_info['file_name']}":
            # Додати parsed content до контексту
            enhanced_text = f"Користувач запитує про документ '{doc_info['file_name']}':\n{event.text}\n\n[DOCUMENT_CONTENT]:\n{parsed_content[:2000]}"
            event.text = enhanced_text
        else:
            # Просто парсинг без питання
            await telegram_listener.send_message(
                agent_id=event.agent_id,
                chat_id=event.chat_id,
                text=f"✅ Документ '{doc_info['file_name']}' оброблено.\n\nЗадай питання про нього!"
            )
            return

#### 2.2. Додати метод `_parse_document()`

    async def _parse_document(self, doc_url: str, file_name: str) -> str:
        """Викликати Parser Service для PDF"""
        try:
            async with httpx.AsyncClient(timeout=90.0) as client:
                # Виклик DAGI Router з mode: "doc_parse"
                response = await client.post(
                    f"{self._router_url}/route",
                    json={
                        "mode": "doc_parse",
                        "agent": "parser",
                        "payload": {
                            "context": {
                                "doc_url": doc_url,
                                "file_name": file_name,
                                "output_mode": "markdown"  # або "chunks" для RAG
                            }
                        }
                    }
                )
                response.raise_for_status()
                result = response.json()

                # Витягнути parsed content
                if "data" in result and "markdown" in result["data"]:
                    return result["data"]["markdown"]
                return result.get("text", "")

        except Exception as e:
            logger.error(f"❌ Parser Service error: {e}")
            return "[Не вдалося прочитати документ]"

#### 2.3. Інтеграція з RAG (опційно)

Для збереження документів у RAG:

    # Після парсингу викликати RAG ingest
    await client.post(
        f"{self._router_url}/route",
        json={
            "mode": "doc_parse",
            "agent": "parser",
            "payload": {
                "context": {
                    "doc_url": doc_url,
                    "file_name": file_name,
                    "output_mode": "chunks",
                    "ingest": True,
                    "dao_id": event.agent_id,
                    "user_id": event.user_id
                }
            }
        }
    )

Файли для зміни: - `/opt/telegram-infrastructure/telegram-gateway/app/router_handler.py`

Очікуваний результат:

    Ти → 📄 whitepaper.pdf
    Бот → ✅ Документ 'whitepaper.pdf' оброблено. Задай питання про нього!

    Ти → "Про що цей документ?"
    Бот → Це whitepaper проєкту MicroDAO, який описує...

### 3. TTS Integration 🔊 (Пріоритет: 🟡 СЕРЕДНІЙ)

Мета: Бот може відповідати голосом.

Кроки:

#### 3.1. Додати опцію для голосових відповідей

Користувач може вибрати режим відповіді (текст або голос).

Варіант 1: Команда `/voice` перемикає режим

    # Зберігати в Memory Service:
    user_preferences = {
        "reply_mode": "voice"  # або "text"
    }

Варіант 2: Реагувати голосом на голосові

    # Якщо користувач надіслав voice → відповісти voice
    if message.voice or message.audio:
        reply_mode = "voice"
    else:
        reply_mode = "text"

#### 3.2. Оновити `router_handler.py`

    async def _send_response(self, event, answer: str, reply_mode: str = "text"):
        if reply_mode == "voice":
            # Синтезувати голос через TTS
            audio_bytes = await self._text_to_speech(answer)

            # Відправити voice message через Telegram
            await telegram_listener.send_voice(
                agent_id=event.agent_id,
                chat_id=event.chat_id,
                audio_bytes=audio_bytes
            )
        else:
            # Звичайний текст
            await telegram_listener.send_message(
                agent_id=event.agent_id,
                chat_id=event.chat_id,
                text=answer
            )

#### 3.3. Додати метод `_text_to_speech()`

    async def _text_to_speech(self, text: str) -> bytes:
        """Викликати TTS Service"""
        try:
            async with httpx.AsyncClient(timeout=60.0) as client:
                response = await client.post(
                    "http://dagi-tts:9001/tts",  # TODO: перевірити endpoint
                    json={
                        "text": text,
                        "voice": "ukrainian_female"  # або "english_male"
                    }
                )
                response.raise_for_status()
                return response.content  # Audio bytes (OGG/MP3)
        except Exception as e:
            logger.error(f"❌ TTS error: {e}")
            return b""  # Fallback to text

#### 3.4. Додати `send_voice()` в `telegram_listener.py`

    async def send_voice(self, agent_id: str, chat_id: int, audio_bytes: bytes):
        """Відправити голосове повідомлення"""
        bot_token = bots_registry.get_token_by_agent(agent_id)
        bot = self._bots.get(bot_token)

        if not bot or not audio_bytes:
            # Fallback to text
            return

        from io import BytesIO
        audio_file = BytesIO(audio_bytes)
        audio_file.name = "voice.ogg"

        await bot.send_voice(
            chat_id=chat_id,
            voice=audio_file
        )

Файли для зміни: - `/opt/telegram-infrastructure/telegram-gateway/app/router_handler.py` - `/opt/telegram-infrastructure/telegram-gateway/app/telegram_listener.py`

Очікуваний результат:

    Ти → 🎤 [Голосове] "Привіт"
    Бот → 🔊 [Голосове] "Привіт! Як справи?"

### 4. Grafana Dashboards 📊 (Пріоритет: 🟢 НИЗЬКИЙ)

Мета: Візуалізація метрик (запити, помилки, latency).

Кроки:

#### 4.1. Створити дашборд "DAARION Services Overview"

Панелі: 1. Total Requests (counter) - `rate(http_requests_total[5m])` 2. Request Duration (histogram) - `histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))` 3. Error Rate (%) - `rate(http_requests_total{status_code=~"5.."}[5m]) / rate(http_requests_total[5m])` 4. Active Bots (gauge) - Custom metric: `telegram_gateway_active_bots` 5. STT Requests (counter) - `rate(http_requests_total{job="dagi-stt"}[5m])` 6. Router Latency (graph) - `http_request_duration_seconds{job="dagi-router"}`

#### 4.2. Створити файл дашборду

`/opt/microdao-daarion/monitoring/grafana/dashboards/daarion_overview.json`

    {
      "dashboard": {
        "title": "DAARION Services Overview",
        "panels": [
          {
            "title": "Total Requests/sec",
            "targets": [
              {
                "expr": "rate(http_requests_total[5m])"
              }
            ]
          },
          // ... інші панелі
        ]
      }
    }

#### 4.3. Імпортувати в Grafana

    # Через UI: http://144.76.224.179:3000
    # Login: admin / admin
    # Dashboards → Import → Upload JSON

Файли для створення: - `/opt/microdao-daarion/monitoring/grafana/dashboards/daarion_overview.json` - `/opt/microdao-daarion/monitoring/grafana/dashboards/telegram_bots.json`

Очікуваний результат: - Красиві графіки в Grafana - Real-time моніторинг всіх сервісів

## 🗓️ Порядок імплементації

### Phase 1 (Сьогодні): Vision + Parser

1. ✅ Vision Encoder integration (~30 хв)
2. ✅ Parser Service integration (~30 хв)
3. ✅ Тестування фото + PDF

### Phase 2 (Завтра/Пізніше): TTS

1. ✅ TTS integration (~45 хв)
2. ✅ `send_voice()` в telegram_listener
3. ✅ Тестування голосових відповідей

### Phase 3 (Опційно): Grafana

1. ✅ Створення дашбордів (~1 год)
2. ✅ Налаштування alerts
3. ✅ Документація

## 📝 Зміни в файлах (Summary)

### Для Vision + Parser:

- `telegram-gateway/app/router_handler.py`: +100 рядків
- `_analyze_photo()`
- `_parse_document()`
- Обробка `metadata.photo` та `metadata.document`

### Для TTS:

- `telegram-gateway/app/router_handler.py`: +50 рядків
- `_text_to_speech()`
- `_send_response()` з підтримкою voice
- `telegram-gateway/app/telegram_listener.py`: +20 рядків
- `send_voice()`

### Для Grafana:

- `monitoring/grafana/dashboards/*.json`: 2 нові файли

## 🚀 Готовий почати?

Рекомендую порядок: 1. Vision Encoder (найпростіше, одразу побачиш результат) 2. Parser Service (корисно для документів) 3. TTS (якщо треба голосові відповіді) 4. Grafana (коли все працює)

Який пункт імплементуємо першим? 🖼️📄🔊📊

Створено: 2025-11-18
Автор: Assistant (via Cursor)