Repository navigation
fix: outfit needs top - #262
Conversation
Reviewer's GuideMakes outfit generation wardrobe-aware so available recommendations include required upper-body, lower-body, and footwear regions, while safely rejecting unusable AI output, completing viable partial outfits with ranked items, and validating cached suggestions. Sequence diagram for wardrobe-aware outfit recommendationsequenceDiagram
participant User
participant RecommendationService
participant AIService
participant ClothingUtils
participant ItemScorer
participant OutfitStore
User->>RecommendationService: generate_recommendations()
RecommendationService->>AIService: generate outfit suggestions
AIService-->>RecommendationService: AI outfit options
RecommendationService->>RecommendationService: resolve_outfit_items()
RecommendationService->>ClothingUtils: missing_regions()
alt complete outfit
RecommendationService->>OutfitStore: _save_outfit()
else partial outfit
RecommendationService->>ItemScorer: rank_by_fit()
ItemScorer-->>RecommendationService: ranked candidate IDs
RecommendationService->>ClothingUtils: fill_missing_regions()
RecommendationService->>OutfitStore: _save_outfit()
else unusable outfit
RecommendationService-->>User: AIRecommendationError
end
Flow diagram for validating cached outfit suggestionsflowchart TD
A[Load cached suggestion] --> B["picked_item_ids()"]
B --> C{All items still available?}
C -- No --> D[Skip cached suggestion]
C -- Yes --> E["resolve_outfit_items()"]
E --> F{Wearable and complete?}
F -- No --> D
F -- Yes --> G["_save_outfit()"]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
… as a last resort
…hat covers nothing
…s for top and bottom
…on coverage with pairings
46c06be to
e66e73d
Compare
There was a problem hiding this comment.
Hey - I've found 4 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="backend/app/services/recommendation_service.py" line_range="1012-1017" />
<code_context>
+ if not has_wearable_piece(item_ids, item_type_map):
+ raise AIRecommendationError("AI did not select any valid items")
+ if missing_regions(item_ids, item_type_map):
+ item_ids = fill_missing_regions(
+ item_ids,
+ rank_by_fit(candidates, weather, occasion, preferences, learned_prefs),
+ item_type_map,
+ )
+ outfit = await self._save_outfit(
outfit_data,
+ item_ids,
</code_context>
<issue_to_address>
**Incomplete outfits are saved**
When fallback candidates all conflict with occupied slots for a missing region, both recommendation paths save the unchanged IDs without rechecking `missing_regions`, so users receive outfits missing a region even though changing a conflicting pick could cover it.
After `fill_missing_regions`, reject or repair any selection that still has missing regions before saving it.
Also at `backend/app/services/recommendation_service.py:181-190`, `backend/app/services/recommendation_service.py:1054`, `backend/app/services/recommendation_service.py:1059`.
</issue_to_address>
### Comment 2
<location path="backend/app/utils/clothing.py" line_range="101-104" />
<code_context>
+ return set().union(*(_regions(item_type_map.get(iid, "")) for iid in item_ids))
+
+
+def has_wearable_piece(item_ids: list[UUID], item_type_map: dict[UUID, str]) -> bool:
+ # A wardrobe with nothing that dresses any region (only layers, say) still gets outfits.
+ if not covered_regions(list(item_type_map), item_type_map):
+ return bool(item_ids)
+ return bool(covered_regions(item_ids, item_type_map))
+
</code_context>
<issue_to_address>
**Accessory-only outfits pass validation**
When the candidate wardrobe contains only regionless items and the AI selects one, `has_wearable_piece` treats any nonempty selection as wearable when the wardrobe covers no region, so the service saves selections such as a bag or hat as outfits.
Require a selected wearable item rather than treating any nonempty selection as wearable when the wardrobe has no region-covering items.
</issue_to_address>
### Comment 3
<location path="backend/app/services/item_scorer.py" line_range="268-281" />
<code_context>
+def rank_by_fit(
+ items: list[ClothingItem],
+ weather: WeatherData,
+ occasion: str,
+ preferences: UserPreference | None,
+ learned_prefs: dict | None,
+) -> list[UUID]:
+ # score_items leaves small wardrobes unscored, so filling a missing piece needs its own
+ # ranking or it would fall back to database order. Most items tie on fit in mild
+ # weather, so the least recently worn goes first to avoid adding the same piece every time.
+ def key(item: ClothingItem) -> tuple[float, date]:
+ fit = (
+ _weather_score(item, weather, preferences)
+ * _formality_score(item, occasion)
+ * _preference_score(item, preferences, learned_prefs)
+ )
+ return -fit, item.last_worn_at or date.min
</code_context>
<issue_to_address>
**Season-mismatched items rank higher**
When fallback candidates differ in seasonal suitability while tying or scoring similarly on weather, formality, and preferences, `rank_by_fit` omits `_season_score`, although `score_items` includes it, so a season-mismatched candidate outranks a season-appropriate one and is added to the outfit.
Include `_season_score` in `rank_by_fit`'s fit calculation, matching `score_items`.
</issue_to_address>
### Comment 4
<location path="backend/app/services/recommendation_service.py" line_range="1012" />
<code_context>
+ if not has_wearable_piece(item_ids, item_type_map):
+ raise AIRecommendationError("AI did not select any valid items")
+ if missing_regions(item_ids, item_type_map):
+ item_ids = fill_missing_regions(
+ item_ids,
+ rank_by_fit(candidates, weather, occasion, preferences, learned_prefs),
</code_context>
<issue_to_address>
**Fill-ins contradict outfit explanation**
When an incomplete AI answer is filled with wardrobe items, when fallback filling adds garments, `rank_by_fit` can select candidates outside the `TOP_N` items shown to the AI, while `_save_outfit` persists the AI’s original headline, styling notes, and raw response unchanged. Users receive an outfit whose explanation describes different pieces.
Record the fallback additions in the saved outfit and provide reasoning that does not imply the AI selected or evaluated those items.
Also at `backend/app/services/recommendation_service.py:1053`.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 4 findings to address first, and if the region-filling logic chooses an unsuitable item, that item is saved as part of the generated outfit and may remain after the code is reverted. The impact is bounded to generated outfit records and can be corrected by editing or removing them.
Blocking findings: backend/app/services/recommendation_service.py:1017, backend/app/utils/clothing.py:104, backend/app/services/item_scorer.py:281, backend/app/services/recommendation_service.py:1012
| if not has_wearable_piece(item_ids, item_type_map): | ||
| raise AIRecommendationError("AI did not select any valid items") | ||
| if missing_regions(item_ids, item_type_map): | ||
| item_ids = fill_missing_regions( |
There was a problem hiding this comment.
🟡 Medium · Fill-ins contradict outfit explanation
When an incomplete AI answer is filled with wardrobe items, when fallback filling adds garments, rank_by_fit can select candidates outside the TOP_N items shown to the AI, while _save_outfit persists the AI’s original headline, styling notes, and raw response unchanged. Users receive an outfit whose explanation describes different pieces.
Record the fallback additions in the saved outfit and provide reasoning that does not imply the AI selected or evaluated those items.
Also at backend/app/services/recommendation_service.py:1053.
Prompt for AI agents
In `backend/app/services/recommendation_service.py` at line 1012:
**Fill-ins contradict outfit explanation**
When an incomplete AI answer is filled with wardrobe items, when fallback filling adds garments, `rank_by_fit` can select candidates outside the `TOP_N` items shown to the AI, while `_save_outfit` persists the AI’s original headline, styling notes, and raw response unchanged. Users receive an outfit whose explanation describes different pieces.
Record the fallback additions in the saved outfit and provide reasoning that does not imply the AI selected or evaluated those items.
Also at `backend/app/services/recommendation_service.py:1053`.
Description
Related Issue
Type of Change
Checklist
Testing
Test Environment
Tests Performed
Screenshots (if applicable)
Additional Notes
Summary by Sourcery
Ensure outfit recommendations are complete and wardrobe-compatible by validating covered regions and filling missing pieces with suitable items when possible.
Bug Fixes:
Enhancements:
Tests: