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:
@@ -4,34 +4,14 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/food-ai/backend/internal/adapters/ai"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// GenerateMenu produces a 7-day × 3-meal plan by issuing three parallel
|
||||
// GenerateRecipes calls (one per meal type). This avoids token-limit errors
|
||||
// that arise from requesting 21 full recipes in a single prompt.
|
||||
func (c *Client) GenerateMenu(ctx context.Context, req MenuRequest) ([]DayPlan, error) {
|
||||
func (c *Client) GenerateMenu(ctx context.Context, req ai.MenuRequest) ([]ai.DayPlan, error) {
|
||||
type mealSlot struct {
|
||||
mealType string
|
||||
fraction float64 // share of daily calories
|
||||
@@ -44,7 +24,7 @@ func (c *Client) GenerateMenu(ctx context.Context, req MenuRequest) ([]DayPlan,
|
||||
}
|
||||
|
||||
type mealResult struct {
|
||||
recipes []Recipe
|
||||
recipes []ai.Recipe
|
||||
err error
|
||||
}
|
||||
|
||||
@@ -57,7 +37,7 @@ func (c *Client) GenerateMenu(ctx context.Context, req MenuRequest) ([]DayPlan,
|
||||
defer wg.Done()
|
||||
// Scale daily calories to what this meal should contribute.
|
||||
mealCal := int(float64(req.DailyCalories) * fraction)
|
||||
r, err := c.GenerateRecipes(ctx, RecipeRequest{
|
||||
r, err := c.GenerateRecipes(ctx, ai.RecipeRequest{
|
||||
UserGoal: req.UserGoal,
|
||||
DailyCalories: mealCal * 3, // prompt divides by 3 internally
|
||||
Restrictions: req.Restrictions,
|
||||
@@ -84,11 +64,11 @@ func (c *Client) GenerateMenu(ctx context.Context, req MenuRequest) ([]DayPlan,
|
||||
}
|
||||
}
|
||||
|
||||
days := make([]DayPlan, 7)
|
||||
days := make([]ai.DayPlan, 7)
|
||||
for day := range 7 {
|
||||
days[day] = DayPlan{
|
||||
days[day] = ai.DayPlan{
|
||||
Day: day + 1,
|
||||
Meals: []MealEntry{
|
||||
Meals: []ai.MealEntry{
|
||||
{MealType: slots[0].mealType, Recipe: results[0].recipes[day]},
|
||||
{MealType: slots[1].mealType, Recipe: results[1].recipes[day]},
|
||||
{MealType: slots[2].mealType, Recipe: results[2].recipes[day]},
|
||||
|
||||
Reference in New Issue
Block a user