ReportRaahat CI commited on
Commit
ee7023b
·
1 Parent(s): 61b86c9

Deploy from GitHub: 7c7b0b67e1dae2679d91ef948e246730d7d10fbf

Browse files
backend/app/ml/model.py CHANGED
@@ -1,14 +1,118 @@
1
  import os
 
 
 
 
 
 
 
 
2
 
3
- # Mock model loader for testing without HuggingFace dependencies
4
  def load_model():
5
  """
6
- Mock model loader. In production, replace with actual model loading.
 
7
  """
8
- print("Using mock model for simplification")
 
 
 
9
  return None, None
10
 
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  def simplify_finding(
13
  parameter: str,
14
  value: str,
@@ -17,54 +121,36 @@ def simplify_finding(
17
  rag_context: str = ""
18
  ) -> dict:
19
  """
20
- Mock simplification of medical findings.
21
- In production, this would use the T5 model.
22
  """
23
-
24
- # Simplified explanations based on common parameters and status
25
- explanations = {
26
- ("HIGH", "GLUCOSE"): {
27
- "english": f"Your blood glucose is elevated at {value} {unit}. This suggests your body is having trouble managing blood sugar. Reduce sugary foods and consult your doctor for diabetes screening.",
28
- "hindi": f"आपका ब्लड ग्लूकोज़ {value} {unit} पर बढ़ा हुआ है। यह दर्शाता है कि आपका शरीर ब्लड शुगर को नियंत्रित करने में परेशानी आ रही है। मीठे खाना कम करें और डॉक्टर से मिलें।"
29
- },
30
- ("HIGH", "SGPT"): {
31
- "english": f"Your liver enzyme SGPT is high at {value} {unit}. This indicates liver inflammation. Avoid fatty foods and alcohol, and get liver function tests repeated.",
32
- "hindi": f"आपका यकृत एंजाइम SGPT {value} {unit} पर बढ़ा हुआ है। यह यकृत में सूजन दर्शाता है। तैलीय खाना और शराब न लें।"
33
- },
34
- ("LOW", "HEMOGLOBIN"): {
35
- "english": f"Your hemoglobin is low at {value} {unit}. You may be anemic. Increase iron-rich foods like spinach, liver, and beans. Get iron supplements if recommended by doctor.",
36
- "hindi": f"आपका हीमोग्लोबिन {value} {unit} पर कम है। आपको एनीमिया हो सकता है। पालक, यकृत, और दाल जैसे आयरन युक्त खाना बढ़ाएं।"
37
- },
38
- ("HIGH", "CHOLESTEROL"): {
39
- "english": f"Your cholesterol is elevated at {value} {unit}. Reduce saturated fats, increase fiber intake, and exercise regularly. Follow up with your doctor.",
40
- "hindi": f"आपका कोलेस्ट्रॉल {value} {unit} पर बढ़ा हुआ है। संतृप्त वसा कम करें और नियमित व्यायाम करें।"
41
- },
42
- ("HIGH", "CREATININE"): {
43
- "english": f"Your creatinine is elevated at {value} {unit}. This may indicate kidney issues. Reduce protein intake and stay hydrated. Consult a nephrologist.",
44
- "hindi": f"आपका क्रिएटिनिन {value} {unit} पर बढ़ा है। यह गुर्दे की समस्या दर्शा सकता है। प्रोटीन इनटेक कम करें।"
45
- }
46
- }
47
-
48
- # Try to match the parameter with explanations
49
  param_upper = parameter.upper()
50
  status_upper = status.upper()
51
-
52
- for (status_key, param_key) in explanations.keys():
53
  if param_key in param_upper and status_key == status_upper:
54
- return explanations[(status_key, param_key)]
55
-
56
- # Default explanation
 
 
 
 
57
  default_exp = {
58
- "HIGH": f"Your {parameter} is high at {value} {unit}. This suggests abnormality. Please consult your doctor for proper evaluation and treatment.",
59
- "LOW": f"Your {parameter} is low at {value} {unit}. This may indicate deficiency. Consult your doctor for recommendations.",
60
- "CRITICAL": f"Your {parameter} is critically high at {value} {unit}. This requires immediate medical attention. Please see a doctor urgently.",
61
  "NORMAL": f"Your {parameter} is normal at {value} {unit}. Keep maintaining healthy habits."
62
  }
63
-
64
- english_text = default_exp.get(status_upper, f"Your {parameter} is {status.lower()}.")
65
- hindi_text = f"{parameter} {status.lower()} है। डॉक्टर से मिलें।"
66
-
67
  return {
68
- "english": english_text,
69
- "hindi": hindi_text
70
  }
 
1
  import os
2
+ import httpx
3
+
4
+ OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY", "")
5
+ BASE_URL = "https://openrouter.ai/api/v1"
6
+
7
+ # Use a fast model for simplification (called per-finding, so speed matters)
8
+ SIMPLIFY_MODEL = "deepseek/deepseek-chat-v3-0324:free"
9
+
10
 
 
11
  def load_model():
12
  """
13
+ Check if OpenRouter API key is available.
14
+ In production, this would also load local models.
15
  """
16
+ if OPENROUTER_API_KEY and not OPENROUTER_API_KEY.startswith("placeholder"):
17
+ print("✅ OpenRouter API key found — using AI for simplification")
18
+ else:
19
+ print("⚠️ No OpenRouter API key — using template-based simplification")
20
  return None, None
21
 
22
 
23
+ # Hardcoded fallback templates for when AI is unavailable
24
+ FALLBACK_EXPLANATIONS = {
25
+ ("HIGH", "GLUCOSE"): {
26
+ "english": "Your blood glucose is elevated at {value} {unit}. This suggests your body is having trouble managing blood sugar. Reduce sugary foods and consult your doctor for diabetes screening.",
27
+ "hindi": "आपका ब्लड ग्लूकोज़ {value} {unit} पर बढ़ा हुआ है। यह दर्शाता है कि आपका शरीर ब्लड शुगर को नियंत्रित करने में परेशानी आ रही है। मीठे खाना कम करें और डॉक्टर से मिलें।"
28
+ },
29
+ ("HIGH", "SGPT"): {
30
+ "english": "Your liver enzyme SGPT is high at {value} {unit}. This indicates liver inflammation. Avoid fatty foods and alcohol.",
31
+ "hindi": "आपका यकृत एंजाइम SGPT {value} {unit} पर बढ़ा हुआ है। यह यकृत में सूजन दर्शाता है। तैलीय खाना और शराब न लें।"
32
+ },
33
+ ("LOW", "HEMOGLOBIN"): {
34
+ "english": "Your hemoglobin is low at {value} {unit}. You may be anemic. Increase iron-rich foods like spinach and beans.",
35
+ "hindi": "आपका हीमोग्लोबिन {value} {unit} पर कम है। आपको एनीमिया हो सकता है। पालक और दाल जैसे आयरन युक्त खाना बढ़ाएं।"
36
+ },
37
+ ("HIGH", "CHOLESTEROL"): {
38
+ "english": "Your cholesterol is elevated at {value} {unit}. Reduce saturated fats, increase fiber, and exercise regularly.",
39
+ "hindi": "आपका कोलेस्ट्रॉल {value} {unit} पर बढ़ा हुआ है। संतृप्त वसा कम करें और नियमित व्यायाम करें।"
40
+ },
41
+ ("HIGH", "CREATININE"): {
42
+ "english": "Your creatinine is elevated at {value} {unit}. This may indicate kidney issues. Reduce protein intake and stay hydrated.",
43
+ "hindi": "आपका क्रिएटिनिन {value} {unit} पर बढ़ा है। यह गुर्दे की समस्या दर्शा सकता है। प्रोटीन इनटेक कम करें।"
44
+ },
45
+ }
46
+
47
+
48
+ def _ai_simplify(parameter: str, value: str, unit: str, status: str, rag_context: str) -> dict | None:
49
+ """Call OpenRouter to generate a layman explanation of a lab finding."""
50
+ if not OPENROUTER_API_KEY or OPENROUTER_API_KEY.startswith("placeholder"):
51
+ return None
52
+
53
+ prompt = f"""You are a friendly Indian doctor explaining lab results to a patient who has no medical knowledge.
54
+
55
+ Lab Finding:
56
+ - Parameter: {parameter}
57
+ - Value: {value} {unit}
58
+ - Status: {status}
59
+ {f'- Context: {rag_context}' if rag_context else ''}
60
+
61
+ Provide two explanations (2-3 sentences each):
62
+ 1. In simple English
63
+ 2. In simple Hindi (Devanagari script, everyday words)
64
+
65
+ Format your response EXACTLY as:
66
+ ENGLISH: <explanation>
67
+ HINDI: <explanation>"""
68
+
69
+ try:
70
+ headers = {
71
+ "Authorization": f"Bearer {OPENROUTER_API_KEY}",
72
+ "Content-Type": "application/json",
73
+ "HTTP-Referer": "https://reportraahat.app",
74
+ "X-Title": "ReportRaahat",
75
+ }
76
+
77
+ with httpx.Client(timeout=15.0) as client:
78
+ resp = client.post(
79
+ f"{BASE_URL}/chat/completions",
80
+ headers=headers,
81
+ json={
82
+ "model": SIMPLIFY_MODEL,
83
+ "messages": [{"role": "user", "content": prompt}],
84
+ "max_tokens": 300,
85
+ "temperature": 0.5,
86
+ },
87
+ )
88
+
89
+ if resp.status_code == 200:
90
+ text = resp.json()["choices"][0]["message"]["content"].strip()
91
+
92
+ # Parse ENGLISH: and HINDI: from response
93
+ english = ""
94
+ hindi = ""
95
+ for line in text.split("\n"):
96
+ line = line.strip()
97
+ if line.upper().startswith("ENGLISH:"):
98
+ english = line[8:].strip()
99
+ elif line.upper().startswith("HINDI:"):
100
+ hindi = line[6:].strip()
101
+
102
+ if english and hindi:
103
+ return {"english": english, "hindi": hindi}
104
+
105
+ # If parsing failed, use the full response as English
106
+ return {"english": text[:200], "hindi": f"{parameter} {status.lower()} है। डॉक्टर से मिलें।"}
107
+
108
+ print(f"⚠️ AI simplify failed ({resp.status_code})")
109
+ return None
110
+
111
+ except Exception as e:
112
+ print(f"⚠️ AI simplify error: {e}")
113
+ return None
114
+
115
+
116
  def simplify_finding(
117
  parameter: str,
118
  value: str,
 
121
  rag_context: str = ""
122
  ) -> dict:
123
  """
124
+ Generate a layman-friendly explanation for a lab finding.
125
+ Tries AI first, then falls back to templates.
126
  """
127
+
128
+ # Try AI-powered simplification
129
+ ai_result = _ai_simplify(parameter, value, unit, status, rag_context)
130
+ if ai_result:
131
+ return ai_result
132
+
133
+ # Fallback to template matching
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  param_upper = parameter.upper()
135
  status_upper = status.upper()
136
+
137
+ for (status_key, param_key) in FALLBACK_EXPLANATIONS.keys():
138
  if param_key in param_upper and status_key == status_upper:
139
+ template = FALLBACK_EXPLANATIONS[(status_key, param_key)]
140
+ return {
141
+ "english": template["english"].format(value=value, unit=unit),
142
+ "hindi": template["hindi"].format(value=value, unit=unit),
143
+ }
144
+
145
+ # Generic default
146
  default_exp = {
147
+ "HIGH": f"Your {parameter} is high at {value} {unit}. This needs attention. Consult your doctor.",
148
+ "LOW": f"Your {parameter} is low at {value} {unit}. This may indicate a deficiency. Consult your doctor.",
149
+ "CRITICAL": f"Your {parameter} is critically abnormal at {value} {unit}. Please see a doctor urgently.",
150
  "NORMAL": f"Your {parameter} is normal at {value} {unit}. Keep maintaining healthy habits."
151
  }
152
+
 
 
 
153
  return {
154
+ "english": default_exp.get(status_upper, f"Your {parameter} is {status.lower()}."),
155
+ "hindi": f"{parameter} {status.lower()} है। डॉक्टर से मिलें।"
156
  }
backend/app/ml/openrouter.py CHANGED
@@ -11,9 +11,9 @@ BASE_URL = "https://openrouter.ai/api/v1"
11
 
12
  # Free models available on OpenRouter — fallback chain
13
  MODELS = [
14
- "stepfun/step-3.5-flash:free",
15
- "nvidia/nemotron-3-super-120b-a12b:free",
16
- "arcee-ai/trinity-large-preview:free",
17
  ]
18
 
19
 
@@ -59,7 +59,6 @@ def build_system_prompt(guc: dict) -> str:
59
  else "Always respond in simple English."
60
  )
61
 
62
- # Add empathy instruction if stress is high
63
  empathy_note = (
64
  "\nNOTE: This patient has high stress levels. "
65
  "Be extra gentle, reassuring and empathetic in your responses. "
@@ -103,8 +102,52 @@ IMPORTANT RULES:
103
  return prompt
104
 
105
 
106
- # Enhanced mock responses moved to app/ml/enhanced_chat.py
107
- # See get_enhanced_mock_response() for detailed contextual responses
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
 
110
  def chat(
@@ -115,19 +158,43 @@ def chat(
115
  """
116
  Send a message to Dr. Raahat via OpenRouter.
117
  Injects GUC context + RAG-retrieved knowledge.
118
- Falls back to enhanced mock responses for testing.
119
  """
120
- retrieved_docs = []
121
-
122
- # Try to retrieve relevant medical documents from RAG
 
 
 
 
123
  try:
124
- if rag_retriever and rag_retriever.loaded:
125
- # Create embedding for the user's message (simplified for now)
126
- # In production, use proper embeddings model
127
- query_embedding = [0.1] * 768 # Placeholder - replace with real embeddings
128
- retrieved_docs = rag_retriever.retrieve(query_embedding, k=3)
 
 
 
129
  except Exception as e:
130
- print(f"⚠️ RAG retrieval failed: {e}")
131
-
132
- # Use enhanced mock responses with RAG grounding
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  return get_enhanced_mock_response(message, guc, retrieved_docs)
 
11
 
12
  # Free models available on OpenRouter — fallback chain
13
  MODELS = [
14
+ "deepseek/deepseek-chat-v3-0324:free",
15
+ "google/gemma-3-27b-it:free",
16
+ "meta-llama/llama-4-maverick:free",
17
  ]
18
 
19
 
 
59
  else "Always respond in simple English."
60
  )
61
 
 
62
  empathy_note = (
63
  "\nNOTE: This patient has high stress levels. "
64
  "Be extra gentle, reassuring and empathetic in your responses. "
 
102
  return prompt
103
 
104
 
105
+ def _call_openrouter(messages: list[dict]) -> str | None:
106
+ """
107
+ Call OpenRouter API with the given messages.
108
+ Tries each model in MODELS until one succeeds.
109
+ Returns the reply string, or None on failure.
110
+ """
111
+ if not OPENROUTER_API_KEY or OPENROUTER_API_KEY.startswith("placeholder"):
112
+ return None
113
+
114
+ headers = {
115
+ "Authorization": f"Bearer {OPENROUTER_API_KEY}",
116
+ "Content-Type": "application/json",
117
+ "HTTP-Referer": "https://reportraahat.app",
118
+ "X-Title": "ReportRaahat",
119
+ }
120
+
121
+ for model in MODELS:
122
+ try:
123
+ payload = {
124
+ "model": model,
125
+ "messages": messages,
126
+ "max_tokens": 500,
127
+ "temperature": 0.7,
128
+ }
129
+
130
+ with httpx.Client(timeout=30.0) as client:
131
+ resp = client.post(
132
+ f"{BASE_URL}/chat/completions",
133
+ headers=headers,
134
+ json=payload,
135
+ )
136
+
137
+ if resp.status_code == 200:
138
+ data = resp.json()
139
+ reply = data["choices"][0]["message"]["content"]
140
+ print(f"✅ OpenRouter reply via {model}: {len(reply)} chars")
141
+ return reply.strip()
142
+ else:
143
+ print(f"⚠️ OpenRouter {model} returned {resp.status_code}: {resp.text[:200]}")
144
+ continue
145
+
146
+ except Exception as e:
147
+ print(f"⚠️ OpenRouter {model} error: {e}")
148
+ continue
149
+
150
+ return None
151
 
152
 
153
  def chat(
 
158
  """
159
  Send a message to Dr. Raahat via OpenRouter.
160
  Injects GUC context + RAG-retrieved knowledge.
161
+ Falls back to enhanced mock responses if API fails.
162
  """
163
+ # Build system prompt with full GUC context
164
+ system_prompt = build_system_prompt(guc)
165
+
166
+ # Build conversation messages
167
+ messages = [{"role": "system", "content": system_prompt}]
168
+
169
+ # Add RAG-retrieved context if available
170
  try:
171
+ if retrieve_doctor_context:
172
+ docs = retrieve_doctor_context(message, top_k=3)
173
+ if docs:
174
+ context = "\n".join(f"- {d['text']}" for d in docs)
175
+ messages.append({
176
+ "role": "system",
177
+ "content": f"Relevant medical knowledge:\n{context}"
178
+ })
179
  except Exception as e:
180
+ print(f"⚠️ RAG retrieval failed: {e}")
181
+
182
+ # Add chat history
183
+ for msg in history[-10:]: # Last 10 messages for context
184
+ role = msg.get("role", "user")
185
+ content = msg.get("content", msg.get("text", ""))
186
+ if content:
187
+ messages.append({"role": role, "content": content})
188
+
189
+ # Add current message
190
+ messages.append({"role": "user", "content": message})
191
+
192
+ # Try OpenRouter API first
193
+ reply = _call_openrouter(messages)
194
+ if reply:
195
+ return reply
196
+
197
+ # Fallback to enhanced mock responses
198
+ print("⚠️ OpenRouter unavailable, using mock responses")
199
+ retrieved_docs = []
200
  return get_enhanced_mock_response(message, guc, retrieved_docs)
frontend/app/api/exercise/route.ts ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // POST /api/exercise
2
+ // Proxies to backend POST /exercise/
3
+
4
+ import { NextRequest, NextResponse } from "next/server"
5
+
6
+ const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000"
7
+ const TIMEOUT_MS = 10_000
8
+
9
+ export async function POST(req: NextRequest) {
10
+ try {
11
+ const body = await req.json()
12
+
13
+ const controller = new AbortController()
14
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS)
15
+
16
+ const res = await fetch(`${API_BASE}/exercise/`, {
17
+ method: "POST",
18
+ headers: { "Content-Type": "application/json" },
19
+ body: JSON.stringify(body),
20
+ signal: controller.signal,
21
+ })
22
+ clearTimeout(timer)
23
+
24
+ if (!res.ok) {
25
+ return NextResponse.json({ error: "Exercise API error" }, { status: res.status })
26
+ }
27
+
28
+ const data = await res.json()
29
+ return NextResponse.json(data)
30
+ } catch {
31
+ return NextResponse.json(
32
+ { error: "Exercise service unavailable" },
33
+ { status: 503 }
34
+ )
35
+ }
36
+ }
frontend/app/api/nutrition/route.ts ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // POST /api/nutrition
2
+ // Proxies to backend POST /nutrition/
3
+ // Accepts: { dietary_flags, allergy_flags, vegetarian }
4
+
5
+ import { NextRequest, NextResponse } from "next/server"
6
+
7
+ const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000"
8
+ const TIMEOUT_MS = 10_000
9
+
10
+ export async function POST(req: NextRequest) {
11
+ try {
12
+ const body = await req.json()
13
+
14
+ const controller = new AbortController()
15
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS)
16
+
17
+ const res = await fetch(`${API_BASE}/nutrition/`, {
18
+ method: "POST",
19
+ headers: { "Content-Type": "application/json" },
20
+ body: JSON.stringify(body),
21
+ signal: controller.signal,
22
+ })
23
+ clearTimeout(timer)
24
+
25
+ if (!res.ok) {
26
+ // Try fallback
27
+ const fallback = await fetch(`${API_BASE}/nutrition/fallback`)
28
+ if (fallback.ok) {
29
+ const data = await fallback.json()
30
+ return NextResponse.json(data)
31
+ }
32
+ return NextResponse.json({ error: "Nutrition API error" }, { status: res.status })
33
+ }
34
+
35
+ const data = await res.json()
36
+ return NextResponse.json(data)
37
+ } catch {
38
+ // Try fallback on timeout/error
39
+ try {
40
+ const fallback = await fetch(`${API_BASE}/nutrition/fallback`)
41
+ if (fallback.ok) {
42
+ const data = await fallback.json()
43
+ return NextResponse.json(data)
44
+ }
45
+ } catch { /* ignore */ }
46
+
47
+ return NextResponse.json(
48
+ { error: "Nutrition service unavailable" },
49
+ { status: 503 }
50
+ )
51
+ }
52
+ }
frontend/app/exercise/page.tsx CHANGED
@@ -67,7 +67,7 @@ export default function ExercisePage() {
67
  const [selectedDay, setSelectedDay] = useState<string | null>(null);
68
  const [completedDays, setCompletedDays] = useState<Set<string>>(new Set());
69
 
70
- const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000";
71
  const severity = latestReport?.severity_level ?? "MILD_CONCERN";
72
 
73
  useEffect(() => {
@@ -80,8 +80,8 @@ export default function ExercisePage() {
80
  const fetchPlan = async () => {
81
  try {
82
  setLoading(true);
83
- // Backend expects POST /exercise/ with empty body (stub)
84
- const res = await fetch(`${API_BASE}/exercise/`, {
85
  method: "POST",
86
  headers: { "Content-Type": "application/json" },
87
  body: JSON.stringify({}),
@@ -107,7 +107,7 @@ export default function ExercisePage() {
107
  }
108
  };
109
  fetchPlan();
110
- }, [exerciseLevel, severity, API_BASE, profile.language]);
111
 
112
  const handleComplete = (day: string) => {
113
  if (completedDays.has(day)) return;
 
67
  const [selectedDay, setSelectedDay] = useState<string | null>(null);
68
  const [completedDays, setCompletedDays] = useState<Set<string>>(new Set());
69
 
70
+
71
  const severity = latestReport?.severity_level ?? "MILD_CONCERN";
72
 
73
  useEffect(() => {
 
80
  const fetchPlan = async () => {
81
  try {
82
  setLoading(true);
83
+ // Call Next.js API route which proxies to backend
84
+ const res = await fetch(`/api/exercise`, {
85
  method: "POST",
86
  headers: { "Content-Type": "application/json" },
87
  body: JSON.stringify({}),
 
107
  }
108
  };
109
  fetchPlan();
110
+ }, [exerciseLevel, severity, profile.language]);
111
 
112
  const handleComplete = (day: string) => {
113
  if (completedDays.has(day)) return;
frontend/app/nutrition/page.tsx CHANGED
@@ -82,7 +82,6 @@ export default function NutritionPage() {
82
  const [loggedToday, setLoggedToday] = useState<string[]>([]);
83
  const [activeCard, setActiveCard] = useState<string | null>(null);
84
 
85
- const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000";
86
  const flags = nutritionProfile.deficiencies.join(",") || "INCREASE_IRON";
87
 
88
  useEffect(() => {
@@ -95,8 +94,8 @@ export default function NutritionPage() {
95
  setLoading(true);
96
  setError(false);
97
 
98
- // Backend expects POST /nutrition/ with NutritionRequest JSON body
99
- const res = await fetch(`${API_BASE}/nutrition/`, {
100
  method: "POST",
101
  headers: { "Content-Type": "application/json" },
102
  body: JSON.stringify({
@@ -132,7 +131,11 @@ export default function NutritionPage() {
132
  setData(transformed);
133
  } catch {
134
  try {
135
- const res = await fetch(`${API_BASE}/nutrition/fallback`);
 
 
 
 
136
  if (!res.ok) throw new Error();
137
  const json = await res.json();
138
 
@@ -163,7 +166,7 @@ export default function NutritionPage() {
163
  }
164
  };
165
  fetchNutrition();
166
- }, [flags, API_BASE, profile.language, nutritionProfile.deficiencies]);
167
 
168
  const handleAddToToday = (food: FoodItem) => {
169
  logFood(food.name_english);
 
82
  const [loggedToday, setLoggedToday] = useState<string[]>([]);
83
  const [activeCard, setActiveCard] = useState<string | null>(null);
84
 
 
85
  const flags = nutritionProfile.deficiencies.join(",") || "INCREASE_IRON";
86
 
87
  useEffect(() => {
 
94
  setLoading(true);
95
  setError(false);
96
 
97
+ // Call Next.js API route which proxies to backend
98
+ const res = await fetch(`/api/nutrition`, {
99
  method: "POST",
100
  headers: { "Content-Type": "application/json" },
101
  body: JSON.stringify({
 
131
  setData(transformed);
132
  } catch {
133
  try {
134
+ const res = await fetch(`/api/nutrition`, {
135
+ method: "POST",
136
+ headers: { "Content-Type": "application/json" },
137
+ body: JSON.stringify({ dietary_flags: ["INCREASE_IRON"], vegetarian: true }),
138
+ });
139
  if (!res.ok) throw new Error();
140
  const json = await res.json();
141
 
 
166
  }
167
  };
168
  fetchNutrition();
169
+ }, [flags, profile.language, nutritionProfile.deficiencies]);
170
 
171
  const handleAddToToday = (food: FoodItem) => {
172
  logFood(food.name_english);
frontend/tsconfig.tsbuildinfo CHANGED
The diff for this file is too large to render. See raw diff