File size: 3,284 Bytes
b5961aa
 
 
 
 
 
 
 
 
 
29b313e
b5961aa
 
29b313e
 
 
b5961aa
 
 
 
 
 
29b313e
 
b5961aa
 
 
 
 
 
29b313e
b5961aa
29b313e
 
 
 
 
 
 
 
b5961aa
29b313e
 
 
 
 
b5961aa
29b313e
 
 
 
 
 
 
 
b5961aa
 
 
 
29b313e
 
b5961aa
195b940
 
 
b5961aa
195b940
 
 
 
 
b5961aa
195b940
29b313e
 
 
b5961aa
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
from scipy.optimize import minimize_scalar
from chatbot.agents.states.state import SwapState
import numpy as np
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def calculate_top_options(state: SwapState):
    logger.info("---NODE: SCIPY RANKING (MATH FILTER)---")
    candidates = state.get("candidates", [])
    food_old = state["food_old"]

    if not candidates or not food_old: 
        logger.warning("⚠️ Candidates hoặc Food_old rỗng, bỏ qua tính toán.")
        return {"top_candidates": []}

    # 1. Xác định "KPI" từ món cũ
    old_scale = float(food_old.get("portion_scale", 1.0))
    target_vector = np.array([
        float(food_old.get("kcal", 0)) * old_scale,
        float(food_old.get("protein", 0)) * old_scale,
        float(food_old.get("totalfat", 0)) * old_scale,
        float(food_old.get("carbs", 0)) * old_scale
    ])
    weights = np.array([3.0, 2.0, 1.0, 1.0])

    # Bound của món cũ
    bounds = food_old.get("solver_bounds", (0.5, 2.0))

    # Hàm tính toán
    def calculate_score(candidate):
        try:
            base_vector = np.array([
                float(candidate.get("kcal", 0)),
                float(candidate.get("protein", 0)),
                float(candidate.get("totalfat", 0)),
                float(candidate.get("carbs", 0))
            ])
            if np.sum(base_vector) == 0: return float('inf'), 1.0

            def objective(x):
                current_vector = base_vector * x
                diff = (current_vector - target_vector) / (target_vector + 1e-5)
                loss = np.sum(weights * (diff ** 2))
                return loss

            res = minimize_scalar(objective, bounds=bounds, method='bounded')
            if res.success:
                return res.fun, res.x
            else:
                return float('inf'), 1.0
        except Exception as inner_e:
            logger.debug(f"Bỏ qua món {candidate.get('name')} do lỗi toán học: {inner_e}")
            return float('inf'), 1.0

    # 3. Chấm điểm hàng loạt
    scored_candidates = []
    for item in candidates:
        try:
            loss, scale = calculate_score(item)

            item_score = item.copy()
            item_score["optimization_loss"] = round(loss, 4)
            item_score["portion_scale"] = round(scale, 2)

            # Tính chỉ số hiển thị sau khi scale
            item_score["final_kcal"] = int(item["kcal"] * scale)
            item_score["final_protein"] = int(item["protein"] * scale)
            item_score["final_totalfat"] = int(item["totalfat"] * scale)
            item_score["final_carbs"] = int(item["carbs"] * scale)

            scored_candidates.append(item_score)
        except Exception as e:
            logger.warning(f"Lỗi khi xử lý món {item.get('name', 'N/A')}: {e}")
            continue

    # 4. Lấy Top 10 tốt nhất
    scored_candidates.sort(key=lambda x: x["optimization_loss"])
    top_10 = scored_candidates[:10]

    logger.info(f"📊 Scipy đã lọc ra {len(top_10)} ứng viên tiềm năng.")
    for item in top_10:
        logger.info(f"   - {item['name']} (Scale x{item['portion_scale']} | Loss: {item['optimization_loss']})")

    return {"top_candidates": top_10}