Skip to content

Commit 5609b98

Browse files
Bugfix: Nutriments not accurately updated when deleting food items from AI analysis
Multi-Circle Nutriment Bug - Both food deletion and serving adjustments now work accurately and predictably.
1 parent 3df7bb5 commit 5609b98

File tree

3 files changed

+26
-5
lines changed

3 files changed

+26
-5
lines changed

Loop/Services/AIFoodAnalysis.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,9 @@ struct AIFoodAnalysisResult {
672672
let visualAssessmentDetails: String?
673673
let notes: String?
674674

675+
// Store original baseline servings for proper scaling calculations
676+
let originalServings: Double
677+
675678
// Advanced dosing fields (optional for backward compatibility)
676679
let fatProteinUnits: String?
677680
let netCarbsAdjustment: String?
@@ -1960,6 +1963,9 @@ class OpenAIFoodAnalysisService {
19601963
print("🔍 Extracted absorptionTimeHours: \(absorptionHours?.description ?? "nil")")
19611964
print("🔍 ========== OPENAI AI ANALYSIS RESULT CREATION COMPLETE ==========")
19621965

1966+
// Calculate original servings for proper scaling
1967+
let originalServings = detailedFoodItems.reduce(0) { $0 + $1.servingMultiplier }
1968+
19631969
return AIFoodAnalysisResult(
19641970
imageType: imageType,
19651971
foodItemsDetailed: detailedFoodItems,
@@ -1976,6 +1982,7 @@ class OpenAIFoodAnalysisService {
19761982
diabetesConsiderations: diabetesConsiderations,
19771983
visualAssessmentDetails: visualAssessmentDetails,
19781984
notes: "Analyzed using OpenAI GPT-4 Vision with detailed portion assessment",
1985+
originalServings: originalServings,
19791986
fatProteinUnits: extractString(from: nutritionData, keys: ["fat_protein_units"]),
19801987
netCarbsAdjustment: extractString(from: nutritionData, keys: ["net_carbs_adjustment"]),
19811988
insulinTimingRecommendations: extractString(from: nutritionData, keys: ["insulin_timing_recommendations"]),
@@ -2708,6 +2715,9 @@ class GoogleGeminiFoodAnalysisService {
27082715
print("🔍 Extracted absorptionTimeHours: \(absorptionHours?.description ?? "nil")")
27092716
print("🔍 ========== GEMINI AI ANALYSIS RESULT CREATION COMPLETE ==========")
27102717

2718+
// Calculate original servings for proper scaling
2719+
let originalServings = detailedFoodItems.reduce(0) { $0 + $1.servingMultiplier }
2720+
27112721
return AIFoodAnalysisResult(
27122722
imageType: imageType,
27132723
foodItemsDetailed: detailedFoodItems,
@@ -2724,6 +2734,7 @@ class GoogleGeminiFoodAnalysisService {
27242734
diabetesConsiderations: diabetesConsiderations,
27252735
visualAssessmentDetails: visualAssessmentDetails,
27262736
notes: "Analyzed using Google Gemini Vision - AI food recognition with enhanced safety measures",
2737+
originalServings: originalServings,
27272738
fatProteinUnits: extractString(from: nutritionData, keys: ["fat_protein_units"]),
27282739
netCarbsAdjustment: extractString(from: nutritionData, keys: ["net_carbs_adjustment"]),
27292740
insulinTimingRecommendations: extractString(from: nutritionData, keys: ["insulin_timing_recommendations"]),
@@ -2862,6 +2873,9 @@ class BasicFoodAnalysisService {
28622873
let totalFiber = foodItems.compactMap { $0.fiber }.reduce(0, +)
28632874
let totalCalories = foodItems.compactMap { $0.calories }.reduce(0, +)
28642875

2876+
// Calculate original servings for proper scaling
2877+
let originalServings = foodItems.reduce(0) { $0 + $1.servingMultiplier }
2878+
28652879
return AIFoodAnalysisResult(
28662880
imageType: .foodPhoto, // Fallback analysis assumes food photo
28672881
foodItemsDetailed: foodItems,
@@ -2878,6 +2892,7 @@ class BasicFoodAnalysisService {
28782892
diabetesConsiderations: "Basic carbohydrate estimate provided. Monitor blood glucose response and adjust insulin as needed.",
28792893
visualAssessmentDetails: nil,
28802894
notes: "This is a basic analysis. For more detailed and accurate nutrition information, consider configuring an AI provider in Settings.",
2895+
originalServings: originalServings,
28812896
fatProteinUnits: nil,
28822897
netCarbsAdjustment: nil,
28832898
insulinTimingRecommendations: nil,
@@ -3337,6 +3352,9 @@ class ClaudeFoodAnalysisService {
33373352
let imageTypeString = json["image_type"] as? String
33383353
let imageType = ImageAnalysisType(rawValue: imageTypeString ?? "food_photo") ?? .foodPhoto
33393354

3355+
// Calculate original servings for proper scaling
3356+
let originalServings = foodItems.reduce(0) { $0 + $1.servingMultiplier }
3357+
33403358
return AIFoodAnalysisResult(
33413359
imageType: imageType,
33423360
foodItemsDetailed: foodItems,
@@ -3353,6 +3371,7 @@ class ClaudeFoodAnalysisService {
33533371
diabetesConsiderations: json["diabetes_considerations"] as? String,
33543372
visualAssessmentDetails: json["visual_assessment_details"] as? String,
33553373
notes: "Analysis provided by Claude (Anthropic)",
3374+
originalServings: originalServings,
33563375
fatProteinUnits: json["fat_protein_units"] as? String,
33573376
netCarbsAdjustment: json["net_carbs_adjustment"] as? String,
33583377
insulinTimingRecommendations: json["insulin_timing_recommendations"] as? String,

Loop/View Models/CarbEntryViewModel.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,6 +1609,7 @@ extension CarbEntryViewModel {
16091609
diabetesConsiderations: "Values estimated from food name - verify portion size for accurate insulin dosing",
16101610
visualAssessmentDetails: nil,
16111611
notes: "Google Gemini nutrition analysis from text query",
1612+
originalServings: 1.0,
16121613
fatProteinUnits: nil,
16131614
netCarbsAdjustment: nil,
16141615
insulinTimingRecommendations: nil,

Loop/Views/CarbEntryView.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,9 @@ struct CarbEntryView: View, HorizontalSizeClassOverride {
305305

306306
let (carbsValue, caloriesValue, fatValue, fiberValue, proteinValue): (Double, Double?, Double?, Double?, Double?) = {
307307
if let aiResult = aiResult {
308-
// For AI results: scale by current servings vs AI's baseline servings
309-
let servingScale = viewModel.numberOfServings / aiResult.servings
308+
// For AI results: scale by current servings vs original baseline servings
309+
// This ensures both food deletion and serving adjustments work correctly
310+
let servingScale = viewModel.numberOfServings / aiResult.originalServings
310311
return (
311312
aiResult.totalCarbohydrates * servingScale,
312313
aiResult.totalCalories.map { $0 * servingScale },
@@ -833,7 +834,7 @@ extension CarbEntryView {
833834
// Expandable header for Advanced Analysis
834835
HStack {
835836
Image(systemName: "brain.head.profile")
836-
.foregroundColor(.indigo)
837+
.foregroundColor(.purple)
837838
.font(.system(size: 16, weight: .medium))
838839

839840
Text("Advanced Analysis")
@@ -1587,7 +1588,7 @@ struct AIAbsorptionTimePickerRow: View {
15871588
HStack(spacing: 4) {
15881589
Image(systemName: "brain.head.profile")
15891590
.font(.caption)
1590-
.foregroundColor(.blue)
1591+
.foregroundColor(.purple)
15911592
Text("AI")
15921593
.font(.caption)
15931594
.fontWeight(.medium)
@@ -1659,7 +1660,7 @@ struct FoodSearchEnableRow: View {
16591660
HStack(spacing: 8) {
16601661
Image(systemName: "brain.head.profile")
16611662
.font(.title3)
1662-
.foregroundColor(.blue)
1663+
.foregroundColor(.purple)
16631664
.scaleEffect(isAnimating ? 1.1 : 1.0)
16641665
.animation(.easeInOut(duration: 2.0).repeatForever(autoreverses: true), value: isAnimating)
16651666

0 commit comments

Comments
 (0)