Repository navigation
feat: Rework outfit recommendation slot rules to treat full body items better - #264
Conversation
…ith garment vocabulary, ensuring consistency and avoiding manual updates. Added tests for vocabulary-based role types and prompt rendering.
|
Thanks for your first pull request to Wardrowbe! Please make sure you've read the CONTRIBUTING guide, especially the PR title format section. A maintainer will review this soon. |
Reviewer's GuideThe PR updates recommendation and pairing prompts to treat separates and one-piece outfits as equal silhouettes, while rendering every garment list from the validated ITEM_ROLE taxonomy to prevent prompt drift. It adds focused coverage for role derivation and prompt rendering; database-backed recommendation tests were not run because they require Postgres. Sequence diagram for taxonomy-driven outfit prompt loadingsequenceDiagram
participant Service as Recommendation_or_Pairing_Service
participant Loader as load_prompt
participant Renderer as render_outfit_prompt
participant Vocabulary as ITEM_ROLE_Vocabulary
Service->>Loader: load_prompt(template)
Loader-->>Service: Prompt_template
Service->>Renderer: render_outfit_prompt(Prompt_template)
loop Each role in ROLES
Renderer->>Vocabulary: types_for_role(role)
Vocabulary-->>Renderer: Role_garment_types
Renderer->>Renderer: Replace_ROLE_TYPES_token
end
Renderer-->>Service: Rendered_outfit_prompt
Flow diagram for equal-weight outfit silhouettesflowchart LR
Prompt[Outfit recommendation or pairing prompt] --> Silhouettes{Equal-weight silhouettes}
Silhouettes --> Separates[Separates: top and bottom roles]
Silhouettes --> OnePiece[One-piece: full-body garment fills top and bottom roles]
Separates --> Legwear[Optional legwear]
OnePiece --> Legwear
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 2 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="backend/app/services/pairing_service.py" line_range="21" />
<code_context>
logger = logging.getLogger(__name__)
-PAIRING_PROMPT_TEMPLATE = load_prompt("item_pairing")
+PAIRING_PROMPT_TEMPLATE = render_outfit_prompt(load_prompt("item_pairing"))
# A pairing is an internally-generated row, or an externally-authored one carrying the
</code_context>
<issue_to_address>
**Pairings omit their source item**
When the source item is a top or bottom and the model selects a full-body garment, `generate_pairings` adds the source to `valid_ids`, but `deduplicate_by_body_slot` processes the multi-slot garment first and removes the source, so the service can persist and return a pairing without its required source item.
Ensure deduplication preserves the source item when resolving body-slot conflicts.
</issue_to_address>
### Comment 2
<location path="backend/tests/test_garment_vocabulary.py" line_range="79-87" />
<code_context>
assert prompt_types == set(DEFAULT_WASH_INTERVALS)
+def test_types_for_role_is_derived_from_the_vocabulary():
+ assert types_for_role("base_top") == [
+ "shirt", "t-shirt", "top", "sweater", "polo", "blouse", "tank-top",
+ ] # fmt: skip
+ assert types_for_role("full_body") == ["dress", "jumpsuit"]
+ assert set(types_for_role("bottom")) == {"pants", "jeans", "shorts", "skirt"}
+ assert set(types_for_role("footwear")) == {"shoes", "sneakers", "boots", "sandals"}
+ assert types_for_role("legwear") == ["tights"]
+ assert all(ITEM_ROLE[garment] == "base_top" for garment in types_for_role("base_top"))
+
+
</code_context>
<issue_to_address>
**New garment roles go unlisted**
When a garment is added to `ITEM_ROLE` for one of the tested roles while `types_for_role` keeps returning its old hard-coded list, `test_types_for_role_is_derived_from_the_vocabulary` checks fixed expected lists at lines 80–86, so an implementation that returns those hard-coded lists still passes after a new garment is added to a role. The assertion at line 87 only checks the listed base-top garments’ roles, not that every taxonomy entry is returned.
Compare each role’s result against the corresponding entries derived from `ITEM_ROLE`, so the test fails when a taxonomy addition is omitted.
</issue_to_address>Sourcery assessment
Approval pending. 2 findings to address first.
Blocking findings: backend/app/services/pairing_service.py:21, backend/tests/test_garment_vocabulary.py:87
test_types_for_role_is_derived_from_the_vocabulary pinned fixed per-role lists, so a hard-coded types_for_role would still pass after a garment is added to a role. Compare each role against the entries derived from ITEM_ROLE and assert the per-role lists cover the taxonomy.
|
Thanks a bunch for your help @geekygirlsarah! 🙏🏽 |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ith garment vocabulary, ensuring consistency and avoiding manual updates. Added tests for vocabulary-based role types and prompt rendering.
Description
Reworks the outfit silhouettes in both AI outfit prompts so separates and one-piece looks are equal peers, and derives the garment type lists from the
ITEM_ROLEtaxonomy so the prompts can't drift from what the tagger validates.Previously
recommendation.txtframed the formula as separates-first withOR pick ONE dressas a less-preferred fallback, anditem_pairing.txtdid the same. The slot enumeration was also a hand-written subset that omitted several realbase_toptypes (e.g.top,polo,tank-top) and never namedjumpsuit. For a wardrobe heavy in dresses, this wording biased the model toward separating.Changes:
backend/app/utils/garment_vocabulary.py: addtypes_for_role(role)andrender_outfit_prompt(template), which fills<<ROLE_TYPES>>tokens fromITEM_ROLE(mirrors the existingrender_tagging_promptpattern).backend/app/prompts/recommendation.txt: replace theORcontinuation with two equal-weight silhouette bullets (separates / one-piece) plus an optional legwear bullet; generalize the proportion guidance to cover one-piece waists.backend/app/prompts/item_pairing.txt: same silhouette treatment, and state that a full-body garment fills both top and bottom slots.backend/app/services/recommendation_service.py,backend/app/services/pairing_service.py: render the prompts at load time.backend/tests/test_garment_vocabulary.py: tests asserting the role lists come from the vocabulary and that both prompts render and contain every role's types.Related Issue
Resolves #213
Type of Change
Checklist
Testing
Test Environment
Tests Performed
The DB-backed tests in
test_recommendation_service.pywere not run (they require Postgres viaTEST_DATABASE_URL); only the prompt/template tests in that file were exercised.Screenshots (if applicable)
N/A — prompt-only change, no UI.
Additional Notes
garment_vocabulary.jsonis untouched, sotest_type_lists_agreecontinues to hold.leggingsis apantssubtype inclothing_analysis.txt, not aTYPE, so it is not a member of the derived bottom list (pants, jeans, shorts, skirt). It remains identifiable through subtype tagging..txtfiles stay readable while the lists stay derived; happy to switch if reviewers prefer statically literal prompts.recommendation_service.pyreadsoutfit_data.get("layers", {}), but no prompt asks the model to emit alayerskey, so that field is always empty.Summary by Sourcery
Improve outfit recommendations and pairings by making one-piece garments first-class options and keeping prompt garment types aligned with the validated taxonomy.
New Features:
Bug Fixes:
Enhancements:
Tests: