# 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` | `generate_image`, редкий «рофл» в чат | | 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¤t=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`.