UNPKG

cook-mcp-windy

Version:

HowToCook MCP Server - Intelligent Chinese recipe management and meal planning for AI assistants

358 lines 13.8 kB
import { Recipe, DishCombination, RecommendedDish, CombinationPreferences, CombinationRequest, DishRole, CombinationType } from '../types/index.js'; /** * Generate dish combination recommendations */ export function generateDishCombination(recipes, request) { const { preferences } = request; // Filter suitable recipes const suitableRecipes = filterRecipesByPreferences(recipes, preferences); // Select dishes for combination const selectedDishes = selectDishesForCombination(suitableRecipes, preferences, request.maxDishes || 6, request.minDishes || 3, request.includeRecipeIds, request.excludeRecipeIds); // Create recommended dishes with roles const recommendedDishes = assignDishRoles(selectedDishes, preferences); // Calculate combination properties const totalPrepTime = calculateTotalPrepTime(recommendedDishes); const totalCookTime = calculateTotalCookTime(recommendedDishes); const difficulty = calculateOverallDifficulty(recommendedDishes); const nutritionalBalance = analyzeNutritionalBalance(recommendedDishes); const flavorProfile = analyzeFlavorProfile(recommendedDishes); return { id: generateCombinationId(), name: generateCombinationName(recommendedDishes, preferences), description: generateCombinationDescription(recommendedDishes, preferences), type: determineCombinationType(preferences), dishes: recommendedDishes, totalPrepTime, totalCookTime, difficulty, servings: preferences.numberOfPeople, nutritionalBalance, flavorProfile, cookingTips: generateCookingTips(recommendedDishes), alternatives: generateAlternatives(recommendedDishes, suitableRecipes), estimatedCost: estimateCombinationCost(recommendedDishes, preferences.budgetLevel), createdAt: new Date().toISOString() }; } /** * Filter recipes based on combination preferences */ function filterRecipesByPreferences(recipes, preferences) { let filtered = [...recipes]; // Filter by dietary restrictions if (preferences.dietaryRestrictions && preferences.dietaryRestrictions.length > 0) { filtered = filtered.filter(recipe => { return !preferences.dietaryRestrictions.some((restriction) => { if (restriction === '素食' && recipe.category === '荤菜') return true; if (restriction === '无海鲜' && recipe.category === '水产') return true; if (restriction === '无坚果' && recipe.ingredients.some(ing => ing.name.includes('花生') || ing.name.includes('核桃') || ing.name.includes('杏仁'))) return true; return false; }); }); } // Filter by skill level if (preferences.skillLevel) { const skillMap = { '初学者': ['简单'], '中级': ['简单', '中等'], '高级': ['简单', '中等', '困难'] }; const allowedDifficulties = skillMap[preferences.skillLevel] || ['简单', '中等', '困难']; filtered = filtered.filter(recipe => allowedDifficulties.includes(recipe.difficulty)); } // Filter by cooking time constraints if (preferences.cookingTime) { const maxMinutes = parseTimeInMinutes(preferences.cookingTime); filtered = filtered.filter(recipe => { const recipeTime = parseTimeInMinutes(recipe.prepTime) + parseTimeInMinutes(recipe.cookTime); return recipeTime <= maxMinutes; }); } return filtered; } /** * Select dishes for combination */ function selectDishesForCombination(recipes, _preferences, maxDishes, minDishes, includeIds, excludeIds) { let availableRecipes = [...recipes]; // Exclude specified recipes if (excludeIds && excludeIds.length > 0) { availableRecipes = availableRecipes.filter(recipe => !excludeIds.includes(recipe.id)); } const selectedRecipes = []; // Include required recipes if (includeIds && includeIds.length > 0) { const requiredRecipes = recipes.filter(recipe => includeIds.includes(recipe.id)); selectedRecipes.push(...requiredRecipes); availableRecipes = availableRecipes.filter(recipe => !includeIds.includes(recipe.id)); } // Select additional recipes to meet requirements const remainingSlots = Math.min(maxDishes - selectedRecipes.length, Math.max(0, minDishes - selectedRecipes.length)); // Prioritize different categories for variety const categoryPriority = ['荤菜', '素菜', '汤羹', '主食', '水产']; const usedCategories = new Set(selectedRecipes.map(r => r.category)); for (const category of categoryPriority) { if (selectedRecipes.length >= maxDishes) break; if (usedCategories.has(category)) continue; const categoryRecipes = availableRecipes.filter(r => r.category === category); if (categoryRecipes.length > 0) { const selected = selectRandomRecipe(categoryRecipes); selectedRecipes.push(selected); usedCategories.add(category); availableRecipes = availableRecipes.filter(r => r.id !== selected.id); } } // Fill remaining slots with any suitable recipes while (selectedRecipes.length < Math.max(minDishes, remainingSlots) && availableRecipes.length > 0) { const selected = selectRandomRecipe(availableRecipes); selectedRecipes.push(selected); availableRecipes = availableRecipes.filter(r => r.id !== selected.id); } return selectedRecipes; } /** * Assign roles to dishes in combination */ function assignDishRoles(recipes, preferences) { const recommendedDishes = []; recipes.forEach((recipe, index) => { const role = determineDishRole(recipe, index, recipes.length); const priority = determineDishPriority(recipe, role, preferences); const reason = generateRecommendationReason(recipe, role, preferences); recommendedDishes.push({ recipe, role, priority, reason, preparationOrder: index + 1, servingOrder: getServingOrder(role) }); }); return recommendedDishes.sort((a, b) => a.preparationOrder - b.preparationOrder); } /** * Determine dish role based on recipe category */ function determineDishRole(recipe, _index, _totalDishes) { const categoryRoleMap = { '荤菜': '主菜', '水产': '主菜', '素菜': '配菜', '汤羹': '汤品', '主食': '主食', '甜品': '甜品' }; return categoryRoleMap[recipe.category] || '配菜'; } /** * Determine dish priority */ function determineDishPriority(_recipe, role, preferences) { if (role === '主菜' || role === '主食') return '必选'; if (role === '汤品' && preferences.numberOfPeople > 2) return '推荐'; return '可选'; } /** * Generate recommendation reason */ function generateRecommendationReason(recipe, role, _preferences) { const reasons = [ `作为${role}${recipe.name}能够为这餐增添丰富的口感`, `${recipe.name}的制作难度为${recipe.difficulty},适合当前的烹饪安排`, `这道${recipe.name}能够很好地平衡整餐的营养搭配` ]; return reasons[Math.floor(Math.random() * reasons.length)]; } /** * Get serving order based on dish role */ function getServingOrder(role) { const orderMap = { '开胃菜': 1, '汤品': 2, '主菜': 3, '配菜': 4, '主食': 5, '甜品': 6 }; return orderMap[role] || 3; } /** * Calculate total preparation time */ function calculateTotalPrepTime(dishes) { const totalMinutes = dishes.reduce((sum, dish) => { return sum + parseTimeInMinutes(dish.recipe.prepTime); }, 0); return formatTimeFromMinutes(totalMinutes); } /** * Calculate total cooking time (considering parallel cooking) */ function calculateTotalCookTime(dishes) { // Simplified calculation - assumes some parallel cooking const cookTimes = dishes.map(dish => parseTimeInMinutes(dish.recipe.cookTime)); const maxCookTime = Math.max(...cookTimes); const totalCookTime = cookTimes.reduce((sum, time) => sum + time, 0); // Estimate actual time considering parallel cooking (60% of total) const estimatedTime = Math.max(maxCookTime, totalCookTime * 0.6); return formatTimeFromMinutes(Math.round(estimatedTime)); } /** * Calculate overall difficulty */ function calculateOverallDifficulty(dishes) { const difficultyScores = { '简单': 1, '中等': 2, '困难': 3 }; const avgScore = dishes.reduce((sum, dish) => sum + difficultyScores[dish.recipe.difficulty], 0) / dishes.length; if (avgScore <= 1.3) return '简单'; if (avgScore <= 2.3) return '中等'; return '困难'; } /** * Analyze nutritional balance */ function analyzeNutritionalBalance(dishes) { const categories = dishes.map(dish => dish.recipe.category); const hasProtein = categories.some(cat => ['荤菜', '水产', '蛋奶'].includes(cat)); const hasVegetables = categories.includes('素菜'); const hasCarbs = categories.includes('主食'); const isBalanced = hasProtein && hasVegetables && hasCarbs; return { hasProtein, hasVegetables, hasCarbs, isBalanced }; } /** * Analyze flavor profile */ function analyzeFlavorProfile(dishes) { const flavors = new Set(); dishes.forEach(dish => { if (dish.recipe.tags) { dish.recipe.tags.forEach(tag => { if (['酸', '甜', '苦', '辣', '咸', '鲜', '香', '清淡', '浓郁'].includes(tag)) { flavors.add(tag); } }); } // Infer flavors from cooking methods if (dish.recipe.cookingMethods) { dish.recipe.cookingMethods.forEach(method => { if (method === '炒') flavors.add('香'); if (method === '蒸') flavors.add('清淡'); if (method === '炖') flavors.add('浓郁'); }); } }); return Array.from(flavors); } /** * Generate cooking tips */ function generateCookingTips(_dishes) { const tips = [ '建议按照准备顺序依次处理食材,提高效率', '可以同时进行多道菜的准备工作,节省时间', '注意火候控制,避免同时操作过多炉灶', '提前准备调料和配菜,确保烹饪过程顺畅' ]; return tips.slice(0, 2); } /** * Generate alternatives */ function generateAlternatives(dishes, availableRecipes) { const alternatives = []; dishes.forEach(dish => { const sameCategory = availableRecipes.filter(r => r.category === dish.recipe.category && r.id !== dish.recipe.id); if (sameCategory.length > 0) { const alternative = sameCategory[0]; alternatives.push(`${dish.recipe.name} 可以替换为 ${alternative.name}`); } }); return alternatives.slice(0, 3); } /** * Estimate combination cost */ function estimateCombinationCost(dishes, budgetLevel) { const dishCount = dishes.length; const costRanges = { '经济': [20, 40], '中等': [40, 80], '高档': [80, 150] }; const range = costRanges[budgetLevel] || costRanges['中等']; const min = range[0]; const max = range[1]; const estimatedCost = min + (dishCount - 3) * 10; return `${Math.max(min, estimatedCost)}-${Math.min(max, estimatedCost + 20)}元`; } /** * Determine combination type */ function determineCombinationType(preferences) { if (preferences.occasion === '招待客人') return '宴客搭配'; if (preferences.occasion === '节日') return '节日搭配'; if (preferences.cookingTime && parseTimeInMinutes(preferences.cookingTime) < 60) return '快手搭配'; if (preferences.nutritionalBalance) return '营养搭配'; return '家常搭配'; } /** * Generate combination name */ function generateCombinationName(dishes, preferences) { const mainDish = dishes.find(d => d.role === '主菜'); const occasion = preferences.occasion || '家常'; if (mainDish) { return `${mainDish.recipe.name}${occasion}套餐`; } return `${occasion}搭配套餐`; } /** * Generate combination description */ function generateCombinationDescription(dishes, preferences) { const dishNames = dishes.map(d => d.recipe.name).join('、'); return `为${preferences.numberOfPeople}人精心搭配的${preferences.occasion || '家常'}套餐,包含${dishNames},营养均衡,口感丰富。`; } // Helper functions function selectRandomRecipe(recipes) { return recipes[Math.floor(Math.random() * recipes.length)]; } function parseTimeInMinutes(timeStr) { const hourMatch = timeStr.match(/(\d+)小时/); const minuteMatch = timeStr.match(/(\d+)分钟/); let totalMinutes = 0; if (hourMatch && hourMatch[1]) totalMinutes += parseInt(hourMatch[1]) * 60; if (minuteMatch && minuteMatch[1]) totalMinutes += parseInt(minuteMatch[1]); return totalMinutes; } function formatTimeFromMinutes(minutes) { if (minutes < 60) return `${minutes}分钟`; const hours = Math.floor(minutes / 60); const remainingMinutes = minutes % 60; if (remainingMinutes === 0) return `${hours}小时`; return `${hours}小时${remainingMinutes}分钟`; } function generateCombinationId() { return `combination_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } //# sourceMappingURL=dishCombinationUtils.js.map