ai cooking
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Pasta baby! (#525)

* first try

* second try

* third try

* my edit

* okay stop on pasta

* we got pasta and grains

---------

Co-authored-by: paul miller <paul.miller>

authored by

Paul Miller
paul miller
and committed by
GitHub
16a4a33d c2e3c928

+52 -12
+23
internal/ai/ingredient_grade.go
··· 21 21 defaultIngredientGradeModel = openai.ChatModelGPT5Mini 22 22 ) 23 23 24 + // should we have category spefic grading prompts? 24 25 const ingredientGradeSystemInstruction = ` 25 26 You review grocery catalog items before they are shown to a home recipe generator. 26 27 ··· 37 38 - formats intended mainly for snacking or immediate eating rather than cooking 38 39 - pre-cut fruit unless it is still broadly useful for cooking or baking 39 40 41 + Additional rules for pasta, grains, rice, legumes, and noodles: 42 + 43 + - Prefer flexible base carbohydrates: 44 + rice, dry pasta, oats, quinoa, farro, freekah 45 + 46 + - Use simple score anchors: 47 + standard dry pasta → 6–7 48 + premium dry pasta → 8–9 49 + alternative pasta (chickpea, lentil, gluten-free) → 5–6 50 + bread → 5–6 51 + prepared sauces → max 6 52 + instant or flavored mixes → 3–5 53 + 54 + - Reward real cooking-performance signals: 55 + bronze-cut, slow-dried, high-protein durum, whole grain, hulled, pearled 56 + 57 + - Reward known higher-quality brands (e.g., Felicetti, De Cecco, Rummo, Rustichella) 58 + 59 + - Do not infer quality from generic terms: 60 + "quality", "non-GMO", "organic", "traditional" 61 + 62 + - Penalize items that are less flexible or more processed 40 63 41 64 Scoring anchors: 42 65 - 9-10: excellent raw/fresh flexible cooking ingredient, e.g. whole vegetables, greens, roots, raw meats, fresh fruit useful in baking/cooking
+10 -5
internal/albertsons/query/client.go
··· 12 12 "time" 13 13 ) 14 14 15 + // this is a strange set. Actual sub categories don't work but thes aisle-vs ones do. 16 + // for en example broke sub category here is beef https://www.safeway.com/shop/aisles/meat-seafood/beef.html?sort=&page=1&loc=1142 15 17 const ( 16 - Category_Vegatables = "GR-C-categ-8c62c848" 17 - Category_Fruit = "GR-C-categ-a8eea474" 18 - Category_Seafood = "GR-C-Categ-6090cd27" 19 - Category_Meat = "GR-MeatF-fffc8662" 20 - Category_Wine = "GR-S-Searc-db592d50" 18 + Category_Vegatables = "GR-C-categ-8c62c848" 19 + Category_Fruit = "GR-C-categ-a8eea474" 20 + Category_Seafood = "GR-C-Categ-6090cd27" // https://www.safeway.com/aisle-vs/meat-seafood/seafood-favorites.html 21 + Category_Meat = "GR-MeatF-fffc8662" // https://www.safeway.com/aisle-vs/meat-seafood/meat-favorites.html 22 + Category_Wine = "GR-S-Searc-db592d50" 23 + Category_Pasta_Grains = "GR-C-Categ-77b9d5dd" // https://www.safeway.com/aisle-vs/grains-pasta-sides/best-sellers.html 24 + Category_Dairy = "GR-C-Categ-f210e5cd" // new and trending seems dubious https://www.safeway.com/aisle-vs/dairy-eggs-cheese/new-trending.html 21 25 ) 22 26 23 27 func StapleCategories() []string { ··· 26 30 Category_Fruit, 27 31 Category_Seafood, 28 32 Category_Meat, 33 + Category_Pasta_Grains, 29 34 } 30 35 } 31 36
+9 -1
internal/kroger/staples.go
··· 143 143 //Taxonomy: product., 144 144 // CountryOrigin: product.CountryOrigin, 145 145 // Favorite: item.Favorite, 146 - // InventoryStockLevel: item.InventoryStockLevel), 147 146 } 148 147 } 149 148 ··· 185 184 Term: "lamb", 186 185 Brands: []string{"Simple Truth"}, 187 186 }, 187 + { 188 + Term: "grains", 189 + Brands: []string{"*"}, 190 + }, 191 + { 192 + Term: "pasta", 193 + Brands: []string{"*"}, // Should we just put our thumb on the scale 194 + }, 195 + // TODO dairy, international 188 196 }...) 189 197 } 190 198
+6 -2
internal/recipes/generator.go
··· 104 104 slog.InfoContext(ctx, "Regenerating recipes for location", "location", p.String(), "response_id", p.ResponseID) 105 105 instructions := regenerateInstructions(p) 106 106 107 + // TODO give them some sort of status. 107 108 shoppingList, err := g.aiClient.Regenerate(ctx, instructions, p.ResponseID) 108 109 if err != nil { 109 110 return nil, fmt.Errorf("failed to regenerate recipes with AI: %w", err) ··· 129 130 if err != nil { 130 131 return nil, fmt.Errorf("failed to get staples: %w", err) 131 132 } 132 - g.writeStatus(ctx, hash, fmt.Sprintf("Looking through %d ingredients", len(ingredients))) 133 + ogCount := len(ingredients) 133 134 ingredients = lo.Filter(ingredients, func(ing ai.InputIngredient, _ int) bool { 134 135 // TODO make configurable? 135 - return ing.Grade == nil || ing.Grade.Score > 5 136 + return ing.Grade == nil || ing.Grade.Score > 6 136 137 }) 138 + // having category would be interesing here. 139 + g.writeStatus(ctx, hash, fmt.Sprintf("Considering %d out of %d ingredients", len(ingredients), ogCount)) 140 + 137 141 mutable.Shuffle(ingredients) 138 142 139 143 instructions := []string{p.Directive, p.Instructions}
+2 -2
internal/recipes/generator_hash_test.go
··· 23 23 } 24 24 25 25 // make sure we're intentional about breaking hash 26 - if h1 != "JjKXkKjKKpE" { 26 + if h1 != "wrxx3dmHzBA" { 27 27 t.Fatalf("expected hash to be stable and equal to JjKXkKjKKpE, got %s", h1) 28 28 } 29 29 ··· 31 31 if !ok { 32 32 t.Fatal("expected current hash passhed to legacy") 33 33 } 34 - if legacyHash != "cmVjaXBlJjKXkKjKKpE=" { 34 + if legacyHash != "cmVjaXBlwrxx3dmHzBA=" { 35 35 t.Fatalf("expected legacy hash to be base64 of recipe hash with prefix, got %s", legacyHash) 36 36 } 37 37
+2 -2
internal/wholefoods/staples.go
··· 108 108 "shellfish", 109 109 "goat-lamb-veal", 110 110 "game-meats", 111 + "rice-grains", 112 + "pasta-noodles", 111 113 } 112 - // rice-grains? 113 - // pasta-noodles 114 114 // red-wine, white-wine, sparkling 115 115 } 116 116