Home AI Assistant
Домашний ИИ-ассистент с REST API, веб-интерфейсом и помидоро-таймером. LLM — OpenRouter (по умолчанию DeepSeek).
Возможности (MVP)
- Чат с потоковыми ответами (SSE)
- Управление помидоро из чата через tool calling
- REST API для внешних клиентов (Telegram-бот, мобильное приложение)
- Веб-морда: вкладки «Чат» и «Помидоро»
Быстрый старт
1. Настройка окружения
cp .env.example .env
Заполните в .env:
OPENROUTER_API_KEY=sk-or-v1-...
BACKEND_PORT=8080
FRONTEND_PORT=3080
Если порт занят (например, 3000 уже используется Gitea), смените FRONTEND_PORT на свободный.
2. Запуск через Docker
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:
cd backend
python -m venv .venv
.venv\Scripts\activate # Windows
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8080
Frontend:
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
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=... # произвольная строка
Первый запуск
# 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— удалить факт по idupdate_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».
Списки покупок
Несколько списков, позиции с количеством, отметка «куплено». Вкладка /shopping, tools в чате (add_shopping_items, list_shopping_lists, …).
Чат: «добавь молоко и хлеб в продукты», «что в списке покупок», «отметь молоко купленным».
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-диагностика (из контейнера или с хоста):
curl -s http://localhost:${BACKEND_PORT:-8202}/api/v1/homelab/status | python3 -m json.tool
Или изнутри backend через Python:
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.