Files
Mavis 2394eff1c0 Initial commit: OmniSVG-Lite MVP before live-streaming work
- LM Studio client (httpx-based, OpenAI-compatible)
- SVG validator (lxml, whitelist tags, no <script>/<foreignObject>/http refs)
- PNG renderer (resvg-py primary, cairosvg fallback - no native cairo dep)
- History (SQLite, tracks raw/validated/preview paths)
- Gradio UI on 127.0.0.1:8788 with:
  * mode radio (icon/illustration)
  * n_candidates slider (default 1)
  * image upload for image-to-SVG
  * LM Studio URL/token inputs
  * model dropdown + refresh button
- prompts/ with system_icon.txt, system_illustration.txt, few_shot_examples.txt
- docs/spec.md, docs/design.md
- 122 unit/integration tests passing
2026-06-13 15:32:54 +03:00

17 KiB
Raw Permalink Blame History

OmniSVG-Lite — Spec

MVP-спецификация. Цель: дать пользователю веб-приложение, в котором можно из текста или картинки получить N кандидатов SVG в одном из двух режимов (icon / illustration), посмотреть PNG-превью и сохранить историю генераций.


1. Цели и не-цели

Цель MVP:

  • Локально запускаемый Gradio UI, который ходит в LM Studio (OpenAI-compatible) и возвращает валидный SVG + PNG-превью.
  • Один пользователь, без авторизации, без облака.
  • История генераций хранится в локальной SQLite.

Не-цели (Out of Scope для MVP):

  • Редактирование SVG в браузере (paint / drag).
  • Экспорт в Lottie / GIF / видео.
  • Batch-генерация (CSV промптов, генерация по расписанию).
  • Авторизация, multi-user, разграничение прав.
  • Деплой / Docker / CI.
  • Per-mode температура — единый слайдер на UI.
  • Fine-tune, RLHF, оценка качества моделей.
  • Поддержка моделей, отличных от LM Studio (Ollama, vLLM, OpenAI cloud) — клиент пишется под OpenAI-compatible chat completions, но другие провайдеры не тестируются.

2. Режимы генерации

2.1. Mode: icon

Назначение: маленькие, узнаваемые, плоские или с лёгкой стилизацией иконки для UI. Допустимы монохром и 2–3 цвета. Геометрия упрощённая, без мелких деталей.

Требования к SVG:

  • viewBox="0 0 64 64" (фиксировано, чтобы иконки были консистентны).
  • Геометрия привязана к пиксельной сетке (целочисленные координаты ±0.5).
  • Без градиентов со множеством stops, без фильтров (feGaussianBlur и т.п.).
  • Допустимы: <path>, <rect>, <circle>, <ellipse>, <line>, <polygon>, <polyline>, <g>, <text>, базовые <linearGradient>/<radialGradient>.

Промпт-стиль (направление для пользователя): "outline battery", "filled magnifying glass", "settings gear, line style".

2.2. Mode: illustration

Назначение: полноценные сцены / объекты / паттерны. Больше деталей, больше цветов, допустимы градиенты и простые фильтры.

Требования к SVG:

  • viewBox="0 0 512 512" (фиксировано).
  • Допустимы все валидные теги SVG 1.1 кроме <script> и <foreignObject>.
  • Допустимы простые <filter> (без <feImage> на внешний URL).
  • Палитра ограничена заявленной пользователем (если указана).

Промпт-стиль: "flat illustration of a fox in a forest, autumn colors", "isometric server room, blue and purple palette".

2.3. Сводная таблица

Параметр icon illustration
viewBox 0 0 64 64 0 0 512 512
Размер файла (лимит) 32 KB 256 KB
Допустимые теги Базовые shapes + g + text Полный SVG 1.1 минус опасные
Градиенты ≤ 2 stops без ограничений
Фильтры нет простые (без feImage на URL)
Текст допустим, но не основной канал допустим
Дефолтный n_candidates 4 2

3. Типы задач

3.1. text-to-SVG

  • Вход: prompt: str (1–1000 символов), mode ∈ {icon, illustration}, n_candidates: int ∈ [1, 8].
  • Опционально: палитра (поле в UI, не обязательное).
  • Модель получает только текст.

3.2. image-to-SVG

  • Вход: prompt: str (1–1000 символов), image: PIL.Image | file path, mode ∈ {icon, illustration}, n_candidates: int ∈ [1, 8].
  • Допустимые форматы картинки: PNG, JPEG, WEBP. Максимум 10 MB, до 4096×4096.
  • Картинка передаётся в модель как image_url (data: URL, base64).
  • В промпте добавляется явная инструкция: "Recreate the visual content of the attached image as SVG. Do not describe, just generate the markup."

4. Поведение N кандидатов

Логика выбора лучшего кандидата (для MVP — простая, детерминированная):

  1. Из N ответов модели берём только валидные (прошедшие lxml-валидацию).
  2. Если валидных 0 — показываем ошибку "no_valid_candidates" (см. §6).
  3. Каждому валидному кандидату считаем score = размер_файла_KB (инвертированный: меньше = лучше). Это прокси "компактность", не качество.
  4. Лучший = кандидат с минимальным score.
  5. Показ в UI:
    • Gallery отображает PNG-превью всех валидных кандидатов в сетке.
    • Лучший помечается визуально: бейдж "★ best" над PNG.
    • В выпадашке/таблице истории сохраняются все валидные SVG + пути к PNG.

Замечание: для MVP не используем VLM-as-judge или pairwise — это сильно увеличит latency и стоимость. Выбор по размеру файла — намеренно простой.


5. User Stories + Acceptance Criteria

US-1. Сгенерировать иконку по текстовому промпту

As a дизайнер, I want ввести текстовое описание иконки и получить N вариантов SVG, so that я могу быстро подобрать визуал для кнопки/меню.

Acceptance Criteria (Given/When/Then):

  • Given UI запущен и LM Studio отвечает, when я выбираю mode=icon, ввожу prompt="filled magnifying glass", n_candidates=4, then:
    • отправляется ровно 1 запрос в LM Studio с n=4,
    • получаю 4 PNG-превью в Gallery (или меньше, если часть невалидна),
    • у лучшего есть бейдж "★ best",
    • история в SQLite содержит запись со всеми raw/validated SVG.
  • Given промпт пустой или длиннее 1000 символов, when я нажимаю Generate, then вижу gr.Warning "Prompt must be 11000 characters", запрос в LM Studio НЕ отправляется.
  • Given LM Studio не отвечает 30+ секунд, when идёт генерация, then запрос отменяется, UI показывает ошибку "LM Studio timeout".

US-2. Сгенерировать иллюстрацию по картинке

As a иллюстратор, I want загрузить референс-картинку и текстовое описание, получить N SVG, so that я могу быстро сделать vector-версию понравившегося эскиза.

Acceptance Criteria:

  • Given UI запущен, when я загружаю PNG/JPEG/WEBP, ввожу prompt="flat illustration, blue and teal palette", выбираю mode=illustration, n_candidates=2, then:
    • картинка кодируется в base64 data: URL и отправляется в LM Studio,
    • получаю до 2 PNG в Gallery,
    • все raw-ответы и валидные SVG попадают в историю.
  • Given загружена картинка > 10 MB или > 4096×4096, when я нажимаю Generate, then UI отклоняет с ошибкой валидации до отправки в LM Studio.

US-3. Посмотреть и скачать результат

As a пользователь, I want увидеть PNG-превью, скачать выбранный SVG и PNG, so that я могу сразу использовать результат в своих задачах.

Acceptance Criteria:

  • Given генерация завершилась с ≥ 1 валидным кандидатом, when я смотрю на Gallery, then:
    • каждая картинка кликабельна,
    • под/возле картинки — кнопки Download SVG и Download PNG,
    • скачиваются именно те файлы, которые видны на превью (source of truth — путь в preview_paths).
  • Given я нажимаю Download SVG, then браузер получает файл с MIME image/svg+xml и расширением .svg.

US-4. Просмотреть историю генераций

As a пользователь, I want видеть таблицу последних генераций и кликнуть строку, чтобы повторно открыть результаты, so that я могу вернуться к удачному варианту.

Acceptance Criteria:

  • Given UI запущен, when я смотрю на History, then:
    • таблица показывает последние 50 записей (id, prompt, mode, model, n, status, created_at),
    • новые записи появляются вверху,
    • строки сортируются по created_at DESC.
  • Given я кликаю строку в History, when выбираю запись, then Gallery обновляется превью из preview_paths этой записи, raw SVG подгружается в отдельный code-block для копирования.
  • Given приложение перезапущено, when я открываю History, then данные сохранились (SQLite на диске).

US-5. Контроль параметров (температура, n)

As a пользователь, I want управлять количеством кандидатов и креативностью, so that балансировать скорость/стоимость и разнообразие.

Acceptance Criteria:

  • Given UI запущен, when я двигаю n_candidates slider, then диапазон 1–8, шаг 1, дефолт зависит от mode (icon=4, illustration=2).
  • Given UI запущен, when я двигаю temperature slider, then диапазон 0.0–1.5, шаг 0.05, дефолт 0.4.
  • Given temperature=0, when идёт генерация, then отправляется параметр temperature=0 (детерминированный режим).

6. Edge Cases (таблица)

# Сценарий Поведение
1 Пустой промпт gr.Warning "Prompt must be 11000 characters", запрос не отправляется.
2 Промпт > 1000 символов То же, что (1).
3 Картинка > 10 MB / > 4096×4096 / не PNG/JPEG/WEBP Отклоняется до LM Studio, gr.Warning с указанием ограничения.
4 LM Studio не отвечает (timeout 30s) gr.Error "LM Studio timeout at ", статус генерации error, в лог пишется traceback.
5 LM Studio вернул 5xx gr.Error "LM Studio error: ", статус error.
6 Модель вернула не-SVG (текст/JSON/мусор) Парсер пытается вытащить <svg>...</svg> regex'ом; если не нашёл — кандидат помечается invalid, идёт в history со status=invalid, в Gallery не попадает.
7 SVG не well-formed XML lxml бросает XMLSyntaxError → кандидат invalid, в лог WARNING, в history.
8 Отсутствует / кривой viewBox ValidatorError("missing_viewbox") → invalid.
9 Внутри <script> или <foreignObject> ValidatorError("disallowed_tag") → invalid.
10 http:// / https:// в href / xlink:href / url(...) ValidatorError("external_ref") → invalid.
11 Размер файла > лимита (32 KB / 256 KB) ValidatorError("too_large") → invalid.
12 Все N кандидатов invalid gr.Info "All N candidates failed validation. See history for details.", в history — запись со status=all_invalid.
13 Картинка в image-to-SVG повреждена PIL при открытии бросает UnidentifiedImageErrorgr.Warning "Cannot read image", запрос не отправляется.
14 Пользователь жмёт Generate повторно во время генерации Кнопка interactive=False на время выполнения, повторный клик игнорируется.

7. Не-функциональные требования

Категория Требование
Performance Генерация 1 иконки (n=1) — p95 ≤ 20 секунд на одной 5090 / Ryzen 9.
Performance PNG-рендер одного SVG — p95 ≤ 200 ms.
Privacy Никакие промпты/картинки не уходят за пределы LM Studio на этой машине.
Reliability Падение LM Studio не валит UI: ошибка показывается, остальные кнопки работают.
Storage SQLite WAL mode, data/omnisvg.db. Превью PNG — data/previews/<id>_<n>.png.
Logging INFO для старта/конца генерации, WARNING для invalid, ERROR для LM Studio errors.
Concurrency MVP — однопользовательский, очередь не требуется. UI блокирует кнопку на время генерации.

8. Открытые вопросы

  • Требуется ли streaming в UI (как в LM Studio)? Для MVP — нет, выбран non-stream режим: n>1 плохо сочетается со streaming, плюс парсить инкрементально тяжелее.
  • Нужна ли кнопка "regenerate" с теми же параметрами? Не вошла в MVP, но UI должен позволять скопировать промпт обратно в textbox.
  • Нужен ли экспорт PNG в высоком разрешении (1024×1024)? Нет, достаточно рендера в нативный viewBox.