Add seeded random character slots

This commit is contained in:
2026-06-24 21:33:28 +02:00
parent 79661c396a
commit cf4fec34b8
3 changed files with 78 additions and 4 deletions
+63 -4
View File
@@ -178,6 +178,7 @@ CHARACTER_MAN_BODY_CHOICES = [
CHARACTER_DESCRIPTOR_DETAIL_CHOICES = ["auto", "full", "medium", "compact", "minimal"]
CHARACTER_PRESENCE_CHOICES = ["visible", "pov"]
CHARACTER_RANDOM_TOKENS = {"", "random", "auto", "global", "from_global", "default"}
CHARACTER_SLOT_SEED_MAX = 0xFFFFFFFF
CAMERA_DETAIL_CHOICES = ["off", "compact", "full"]
HARDCORE_DETAIL_DENSITY_CHOICES = ["compact", "balanced", "dense"]
@@ -2307,6 +2308,47 @@ def _slot_expression_intensity_for_phase(slot: dict[str, Any] | None, phase: str
return _slot_expression_intensity(slot)
def _normalize_slot_seed(value: Any) -> int:
try:
seed = int(value)
except (TypeError, ValueError):
return -1
if seed < 0:
return -1
return min(seed, CHARACTER_SLOT_SEED_MAX)
def _slot_seed(slot: dict[str, Any] | None) -> int:
if not slot:
return -1
return _normalize_slot_seed(slot.get("slot_seed"))
def _slot_seeded_rng(slot: dict[str, Any] | None, salt: int) -> random.Random | None:
seed = _slot_seed(slot)
if seed < 0:
return None
return random.Random(_row_seed(seed, 1, salt))
def _slot_context_rng(slot: dict[str, Any], fallback_rng: random.Random) -> random.Random:
return _slot_seeded_rng(slot, 701) or fallback_rng
def _slot_effective_figure(
slot: dict[str, Any],
subject_type: str,
fallback_figure: str,
) -> str:
raw_figure = str(slot.get("figure") or "random").strip()
if raw_figure in ("curvy", "balanced", "bombshell"):
return raw_figure
seeded_rng = _slot_seeded_rng(slot, 709)
if subject_type == "woman" and seeded_rng is not None:
return g.choose(seeded_rng, ["curvy", "balanced", "bombshell"])
return fallback_figure
def _mean(values: list[float]) -> float:
return sum(values) / len(values)
@@ -2468,6 +2510,7 @@ def _normalize_character_slot(slot: dict[str, Any]) -> dict[str, Any]:
"profile_type": "character_slot",
"subject_type": subject_type,
"label": label,
"slot_seed": _normalize_slot_seed(slot.get("slot_seed")),
"age": age,
"ethnicity": _normalize_slot_ethnicity(slot.get("ethnicity")),
"figure": figure,
@@ -2522,12 +2565,14 @@ def _character_slot_summary(slot: dict[str, Any]) -> str:
parts = [
subject,
label_text,
f"seed={slot.get('slot_seed')}" if _slot_seed(slot) >= 0 else "",
f"age={slot.get('age', 'random')}",
f"ethnicity={slot.get('ethnicity', 'random')}",
f"figure={slot.get('figure', 'random')}",
f"body={slot.get('body', 'random')}",
f"detail={slot.get('descriptor_detail', 'auto')}",
]
parts = [part for part in parts if part]
if _slot_is_pov(slot):
parts.append("presence=pov")
if not _slot_expression_enabled(slot):
@@ -2556,6 +2601,7 @@ def _character_slot_summary(slot: dict[str, Any]) -> str:
def build_character_slot_json(
subject_type: str = "woman",
label: str = "auto_chain",
slot_seed: int = -1,
age: str = "random",
manual_age: str = "",
ethnicity: str = "random",
@@ -2582,6 +2628,7 @@ def build_character_slot_json(
{
"subject_type": subject_type,
"label": label,
"slot_seed": slot_seed,
"age": age,
"manual_age": manual_age,
"ethnicity": ethnicity,
@@ -2786,14 +2833,14 @@ def _context_from_character_slot(
no_black: bool,
) -> dict[str, str]:
slot_ethnicity = _slot_value(slot.get("ethnicity"))
slot_figure = _slot_value(slot.get("figure"))
slot_body = _slot_value(slot.get("body"))
effective_ethnicity = slot_ethnicity or ethnicity
effective_figure = slot_figure if slot_figure in ("curvy", "balanced", "bombshell") else figure
effective_figure = _slot_effective_figure(slot, subject_type, figure)
effective_no_plus = bool(no_plus_women) and not slot_body
effective_no_black = bool(no_black) and not slot_ethnicity
appearance_rng = _slot_context_rng(slot, rng)
context = _appearance_for_subject(
rng,
appearance_rng,
subject_type,
effective_ethnicity,
effective_figure,
@@ -2914,7 +2961,19 @@ def _row_from_character_slot(character_slot: str | dict[str, Any] | None) -> dic
slots = _parse_character_cast(character_slot)
if not slots:
return {}
return slots[-1]
slot = slots[-1]
if _slot_seed(slot) >= 0:
subject_type = str(slot.get("subject_type") or "woman")
return _context_from_character_slot(
random.Random(_row_seed(_slot_seed(slot), 1, 719)),
slot,
subject_type,
"any",
"curvy",
False,
False,
)
return slot
def _character_profile_descriptor(profile: dict[str, Any]) -> str: