Split SwarmUI portraits into a model catalog and layered prompts.
Picker is Swarm intersect config, preset style/shot inherit model defaults, and living saves lift the old preset shape.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
| [Пропуск сборки](skip-stale-build.md) | 49 |
|
||||
| [Что нового](whats-new.md) | 54 |
|
||||
| [Состояние экрана](view-state.md) | 55 |
|
||||
| [Модели портретов](portrait-models.md) | 60 |
|
||||
|
||||
Люди: [`../02-people/people.md`](../02-people/people.md). Штат: [`../03-staffing/staffing.md`](../03-staffing/staffing.md).
|
||||
Расписание: [`../04-schedule/schedule.md`](../04-schedule/schedule.md). Присутствие: [`../05-ai/ai.md`](../05-ai/ai.md).
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
Часть [Вне очереди](README.md). Соседей по папке не читать, если задача не про них.
|
||||
|
||||
# Модели Swarm и слои промпта
|
||||
|
||||
### Зачем
|
||||
|
||||
Пикер моделей не должен предлагать то, чего нет в нашем конфиге. Дефолты генерации живут
|
||||
на модели; пресет задаёт стиль и тип кадра и может переопределить дефолты. Промпт собирается
|
||||
слоями, а не одним «базовым» текстом пресета.
|
||||
|
||||
### Было / Стало / Почему
|
||||
|
||||
**Было.** Пресет сам держал модель, steps/cfg/sampler и `positive`. Список Swarm только
|
||||
наполнял комбобоксы. «Свой промпт» у custom **заменял** текст кадра. `portraitSettings` в
|
||||
сейве — копия этого файла.
|
||||
|
||||
**Стало.** В конфиге каталог `models` (whitelist) и пресеты. Пикер = Swarm ∩ каталог; при
|
||||
офлайне Swarm — все id из каталога. У модели: закрытый набор настроек генерации (steps, cfg,
|
||||
clip skip, sampler, scheduler, seed, LoRA) и обычно пустые base positive/negative. У пресета:
|
||||
модель, **стиль** (бывший `positive`), доп. negative, размеры и **тип кадра** на avatar/custom/full.
|
||||
Оверрайды генерации у пресета — nullable, в UI под спойлером. Промпт:
|
||||
|
||||
1. base positive модели
|
||||
2. стиль пресета
|
||||
3. тип кадра
|
||||
4. «Свой промпт» (`promptExtra`, только custom; после кадра, не вместо)
|
||||
5. поза (слот пустой, без поля в UI)
|
||||
6. внешность: кто это, возраст, тело
|
||||
7. одежда
|
||||
8. хвост пустой
|
||||
|
||||
Negative: модель + необязательное дополнение пресета. Ширина/высота остаются на виде кадра.
|
||||
|
||||
**Почему.** Модель задаёт «как считать картинку»; пресет — «какой это школьный снимок».
|
||||
Гость по-прежнему рисует с копии школы, не с глобального файла.
|
||||
|
||||
### Что не входит
|
||||
|
||||
Зеркало всех `ListT2IParams`. Кнопка настроек в меню. Редактор каталога «добавить модель из
|
||||
Swarm». Поза в UI. Бамп версии сокета. Перегенерация уже лежащих PNG.
|
||||
|
||||
Живые школы: `NormalizeAfterLoad` поднимает старый пресет (`positive` → `style`, kind
|
||||
`positive` → `shotType`, уникальные `model` → `models[]`). Старые картинки не трогаем.
|
||||
@@ -0,0 +1,45 @@
|
||||
# Фаза 60. Модели портретов
|
||||
|
||||
## Зависимости
|
||||
|
||||
Нет. Портреты и `swarmui.json` уже живут вне среза.
|
||||
|
||||
## Зачем
|
||||
|
||||
Пикер показывает только модели из нашего каталога, которые есть в Swarm. Дефолты генерации
|
||||
на модели; пресет даёт стиль и тип кадра и может переопределить дефолты под спойлером.
|
||||
Промпт собирается слоями.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [x] `swarmui.json`: каталог `models` на все четыре модели из текущего Swarm; пресеты ссылаются
|
||||
на модель, `style` вместо бывшего base `positive`, `shotType` на виде кадра
|
||||
- [x] Пикер = Swarm ∩ каталог; Swarm офлайн — id из каталога. Чего нет в конфиге, с пикера нет
|
||||
- [x] Сборка промпта: модель → стиль → кадр → свой промпт → пустая поза → внешность (кто, возраст,
|
||||
тело) → одежда. Custom **не** заменяет тип кадра. Negative: модель + доп. пресета
|
||||
- [x] UI диалога пресетов (создание школы): стиль и кадр сверху; дефолты модели видны; оверрайды
|
||||
пресета под `<details>`. Строки через `t(...)`
|
||||
- [x] Живой сейв поднимается в `NormalizeAfterLoad`. HTTP `GET`/`PUT /api/settings/swarmui` и
|
||||
`portraitSettings` — новая форма. Сокет не бампить. [`protocol.md`](../../protocol.md) тем же
|
||||
коммитом
|
||||
|
||||
## Тесты, без которых фаза не закрыта
|
||||
|
||||
- [x] Лифт старого пресета: `positive` → `style`, kind `positive` → `shotType`, модель в `models`
|
||||
- [x] Resolve без оверрайда берёт steps/cfg с модели; с оверрайдом — с пресета
|
||||
- [x] Пикер: пересечение Swarm ∩ каталог; офлайн — каталог; лишняя модель Swarm отброшена
|
||||
- [x] Промпт: порядок слоёв; custom держит тип кадра и `promptExtra`; пустая поза не попадает в текст
|
||||
- [x] `GET /api/settings/swarmui` отдаёт `models`; create копирует их в сейв
|
||||
- [x] Диалог: поле стиля, спойлер оверрайдов, в селекте модели нет id вне каталога при живом Swarm
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- В диалоге создания школы в пикере только модели из `swarmui.json`, которые ответил Swarm
|
||||
- Смена модели подставляет её дефолты; пресет может перебить их под спойлером
|
||||
- «Показать промпт» на карточке: стиль, затем кадр, затем тело и одежда; свой промпт после кадра
|
||||
- Старая школа открывается и генерирует без ручного правок сейва
|
||||
|
||||
## Стоп
|
||||
|
||||
Не бампить сокет. Не зеркалить все параметры Swarm. Не возвращать кнопку настроек в меню.
|
||||
Не перегенерировать PNG. Не писать позу в UI.
|
||||
@@ -19,3 +19,4 @@
|
||||
| [53. Погода на дороге](53-weather-commute.md) | ✅ | Снег и дождь добавляют минуты к приходу |
|
||||
| [54. Что нового](54-whats-new.md) | ✅ | После входа — окно коммитов с прошлого визита |
|
||||
| [55. Состояние экрана](55-view-state.md) | ✅ | F5 и переходы не сбрасывают школу, вкладки и фильтры |
|
||||
| [60. Модели портретов](60-portrait-models.md) | 🔄 | Каталог моделей Swarm ∩ конфиг, слои промпта, спойлер оверрайдов |
|
||||
|
||||
+16
-12
@@ -495,9 +495,9 @@ is `image/png`. Opening the card does not generate; use POST when the player ask
|
||||
### `POST /api/schools/{id}/people/{personId}/portrait`
|
||||
|
||||
Generates (or regenerates) a portrait through SwarmUI on the server. Same `kind` query as GET.
|
||||
For `kind=custom` the body is `{ "promptExtra": "..." }` — appended to the base SwarmUI prompt and
|
||||
the person's body/clothing; required, non-empty, at most 2000 characters. Avatar and full-body POST
|
||||
need no body.
|
||||
For `kind=custom` the body is `{ "promptExtra": "..." }` — appended after the shot type (and the
|
||||
model/style layers), then the person's appearance and clothing; required, non-empty, at most 2000
|
||||
characters. Avatar and full-body POST need no body.
|
||||
Success is `201` with `{ "kind", "hasAvatar", "hasCustom", "hasFullBody", "customPortraitPrompt" }` and a `Location` header pointing
|
||||
at GET. SwarmUI is not configured when `SwarmUi:BaseUrl` is empty — `503` `swarmui-not-configured`.
|
||||
Swarm errors are `502` `swarmui-unavailable`; a slow backend is `504` `swarmui-timeout`. Files
|
||||
@@ -506,8 +506,8 @@ land under `saves/{id}.portraits/` and survive until the school is deleted.
|
||||
### `GET /api/schools/{id}/people/{personId}/portrait/prompt`
|
||||
|
||||
Returns the positive and negative prompts SwarmUI would receive, without generating an image.
|
||||
Same `kind` query as GET portrait. For `kind=custom`, optional query `promptExtra` is appended to
|
||||
the base prompt; when omitted, the last saved custom prompt is used if one exists. Unknown school
|
||||
Same `kind` query as GET portrait. For `kind=custom`, optional query `promptExtra` is appended after
|
||||
the shot type; when omitted, the last saved custom prompt is used if one exists. Unknown school
|
||||
is `404` `unknown-school`; unknown person is `404` `unknown-person`. Invalid `kind` is
|
||||
`400` `invalid-query`; custom without a usable prompt is `400` `invalid-body`.
|
||||
|
||||
@@ -527,21 +527,25 @@ so the client can disable generate buttons and show reachability without trying
|
||||
|
||||
### `GET /api/settings/swarmui`
|
||||
|
||||
Returns the **default** SwarmUI preset template (`swarmui.json`): named presets (model, steps,
|
||||
sampler, LoRA lists, per-kind sizes/prompts), `activePresetId` and `ageRules`. A new school copies
|
||||
this into its save as `portraitSettings`. Living schools generate from that copy, not from this
|
||||
file.
|
||||
Returns the **default** SwarmUI template (`swarmui.json`): a `models` catalog (id, default
|
||||
generation knobs, usually empty base positive/negative), named presets (model id, `style`, extra
|
||||
negative, optional generation overrides, per-kind size/`shotType`), `activePresetId` and `ageRules`.
|
||||
The model picker in the UI is Swarm's list intersected with `models`. A new school copies this into
|
||||
its save as `portraitSettings`. Living schools generate from that copy, not from this file. Older
|
||||
saves without `models` lift on load (`positive` → `style`, kind `positive` → `shotType`).
|
||||
|
||||
### `PUT /api/settings/swarmui`
|
||||
|
||||
Replaces the default template after validation. Invalid preset ids, age rules or numeric ranges
|
||||
Replaces the default template after validation. Invalid model/preset ids, age rules or numeric ranges
|
||||
return `400` `invalid-body`. Already-created schools keep the copy they were created with.
|
||||
|
||||
### `GET /api/settings/swarmui/discovery`
|
||||
|
||||
When SwarmUI is configured and reachable, proxies `ListT2IParams` and returns
|
||||
`{ connected, models, loras, samplers, schedulers }` for the settings UI comboboxes. When SwarmUI
|
||||
is off or unreachable, `connected` is false and the lists are empty.
|
||||
`{ connected, models, loras, samplers, schedulers }` for the settings UI. The client hides Swarm
|
||||
models that are missing from the template catalog. When SwarmUI is off or unreachable, `connected`
|
||||
is false and the lists are empty; the picker then shows the catalog ids so fields can be filled
|
||||
manually.
|
||||
|
||||
### `GET /api/schools/{id}/dress-rules`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user