Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

487 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RateMeAI (AI Look Studio)

ВСЕГДА ОТВЕЧАТЬ НА РУССКОМ ЯЗЫКЕ!

AI-стилист, который анализирует фото и показывает, как человек воспринимается в разных контекстах (знакомства, карьера, соцсети), с генерацией улучшенных образов.

Стек: Python 3.12 (FastAPI + ARQ + aiogram) + React SPA (Vite + Tailwind) + PostgreSQL + Redis

Платформы: Telegram bot, Web app (Vercel), OK/VK Mini Apps, RU edge

Архитектура (двухрегиональная)

Проект развёрнут в двух независимых регионах с одним codebase:

  • Global (Railway, MARKET_ID=global) — домен ailookstudio.vercel.app, Telegram-бот @AI_Look_Studio_bot (единственный, webhook, Telegram Stars), платежи Xsolla для веба.
  • RU edge (VPS, MARKET_ID=ru) — домен ailookstudio.ru, только веб-приложение, платежи YooKassa. Бота на VPS нет начиная с 1.62.0 — РКН блокирует исходящий трафик к api.telegram.org, поэтому polling из РФ невозможен; webhook-бот живёт на Railway и принимает обновления одинаково для всех языков.

Ключевые инварианты, которые надо знать перед любым изменением кода:

  1. PII RU-юзеров никогда не покидает VPS для веб-флоу. Edge → primary вызовы используют синтетический internal_user_id и extra="forbid" на Pydantic-схеме (см. RemoteAnalysisRequest в src/api/v1/internal.py).
  2. Один Telegram-бот на Railway. Лендинг-ссылки внутри бота выбираются по language_code через settings.resolve_landing_url (src/config.py). Кросс-регион нужен только при link-token redeem: RU edge тянет image_credits бота с Railway через подписанный internal endpoint (src/api/v1/internal_bot.py).
  3. Каждое фото сначала проходит через PrivacyLayer.sanitize_and_normalize (EXIF/ICC strip), потом — в модель. Оригиналы хранятся в Redis-stash 15 мин и удаляются.
  4. Логи маскируют PII через PIIFilter.
  5. Админки две и независимые — у каждого региона свой /admin. AdminStatusBanner (web/src/components/admin/AdminStatusBanner.tsx) делает разделение явным.

Полный документ: docs/ARCHITECTURE.md — содержит диаграммы, описание DNS-rollout'а, CMS-репликацию, бот-маршрутизацию и список ограничений.

Документация

Документ Содержание
docs/ARCHITECTURE.md Двухрегиональная архитектура: домены, PII-инварианты, edge→primary delegation, бот-маршрутизация по языку, DNS rollout
docs/DEPLOYMENT.md CI/CD, Railway, Vercel, RU edge, переменные окружения, чеклисты, troubleshooting
docs/DEVELOPMENT.md Локальный запуск, структура проекта, тесты, гайды по добавлению режимов/стилей/auth
docs/master_product_constitution.md Продуктовая конституция: концепция AI-стилиста, принципы, UX, scoring, pipeline requirements
docs/architecture/reserved.md Карта reserved-кода (orchestrator/advanced/, запасные провайдеры): что там лежит, зачем сохранено, как включать и дорожная карта Phase 2 (Scenario Engine) и Phase 3 (FLUX через FAL.ai)

Runtime сегодня работает по одному single-pass пути (AnalysisPipelineImageGenerationExecutor.single_pass → Reve). Multi-pass планнер, capability-router и fallback-цепочки провайдеров намеренно изолированы в src/orchestrator/advanced/ и под флагами MULTI_PASS_ENABLED / SEGMENTATION_ENABLED / IMAGE_GEN_PROVIDER. Подробности — в docs/architecture/reserved.md.

Деплой на сервер (обязательно читать)

Любое изменение в коде бессмысленно, пока все процессы с очередью не обновлены одной и той же сборкой:

Сервис Зачем
API (app) Принимает фото, пишет задачу и кладёт вход в Redis (ratemeai:task_input:…)
Worker Берёт задачу из ARQ и должен та же логика, что и API (чтение из Redis → пайплайн)
Bot Ходит в API; при изменении контрактов API стоит обновлять и его

Если обновить только API, а воркер оставить старый образ, снова возможны ошибки вроде File not found: inputs/... — воркер не умеет новый протокол.

Что делать после мерджа / смены ветки:

  1. Пересобрать образ(ы) и задеплоить app + worker + bot с одного commit (на Railway — redeploy каждого сервиса или общий pipeline).
  2. Проверка: GET https://<ваш-api>/health — в ответе должны быть version (см. src/version.py) и при необходимости git, если задали DEPLOY_GIT_SHA в env.
  3. В логах воркера при старте должна быть строка Worker started RateMeAI version=… с той же version, что и у API.

Версию приложения поднимайте в src/version.py при каждой выкладке, чтобы по логам и /health было видно расхождение сервер ↔ репозиторий. Описание изменений добавляйте в CHANGELOG.md (newest-first) — src/version.py теперь содержит только литерал APP_VERSION, чтобы не раздувать импорт на старте app/worker/bot.

Переменная API_BASE_URL: кому какой адрес

Компонент Что должен видеть Зачем
app, worker Публичный HTTPS URL API (или туннель типа ngrok в разработке) LocalStorageProvider.get_url строит ссылки на файлы в /storage/.... Эти URL попадают в результат задачи и в share-карточку.
bot (Docker Compose) Внутренний URL сервиса app, например http://app:8000 В docker-compose.yml для сервиса bot задано API_BASE_URL: http://app:8000, чтобы запросы шли по Docker-сети, а не наружу.
Telegram Только публичный HTTPS send_photo по URL заставляет серверы Telegram скачать файл по указанному адресу. http://localhost и приватные IP не подходят.

Итог: в .env для локальной разработки с ботом в Docker оставьте публичный API_BASE_URL для app/worker (туннель или боевой домен), а переопределение для бота задавайте только через Compose, как в репозитории.

Хранилище в проде (несколько инстансов / без общего диска)

При STORAGE_PROVIDER=local файлы лежат на диске; в Compose для app, worker, bot смонтирован один каталог ./storage. Входное фото дублируется в Redis (ratemeai:task_input:<task_id>) до обработки воркером — так анализ не зависит от общего тома между API и worker.

В облаке без общего тома (несколько реплик API или воркера) используйте S3-совместимое хранилище:

  1. Задайте STORAGE_PROVIDER=s3.
  2. Заполните S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY, S3_BUCKET, S3_REGION.
  3. Либо настройте публичный доступ к объектам и укажите S3_PUBLIC_BASE_URL (CDN или публичный endpoint бакета), либо полагайтесь на presigned URL из S3StorageProvider.
  4. Установите S3_PRESIGN_TTL_SECONDS с запасом (например 3600), чтобы ссылка не истекла до того, как пользователь откроет результат в боте.

Для Replicate провайдер передаёт в модель публичный URL референса — объекты должны быть доступны по URL извне.

Локальный запуск (Docker)

cp .env.example .env
# Заполните секреты. API_BASE_URL — публичный, если тестируете Telegram с медиа по URL.
docker compose up --build
  • API: http://localhost:8000
  • Проверка: GET http://localhost:8000/health

Тесты

Интеграционные API-тесты (tests/test_api/) требуют PostgreSQL и Redis на 127.0.0.1 (порты по умолчанию как в Compose). Если сервисы не запущены, эти тесты пропускаются (skipped), остальные всё равно выполняются.

python -m pytest
# или: docker compose up -d postgres redis && python -m pytest

В CI см. .github/workflows/ci.yml.

Ручной смок (чеклист)

  1. Поднять стек, GET /healthstatus, поле version совпадает с src/version.py.
  2. В боте: /start, отправить портретное фото (с лицом).
  3. Режим rating (через /rating): дождаться результата, проверить текст и карточку.
  4. Режим dating: выбор стиля (На прогулке / Студия / Кафе) → сгенерированное фото + скор.
  5. Режим cv: выбор стиля (Корпоративный / Креативный / Нейтральный) → профессиональный образ.
  6. Режим social: выбор стиля (Influencer / Luxury / Casual / Artistic) → образ для соцсетей.
  7. Режим emoji (через /emoji): стикер-аватар из фото.
  8. При ошибке пайплайна: в боте отображается сообщение из error_message задачи.

Releases

Packages

Contributors

Languages