Add typed category route metadata
This commit is contained in:
@@ -131,10 +131,12 @@ Already isolated:
|
|||||||
- row item selection, weighted item/pair choice, item-template axis filling,
|
- row item selection, weighted item/pair choice, item-template axis filling,
|
||||||
and oral/outercourse axis compatibility filters live in `row_item.py`;
|
and oral/outercourse axis compatibility filters live in `row_item.py`;
|
||||||
`prompt_builder.py` keeps public delegate wrappers.
|
`prompt_builder.py` keeps public delegate wrappers.
|
||||||
- row category/subcategory/item route resolution, hardcore position-category
|
- row category/subcategory/item route resolution lives in
|
||||||
filtering, cast-count adjustment, pose-vs-content seed-axis choice, item
|
`row_category_route.py` behind `CategoryItemRoute`, covering hardcore
|
||||||
metadata collection, and pose-category item sanitizing live in
|
position-category filtering, cast-count adjustment, pose-vs-content seed-axis
|
||||||
`row_category_route.py`; `prompt_builder.py` keeps public delegate wrappers.
|
choice, item metadata collection, legacy dict compatibility, and
|
||||||
|
pose-category item sanitizing; `prompt_builder.py` keeps public delegate
|
||||||
|
wrappers.
|
||||||
- row prompt/caption template selection, safe formatting, default prompt
|
- row prompt/caption template selection, safe formatting, default prompt
|
||||||
templates, configured-cast descriptor insertion, and POV directive insertion
|
templates, configured-cast descriptor insertion, and POV directive insertion
|
||||||
live in `row_rendering.py`; `prompt_builder.py` keeps compatibility aliases.
|
live in `row_rendering.py`; `prompt_builder.py` keeps compatibility aliases.
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ Core helper ownership:
|
|||||||
| `category_extensions.py` | JSON `pool_extensions`, legacy pool patching, built-in category choice lists, and category/subcategory UI choices. |
|
| `category_extensions.py` | JSON `pool_extensions`, legacy pool patching, built-in category choice lists, and category/subcategory UI choices. |
|
||||||
| `category_template_metadata.py` | Object-style item-template metadata extraction, action/position family normalization, position-key normalization, key merging, and audit validation errors. |
|
| `category_template_metadata.py` | Object-style item-template metadata extraction, action/position family normalization, position-key normalization, key merging, and audit validation errors. |
|
||||||
| `row_item.py` | Row item selection, weighted item/pair choice, item-template axis filling, and oral/outercourse axis compatibility filters. |
|
| `row_item.py` | Row item selection, weighted item/pair choice, item-template axis filling, and oral/outercourse axis compatibility filters. |
|
||||||
| `row_category_route.py` | Row category/subcategory/item route resolution, hardcore position-category filtering, cast-count adjustment, pose-vs-content seed-axis choice, item metadata collection, and pose-category item sanitizing. |
|
| `row_category_route.py` | Row category/subcategory/item route resolution behind `CategoryItemRoute`, hardcore position-category filtering, cast-count adjustment, pose-vs-content seed-axis choice, item metadata collection, legacy dict compatibility, and pose-category item sanitizing. |
|
||||||
| `row_rendering.py` | Row prompt/caption text-field resolution, template selection, safe formatting, default prompt templates, configured-cast descriptor insertion, and POV directive insertion. |
|
| `row_rendering.py` | Row prompt/caption text-field resolution, template selection, safe formatting, default prompt templates, configured-cast descriptor insertion, and POV directive insertion. |
|
||||||
| `row_role_graph.py` | Row role-graph route sequencing, including hardcore source graph construction, pose-category environment-anchor cleanup, and POV role-graph rewriting. |
|
| `row_role_graph.py` | Row role-graph route sequencing, including hardcore source graph construction, pose-category environment-anchor cleanup, and POV role-graph rewriting. |
|
||||||
| `row_assembly.py` | Final custom-row dictionary assembly behind `CustomRowAssemblyRequest`, render-context metadata population, prompt/caption rendering delegation, row-base indexing, cast/profile/slot metadata copying, and disabled-expression cleanup. |
|
| `row_assembly.py` | Final custom-row dictionary assembly behind `CustomRowAssemblyRequest`, render-context metadata population, prompt/caption rendering delegation, row-base indexing, cast/profile/slot metadata copying, and disabled-expression cleanup. |
|
||||||
|
|||||||
+39
-14
@@ -833,6 +833,31 @@ def _select_category_item_route(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _select_category_item_route_result(
|
||||||
|
*,
|
||||||
|
category_choice: str,
|
||||||
|
subcategory_choice: str,
|
||||||
|
seed_config: dict[str, int],
|
||||||
|
seed: int,
|
||||||
|
row_number: int,
|
||||||
|
women_count: int,
|
||||||
|
men_count: int,
|
||||||
|
hardcore_position_config: dict[str, Any] | None = None,
|
||||||
|
categories: list[dict[str, Any]] | None = None,
|
||||||
|
) -> row_category_route_policy.CategoryItemRoute:
|
||||||
|
return row_category_route_policy.select_category_item_route_result(
|
||||||
|
category_choice=category_choice,
|
||||||
|
subcategory_choice=subcategory_choice,
|
||||||
|
seed_config=seed_config,
|
||||||
|
seed=seed,
|
||||||
|
row_number=row_number,
|
||||||
|
women_count=women_count,
|
||||||
|
men_count=men_count,
|
||||||
|
hardcore_position_config=hardcore_position_config,
|
||||||
|
categories=categories,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _format(template: str, context: dict[str, Any]) -> str:
|
def _format(template: str, context: dict[str, Any]) -> str:
|
||||||
return row_rendering_policy.format_template(template, context)
|
return row_rendering_policy.format_template(template, context)
|
||||||
|
|
||||||
@@ -2255,7 +2280,7 @@ def _build_custom_row(
|
|||||||
parsed_location_config = _parse_location_config(location_config)
|
parsed_location_config = _parse_location_config(location_config)
|
||||||
parsed_composition_config = _parse_composition_config(composition_config)
|
parsed_composition_config = _parse_composition_config(composition_config)
|
||||||
|
|
||||||
category_route = _select_category_item_route(
|
category_route = _select_category_item_route_result(
|
||||||
category_choice=category_choice,
|
category_choice=category_choice,
|
||||||
subcategory_choice=subcategory_choice,
|
subcategory_choice=subcategory_choice,
|
||||||
seed_config=seed_config,
|
seed_config=seed_config,
|
||||||
@@ -2265,19 +2290,19 @@ def _build_custom_row(
|
|||||||
men_count=men_count,
|
men_count=men_count,
|
||||||
hardcore_position_config=parsed_hardcore_position_config,
|
hardcore_position_config=parsed_hardcore_position_config,
|
||||||
)
|
)
|
||||||
category = category_route["category"]
|
category = category_route.category
|
||||||
subcategory = category_route["subcategory"]
|
subcategory = category_route.subcategory
|
||||||
women_count = int(category_route["women_count"])
|
women_count = category_route.women_count
|
||||||
men_count = int(category_route["men_count"])
|
men_count = category_route.men_count
|
||||||
count_adjustment = dict(category_route.get("count_adjustment") or {})
|
count_adjustment = dict(category_route.count_adjustment)
|
||||||
content_axis = str(category_route.get("content_axis") or "content")
|
content_axis = category_route.content_axis
|
||||||
item = category_route["item"]
|
item = category_route.item
|
||||||
item_text = str(category_route.get("item_text") or "")
|
item_text = category_route.item_text
|
||||||
item_name = str(category_route.get("item_name") or "")
|
item_name = category_route.item_name
|
||||||
item_axis_values = dict(category_route.get("item_axis_values") or {})
|
item_axis_values = dict(category_route.item_axis_values)
|
||||||
item_template_metadata = dict(category_route.get("item_template_metadata") or {})
|
item_template_metadata = dict(category_route.item_template_metadata)
|
||||||
item_formatter_hints = dict(category_route.get("formatter_hints") or {})
|
item_formatter_hints = dict(category_route.formatter_hints)
|
||||||
is_pose_category = bool(category_route.get("is_pose_category"))
|
is_pose_category = category_route.is_pose_category
|
||||||
subject_type = str(_merged_field(category, subcategory, item, "subject_type", "single_any"))
|
subject_type = str(_merged_field(category, subcategory, item, "subject_type", "single_any"))
|
||||||
subject_route = _subject_route(
|
subject_route = _subject_route(
|
||||||
subject_type=subject_type,
|
subject_type=subject_type,
|
||||||
|
|||||||
+77
-17
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -63,7 +64,41 @@ def cast_count_adjustment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def select_category_item_route(
|
@dataclass(frozen=True)
|
||||||
|
class CategoryItemRoute:
|
||||||
|
category: dict[str, Any]
|
||||||
|
subcategory: dict[str, Any]
|
||||||
|
women_count: int
|
||||||
|
men_count: int
|
||||||
|
count_adjustment: dict[str, int]
|
||||||
|
content_axis: str
|
||||||
|
item: Any
|
||||||
|
item_text: str
|
||||||
|
item_name: str
|
||||||
|
item_axis_values: dict[str, Any]
|
||||||
|
item_template_metadata: dict[str, Any]
|
||||||
|
formatter_hints: dict[str, Any]
|
||||||
|
is_pose_category: bool
|
||||||
|
|
||||||
|
def as_dict(self) -> dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"category": self.category,
|
||||||
|
"subcategory": self.subcategory,
|
||||||
|
"women_count": self.women_count,
|
||||||
|
"men_count": self.men_count,
|
||||||
|
"count_adjustment": dict(self.count_adjustment),
|
||||||
|
"content_axis": self.content_axis,
|
||||||
|
"item": self.item,
|
||||||
|
"item_text": self.item_text,
|
||||||
|
"item_name": self.item_name,
|
||||||
|
"item_axis_values": dict(self.item_axis_values),
|
||||||
|
"item_template_metadata": dict(self.item_template_metadata),
|
||||||
|
"formatter_hints": dict(self.formatter_hints),
|
||||||
|
"is_pose_category": self.is_pose_category,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def select_category_item_route_result(
|
||||||
*,
|
*,
|
||||||
category_choice: str,
|
category_choice: str,
|
||||||
subcategory_choice: str,
|
subcategory_choice: str,
|
||||||
@@ -74,7 +109,7 @@ def select_category_item_route(
|
|||||||
men_count: int,
|
men_count: int,
|
||||||
hardcore_position_config: dict[str, Any] | None = None,
|
hardcore_position_config: dict[str, Any] | None = None,
|
||||||
categories: list[dict[str, Any]] | None = None,
|
categories: list[dict[str, Any]] | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> CategoryItemRoute:
|
||||||
source_categories = category_policy.load_category_library() if categories is None else categories
|
source_categories = category_policy.load_category_library() if categories is None else categories
|
||||||
parsed_hardcore_position_config = hardcore_position_config or {}
|
parsed_hardcore_position_config = hardcore_position_config or {}
|
||||||
requested_women_count = women_count
|
requested_women_count = women_count
|
||||||
@@ -126,18 +161,43 @@ def select_category_item_route(
|
|||||||
item_text = sanitize_hardcore_environment_anchors(item_text)
|
item_text = sanitize_hardcore_environment_anchors(item_text)
|
||||||
item_axis_values = sanitize_hardcore_axis_values(item_axis_values)
|
item_axis_values = sanitize_hardcore_axis_values(item_axis_values)
|
||||||
|
|
||||||
return {
|
return CategoryItemRoute(
|
||||||
"category": category,
|
category=category,
|
||||||
"subcategory": subcategory,
|
subcategory=subcategory,
|
||||||
"women_count": women_count,
|
women_count=women_count,
|
||||||
"men_count": men_count,
|
men_count=men_count,
|
||||||
"count_adjustment": count_adjustment,
|
count_adjustment=count_adjustment,
|
||||||
"content_axis": content_axis,
|
content_axis=content_axis,
|
||||||
"item": item,
|
item=item,
|
||||||
"item_text": item_text,
|
item_text=item_text,
|
||||||
"item_name": item_name,
|
item_name=item_name,
|
||||||
"item_axis_values": item_axis_values,
|
item_axis_values=item_axis_values,
|
||||||
"item_template_metadata": item_template_metadata,
|
item_template_metadata=item_template_metadata,
|
||||||
"formatter_hints": template_policy.formatter_hints(item_template_metadata),
|
formatter_hints=template_policy.formatter_hints(item_template_metadata),
|
||||||
"is_pose_category": is_pose_category,
|
is_pose_category=is_pose_category,
|
||||||
}
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def select_category_item_route(
|
||||||
|
*,
|
||||||
|
category_choice: str,
|
||||||
|
subcategory_choice: str,
|
||||||
|
seed_config: dict[str, int],
|
||||||
|
seed: int,
|
||||||
|
row_number: int,
|
||||||
|
women_count: int,
|
||||||
|
men_count: int,
|
||||||
|
hardcore_position_config: dict[str, Any] | None = None,
|
||||||
|
categories: list[dict[str, Any]] | None = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
return select_category_item_route_result(
|
||||||
|
category_choice=category_choice,
|
||||||
|
subcategory_choice=subcategory_choice,
|
||||||
|
seed_config=seed_config,
|
||||||
|
seed=seed,
|
||||||
|
row_number=row_number,
|
||||||
|
women_count=women_count,
|
||||||
|
men_count=men_count,
|
||||||
|
hardcore_position_config=hardcore_position_config,
|
||||||
|
categories=categories,
|
||||||
|
).as_dict()
|
||||||
|
|||||||
@@ -1027,6 +1027,16 @@ def smoke_row_category_route_policy() -> None:
|
|||||||
men_count=1,
|
men_count=1,
|
||||||
hardcore_position_config=hard_config,
|
hardcore_position_config=hard_config,
|
||||||
)
|
)
|
||||||
|
route_result = row_category_route.select_category_item_route_result(
|
||||||
|
category_choice="custom_random",
|
||||||
|
subcategory_choice="Hardcore sexual poses / Oral sex",
|
||||||
|
seed_config=seed_cfg,
|
||||||
|
seed=2301,
|
||||||
|
row_number=1,
|
||||||
|
women_count=1,
|
||||||
|
men_count=1,
|
||||||
|
hardcore_position_config=hard_config,
|
||||||
|
)
|
||||||
delegated = pb._select_category_item_route(
|
delegated = pb._select_category_item_route(
|
||||||
category_choice="custom_random",
|
category_choice="custom_random",
|
||||||
subcategory_choice="Hardcore sexual poses / Oral sex",
|
subcategory_choice="Hardcore sexual poses / Oral sex",
|
||||||
@@ -1037,7 +1047,20 @@ def smoke_row_category_route_policy() -> None:
|
|||||||
men_count=1,
|
men_count=1,
|
||||||
hardcore_position_config=hard_config,
|
hardcore_position_config=hard_config,
|
||||||
)
|
)
|
||||||
|
typed_delegated = pb._select_category_item_route_result(
|
||||||
|
category_choice="custom_random",
|
||||||
|
subcategory_choice="Hardcore sexual poses / Oral sex",
|
||||||
|
seed_config=seed_cfg,
|
||||||
|
seed=2301,
|
||||||
|
row_number=1,
|
||||||
|
women_count=1,
|
||||||
|
men_count=1,
|
||||||
|
hardcore_position_config=hard_config,
|
||||||
|
)
|
||||||
_expect(delegated == route, "Prompt builder category/item route should delegate to row_category_route")
|
_expect(delegated == route, "Prompt builder category/item route should delegate to row_category_route")
|
||||||
|
_expect(route_result.as_dict() == route, "Typed category/item route should match legacy dict route")
|
||||||
|
_expect(typed_delegated == route_result, "Prompt builder typed category/item route should delegate")
|
||||||
|
_expect(route_result.content_axis == "pose", "Typed category/item route lost content seed axis")
|
||||||
_expect(route["category"]["slug"] == "hardcore_sexual_poses", "Row category route selected wrong hardcore category")
|
_expect(route["category"]["slug"] == "hardcore_sexual_poses", "Row category route selected wrong hardcore category")
|
||||||
_expect(route["subcategory"]["slug"] == "oral_sex", "Row category route selected wrong hardcore subcategory")
|
_expect(route["subcategory"]["slug"] == "oral_sex", "Row category route selected wrong hardcore subcategory")
|
||||||
_expect(route["content_axis"] == "pose", "Hardcore pose category should use pose seed axis")
|
_expect(route["content_axis"] == "pose", "Hardcore pose category should use pose seed axis")
|
||||||
|
|||||||
Reference in New Issue
Block a user