Hetansh Waghela commited on
Commit
88fafca
·
1 Parent(s): e560ae3

minor bug fixes

Browse files
backend/app/routes/ai_summary.py CHANGED
@@ -9,6 +9,7 @@ from sqlalchemy import select
9
  from typing import List
10
  from uuid import UUID
11
  import logging
 
12
 
13
  from app.db import get_db
14
  from app.security import get_current_user
@@ -53,22 +54,35 @@ async def list_reports_for_summary(
53
  result = await db.execute(query)
54
  reports = result.scalars().all()
55
 
56
- response = []
57
  for report in reports:
58
- # Build preview URL based on file path
59
- preview_url = f"/api/reports/{report.id}/file"
 
 
 
 
 
 
 
 
 
 
 
60
 
61
- response.append(ReportForSummary(
62
- id=report.id,
63
- filename=report.filename,
64
- category=report.category,
65
- doc_type=report.doc_type,
66
- report_date=report.report_date,
67
- uploaded_at=report.uploaded_at,
68
- file_path=report.file_path,
69
- file_type=report.file_type,
70
- preview_url=preview_url
71
- ))
 
 
72
 
73
  return response
74
 
@@ -99,20 +113,25 @@ async def get_report_file(
99
  if not os.path.exists(report.file_path):
100
  raise HTTPException(status_code=404, detail="File not found on disk")
101
 
102
- # Determine media type
 
 
103
  media_types = {
104
- '.pdf': 'application/pdf',
105
- '.png': 'image/png',
106
- '.jpg': 'image/jpeg',
107
- '.jpeg': 'image/jpeg',
108
- '.tiff': 'image/tiff'
109
  }
110
- media_type = media_types.get(report.file_type, 'application/octet-stream')
 
111
 
 
112
  return FileResponse(
113
  report.file_path,
114
  media_type=media_type,
115
- filename=report.filename
 
116
  )
117
 
118
 
 
9
  from typing import List
10
  from uuid import UUID
11
  import logging
12
+ import os
13
 
14
  from app.db import get_db
15
  from app.security import get_current_user
 
54
  result = await db.execute(query)
55
  reports = result.scalars().all()
56
 
57
+ response: List[ReportForSummary] = []
58
  for report in reports:
59
+ # Skip reports whose underlying file is missing on disk
60
+ if not report.file_path or not os.path.exists(report.file_path):
61
+ logger.warning(
62
+ "AI Summary: skipping report %s (%s) because file is missing at %s",
63
+ report.id,
64
+ report.filename,
65
+ report.file_path,
66
+ )
67
+ continue
68
+
69
+ # Build preview URL based on AI summary file endpoint
70
+ # Note: this route is defined on the /api/ai router (see get_report_file below)
71
+ preview_url = f"/api/ai/reports/{report.id}/file"
72
 
73
+ response.append(
74
+ ReportForSummary(
75
+ id=report.id,
76
+ filename=report.filename,
77
+ category=report.category,
78
+ doc_type=report.doc_type,
79
+ report_date=report.report_date,
80
+ uploaded_at=report.uploaded_at,
81
+ file_path=report.file_path,
82
+ file_type=report.file_type,
83
+ preview_url=preview_url,
84
+ )
85
+ )
86
 
87
  return response
88
 
 
113
  if not os.path.exists(report.file_path):
114
  raise HTTPException(status_code=404, detail="File not found on disk")
115
 
116
+ # Determine media type. Note: in the new pipeline `file_type` is stored
117
+ # without the leading dot (e.g. "pdf"), while older records may still
118
+ # include the dot (".pdf"), so we normalize.
119
  media_types = {
120
+ 'pdf': 'application/pdf',
121
+ 'png': 'image/png',
122
+ 'jpg': 'image/jpeg',
123
+ 'jpeg': 'image/jpeg',
124
+ 'tiff': 'image/tiff',
125
  }
126
+ normalized_type = (report.file_type or '').lstrip('.').lower()
127
+ media_type = media_types.get(normalized_type, 'application/octet-stream')
128
 
129
+ # Return inline so the browser renders in an <iframe> instead of forcing download
130
  return FileResponse(
131
  report.file_path,
132
  media_type=media_type,
133
+ filename=report.filename,
134
+ content_disposition_type="inline",
135
  )
136
 
137
 
backend/app/services/rag_service.py CHANGED
@@ -33,6 +33,7 @@ class RAGService:
33
  self._client = None
34
  self._collection = None
35
  self._embedding_model = None
 
36
 
37
  def _get_client(self):
38
  """Lazy initialization of ChromaDB client."""
@@ -61,9 +62,21 @@ class RAGService:
61
 
62
  def _get_embedding_model(self):
63
  """Lazy initialization of embedding model."""
 
 
 
64
  if self._embedding_model is None:
65
- from sentence_transformers import SentenceTransformer
66
- self._embedding_model = SentenceTransformer(settings.EMBEDDING_MODEL)
 
 
 
 
 
 
 
 
 
67
  return self._embedding_model
68
 
69
  def _embed_texts(self, texts: List[str]) -> List[List[float]]:
@@ -82,6 +95,7 @@ class RAGService:
82
  return [list(e) for e in embeddings]
83
  except Exception as e:
84
  logger.error(f"Embedding failed: {e}")
 
85
  raise ValueError(f"Failed to embed texts: {e}")
86
 
87
  async def sync_user_reports(self, user_id: uuid.UUID, db: AsyncSession) -> int:
@@ -269,11 +283,20 @@ class RAGService:
269
  """
270
  # First, ensure user data is synced (if db provided)
271
  if db:
272
- await self.sync_user_reports(user_id, db)
273
- await self.sync_user_observations(user_id, db)
 
 
 
 
274
 
275
- # Query for relevant documents
276
- docs = await self.query(user_id, query)
 
 
 
 
 
277
 
278
  if not docs:
279
  return "No health data available for this user yet."
 
33
  self._client = None
34
  self._collection = None
35
  self._embedding_model = None
36
+ self._embedding_available = True # Disable gracefully on failure
37
 
38
  def _get_client(self):
39
  """Lazy initialization of ChromaDB client."""
 
62
 
63
  def _get_embedding_model(self):
64
  """Lazy initialization of embedding model."""
65
+ if not self._embedding_available:
66
+ raise RuntimeError("Embedding model is disabled due to previous initialization failure")
67
+
68
  if self._embedding_model is None:
69
+ try:
70
+ from sentence_transformers import SentenceTransformer
71
+ logger.info(f"Initializing embedding model: {settings.EMBEDDING_MODEL}")
72
+ self._embedding_model = SentenceTransformer(settings.EMBEDDING_MODEL)
73
+ except Exception as e:
74
+ # If the model can't be loaded (no internet, HF issue, etc.),
75
+ # disable embeddings so the rest of the app can continue.
76
+ self._embedding_available = False
77
+ logger.error(f"Failed to initialize embedding model '{settings.EMBEDDING_MODEL}': {e}")
78
+ raise RuntimeError("Embedding model initialization failed") from e
79
+
80
  return self._embedding_model
81
 
82
  def _embed_texts(self, texts: List[str]) -> List[List[float]]:
 
95
  return [list(e) for e in embeddings]
96
  except Exception as e:
97
  logger.error(f"Embedding failed: {e}")
98
+ # Propagate a clean error so callers can gracefully fall back
99
  raise ValueError(f"Failed to embed texts: {e}")
100
 
101
  async def sync_user_reports(self, user_id: uuid.UUID, db: AsyncSession) -> int:
 
283
  """
284
  # First, ensure user data is synced (if db provided)
285
  if db:
286
+ try:
287
+ await self.sync_user_reports(user_id, db)
288
+ await self.sync_user_observations(user_id, db)
289
+ except Exception as e:
290
+ # If syncing fails (embeddings/model/Chroma issues), log and continue.
291
+ logger.warning(f"RAG sync failed for user {user_id}: {e}")
292
 
293
+ try:
294
+ # Query for relevant documents
295
+ docs = await self.query(user_id, query)
296
+ except Exception as e:
297
+ # If RAG fails (e.g. embeddings unavailable, Chroma issue), fall back
298
+ logger.warning(f"RAG context unavailable for user {user_id}: {e}")
299
+ return "No health data available for this user yet."
300
 
301
  if not docs:
302
  return "No health data available for this user yet."
frontend/src/components/AuthShell.jsx CHANGED
@@ -1,5 +1,6 @@
1
  import { motion } from 'framer-motion'
2
  import { Link } from 'react-router-dom'
 
3
  import './AuthShell.css'
4
 
5
  function AuthShell({ children, title, subtitle, footerText, footerLink, footerLinkText }) {
@@ -13,8 +14,7 @@ function AuthShell({ children, title, subtitle, footerText, footerLink, footerLi
13
  transition={{ duration: 0.6 }}
14
  >
15
  <Link to="/" className="auth-logo">
16
- <span className="logo-main">Co-Code</span>
17
- <span className="logo-sub">GGW</span>
18
  </Link>
19
  </motion.div>
20
 
 
1
  import { motion } from 'framer-motion'
2
  import { Link } from 'react-router-dom'
3
+ import Logo from './ui/Logo'
4
  import './AuthShell.css'
5
 
6
  function AuthShell({ children, title, subtitle, footerText, footerLink, footerLinkText }) {
 
14
  transition={{ duration: 0.6 }}
15
  >
16
  <Link to="/" className="auth-logo">
17
+ <Logo variant="header" />
 
18
  </Link>
19
  </motion.div>
20
 
frontend/src/components/AuthShell.tsx CHANGED
@@ -3,6 +3,7 @@ import { Link } from 'react-router-dom'
3
  import { ReactNode } from 'react'
4
  import './AuthShell.css'
5
 
 
6
  interface AuthShellProps {
7
  children: ReactNode
8
  title: string
 
3
  import { ReactNode } from 'react'
4
  import './AuthShell.css'
5
 
6
+
7
  interface AuthShellProps {
8
  children: ReactNode
9
  title: string
frontend/src/components/Hero.tsx CHANGED
@@ -56,7 +56,7 @@ function Hero() {
56
  >
57
  {t('hero.tagline')}
58
  <br />
59
- is <motion.span
60
  className="highlight"
61
  initial={{ scale: 0, opacity: 0 }}
62
  animate={{ scale: 1, opacity: 1 }}
 
56
  >
57
  {t('hero.tagline')}
58
  <br />
59
+ <motion.span
60
  className="highlight"
61
  initial={{ scale: 0, opacity: 0 }}
62
  animate={{ scale: 1, opacity: 1 }}
frontend/src/i18n/locales/en.json CHANGED
@@ -11,8 +11,8 @@
11
  "cta": "Get Started"
12
  },
13
  "hero": {
14
- "tagline": "First priority is",
15
- "taglineHighlight": "your health",
16
  "subtitle": "Track it. Understand it. Prevent it.",
17
  "description": "Upload your lab reports, get them interpreted, and receive smart reminders for checkups + medicines. Lumea helps you stay preventive—without becoming your doctor.",
18
  "disclaimer": "Not medical advice. Always consult a licensed clinician for diagnosis/treatment.",
 
11
  "cta": "Get Started"
12
  },
13
  "hero": {
14
+ "tagline": "Your health",
15
+ "taglineHighlight": "comes first",
16
  "subtitle": "Track it. Understand it. Prevent it.",
17
  "description": "Upload your lab reports, get them interpreted, and receive smart reminders for checkups + medicines. Lumea helps you stay preventive—without becoming your doctor.",
18
  "disclaimer": "Not medical advice. Always consult a licensed clinician for diagnosis/treatment.",