Pontonkid commited on
Commit
466a383
·
verified ·
1 Parent(s): d5f6a93

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +135 -172
src/streamlit_app.py CHANGED
@@ -5,9 +5,10 @@ import hashlib
5
  import google.generativeai as genai
6
  from PIL import Image
7
  from datetime import datetime
 
8
 
9
  # -----------------------------------------------------------------------------
10
- # 0. AUTO-CONFIG (FIX UPLOAD ERROR)
11
  # -----------------------------------------------------------------------------
12
  config_dir = ".streamlit"
13
  if not os.path.exists(config_dir):
@@ -16,15 +17,13 @@ with open(os.path.join(config_dir, "config.toml"), "w") as f:
16
  f.write("[server]\nenableXsrfProtection=false\nenableCORS=false\nmaxUploadSize=200\n")
17
 
18
  # -----------------------------------------------------------------------------
19
- # 1. DATABASE SETUP (REAL USER SYSTEM)
20
  # -----------------------------------------------------------------------------
21
  def init_db():
22
  conn = sqlite3.connect('users.db')
23
  c = conn.cursor()
24
- # Create users table
25
  c.execute('''CREATE TABLE IF NOT EXISTS users
26
  (email TEXT PRIMARY KEY, password TEXT, joined_date TEXT)''')
27
- # Create history table
28
  c.execute('''CREATE TABLE IF NOT EXISTS history
29
  (id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT, type TEXT, summary TEXT, date TEXT)''')
30
  conn.commit()
@@ -69,90 +68,61 @@ def get_history(email):
69
  conn.close()
70
  return data
71
 
72
- # Initialize DB on startup
73
  init_db()
74
 
75
  # -----------------------------------------------------------------------------
76
- # 2. AI CONFIGURATION
77
  # -----------------------------------------------------------------------------
78
- st.set_page_config(page_title="Shinui | Medical MVP", page_icon="✨", layout="wide")
79
 
80
  GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
81
  if not GOOGLE_API_KEY:
82
- st.error("⚠️ System Error: Google API Key Missing.")
83
  st.stop()
84
 
85
  genai.configure(api_key=GOOGLE_API_KEY)
86
- model = genai.GenerativeModel('gemini-1.5-flash')
 
 
87
 
88
  # -----------------------------------------------------------------------------
89
- # 3. STATE MANAGEMENT
90
  # -----------------------------------------------------------------------------
91
  if 'page' not in st.session_state: st.session_state.page = 'auth'
92
  if 'logged_in' not in st.session_state: st.session_state.logged_in = False
93
  if 'user_email' not in st.session_state: st.session_state.user_email = ""
94
  if 'current_result' not in st.session_state: st.session_state.current_result = None
95
 
96
- # -----------------------------------------------------------------------------
97
- # 4. UI STYLING (PROFESSIONAL DARK MODE)
98
- # -----------------------------------------------------------------------------
99
  st.markdown("""
100
  <style>
101
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;800&display=swap');
 
102
 
103
- .stApp {
104
- background-color: #0f172a;
105
- font-family: 'Inter', sans-serif;
106
- color: #f8fafc;
107
- }
108
-
109
- /* Cards */
110
  .shinui-card {
111
- background: #1e293b;
112
- border: 1px solid #334155;
113
- border-radius: 12px;
114
- padding: 24px;
115
- margin-bottom: 20px;
116
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
117
  }
118
 
 
 
 
 
 
 
119
  /* Buttons */
120
  div.stButton > button {
121
- background-color: #3b82f6;
122
- color: white;
123
- border-radius: 8px;
124
- padding: 10px 24px;
125
- font-weight: 600;
126
- border: none;
127
- width: 100%;
128
- transition: all 0.2s;
129
- }
130
- div.stButton > button:hover {
131
- background-color: #2563eb;
132
- }
133
-
134
- /* Results */
135
- .result-section {
136
- padding: 15px;
137
- border-left: 4px solid #3b82f6;
138
- background: rgba(59, 130, 246, 0.1);
139
- margin-bottom: 10px;
140
- border-radius: 0 8px 8px 0;
141
- }
142
-
143
- /* Inputs */
144
- .stTextInput input {
145
- background-color: #334155;
146
- color: white;
147
- border: 1px solid #475569;
148
  }
 
149
 
150
  #MainMenu, footer, header {visibility: hidden;}
151
  </style>
152
  """, unsafe_allow_html=True)
153
 
154
  # -----------------------------------------------------------------------------
155
- # 5. LOGIC FUNCTIONS
156
  # -----------------------------------------------------------------------------
157
  def nav_to(page):
158
  st.session_state.page = page
@@ -164,176 +134,169 @@ def sign_out():
164
  st.session_state.current_result = None
165
  nav_to('auth')
166
 
167
- def get_gemini_insight(input_type, content):
168
- # Detailed MVP System Prompt
169
  prompt = """
170
- You are Shinui, a professional medical AI assistant.
171
- Analyze the input carefully.
172
- Format your response using these EXACT headers:
173
-
174
- ### 1. Clinical Observation
175
- (Describe what you see or read clearly)
176
 
177
- ### 2. Potential Risks
178
- (List potential conditions or risks associated)
 
179
 
180
- ### 3. Recommended Actions
181
- (Suggest next steps, e.g., see a specialist, apply ice, etc.)
182
  """
183
 
184
  try:
185
  if input_type == "Image":
186
  response = model.generate_content([prompt, content])
187
- return response.text
188
- elif input_type == "Text":
189
- response = model.generate_content(f"{prompt}\n\nInput Data: {content}")
190
- return response.text
191
  except Exception as e:
192
- return f"Error: {str(e)}"
193
 
194
  # -----------------------------------------------------------------------------
195
- # 6. PAGES
196
  # -----------------------------------------------------------------------------
197
 
198
- # --- AUTH PAGE (LOGIN / REGISTER) ---
199
  def show_auth():
200
- c1, c2, c3 = st.columns([1, 2, 1])
201
  with c2:
202
- st.markdown("<br><br>", unsafe_allow_html=True)
203
- st.markdown("<h1 style='text-align:center; color:#3b82f6;'> Shinui</h1>", unsafe_allow_html=True)
204
- st.markdown("<p style='text-align:center; color:#94a3b8;'>Intelligent Medical Analysis MVP</p>", unsafe_allow_html=True)
205
 
206
- tab1, tab2 = st.tabs(["Sign In", "Create Account"])
207
 
208
  with tab1:
209
  st.markdown("<div class='shinui-card'>", unsafe_allow_html=True)
210
- email = st.text_input("Email Address", key="l_email")
211
  password = st.text_input("Password", type="password", key="l_pass")
212
- if st.button("Login"):
213
  if login_user(email, password):
214
  st.session_state.logged_in = True
215
  st.session_state.user_email = email
216
- st.success("Login Successful")
217
- time.sleep(0.5) # Smooth transition
218
  nav_to('dashboard')
219
  else:
220
- st.error("Invalid email or password")
221
  st.markdown("</div>", unsafe_allow_html=True)
222
 
223
  with tab2:
224
  st.markdown("<div class='shinui-card'>", unsafe_allow_html=True)
225
- new_email = st.text_input("Email Address", key="r_email")
226
- new_pass = st.text_input("Password", type="password", key="r_pass")
227
- confirm_pass = st.text_input("Confirm Password", type="password", key="r_pass2")
228
- if st.button("Register"):
229
- if new_pass != confirm_pass:
230
- st.error("Passwords do not match")
231
- elif register_user(new_email, new_pass):
232
- st.success("Account created! Please log in.")
233
  else:
234
- st.error("Email already exists")
235
  st.markdown("</div>", unsafe_allow_html=True)
236
 
237
  # --- DASHBOARD ---
238
  def show_dashboard():
239
- # SIDEBAR NAVIGATION
240
  with st.sidebar:
241
  st.markdown(f"### 👤 {st.session_state.user_email}")
242
- st.markdown("<span style='color:#22c55e; font-size:0.8rem;'>● Online</span>", unsafe_allow_html=True)
243
  st.markdown("---")
 
 
 
244
 
245
- if st.button("📊 Dashboard"): nav_to('dashboard')
246
- if st.button("ℹ️ About Shinui"): nav_to('about')
247
-
248
- st.markdown("### Recent History")
249
  history = get_history(st.session_state.user_email)
250
  if history:
251
  for h_type, h_sum, h_date in history:
252
- st.markdown(f"""
253
- <div style='font-size:0.8rem; padding:8px; background:#334155; border-radius:6px; margin-bottom:6px;'>
254
- <b>{h_type}</b><br>{h_date}<br><span style='color:#94a3b8'>{h_sum[:40]}...</span>
255
- </div>
256
- """, unsafe_allow_html=True)
257
- else:
258
- st.caption("No activity found.")
259
-
260
- st.markdown("---")
261
  if st.button("Sign Out"): sign_out()
262
 
263
- # MAIN CONTENT
264
- st.title("Medical Analysis Dashboard")
265
 
266
- col1, col2 = st.columns([2, 1])
267
- with col1:
268
- st.markdown("### New Scan")
269
- t1, t2 = st.tabs(["📷 Visual Analysis", "📝 Symptom Check"])
270
-
271
- with t1:
272
- st.markdown("<div class='shinui-card'>", unsafe_allow_html=True)
273
- img = st.file_uploader("Upload Medical Image", type=['png','jpg','jpeg'])
274
- if img and st.button("Analyze Image"):
275
- image = Image.open(img)
276
- with st.spinner("Shinui AI Analyzing..."):
277
- res = get_gemini_insight("Image", image)
278
- st.session_state.current_result = res
279
- # Save detailed result to DB
280
- add_history(st.session_state.user_email, "Visual Scan", res[:100].replace('\n', ' '))
281
-
282
- with t2:
283
- st.markdown("<div class='shinui-card'>", unsafe_allow_html=True)
284
- txt = st.text_area("Describe Symptoms")
285
- if txt and st.button("Analyze Text"):
286
- with st.spinner("Shinui AI Analyzing..."):
287
- res = get_gemini_insight("Text", txt)
288
- st.session_state.current_result = res
289
- add_history(st.session_state.user_email, "Text Analysis", res[:100].replace('\n', ' '))
 
 
 
 
290
 
291
- # RESULTS SECTION (Right Side / Bottom)
292
- with col2:
293
- st.markdown("### Results")
294
- if st.session_state.current_result:
295
- # Parsing Markdown for pretty display
296
- content = st.session_state.current_result
 
 
 
 
 
 
 
 
 
 
297
  st.markdown(f"""
298
- <div class='shinui-card' style='border-top: 4px solid #3b82f6;'>
299
- {content}
 
300
  </div>
301
  """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  else:
303
- st.info("Upload an image or enter text to see analysis results here.")
304
-
305
- # --- ABOUT PAGE ---
306
- def show_about():
307
- with st.sidebar:
308
- if st.button("← Back to Dashboard"): nav_to('dashboard')
309
-
310
- st.markdown("""
311
- <div class='shinui-card'>
312
- <h2 style='color:#3b82f6'>About Shinui</h2>
313
- <p style='font-size:1.1rem; line-height:1.6'>
314
- Shinui is an MVP-stage medical intelligence platform designed to democratize access to health data analysis.
315
- </p>
316
- <hr style='border-color:#334155'>
317
- <h3>The Problem</h3>
318
- <p>Medical data is complex, jargon-heavy, and hard for patients to interpret.</p>
319
- <h3>The Solution</h3>
320
- <p>Shinui uses Google's Gemini 1.5 Flash model to act as a translator, turning images and clinical notes into actionable, understandable insights.</p>
321
- <h3>Version Info</h3>
322
- <p><b>Build:</b> v1.0 MVP</p>
323
- <p><b>Engine:</b> Gemini 1.5 Flash</p>
324
- </div>
325
- """, unsafe_allow_html=True)
326
 
327
  # -----------------------------------------------------------------------------
328
- # 7. ROUTER
329
  # -----------------------------------------------------------------------------
330
- import time # Import needed for sleep
331
-
332
  if st.session_state.page == 'auth':
333
  show_auth()
334
  elif st.session_state.page == 'dashboard':
335
- if st.session_state.logged_in: show_dashboard()
336
- else: nav_to('auth')
337
- elif st.session_state.page == 'about':
338
- if st.session_state.logged_in: show_about()
339
- else: nav_to('auth')
 
5
  import google.generativeai as genai
6
  from PIL import Image
7
  from datetime import datetime
8
+ import time
9
 
10
  # -----------------------------------------------------------------------------
11
+ # 0. AUTO-CONFIG
12
  # -----------------------------------------------------------------------------
13
  config_dir = ".streamlit"
14
  if not os.path.exists(config_dir):
 
17
  f.write("[server]\nenableXsrfProtection=false\nenableCORS=false\nmaxUploadSize=200\n")
18
 
19
  # -----------------------------------------------------------------------------
20
+ # 1. DATABASE & AUTH SYSTEM (The part you liked)
21
  # -----------------------------------------------------------------------------
22
  def init_db():
23
  conn = sqlite3.connect('users.db')
24
  c = conn.cursor()
 
25
  c.execute('''CREATE TABLE IF NOT EXISTS users
26
  (email TEXT PRIMARY KEY, password TEXT, joined_date TEXT)''')
 
27
  c.execute('''CREATE TABLE IF NOT EXISTS history
28
  (id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT, type TEXT, summary TEXT, date TEXT)''')
29
  conn.commit()
 
68
  conn.close()
69
  return data
70
 
 
71
  init_db()
72
 
73
  # -----------------------------------------------------------------------------
74
+ # 2. AI SETUP (Reverted to the SMART Model)
75
  # -----------------------------------------------------------------------------
76
+ st.set_page_config(page_title="SHINUI | Intelligent Care", page_icon="✨", layout="wide")
77
 
78
  GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
79
  if not GOOGLE_API_KEY:
80
+ st.error("⚠️ API Key Missing. Add 'GOOGLE_API_KEY' to Secrets.")
81
  st.stop()
82
 
83
  genai.configure(api_key=GOOGLE_API_KEY)
84
+
85
+ # Reverting to 'gemini-1.5-pro' (High Intelligence) instead of Flash
86
+ model = genai.GenerativeModel('gemini-2.5-pro')
87
 
88
  # -----------------------------------------------------------------------------
89
+ # 3. STATE & STYLING
90
  # -----------------------------------------------------------------------------
91
  if 'page' not in st.session_state: st.session_state.page = 'auth'
92
  if 'logged_in' not in st.session_state: st.session_state.logged_in = False
93
  if 'user_email' not in st.session_state: st.session_state.user_email = ""
94
  if 'current_result' not in st.session_state: st.session_state.current_result = None
95
 
 
 
 
96
  st.markdown("""
97
  <style>
98
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;800&display=swap');
99
+ .stApp { background-color: #0f172a; font-family: 'Inter', sans-serif; color: #f8fafc; }
100
 
101
+ /* Custom Cards */
 
 
 
 
 
 
102
  .shinui-card {
103
+ background: #1e293b; border: 1px solid #334155; border-radius: 12px;
104
+ padding: 24px; margin-bottom: 20px;
 
 
 
 
105
  }
106
 
107
+ /* Result Boxes */
108
+ .res-box { padding: 15px; border-radius: 8px; margin-bottom: 10px; }
109
+ .res-obs { background: rgba(59, 130, 246, 0.15); border-left: 4px solid #3b82f6; } /* Blue */
110
+ .res-risk { background: rgba(245, 158, 11, 0.15); border-left: 4px solid #f59e0b; } /* Orange */
111
+ .res-act { background: rgba(16, 185, 129, 0.15); border-left: 4px solid #10b981; } /* Green */
112
+
113
  /* Buttons */
114
  div.stButton > button {
115
+ background: #3b82f6; color: white; border: none; border-radius: 8px;
116
+ padding: 10px 20px; font-weight: 600; width: 100%; transition: 0.2s;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  }
118
+ div.stButton > button:hover { background: #2563eb; }
119
 
120
  #MainMenu, footer, header {visibility: hidden;}
121
  </style>
122
  """, unsafe_allow_html=True)
123
 
124
  # -----------------------------------------------------------------------------
125
+ # 4. LOGIC (With Structured Formatting)
126
  # -----------------------------------------------------------------------------
127
  def nav_to(page):
128
  st.session_state.page = page
 
134
  st.session_state.current_result = None
135
  nav_to('auth')
136
 
137
+ def get_ai_analysis(input_type, content):
138
+ # We force the AI to use a specific separator "|||" so we can split the result nicely
139
  prompt = """
140
+ You are SHINUI, a medical AI. Analyze the input.
141
+ Provide the output in exactly 3 sections separated by '|||'.
 
 
 
 
142
 
143
+ Section 1: Clinical Observation (What is visible/described?)
144
+ Section 2: Potential Risks (What are the dangers?)
145
+ Section 3: Next Steps (Actionable advice)
146
 
147
+ Do not add Markdown headers like '##'. Just the content.
 
148
  """
149
 
150
  try:
151
  if input_type == "Image":
152
  response = model.generate_content([prompt, content])
153
+ else:
154
+ response = model.generate_content(f"{prompt}\n\nInput: {content}")
155
+
156
+ return response.text
157
  except Exception as e:
158
+ return f"Error|||System Failure|||{str(e)}"
159
 
160
  # -----------------------------------------------------------------------------
161
+ # 5. PAGES
162
  # -----------------------------------------------------------------------------
163
 
164
+ # --- AUTH ---
165
  def show_auth():
166
+ c1, c2, c3 = st.columns([1, 1.5, 1])
167
  with c2:
168
+ st.markdown("<br><br><h1 style='text-align:center; color:#3b82f6;'>SHINUI</h1>", unsafe_allow_html=True)
169
+ st.markdown("<p style='text-align:center; color:#94a3b8;'>Advanced Medical Intelligence</p>", unsafe_allow_html=True)
 
170
 
171
+ tab1, tab2 = st.tabs(["Login", "Register"])
172
 
173
  with tab1:
174
  st.markdown("<div class='shinui-card'>", unsafe_allow_html=True)
175
+ email = st.text_input("Email", key="l_email")
176
  password = st.text_input("Password", type="password", key="l_pass")
177
+ if st.button("Sign In"):
178
  if login_user(email, password):
179
  st.session_state.logged_in = True
180
  st.session_state.user_email = email
181
+ st.success("Success")
182
+ time.sleep(0.5)
183
  nav_to('dashboard')
184
  else:
185
+ st.error("Invalid credentials")
186
  st.markdown("</div>", unsafe_allow_html=True)
187
 
188
  with tab2:
189
  st.markdown("<div class='shinui-card'>", unsafe_allow_html=True)
190
+ r_email = st.text_input("Email", key="r_email")
191
+ r_pass = st.text_input("Password", type="password", key="r_pass")
192
+ if st.button("Create Account"):
193
+ if register_user(r_email, r_pass):
194
+ st.success("Account created. Please log in.")
 
 
 
195
  else:
196
+ st.error("Email taken.")
197
  st.markdown("</div>", unsafe_allow_html=True)
198
 
199
  # --- DASHBOARD ---
200
  def show_dashboard():
201
+ # SIDEBAR
202
  with st.sidebar:
203
  st.markdown(f"### 👤 {st.session_state.user_email}")
204
+ st.markdown("🟢 **System Online**")
205
  st.markdown("---")
206
+ if st.button("New Scan"):
207
+ st.session_state.current_result = None
208
+ st.rerun()
209
 
210
+ st.markdown("### History")
 
 
 
211
  history = get_history(st.session_state.user_email)
212
  if history:
213
  for h_type, h_sum, h_date in history:
214
+ st.markdown(f"<div style='font-size:0.75rem; color:#94a3b8; border-bottom:1px solid #333; padding:5px;'>{h_date}<br><b style='color:white'>{h_type}</b></div>", unsafe_allow_html=True)
215
+
216
+ st.markdown("<br><br>", unsafe_allow_html=True)
 
 
 
 
 
 
217
  if st.button("Sign Out"): sign_out()
218
 
219
+ # MAIN
220
+ st.title("Diagnostic Interface")
221
 
222
+ # INPUT SECTION
223
+ st.markdown("<div class='shinui-card'>", unsafe_allow_html=True)
224
+ t1, t2 = st.tabs(["📷 Visual", "📝 Text"])
225
+
226
+ input_content = None
227
+ input_type = None
228
+
229
+ with t1:
230
+ img_file = st.file_uploader("Upload Image", type=['png','jpg','jpeg'])
231
+ if img_file and st.button("Analyze Image"):
232
+ input_content = Image.open(img_file)
233
+ input_type = "Image"
234
+
235
+ with t2:
236
+ txt_val = st.text_area("Enter Symptoms")
237
+ if txt_val and st.button("Analyze Text"):
238
+ input_content = txt_val
239
+ input_type = "Text"
240
+
241
+ if input_content:
242
+ with st.spinner("SHINUI is thinking..."):
243
+ # Call AI
244
+ raw_res = get_ai_analysis(input_type, input_content)
245
+ st.session_state.current_result = raw_res
246
+ # Save simple log
247
+ add_history(st.session_state.user_email, f"{input_type} Analysis", "Analysis Complete")
248
+ st.rerun()
249
+ st.markdown("</div>", unsafe_allow_html=True)
250
 
251
+ # RESULT DISPLAY (THE FIXED PART)
252
+ if st.session_state.current_result:
253
+ # Split the result by our secret separator |||
254
+ parts = st.session_state.current_result.split("|||")
255
+
256
+ # Ensure we have 3 parts, otherwise just show text
257
+ if len(parts) >= 3:
258
+ obs = parts[0].strip()
259
+ risks = parts[1].strip()
260
+ actions = parts[2].strip()
261
+
262
+ col1, col2 = st.columns([1, 1])
263
+
264
+ st.markdown("### Analysis Results")
265
+
266
+ # Observation (Full Width)
267
  st.markdown(f"""
268
+ <div class='res-box res-obs'>
269
+ <h4 style='color:#3b82f6; margin:0;'>🔍 Clinical Observation</h4>
270
+ <p style='color:#e2e8f0;'>{obs}</p>
271
  </div>
272
  """, unsafe_allow_html=True)
273
+
274
+ # Split Risks and Actions
275
+ with col1:
276
+ st.markdown(f"""
277
+ <div class='res-box res-risk'>
278
+ <h4 style='color:#f59e0b; margin:0;'>⚠️ Potential Risks</h4>
279
+ <p style='color:#e2e8f0;'>{risks}</p>
280
+ </div>
281
+ """, unsafe_allow_html=True)
282
+ with col2:
283
+ st.markdown(f"""
284
+ <div class='res-box res-act'>
285
+ <h4 style='color:#10b981; margin:0;'>✅ Recommended Actions</h4>
286
+ <p style='color:#e2e8f0;'>{actions}</p>
287
+ </div>
288
+ """, unsafe_allow_html=True)
289
  else:
290
+ # Fallback if AI format varies
291
+ st.info(st.session_state.current_result)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
 
293
  # -----------------------------------------------------------------------------
294
+ # 6. ROUTING
295
  # -----------------------------------------------------------------------------
 
 
296
  if st.session_state.page == 'auth':
297
  show_auth()
298
  elif st.session_state.page == 'dashboard':
299
+ if st.session_state.logged_in:
300
+ show_dashboard()
301
+ else:
302
+ nav_to('auth')