refactor: introduce adapter pattern for AI provider (OpenAI)
- Add internal/adapters/ai/types.go with neutral shared types (Recipe, DayPlan, RecognizedItem, IngredientClassification, etc.) - Remove types from internal/adapters/openai/ — adapter now uses ai.* - Define Recognizer interface in recognition package - Define MenuGenerator interface in menu package - Define RecipeGenerator interface in recommendation package - Handler structs now hold interfaces, not *openai.Client - Add wire.Bind entries for the three new interface bindings To swap OpenAI for another provider: implement the three interfaces using ai.* types and change the wire.Bind lines in cmd/server/wire.go. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
128
backend/internal/adapters/ai/types.go
Normal file
128
backend/internal/adapters/ai/types.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package ai
|
||||
|
||||
// RecipeRequest contains parameters for recipe generation.
|
||||
type RecipeRequest struct {
|
||||
UserGoal string // "lose" | "maintain" | "gain"
|
||||
DailyCalories int
|
||||
Restrictions []string // e.g. ["gluten_free", "vegetarian"]
|
||||
CuisinePrefs []string // e.g. ["russian", "asian"]
|
||||
Count int
|
||||
AvailableProducts []string // human-readable list of products in user's pantry
|
||||
Lang string // ISO 639-1 target language code, e.g. "en", "ru"
|
||||
}
|
||||
|
||||
// Recipe is an AI-generated recipe.
|
||||
type Recipe struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Cuisine string `json:"cuisine"`
|
||||
Difficulty string `json:"difficulty"`
|
||||
PrepTimeMin int `json:"prep_time_min"`
|
||||
CookTimeMin int `json:"cook_time_min"`
|
||||
Servings int `json:"servings"`
|
||||
ImageQuery string `json:"image_query"`
|
||||
ImageURL string `json:"image_url"`
|
||||
Ingredients []Ingredient `json:"ingredients"`
|
||||
Steps []Step `json:"steps"`
|
||||
Tags []string `json:"tags"`
|
||||
Nutrition NutritionInfo `json:"nutrition_per_serving"`
|
||||
}
|
||||
|
||||
// Ingredient is a single ingredient in a recipe.
|
||||
type Ingredient struct {
|
||||
Name string `json:"name"`
|
||||
Amount float64 `json:"amount"`
|
||||
Unit string `json:"unit"`
|
||||
}
|
||||
|
||||
// Step is a single preparation step.
|
||||
type Step struct {
|
||||
Number int `json:"number"`
|
||||
Description string `json:"description"`
|
||||
TimerSeconds *int `json:"timer_seconds"`
|
||||
}
|
||||
|
||||
// NutritionInfo contains approximate nutritional information per serving.
|
||||
type NutritionInfo struct {
|
||||
Calories float64 `json:"calories"`
|
||||
ProteinG float64 `json:"protein_g"`
|
||||
FatG float64 `json:"fat_g"`
|
||||
CarbsG float64 `json:"carbs_g"`
|
||||
Approximate bool `json:"approximate"`
|
||||
}
|
||||
|
||||
// MenuRequest contains parameters for weekly menu generation.
|
||||
type MenuRequest struct {
|
||||
UserGoal string
|
||||
DailyCalories int
|
||||
Restrictions []string
|
||||
CuisinePrefs []string
|
||||
AvailableProducts []string
|
||||
Lang string // ISO 639-1 target language code, e.g. "en", "ru"
|
||||
}
|
||||
|
||||
// DayPlan is the AI-generated plan for a single day.
|
||||
type DayPlan struct {
|
||||
Day int `json:"day"`
|
||||
Meals []MealEntry `json:"meals"`
|
||||
}
|
||||
|
||||
// MealEntry is a single meal within a day plan.
|
||||
type MealEntry struct {
|
||||
MealType string `json:"meal_type"` // breakfast | lunch | dinner
|
||||
Recipe Recipe `json:"recipe"`
|
||||
}
|
||||
|
||||
// RecognizedItem is a food item identified in an image.
|
||||
type RecognizedItem struct {
|
||||
Name string `json:"name"`
|
||||
Quantity float64 `json:"quantity"`
|
||||
Unit string `json:"unit"`
|
||||
Category string `json:"category"`
|
||||
Confidence float64 `json:"confidence"`
|
||||
}
|
||||
|
||||
// UnrecognizedItem is text from a receipt that could not be identified as food.
|
||||
type UnrecognizedItem struct {
|
||||
RawText string `json:"raw_text"`
|
||||
Price float64 `json:"price,omitempty"`
|
||||
}
|
||||
|
||||
// ReceiptResult is the full result of receipt OCR.
|
||||
type ReceiptResult struct {
|
||||
Items []RecognizedItem `json:"items"`
|
||||
Unrecognized []UnrecognizedItem `json:"unrecognized"`
|
||||
}
|
||||
|
||||
// DishResult is the result of dish recognition.
|
||||
type DishResult struct {
|
||||
DishName string `json:"dish_name"`
|
||||
WeightGrams int `json:"weight_grams"`
|
||||
Calories float64 `json:"calories"`
|
||||
ProteinG float64 `json:"protein_g"`
|
||||
FatG float64 `json:"fat_g"`
|
||||
CarbsG float64 `json:"carbs_g"`
|
||||
Confidence float64 `json:"confidence"`
|
||||
SimilarDishes []string `json:"similar_dishes"`
|
||||
}
|
||||
|
||||
// IngredientTranslation holds the localized name and aliases for one language.
|
||||
type IngredientTranslation struct {
|
||||
Lang string `json:"lang"`
|
||||
Name string `json:"name"`
|
||||
Aliases []string `json:"aliases"`
|
||||
}
|
||||
|
||||
// IngredientClassification is the AI-produced classification of an unknown food item.
|
||||
type IngredientClassification struct {
|
||||
CanonicalName string `json:"canonical_name"`
|
||||
Aliases []string `json:"aliases"` // English aliases
|
||||
Translations []IngredientTranslation `json:"translations"` // other languages
|
||||
Category string `json:"category"`
|
||||
DefaultUnit string `json:"default_unit"`
|
||||
CaloriesPer100g *float64 `json:"calories_per_100g"`
|
||||
ProteinPer100g *float64 `json:"protein_per_100g"`
|
||||
FatPer100g *float64 `json:"fat_per_100g"`
|
||||
CarbsPer100g *float64 `json:"carbs_per_100g"`
|
||||
StorageDays int `json:"storage_days"`
|
||||
}
|
||||
Reference in New Issue
Block a user