Files
Home_assistant/README.md
T
2026-06-10 11:49:01 +03:00

266 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Home AI Assistant
Домашний ИИ-ассистент с REST API, веб-интерфейсом и помидоро-таймером. LLM — OpenRouter (по умолчанию DeepSeek).
## Возможности (MVP)
- Чат с потоковыми ответами (SSE)
- Управление помидоро из чата через tool calling
- REST API для внешних клиентов (Telegram-бот, мобильное приложение)
- Веб-морда: вкладки «Чат» и «Помидоро»
## Быстрый старт
### 1. Настройка окружения
```bash
cp .env.example .env
```
Заполните в `.env`:
```env
OPENROUTER_API_KEY=sk-or-v1-...
BACKEND_PORT=8080
FRONTEND_PORT=3080
```
Если порт занят (например, 3000 уже используется Gitea), смените `FRONTEND_PORT` на свободный.
### 2. Запуск через Docker
```bash
docker compose up --build
```
- Backend API: http://localhost:${BACKEND_PORT:-8080}
- Web UI: http://localhost:${FRONTEND_PORT:-3080}
- Healthcheck: http://localhost:8080/api/v1/health
Порты в `.env`:
| Переменная | По умолчанию | Назначение |
|------------|--------------|------------|
| `BACKEND_PORT` | 8080 | API с хоста |
| `FRONTEND_PORT` | 3080 | Веб-морда с хоста |
| `VITE_DEV_PORT` | 5173 | Frontend при `npm run dev` |
| `TAIGA_PORT` | 9000 | Taiga (фаза 2) |
| `GITEA_PORT` | 3000 | Gitea HTTP (фаза 2) |
| `GITEA_SSH_PORT` | 222 | Gitea SSH (фаза 2) |
| `QDRANT_PORT` | 6333 | Qdrant HTTP (фаза 3) |
### 3. Локальная разработка
**Backend:**
```bash
cd backend
python -m venv .venv
.venv\Scripts\activate # Windows
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8080
```
**Frontend:**
```bash
cd frontend
npm install
npm run dev
```
Vite dev-server: http://localhost:5173 (проксирует `/api` на backend).
## REST API
| Method | Path | Описание |
|--------|------|----------|
| GET | `/api/v1/health` | Healthcheck |
| POST | `/api/v1/chat/sessions` | Создать чат-сессию |
| GET | `/api/v1/chat/sessions` | Список сессий |
| GET | `/api/v1/chat/sessions/{id}` | История сообщений |
| POST | `/api/v1/chat/sessions/{id}/messages` | Отправить сообщение (SSE) |
| DELETE | `/api/v1/chat/sessions/{id}` | Удалить сессию |
| GET | `/api/v1/pomodoro/status` | Статус таймера |
| POST | `/api/v1/pomodoro/start` | Старт `{duration_min, task_note}` |
| POST | `/api/v1/pomodoro/pause` | Пауза |
| POST | `/api/v1/pomodoro/resume` | Продолжить |
| POST | `/api/v1/pomodoro/stop` | Стоп `{result, completed}` |
| GET | `/api/v1/pomodoro/history` | История сессий |
| GET | `/api/v1/projects` | Проекты Taiga + привязка Gitea |
| POST | `/api/v1/projects/sync-taiga` | Синхронизировать проекты из Taiga |
| PUT | `/api/v1/projects/{slug}/gitea` | Привязать Gitea repo |
| POST | `/api/v1/work-items` | Создать фичу/баг → Taiga + Gitea |
| GET | `/api/v1/work-items` | Список work items |
| POST | `/api/v1/webhooks/gitea` | Webhook для автозакрытия по push |
## Taiga + Gitea (фаза 2)
Taiga и Gitea работают **на хосте** (не в Docker):
- Taiga: `127.0.0.1:9000``taiga.grigowashere.ru`
- Gitea: `127.0.0.1:3000``git.grigowashere.ru`
Контейнер backend достучится через `host.docker.internal` (настроено в `docker-compose.yml`).
### Настройка `.env`
```env
TAIGA_BASE_URL=http://host.docker.internal:9000
TAIGA_USERNAME=...
TAIGA_PASSWORD=...
TAIGA_PUBLIC_URL=https://taiga.grigowashere.ru
GITEA_BASE_URL=http://host.docker.internal:3000
GITEA_TOKEN=... # Settings → Applications → Generate Token
GITEA_PUBLIC_URL=https://git.grigowashere.ru
GITEA_WEBHOOK_SECRET=... # произвольная строка
```
### Первый запуск
```bash
# 1. Синхронизировать проекты Taiga (ID подтянутся автоматически)
curl -X POST http://localhost:8080/api/v1/projects/sync-taiga
# 2. Привязать Gitea repo к проекту Taiga
curl -X PUT http://localhost:8080/api/v1/projects/home-assistant/gitea \
-H "Content-Type: application/json" \
-d '{"gitea_owner":"Grigo","gitea_repo":"Home_assistant","default_branch":"main"}'
```
### Gitea webhook
В репозитории: **Settings → Webhooks → Add Webhook**:
- URL (выбери один вариант):
- **Рекомендуется:** `https://assistant.example.com/api/v1/webhooks/gitea` — nginx → `127.0.0.1:${BACKEND_PORT}`
- **Если Gitea в Docker:** `http://172.17.0.1:${BACKEND_PORT}/api/v1/webhooks/gitea` — не `127.0.0.1` (это localhost контейнера Gitea)
- Content type: `application/json`
- Secret: значение `GITEA_WEBHOOK_SECRET`
- Events: **Push**
Проверка из контейнера Gitea: `docker exec gitea wget -qO- http://172.17.0.1:8202/api/v1/health`
Test delivery в Gitea должен вернуть **200**, не **0**.
### Автозакрытие по коммиту
В сообщении коммита:
```
fix: кнопка сохранения
Closes gitea #12, taiga #45
```
Закроются Gitea issue #12 и Taiga story #45 (если только один ref — второй найдётся по связи в БД).
### Чат
«Заведи баг: кнопка не сохраняет настройки» → `create_work_item` → Taiga story + Gitea issue + ветка `feature/45-...`.
## Структура проекта
```
backend/ FastAPI, OpenRouter, SQLite, помидоро
frontend/ React + Vite, чат и таймер
data/ SQLite БД (создаётся автоматически)
```
## Память и контекст (фаза 3a)
Долгосрочная память в SQLite, без векторов:
| Слой | Что хранит |
|------|------------|
| **Профиль** | имя, timezone, language, notes |
| **Факты** | устойчивые знания с категорией и важностью |
| **Сводка чата** | краткое содержание длинной сессии |
В system prompt на каждый ответ: персонаж → **время** → память → фитнес → **погода** → помидоро → проекты.
История чата обрезается до 40 последних сообщений; раннее — в `session_summaries`.
**Автоизвлечение:** после каждого ответа LLM анализирует ход диалога и сохраняет
устойчивые факты (`source=auto`). Отключить: `MEMORY_AUTO_EXTRACT=false`.
**UI:** вкладка `/memory` — профиль, факты, JSON-снимок для отладки.
### Tools
- `remember_fact` — «запомни, что…»
- `recall_memories` — поиск по памяти
- `forget_memory` — удалить факт по id
- `update_profile` — имя, часовой пояс и т.д.
- `update_session_summary` — сжать тему длинного чата
### API
| Method | Path | Описание |
|--------|------|----------|
| GET | `/api/v1/memory` | снимок памяти (+ `?session_id=`) |
| GET/PUT | `/api/v1/profile` | профиль |
| GET/POST | `/api/v1/memory/facts` | список / создать факт |
| DELETE | `/api/v1/memory/facts/{id}` | забыть |
| PUT | `/api/v1/memory/sessions/{id}/summary` | сводка чата |
## Фитнес-трекер
Профиль, дневник (еда/вода/вес/тренировки), калькуляторы TDEE, LLM-оценка ккал/БЖУ,
lookup wger + Open Food Facts, напоминания в чат (`💪`), вкладка `/fitness`.
Чат: «обед: гречка 200г, курица 150г», «выпил 300 мл воды», «жим 80×5×3».
## Homelab API (фаза 4)
Интеграции с домашней инфраструктурой:
| Сервис | URL по умолчанию | Назначение |
|--------|------------------|------------|
| Open-Meteo | `http://192.168.1.109:8085` | Погода СПб в контексте и tool `get_weather` |
| ComfyUI | `http://192.168.1.109:8188` | fallback / рофл-watcher |
| RP Chat (aiChatBot) | `http://host.docker.internal:8201` | `generate_image`: sd-prompt + Anima; appearance в `/character` |
| Netdata | `http://host.docker.internal:19999` | Алерты warning/critical → notice в чат |
**Утренний дайджест** (`MORNING_DIGEST_HOUR=8`): погода + RSS (Habr, r/programming по умолчанию).
По запросу: «что на улице», «будет ли дождь» → `get_weather`; полный брифинг → `get_morning_briefing`.
Переменные — в `.env.example` (секция Homelab).
### Проверка доступности
В образе backend нет `curl`/`wget`. Удобнее всего — API-диагностика (из контейнера или с хоста):
```bash
curl -s http://localhost:${BACKEND_PORT:-8202}/api/v1/homelab/status | python3 -m json.tool
```
Или изнутри backend через Python:
```bash
docker compose exec backend python -c "
import httpx
for url in [
'http://192.168.1.109:8085/v1/forecast?latitude=59.93&longitude=30.33&current=temperature_2m',
'http://192.168.1.109:8188/system_stats',
'http://host.docker.internal:19999/api/v1/info',
]:
try:
r = httpx.get(url, timeout=10)
print(url, '->', r.status_code, r.text[:120])
except Exception as e:
print(url, '-> ERROR', e)
"
```
По умолчанию **Anima** (как в aiChatBot): `COMFYUI_UNET` + `COMFYUI_CLIP` + `COMFYUI_VAE` + style LoRA.
`COMFYUI_CHECKPOINT` оставь пустым. Для SD1.5/Pony — укажи checkpoint и очисти `COMFYUI_UNET`.
## Следующие фазы
- RAG по файлам (Qdrant)
- Telegram-бот
- Taiga/fitness в утреннем дайджесте
- Графики веса, LLM-мотивация в напоминаниях
## Модель
По умолчанию: `deepseek/deepseek-chat` через OpenRouter. Альтернатива для болтовни: `google/gemini-2.0-flash`.