From 87f3645115cedcbb1313d6d0f60b658963e4c715 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Sat, 27 Jun 2026 16:48:43 +0200 Subject: [PATCH] Expand private scene camera profiles --- docs/prompt-pool-routing-map.md | 14 +- location_config.py | 52 ++++++ scene_camera_adapters.py | 283 +++++++++++++++++++++++++++++++- tools/prompt_smoke.py | 72 ++++++++ 4 files changed, 410 insertions(+), 11 deletions(-) diff --git a/docs/prompt-pool-routing-map.md b/docs/prompt-pool-routing-map.md index 9e06e00..332c163 100644 --- a/docs/prompt-pool-routing-map.md +++ b/docs/prompt-pool-routing-map.md @@ -127,7 +127,7 @@ Core helper ownership: | `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. | | `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 for coworking, library, semi-public corridor/garage/archive/etc. profiles, metadata-first profile resolution, and camera-aware composition cleanup. | +| `scene_camera_adapters.py` | Location-aware camera/scene prose for coworking, library, private creator, mirror/bedroom/studio/bathroom, and semi-public corridor/garage/archive/etc. profiles, metadata-first profile resolution, and camera-aware composition cleanup. | | `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_cast.py` | Shared formatter cast descriptor parsing, cast labels, cast prose, natural cast descriptor text, and label replacement used by Krea2 and caption routes. | @@ -697,10 +697,14 @@ Current camera-aware scene adapter: `foreground`, `midground`, `background`, `detail_label`, and optional per-subject `composition` text. - Coworking/business-cafe/office scenes, classical library/book-stack scenes, - 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`. + private creator/mirror/bedroom/studio/bathroom scenes, 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`. Known JSON scene slugs route through + `SCENE_SLUG_PROFILE_KEYS` before text matching, which prevents broad terms + like cafe, mirror, tiled walls, or bookshelves from hijacking unrelated + locations. - Location themes preserve `theme` on configs and selected scene entries, and rows expose `location_theme`, `scene_theme`, `composition_theme`, and `scene_camera_profile_key` for debugging and future route rules. diff --git a/location_config.py b/location_config.py index c4a65cf..c2863f3 100644 --- a/location_config.py +++ b/location_config.py @@ -69,6 +69,58 @@ THEMATIC_LOCATION_PRESETS = { "partly hidden frame behind carved columns and shelf edges", ], }, + "creator_bedroom": { + "locations": [ + {"slug": "creator_bedroom_ring_light", "prompt": "private creator bedroom with a ring light, phone tripod, rumpled bedding, and warm lamps"}, + {"slug": "hotel_bed_phone_tripod", "prompt": "hotel bed content setup with a phone on a mini tripod, city lights, and satin bedding"}, + {"slug": "studio_bedroom_backdrop", "prompt": "small creator studio with a bed, seamless backdrop, ring light, and visible phone stand"}, + ], + "compositions": [ + "creator bedroom frame with bed edge and phone tripod readable", + "vertical creator-shot frame with ring light and warm lamps behind the body", + "bedside content setup composition with bedding and tripod placement visible", + "close room-context frame keeping the phone setup and bed plane clear", + ], + }, + "mirror_room": { + "locations": [ + {"slug": "large_bedroom_mirror_selfie", "prompt": "large bedroom mirror with the phone visible, bed behind the subject, and warm side lamps"}, + {"slug": "neon_mirror_wall", "prompt": "neon mirror wall with glossy floor reflections and saturated magenta-blue edge light"}, + {"slug": "gold_vanity_mirror", "prompt": "gold-framed vanity mirror with makeup lights, silk fabric, and close reflected framing"}, + ], + "compositions": [ + "mirror-room frame with the reflected phone angle and room depth aligned", + "full-length mirror composition keeping reflection lines readable", + "vanity-mirror frame with bulbs and reflected body plane visible", + "glossy mirror-wall composition with floor reflection line at the lower edge", + ], + }, + "boudoir_bedroom": { + "locations": [ + {"slug": "warm_boudoir_canopy_bed", "prompt": "warm boudoir bedroom with satin sheets, canopy curtains, low lamplight, and bedside phone framing"}, + {"slug": "velvet_headboard_bedroom", "prompt": "velvet headboard bedroom with gold lamps, rumpled bedding, and close sensual framing"}, + {"slug": "hotel_satin_bedroom", "prompt": "luxury hotel bedroom with satin bedding, city glow, and a visible mirror near the bed"}, + ], + "compositions": [ + "boudoir bedroom frame with sheet folds and warm bedroom depth visible", + "bed-edge composition with pillows, lamp glow, and headboard depth", + "low bedroom frame using bedding lines as the foreground anchor", + "hotel-bed composition with satin sheets and mirror edge readable", + ], + }, + "fetish_studio": { + "locations": [ + {"slug": "black_latex_studio_floor", "prompt": "dark private studio with glossy black floor reflections, rim light, and a phone tripod"}, + {"slug": "red_velvet_lacquer_room", "prompt": "red velvet room with black lacquer furniture, low spotlights, and reflective surfaces"}, + {"slug": "chrome_fetish_set", "prompt": "chrome studio set with reflective panels, black curtains, and hard-edged erotic lighting"}, + ], + "compositions": [ + "private studio frame with glossy floor reflection and controlled rim light", + "lacquer-room composition with reflective furniture and backdrop depth", + "chrome studio frame with panel seams and lighting stands readable", + "low studio-floor composition keeping reflection lines and set geometry clear", + ], + }, "semi_public_affair": { "locations": [ {"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"}, diff --git a/scene_camera_adapters.py b/scene_camera_adapters.py index 8e20bf1..da557a5 100644 --- a/scene_camera_adapters.py +++ b/scene_camera_adapters.py @@ -29,7 +29,7 @@ SCENE_CAMERA_PROFILES: tuple[dict[str, Any], ...] = ( { "key": "business_cafe", "family": "coworking", - "terms": ("business cafe", "work cafe", "cafe"), + "terms": ("business cafe", "work cafe", "coworking counter", "cafe counter with laptops", "coffee-counter work spots"), "layout_label": "Business cafe camera layout", "place": "business cafe coworking counter", "foreground": "counter edge, laptop corner, and small plant", @@ -91,8 +91,6 @@ SCENE_CAMERA_PROFILES: tuple[dict[str, Any], ...] = ( "large library", "grand library", "reading room", - "bookshelves", - "book shelves", "book stacks", "rare-books", "rare books", @@ -110,6 +108,158 @@ SCENE_CAMERA_PROFILES: tuple[dict[str, Any], ...] = ( "default": "classical library frame with the subjects near a bookshelf edge and long shelf depth behind them", }, }, + { + "key": "creator_bedroom", + "family": "private_creator", + "terms": ( + "creator bedroom", + "content setup", + "phone tripod", + "ring light", + "phone on a mini tripod", + "creator studio", + "creator-shot framing", + "vertical creator-video", + ), + "layout_label": "Creator room camera layout", + "place": "private creator room", + "foreground": "bed edge, phone tripod, and rumpled sheets", + "midground": "ring light stand, warm lamps, pillows, and creator props", + "background": "soft bedding, curtains, mirror edge, and warm private-room depth", + "detail_label": "creator-room details", + "composition": { + "woman": "creator-room frame with the woman near the bed edge and phone-tripod setup behind her", + "man": "creator-room frame with the man near the bed edge and phone-tripod setup behind him", + "default": "creator-room frame with the subjects near the bed edge and phone-tripod setup behind them", + }, + }, + { + "key": "mirror_room", + "family": "private_creator", + "terms": ( + "mirror selfie setup", + "mirror wall", + "mirror-facing", + "floor mirror", + "vanity mirror", + "phone reflection", + "reflected bodies", + "black lacquer mirror", + "neon mirror wall", + ), + "layout_label": "Mirror-room camera layout", + "place": "private mirror room", + "foreground": "mirror edge, reflected phone angle, and floor reflection line", + "midground": "bedside surface, vanity bulbs, glossy furniture, and reflected body plane", + "background": "mirror depth, warm lamps, curtains, and repeated reflected sightlines", + "detail_label": "mirror-room details", + "composition": { + "woman": "mirror-room frame with the woman aligned to the reflected phone angle and room depth behind her", + "man": "mirror-room frame with the man aligned to the reflected phone angle and room depth behind him", + "default": "mirror-room frame with the subjects aligned to the reflected phone angle and room depth behind them", + }, + }, + { + "key": "boudoir_bedroom", + "family": "private_creator", + "terms": ( + "boudoir bedroom", + "silk-sheet bed", + "silk sheets", + "velvet headboard", + "canopy bed", + "four-poster bed", + "satin bedding", + "bedside phone", + "hotel bedroom", + ), + "layout_label": "Boudoir bedroom camera layout", + "place": "boudoir bedroom", + "foreground": "sheet fold, bedside edge, and pillow line", + "midground": "rumpled bedding, warm lamps, canopy curtains, and soft floor shadows", + "background": "headboard, drapes, mirror edge, and intimate bedroom depth", + "detail_label": "bedroom details", + "composition": { + "woman": "boudoir bedroom frame with the woman on or beside the bed and warm bedroom depth behind her", + "man": "boudoir bedroom frame with the man on or beside the bed and warm bedroom depth behind him", + "default": "boudoir bedroom frame with the subjects on or beside the bed and warm bedroom depth behind them", + }, + }, + { + "key": "bathroom_shower", + "family": "private_creator", + "terms": ( + "bathroom counter", + "private bathroom", + "shower room", + "wet tile", + "steam", + "steamy", + "glass reflections", + "vanity counter", + "wet towels", + ), + "layout_label": "Bathroom camera layout", + "place": "private bathroom", + "foreground": "counter edge, glass partition line, and towel edge", + "midground": "mirror haze, vanity bulbs, wet tile, and reflected glass seams", + "background": "shower wall, warm reflected light, steam, and tight private-room depth", + "detail_label": "bathroom details", + "composition": { + "woman": "bathroom frame with the woman near the mirror or glass partition and tile depth behind her", + "man": "bathroom frame with the man near the mirror or glass partition and tile depth behind him", + "default": "bathroom frame with the subjects near the mirror or glass partition and tile depth behind them", + }, + }, + { + "key": "private_studio", + "family": "private_creator", + "terms": ( + "fetish studio", + "private studio", + "glossy black floor", + "harness-wall", + "chrome studio", + "industrial loft", + "neon-lit lacquer", + "reflective panels", + "controlled rim light", + ), + "layout_label": "Private studio camera layout", + "place": "private studio set", + "foreground": "floor reflection edge, prop stand, and lighting-stand line", + "midground": "controlled lights, reflective panels, backdrop seams, and studio props", + "background": "dark curtains, glossy walls, rim light, and staged private-set depth", + "detail_label": "studio details", + "composition": { + "woman": "private studio frame with the woman on the glossy floor plane and controlled lights behind her", + "man": "private studio frame with the man on the glossy floor plane and controlled lights behind him", + "default": "private studio frame with the subjects on the glossy floor plane and controlled lights behind them", + }, + }, + { + "key": "car_interior", + "family": "private_creator", + "terms": ( + "parked car interior", + "private car backseat", + "car backseat", + "dashboard glow", + "tinted windows", + "seat reflections", + ), + "layout_label": "Car interior camera layout", + "place": "parked car interior", + "foreground": "seat edge, door frame, and dashboard glow", + "midground": "upholstery seams, window reflections, center console, and tight cabin geometry", + "background": "tinted windows, rear seat depth, and enclosed car interior shadows", + "detail_label": "car-interior details", + "composition": { + "woman": "car interior frame with the woman inside the tight cabin geometry and window reflections behind her", + "man": "car interior frame with the man inside the tight cabin geometry and window reflections behind him", + "default": "car interior frame with the subjects inside the tight cabin geometry and window reflections behind them", + }, + }, { "key": "hotel_corridor", "family": "semi_public", @@ -175,7 +325,6 @@ SCENE_CAMERA_PROFILES: tuple[dict[str, Any], ...] = ( "prop racks", "costume racks", "costume rails", - "velvet curtains", "stage ropes", "scenery flats", ), @@ -279,7 +428,6 @@ SCENE_CAMERA_PROFILES: tuple[dict[str, Any], ...] = ( "station service passage", "metal lockers", "vending machines", - "tiled walls", "utility doors", "warning stripes", ), @@ -355,6 +503,13 @@ SCENE_CAMERA_PROFILE_KEYS = {str(profile["key"]): dict(profile) for profile in S THEME_PROFILE_KEYS = { "classical_library": "classical_library", + "creator_bedroom": "creator_bedroom", + "mirror_room": "mirror_room", + "boudoir_bedroom": "boudoir_bedroom", + "bathroom_shower": "bathroom_shower", + "private_studio": "private_studio", + "car_interior": "car_interior", + "fetish_studio": "private_studio", "hotel_corridor": "hotel_corridor", "parking_garage": "parking_garage", "theater_backstage": "theater_backstage", @@ -366,6 +521,118 @@ THEME_PROFILE_KEYS = { "restaurant_private_booth": "restaurant_private_booth", } +SCENE_SLUG_PROFILE_KEYS = { + "coworking_lounge_window": "coworking_lounge", + "business_cafe_counter": "business_cafe", + "office_afterhours_affair": "office_after_hours", + "classical_large_library": "classical_library", + "old_world_reading_room": "classical_library", + "hidden_library_stacks": "classical_library", + "library_stacks_secret": "classical_library", + "creator_bedroom_ring_light": "creator_bedroom", + "onlyfans_mirror_bedroom": "mirror_room", + "walk_in_closet_tryon": "mirror_room", + "hotel_bed_phone_tripod": "creator_bedroom", + "bathroom_counter_selfie": "bathroom_shower", + "vanity_ring_light_close": "mirror_room", + "apartment_floor_content": "creator_bedroom", + "balcony_phone_selfie": "creator_bedroom", + "car_interior_creator_selfie": "car_interior", + "shower_steam_phone_reflection": "bathroom_shower", + "studio_bedroom_backdrop": "creator_bedroom", + "couch_lamp_creator_clip": "creator_bedroom", + "large_bedroom_mirror_selfie": "mirror_room", + "antique_mirror_boudoir": "mirror_room", + "bathroom_mirror_haze": "bathroom_shower", + "closet_full_length_mirror": "mirror_room", + "hotel_mirror_city_view": "mirror_room", + "neon_mirror_wall": "mirror_room", + "gold_vanity_mirror": "mirror_room", + "black_lacquer_mirror_room": "mirror_room", + "hardcore_bedroom_mirror_pair": "mirror_room", + "hardcore_hotel_mirror_pair": "mirror_room", + "hardcore_shower_mirror_pair": "bathroom_shower", + "hardcore_threesome_mirror_suite": "mirror_room", + "warm_boudoir_canopy_bed": "boudoir_bedroom", + "silk_bed_close_creator": "boudoir_bedroom", + "velvet_headboard_bedroom": "boudoir_bedroom", + "four_poster_lingerie_room": "boudoir_bedroom", + "hotel_satin_bedroom": "boudoir_bedroom", + "rose_lamp_bedroom": "boudoir_bedroom", + "black_latex_studio_floor": "private_studio", + "red_velvet_lacquer_room": "private_studio", + "industrial_loft_private_set": "private_studio", + "neon_lacquer_private_room": "private_studio", + "harness_wall_studio": "private_studio", + "chrome_fetish_set": "private_studio", + "costume_dressing_room_phone": "theater_backstage", + "burlesque_stage_close": "theater_backstage", + "cabaret_backstage_vanity": "theater_backstage", + "after_dark_private_office": "office_after_hours", + "fantasy_parlor_content_set": "private_studio", + "cosplay_hotel_mirror": "mirror_room", + "hardcore_bedroom_phone_tripod": "creator_bedroom", + "hardcore_hotel_bed_city": "boudoir_bedroom", + "hardcore_mirror_bedroom": "mirror_room", + "hardcore_low_mattress_studio": "private_studio", + "hardcore_velvet_room": "private_studio", + "hardcore_shower_room": "bathroom_shower", + "hardcore_lounge_couch": "private_studio", + "hardcore_floor_cushion_room": "boudoir_bedroom", + "hardcore_ring_light_bed": "creator_bedroom", + "hardcore_bathroom_counter": "bathroom_shower", + "hardcore_walk_in_closet_floor": "mirror_room", + "hardcore_car_backseat": "car_interior", + "bed_edge_close_contact": "boudoir_bedroom", + "low_bed_mirror_angle": "mirror_room", + "hotel_bed_overhead": "boudoir_bedroom", + "floor_mattress_creator_set": "creator_bedroom", + "canopy_bed_explicit_set": "boudoir_bedroom", + "velvet_bedroom_wide": "boudoir_bedroom", + "penetration_mirror_bedroom": "mirror_room", + "penetration_edge_of_bed": "boudoir_bedroom", + "penetration_low_mattress": "private_studio", + "penetration_couch_lounge": "private_studio", + "penetration_shower_bench": "bathroom_shower", + "penetration_floor_cushions": "boudoir_bedroom", + "oral_bed_kneeling_close": "boudoir_bedroom", + "oral_mirror_floor": "mirror_room", + "oral_couch_front_view": "private_studio", + "oral_shower_steam": "bathroom_shower", + "oral_vanity_floor": "mirror_room", + "oral_hotel_bed_close": "boudoir_bedroom", + "anal_rear_mirror_bed": "mirror_room", + "anal_bent_over_couch": "private_studio", + "anal_edge_bed_low_angle": "boudoir_bedroom", + "anal_shower_wall": "bathroom_shower", + "anal_velvet_bench": "private_studio", + "anal_floor_mattress_mirror": "mirror_room", + "threesome_wide_bedroom": "boudoir_bedroom", + "threesome_hotel_suite": "boudoir_bedroom", + "threesome_floor_cushions": "boudoir_bedroom", + "threesome_studio_mattress": "private_studio", + "threesome_shower_room": "bathroom_shower", + "threesome_velvet_lounge": "private_studio", + "group_suite_wide_bed": "boudoir_bedroom", + "group_studio_mattress_room": "private_studio", + "group_velvet_orgy_room": "private_studio", + "group_lounge_couches": "private_studio", + "group_floor_pillow_room": "boudoir_bedroom", + "group_shower_spa_room": "bathroom_shower", + "group_rooftop_private_party": "creator_bedroom", + "group_hotel_party_bedroom": "boudoir_bedroom", + "group_backstage_private_room": "theater_backstage", + "group_neon_loft_room": "private_studio", + "group_mirror_wall_suite": "mirror_room", + "group_lacquer_mirror_lounge": "mirror_room", + "climax_bed_close_flash": "boudoir_bedroom", + "climax_mirror_counter": "mirror_room", + "climax_floor_sheets": "boudoir_bedroom", + "climax_hotel_bed_flash": "boudoir_bedroom", + "climax_shower_tile": "bathroom_shower", + "climax_velvet_couch": "private_studio", +} + PROFILE_TEXT_FIELDS = ( "key", "family", @@ -475,13 +742,17 @@ def _scene_entry_text(scene_entry: Any) -> str: def _scene_entry_profile_key(scene_entry: Any) -> str: if not isinstance(scene_entry, dict): return "" - return str( + explicit = str( scene_entry.get("scene_camera_profile_key") or scene_entry.get("camera_profile_key") or scene_entry.get("camera_profile") or scene_entry.get("profile") or "" ).strip() + if explicit: + return explicit + slug = str(scene_entry.get("slug") or "").strip() + return SCENE_SLUG_PROFILE_KEYS.get(slug, "") def _scene_entry_profile(scene_entry: Any) -> dict[str, Any]: diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index 2e01997..d20cc15 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -86,6 +86,7 @@ import row_rendering # noqa: E402 import row_role_graph # noqa: E402 import row_route_metadata # noqa: E402 import row_subject_route # noqa: E402 +import scene_camera_adapters # noqa: E402 import server_routes # noqa: E402 import sdxl_formatter # noqa: E402 import sdxl_format_route # noqa: E402 @@ -848,6 +849,33 @@ def smoke_row_camera_policy() -> None: _expect("glass conservatory" in inline_composition.lower(), "inline profile did not drive composition cleanup") _expect("bag" not in inline_composition.lower() and "shoes" not in inline_composition.lower(), "inline profile composition leaked unrelated props") + beach_profile = scene_camera_adapters.scene_camera_profile( + "beach cafe table with woven chairs, linen shade, and ocean light in the background", + scene_entry={"slug": "beach_cafe_table"}, + ) + subway_profile = scene_camera_adapters.scene_camera_profile( + "clean subway platform with tiled walls, overhead lights, and a quiet selfie corner", + scene_entry={"slug": "subway_tile_selfie_corner"}, + ) + apartment_profile = scene_camera_adapters.scene_camera_profile( + "sunny apartment corner with bookshelves, a warm rug, and a phone on a small tripod", + scene_entry={"slug": "sunny_apartment_phone_tripod"}, + ) + _expect(not beach_profile, "scene camera resolver should not classify beach cafe as business cafe") + _expect(not subway_profile, "scene camera resolver should not classify subway tile as station lockers") + _expect(not apartment_profile, "scene camera resolver should not classify apartment bookshelves as classical library") + + mirror_profile = scene_camera_adapters.scene_camera_profile( + "large bedroom mirror with the phone visible, bed behind the subject, and warm side lamps", + scene_entry={"slug": "large_bedroom_mirror_selfie"}, + ) + studio_profile = scene_camera_adapters.scene_camera_profile( + "dark private studio with glossy black floor reflections, rim light, and a phone tripod", + scene_entry={"slug": "black_latex_studio_floor"}, + ) + _expect(mirror_profile.get("key") == "mirror_room", "scene slug resolver missed mirror-room profile") + _expect(studio_profile.get("key") == "private_studio", "scene slug resolver missed private-studio profile") + def smoke_config_route_location_theme() -> None: location_config, composition_config = _classical_library_theme_configs() @@ -958,6 +986,47 @@ def smoke_config_route_location_theme() -> None: _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") + creator_location_config, creator_composition_config = _thematic_location_configs("creator_bedroom") + creator_row = pb.build_prompt_from_configs( + row_number=1, + start_index=1, + seed=3321, + 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=45, + vertical_angle=0, + zoom=5.5, + subject_focus="environment", + ), + location_config=creator_location_config, + composition_config=creator_composition_config, + ) + _expect_row_base(creator_row, "config_route_location_theme.creator") + creator_scene = _expect_text("config_route_location_theme.creator_scene", creator_row.get("scene_text"), 20) + creator_composition = _expect_text("config_route_location_theme.creator_composition", creator_row.get("composition"), 10) + creator_directive = _expect_text( + "config_route_location_theme.creator_camera_scene_directive", + creator_row.get("camera_scene_directive"), + 40, + ) + creator_profile = creator_row.get("scene_camera_profile") if isinstance(creator_row.get("scene_camera_profile"), dict) else {} + _expect("creator" in creator_scene.lower() or "phone" in creator_scene.lower(), "creator theme did not drive scene") + _expect( + any(token in creator_composition.lower() for token in ("creator", "tripod", "phone", "bed")), + "creator theme did not drive composition", + ) + _expect(creator_row.get("location_theme") == "creator_bedroom", "creator location theme did not survive") + _expect(creator_row.get("scene_theme") == "creator_bedroom", "creator scene theme did not survive") + _expect(creator_row.get("scene_camera_profile_key") == "creator_bedroom", "creator theme did not expose camera profile key") + _expect(creator_profile.get("family") == "private_creator", "creator camera profile family should be private_creator") + _expect("Creator room camera layout" in creator_directive, "creator theme did not drive camera-scene adapter") + _expect("front-right quarter view" in creator_directive, "creator camera-scene adapter missed orbit direction") + creator_krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(creator_row), target="single") + creator_prompt = creator_krea.get("krea_prompt") or "" + _expect("Creator room camera layout" in creator_prompt, "Krea config route lost creator camera-scene directive") + def smoke_builder_prompt_route_policy() -> None: def legacy_from_request(request: builder_prompt_route.PromptBuildRequest) -> dict[str, Any]: @@ -1364,6 +1433,9 @@ def smoke_location_config_policy() -> None: _expect(pb.LOCATION_POOL_PRESETS is location_config.LOCATION_POOL_PRESETS, "Prompt builder location presets are not delegated") _expect(pb.COMPOSITION_POOL_PRESETS is location_config.COMPOSITION_POOL_PRESETS, "Prompt builder composition presets are not delegated") _expect("classical_library" in location_config.location_theme_choices(), "Location themes lost classical_library") + _expect("creator_bedroom" in location_config.location_theme_choices(), "Location themes lost creator_bedroom") + _expect("mirror_room" in location_config.location_theme_choices(), "Location themes lost mirror_room") + _expect("fetish_studio" in location_config.location_theme_choices(), "Location themes lost fetish_studio") custom = json.loads( pb.build_location_pool_json(