Expand semi-public camera scene profiles

This commit is contained in:
2026-06-27 16:23:51 +02:00
parent 002c3b79d4
commit c69274d2ee
5 changed files with 352 additions and 20 deletions
+6 -3
View File
@@ -419,9 +419,12 @@ generic Qwen camera views do not add `phone hidden` or other phone wording.
For camera-aware locations, the prompt builder also uses the translated camera For camera-aware locations, the prompt builder also uses the translated camera
geometry to add a location-aware framing sentence. It currently has scene geometry to add a location-aware framing sentence. It currently has scene
profiles for coworking/business-office spaces and classical library/book-stack profiles for coworking/business-office spaces, classical library/book-stack
spaces: front/side/back views, zoom, and elevation change which desks, windows, spaces, and semi-public repeating-structure locations such as hotel corridors,
partitions, bookshelves, reading tables, lamps, or aisles are kept visible. In parking garages, archives, laundromats, station lockers, backstage halls, wine
cellars, nightclub back halls, and restaurant booths. Front/side/back views,
zoom, and elevation change which desks, windows, partitions, bookshelves,
corridors, pillars, shelves, tables, lamps, or aisles are kept visible. In
male-POV setups this becomes a first-person spatial description and the male-POV setups this becomes a first-person spatial description and the
external camera sentence is suppressed. external camera sentence is suppressed.
Rows keep the selected `scene_entry`, `location_theme`, `scene_theme`, Rows keep the selected `scene_entry`, `location_theme`, `scene_theme`,
+4 -3
View File
@@ -276,9 +276,10 @@ Already isolated:
side-lying, and front/back group layouts. side-lying, and front/back group layouts.
- camera option schema, orbit/Qwen translation, config parsing, camera - camera option schema, orbit/Qwen translation, config parsing, camera
directive text, and camera caption text live in `camera_config.py`; directive text, and camera caption text live in `camera_config.py`;
camera-scene prose lives in `scene_camera_adapters.py`; row-level camera camera-scene prose and contextual scene composition mutation for coworking,
insertion, contextual coworking composition mutation, subject-kind detection, library, and semi-public profiles live in `scene_camera_adapters.py`;
and POV suppression live in `row_camera.py`. row-level camera insertion, subject-kind detection, and POV suppression live
in `row_camera.py`.
- shared POV slot detection, label merging/filtering, builder-side POV - shared POV slot detection, label merging/filtering, builder-side POV
directives, source role-graph viewer replacement, and shared composition directives, source role-graph viewer replacement, and shared composition
cleanup live in `pov_policy.py`; prompt builder and Krea POV routes delegate cleanup live in `pov_policy.py`; prompt builder and Krea POV routes delegate
+7 -4
View File
@@ -126,8 +126,8 @@ Core helper ownership:
| `hardcore_action_metadata.py` | Source action-family and position-family metadata used by Krea2, SDXL, and caption routes. | | `hardcore_action_metadata.py` | Source action-family and position-family metadata used by Krea2, SDXL, and caption routes. |
| `route_metadata.py` | Shared row-level route metadata readers for normalized action family, position family/keys, and formatter hints used by Krea2, SDXL, and caption routes. | | `route_metadata.py` | Shared row-level route metadata readers for normalized action family, position family/keys, and formatter hints used by Krea2, SDXL, and caption routes. |
| `pov_policy.py` | Shared POV slot detection, POV label merging/filtering, builder POV directives, source role-graph viewer replacement, and shared POV composition cleanup used by builder and Krea2 routes. | | `pov_policy.py` | Shared POV slot detection, POV label merging/filtering, builder POV directives, source role-graph viewer replacement, and shared POV composition cleanup used by builder and Krea2 routes. |
| `scene_camera_adapters.py` | Location-aware camera/scene prose such as coworking lounge camera layout. | | `scene_camera_adapters.py` | Location-aware camera/scene prose for coworking, library, semi-public corridor/garage/archive/etc. profiles, metadata-first profile resolution, and camera-aware composition cleanup. |
| `row_camera.py` | Row-level camera insertion, contextual coworking composition mutation, subject-kind detection, POV label fallback, and POV suppression of normal camera directives. | | `row_camera.py` | Row-level camera insertion, contextual scene composition mutation, subject-kind detection, POV label fallback, and POV suppression of normal camera directives. |
| `krea_row_fields.py` | Shared Krea normal-row field extraction for item, scene, pose, expression, composition/source-composition, camera, and style used by normal and configured-cast routes. | | `krea_row_fields.py` | Shared Krea normal-row field extraction for item, scene, pose, expression, composition/source-composition, camera, and style used by normal and configured-cast routes. |
| `krea_cast.py` | Shared formatter cast descriptor parsing, cast labels, cast prose, natural cast descriptor text, and label replacement used by Krea2 and caption routes. | | `krea_cast.py` | Shared formatter cast descriptor parsing, cast labels, cast prose, natural cast descriptor text, and label replacement used by Krea2 and caption routes. |
| `prompt_hygiene.py` | Generic prompt, caption, and negative-prompt cleanup, including route-agnostic negative-prompt merge/dedupe. | | `prompt_hygiene.py` | Generic prompt, caption, and negative-prompt cleanup, including route-agnostic negative-prompt merge/dedupe. |
@@ -695,8 +695,11 @@ Current camera-aware scene adapter:
`scene_camera_profile` objects with `key`, `family`, `layout_label`, `place`, `scene_camera_profile` objects with `key`, `family`, `layout_label`, `place`,
`foreground`, `midground`, `background`, `detail_label`, and optional `foreground`, `midground`, `background`, `detail_label`, and optional
per-subject `composition` text. per-subject `composition` text.
- Coworking/business-cafe/office scenes and classical library/book-stack scenes - Coworking/business-cafe/office scenes, classical library/book-stack scenes,
are detected by `scene_camera_profile`. and semi-public repeating-structure scenes such as hotel corridors, parking
garages, archives, laundromats, station lockers, backstage halls, wine
cellars, nightclub back halls, and restaurant booths are detected by
`scene_camera_profile`.
- Location themes preserve `theme` on configs and selected scene entries, and - Location themes preserve `theme` on configs and selected scene entries, and
rows expose `location_theme`, `scene_theme`, `composition_theme`, and rows expose `location_theme`, `scene_theme`, `composition_theme`, and
`scene_camera_profile_key` for debugging and future route rules. `scene_camera_profile_key` for debugging and future route rules.
+261 -9
View File
@@ -110,12 +110,260 @@ SCENE_CAMERA_PROFILES: tuple[dict[str, Any], ...] = (
"default": "classical library frame with the subjects near a bookshelf edge and long shelf depth behind them", "default": "classical library frame with the subjects near a bookshelf edge and long shelf depth behind them",
}, },
}, },
{
"key": "hotel_corridor",
"family": "semi_public",
"terms": (
"hotel corridor",
"hotel service corridor",
"hotel service alcove",
"service alcove",
"service hallway",
"service hall",
"repeating numbered doors",
"numbered doors",
"luggage carts",
"stair landing",
"hotel stair landing",
),
"layout_label": "Hotel corridor camera layout",
"place": "hotel corridor",
"foreground": "nearest doorframe edge, patterned carpet line, and wall sconce",
"midground": "repeating numbered doors, brass wall lamps, service-alcove turns, and luggage carts",
"background": "long corridor perspective, closed doors, warm late-night depth, and quiet hotel sightlines",
"detail_label": "hotel corridor details",
"composition": {
"woman": "hotel corridor frame with the woman near a doorframe edge and repeated doors behind her",
"man": "hotel corridor frame with the man near a doorframe edge and repeated doors behind him",
"default": "hotel corridor frame with the subjects near a doorframe edge and repeated doors behind them",
},
},
{
"key": "parking_garage",
"family": "semi_public",
"terms": (
"parking garage",
"parking deck",
"underground garage",
"multi-level parking",
"concrete pillars",
"numbered pillars",
"painted floor lines",
"painted bay lines",
"parked cars",
),
"layout_label": "Parking garage camera layout",
"place": "parking garage",
"foreground": "nearest concrete pillar, painted floor line, and car bumper edge",
"midground": "repeating concrete pillars, parked cars, painted bay lines, and glossy concrete lanes",
"background": "shadowed corners, fluorescent depth, numbered pillars, and long garage perspective",
"detail_label": "parking garage details",
"composition": {
"woman": "parking garage frame with the woman beside a concrete pillar and repeated bay lines behind her",
"man": "parking garage frame with the man beside a concrete pillar and repeated bay lines behind him",
"default": "parking garage frame with the subjects beside a concrete pillar and repeated bay lines behind them",
},
},
{
"key": "theater_backstage",
"family": "semi_public",
"terms": (
"theater backstage",
"backstage wings",
"cabaret backstage",
"prop storage",
"prop racks",
"costume racks",
"costume rails",
"velvet curtains",
"stage ropes",
"scenery flats",
),
"layout_label": "Backstage camera layout",
"place": "theater backstage",
"foreground": "curtain edge, prop trunk corner, and costume-rack line",
"midground": "layered velvet curtains, costume racks, prop shelves, and vanity bulb mirrors",
"background": "dark stage wings, repeated scenery flats, narrow backstage passages, and warm light spill",
"detail_label": "backstage details",
"composition": {
"woman": "backstage frame with the woman partly framed by curtains and costume racks behind her",
"man": "backstage frame with the man partly framed by curtains and costume racks behind him",
"default": "backstage frame with the subjects partly framed by curtains and costume racks behind them",
},
},
{
"key": "wine_cellar",
"family": "semi_public",
"terms": (
"wine cellar",
"wine storage",
"bottle racks",
"bottle shelves",
"arched cellar",
"brick niches",
"cellar corridor",
"stacked bottle",
),
"layout_label": "Wine cellar camera layout",
"place": "wine cellar",
"foreground": "near bottle-rack edge, crate corner, and stone floor line",
"midground": "repeating bottle racks, arched brick niches, narrow aisles, and low amber lamps",
"background": "cool shadowed depth, stacked shelves, cellar arches, and secluded rack rows",
"detail_label": "wine cellar details",
"composition": {
"woman": "wine cellar frame with the woman between bottle racks and arched cellar depth behind her",
"man": "wine cellar frame with the man between bottle racks and arched cellar depth behind him",
"default": "wine cellar frame with the subjects between bottle racks and arched cellar depth behind them",
},
},
{
"key": "museum_archive",
"family": "semi_public",
"terms": (
"museum archive",
"gallery storage",
"rare-books archive",
"archive room",
"storage shelves",
"labeled boxes",
"rolling shelves",
"catalog drawers",
"compact shelving",
),
"layout_label": "Archive camera layout",
"place": "museum archive",
"foreground": "storage-shelf edge, archive box corner, and work-table line",
"midground": "labeled boxes, rolling shelves, frame racks, catalog drawers, and long work tables",
"background": "compact shelving rows, soft overhead lights, archival aisles, and hidden storage depth",
"detail_label": "archive details",
"composition": {
"woman": "archive frame with the woman beside labeled storage shelves and compact rows behind her",
"man": "archive frame with the man beside labeled storage shelves and compact rows behind him",
"default": "archive frame with the subjects beside labeled storage shelves and compact rows behind them",
},
},
{
"key": "laundromat_late_night",
"family": "semi_public",
"terms": (
"laundromat",
"coin laundry",
"washing machines",
"stacked dryers",
"washer-door",
"washer door",
"folding tables",
"detergent shelves",
"machine row",
),
"layout_label": "Laundromat camera layout",
"place": "late-night laundromat",
"foreground": "folding-table edge, chrome washer door, and tiled floor line",
"midground": "repeating washing machines, stacked dryers, detergent shelves, and empty machine rows",
"background": "cool fluorescent depth, mirrored machine doors, front glass, and quiet back-corner sightlines",
"detail_label": "laundromat details",
"composition": {
"woman": "laundromat frame with the woman near a folding table and repeated washer doors behind her",
"man": "laundromat frame with the man near a folding table and repeated washer doors behind him",
"default": "laundromat frame with the subjects near a folding table and repeated washer doors behind them",
},
},
{
"key": "train_station_lockers",
"family": "semi_public",
"terms": (
"train-station locker",
"train station locker",
"locker corridor",
"station underpass",
"station service passage",
"metal lockers",
"vending machines",
"tiled walls",
"utility doors",
"warning stripes",
),
"layout_label": "Station locker camera layout",
"place": "train-station locker corridor",
"foreground": "locker edge, vending-machine corner, and tiled floor line",
"midground": "repeating metal lockers, tiled wall seams, poster frames, and utility doors",
"background": "fluorescent underpass depth, stair railings, warning stripes, and hidden side alcoves",
"detail_label": "station locker details",
"composition": {
"woman": "station locker frame with the woman beside metal lockers and tiled depth behind her",
"man": "station locker frame with the man beside metal lockers and tiled depth behind him",
"default": "station locker frame with the subjects beside metal lockers and tiled depth behind them",
},
},
{
"key": "nightclub_back_hall",
"family": "semi_public",
"terms": (
"nightclub back hallway",
"club vip corridor",
"vip club corridor",
"music venue greenroom",
"greenroom corridor",
"coat-check racks",
"neon strips",
"velvet ropes",
"mirrored wall panels",
"stickered doors",
),
"layout_label": "Nightclub back-hall camera layout",
"place": "nightclub back hallway",
"foreground": "black door edge, velvet-rope post, and mirrored wall strip",
"midground": "repeated dark doors, neon strips, coat-check racks, mirrored panels, and booth edges",
"background": "distant colored dance-floor light, dim practical lamps, cable cases, and narrow hallway depth",
"detail_label": "nightclub back-hall details",
"composition": {
"woman": "nightclub back-hall frame with the woman near a dark door edge and neon hallway depth behind her",
"man": "nightclub back-hall frame with the man near a dark door edge and neon hallway depth behind him",
"default": "nightclub back-hall frame with the subjects near a dark door edge and neon hallway depth behind them",
},
},
{
"key": "restaurant_private_booth",
"family": "semi_public",
"terms": (
"restaurant private booth",
"private booth",
"bistro back corner",
"after-hours dining",
"afterhours dining",
"high banquettes",
"dark wood partitions",
"folded napkins",
"stacked chairs",
"small round tables",
),
"layout_label": "Restaurant booth camera layout",
"place": "restaurant private booth",
"foreground": "table edge, high banquette back, and dark wood partition",
"midground": "repeating table lamps, folded napkins, mirrored wall panels, and empty tables",
"background": "after-hours dining-room depth, stacked chairs, service doorway, and secluded sightlines",
"detail_label": "restaurant booth details",
"composition": {
"woman": "restaurant booth frame with the woman beside a high banquette and table lamps behind her",
"man": "restaurant booth frame with the man beside a high banquette and table lamps behind him",
"default": "restaurant booth frame with the subjects beside a high banquette and table lamps behind them",
},
},
) )
SCENE_CAMERA_PROFILE_KEYS = {str(profile["key"]): dict(profile) for profile in SCENE_CAMERA_PROFILES} SCENE_CAMERA_PROFILE_KEYS = {str(profile["key"]): dict(profile) for profile in SCENE_CAMERA_PROFILES}
THEME_PROFILE_KEYS = { THEME_PROFILE_KEYS = {
"classical_library": "classical_library", "classical_library": "classical_library",
"hotel_corridor": "hotel_corridor",
"parking_garage": "parking_garage",
"theater_backstage": "theater_backstage",
"wine_cellar": "wine_cellar",
"museum_archive": "museum_archive",
"laundromat_late_night": "laundromat_late_night",
"train_station_lockers": "train_station_lockers",
"nightclub_back_hall": "nightclub_back_hall",
"restaurant_private_booth": "restaurant_private_booth",
} }
PROFILE_TEXT_FIELDS = ( PROFILE_TEXT_FIELDS = (
@@ -348,7 +596,7 @@ def coworking_location_profile(scene_text: Any) -> dict[str, str]:
return scene_camera_profile("coworking lounge") return scene_camera_profile("coworking lounge")
def coworking_subject_terms(subject_kind: str, pov_labels: list[str] | None = None) -> tuple[str, str]: def scene_subject_terms(subject_kind: str, pov_labels: list[str] | None = None) -> tuple[str, str]:
if pov_labels: if pov_labels:
return "the visible partner", "them" return "the visible partner", "them"
if subject_kind == "woman": if subject_kind == "woman":
@@ -360,6 +608,10 @@ def coworking_subject_terms(subject_kind: str, pov_labels: list[str] | None = No
return "the subjects", "them" return "the subjects", "them"
def coworking_subject_terms(subject_kind: str, pov_labels: list[str] | None = None) -> tuple[str, str]:
return scene_subject_terms(subject_kind, pov_labels)
def scene_direction_detail( def scene_direction_detail(
direction: str, direction: str,
profile: dict[str, str], profile: dict[str, str],
@@ -371,7 +623,7 @@ def scene_direction_detail(
midground = profile["midground"] midground = profile["midground"]
background = profile["background"] background = profile["background"]
detail_label = profile.get("detail_label") or "location details" detail_label = profile.get("detail_label") or "location details"
subject, pronoun = coworking_subject_terms(subject_kind, pov_labels) subject, pronoun = scene_subject_terms(subject_kind, pov_labels)
if pov_labels: if pov_labels:
if "right side" in direction: if "right side" in direction:
return f"{subject} is in right-side profile; {midground} run behind {pronoun} toward {background}, with {detail_label} kept at the frame edges" return f"{subject} is in right-side profile; {midground} run behind {pronoun} toward {background}, with {detail_label} kept at the frame edges"
@@ -411,7 +663,7 @@ def scene_distance_detail(
pov_labels: list[str] | None = None, pov_labels: list[str] | None = None,
) -> str: ) -> str:
distance = str(distance or "").strip().lower() distance = str(distance or "").strip().lower()
subject, _pronoun = coworking_subject_terms(subject_kind, pov_labels) subject, _pronoun = scene_subject_terms(subject_kind, pov_labels)
if pov_labels: if pov_labels:
if "wide" in distance or "full-body" in distance or "full body" in distance: if "wide" in distance or "full-body" in distance or "full body" in distance:
return f"wide POV keeps {subject} readable with {profile['place']} context behind them" return f"wide POV keeps {subject} readable with {profile['place']} context behind them"
@@ -441,7 +693,7 @@ def scene_elevation_detail(
pov_labels: list[str] | None = None, pov_labels: list[str] | None = None,
) -> str: ) -> str:
elevation = str(elevation or "").strip().lower() elevation = str(elevation or "").strip().lower()
subject, pronoun = coworking_subject_terms(subject_kind, pov_labels) subject, pronoun = scene_subject_terms(subject_kind, pov_labels)
if pov_labels: if pov_labels:
if "low-angle" in elevation: if "low-angle" in elevation:
return f"low angle keeps POV body cues low while the {profile['background']} rises behind {pronoun}" return f"low angle keeps POV body cues low while the {profile['background']} rises behind {pronoun}"
@@ -494,13 +746,13 @@ def scene_camera_directive(
direction_detail = scene_direction_detail(direction, profile, pov_labels, subject_kind) direction_detail = scene_direction_detail(direction, profile, pov_labels, subject_kind)
distance_detail = scene_distance_detail(distance, profile, subject_kind, pov_labels) distance_detail = scene_distance_detail(distance, profile, subject_kind, pov_labels)
elevation_detail = scene_elevation_detail(elevation, profile, subject_kind, pov_labels) elevation_detail = scene_elevation_detail(elevation, profile, subject_kind, pov_labels)
if pov_labels:
return (
f"{profile['layout_label']} from POV: {direction_detail}. "
f"{distance_detail}; {elevation_detail}; use the multiangle camera only as first-person spatial geometry."
)
geometry = camera_geometry_phrase(parsed, compact_labels) geometry = camera_geometry_phrase(parsed, compact_labels)
geometry_clause = f" ({geometry})" if geometry else "" geometry_clause = f" ({geometry})" if geometry else ""
if pov_labels:
return (
f"{profile['layout_label']} from POV{geometry_clause}: {direction_detail}. "
f"{distance_detail}; {elevation_detail}; use the multiangle camera only as first-person spatial geometry."
)
return ( return (
f"{profile['layout_label']}{geometry_clause}: {direction_detail}; " f"{profile['layout_label']}{geometry_clause}: {direction_detail}; "
f"{distance_detail}; {elevation_detail}." f"{distance_detail}; {elevation_detail}."
+74 -1
View File
@@ -406,10 +406,14 @@ def _coworking_location_config() -> str:
def _classical_library_theme_configs() -> tuple[str, str]: def _classical_library_theme_configs() -> tuple[str, str]:
return _thematic_location_configs("classical_library")
def _thematic_location_configs(theme: str) -> tuple[str, str]:
location_config, composition_config, _summary = pb.build_thematic_location_json( location_config, composition_config, _summary = pb.build_thematic_location_json(
enabled=True, enabled=True,
combine_mode="replace", combine_mode="replace",
theme="classical_library", theme=theme,
) )
return location_config, composition_config return location_config, composition_config
@@ -694,6 +698,39 @@ def smoke_row_camera_policy() -> None:
_expect("bag" not in library_composition.lower(), "row camera library composition leaked bag wording") _expect("bag" not in library_composition.lower(), "row camera library composition leaked bag wording")
_expect("shoes" not in library_composition.lower(), "row camera library composition leaked shoes wording") _expect("shoes" not in library_composition.lower(), "row camera library composition leaked shoes wording")
_expect("library" in library_composition.lower(), "row camera library composition did not become location-aware") _expect("library" in library_composition.lower(), "row camera library composition did not become location-aware")
semi_public_row = {
"prompt": "A generated adult prompt. Composition: vertical polished mirror view with bag and shoes visible. Avoid: low quality.",
"caption": "sxcppnl7, generated adult prompt, polished mirror view with bag and shoes visible, illustration",
"scene_text": "upscale hotel corridor with repeating numbered doors, patterned carpet, brass wall lamps, luggage carts, and a secluded corner near a service alcove",
"scene_entry": {
"slug": "hotel_corridor_affair",
"prompt": "upscale hotel corridor with repeating numbered doors, patterned carpet, brass wall lamps, luggage carts, and a secluded corner near a service alcove",
"theme": "semi_public_affair",
},
"scene_theme": "semi_public_affair",
"composition": "polished mirror view with bag and shoes visible",
"subject_type": "configured_cast",
"women_count": 1,
"men_count": 1,
"pov_character_labels": ["Man A"],
}
updated_semi_public = row_camera.apply_camera_config(
semi_public_row,
_orbit_camera(horizontal_angle=180, vertical_angle=30, zoom=7.5),
compact_labels=pb.CAMERA_COMPACT_LABELS,
)
semi_public_scene = _expect_text("row_camera_policy.semi_public_scene", updated_semi_public.get("camera_scene_directive"), 40)
semi_public_composition = _expect_text(
"row_camera_policy.semi_public_composition",
updated_semi_public.get("composition"),
20,
)
_expect("Hotel corridor camera layout from POV" in semi_public_scene, "row camera semi-public scene did not use hotel corridor profile")
_expect("back view" in semi_public_scene, "row camera semi-public scene missed orbit direction")
_expect("first-person spatial geometry" in semi_public_scene, "row camera semi-public POV scene lost first-person geometry")
_expect(updated_semi_public.get("scene_camera_profile_key") == "hotel_corridor", "row camera semi-public scene did not expose text-matched profile key")
_expect("hotel corridor" in semi_public_composition.lower(), "row camera semi-public composition did not become location-aware")
_expect("bag" not in semi_public_composition.lower() and "shoes" not in semi_public_composition.lower(), "row camera semi-public composition leaked outfit-check props")
metadata_profile_row = { metadata_profile_row = {
"prompt": "A generated adult prompt. Composition: vertical polished mirror view with bag and shoes visible. Avoid: low quality.", "prompt": "A generated adult prompt. Composition: vertical polished mirror view with bag and shoes visible. Avoid: low quality.",
"caption": "sxcppnl7, generated adult prompt, polished mirror view with bag and shoes visible, illustration", "caption": "sxcppnl7, generated adult prompt, polished mirror view with bag and shoes visible, illustration",
@@ -870,6 +907,42 @@ def smoke_config_route_location_theme() -> None:
_expect("315-degree front-left quarter view" in prompt, "Krea config route lost camera directive") _expect("315-degree front-left quarter view" in prompt, "Krea config route lost camera directive")
_expect_formatter_outputs(row, "config_route_location_theme", target="single") _expect_formatter_outputs(row, "config_route_location_theme", target="single")
parking_location_config, parking_composition_config = _thematic_location_configs("parking_garage")
parking_row = pb.build_prompt_from_configs(
row_number=1,
start_index=1,
seed=3311,
category_config=pb.build_category_config_json("woman", "random"),
cast_config=pb.build_cast_config_json("solo_woman", 1, 0),
generation_profile=pb.build_generation_profile_json(profile="balanced"),
camera_config=_orbit_camera(
horizontal_angle=135,
vertical_angle=-30,
zoom=4.0,
subject_focus="environment",
),
location_config=parking_location_config,
composition_config=parking_composition_config,
)
_expect_row_base(parking_row, "config_route_location_theme.parking")
parking_scene = _expect_text("config_route_location_theme.parking_scene", parking_row.get("scene_text"), 20)
parking_composition = _expect_text("config_route_location_theme.parking_composition", parking_row.get("composition"), 10)
parking_directive = _expect_text(
"config_route_location_theme.parking_camera_scene_directive",
parking_row.get("camera_scene_directive"),
40,
)
parking_profile = parking_row.get("scene_camera_profile") if isinstance(parking_row.get("scene_camera_profile"), dict) else {}
_expect("parking" in parking_scene.lower() or "garage" in parking_scene.lower(), "parking theme did not drive scene")
_expect("parking" in parking_composition.lower() or "garage" in parking_composition.lower() or "pillar" in parking_composition.lower(), "parking theme did not drive composition")
_expect(parking_row.get("location_theme") == "parking_garage", "parking location theme did not survive")
_expect(parking_row.get("scene_theme") == "parking_garage", "parking scene theme did not survive")
_expect(parking_row.get("scene_camera_profile_key") == "parking_garage", "parking theme did not expose camera profile key")
_expect(parking_profile.get("family") == "semi_public", "parking camera profile family should be semi_public")
_expect("Parking garage camera layout" in parking_directive, "parking theme did not drive camera-scene adapter")
_expect("back-right quarter view" in parking_directive, "parking camera-scene adapter missed orbit direction")
_expect("low-angle shot" in parking_directive, "parking camera-scene adapter missed elevation")
def smoke_builder_prompt_route_policy() -> None: def smoke_builder_prompt_route_policy() -> None:
def legacy_from_request(request: builder_prompt_route.PromptBuildRequest) -> dict[str, Any]: def legacy_from_request(request: builder_prompt_route.PromptBuildRequest) -> dict[str, Any]: