rdz-falcon commited on
Commit
958aee6
·
verified ·
1 Parent(s): 248b10b

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +466 -25
src/streamlit_app.py CHANGED
@@ -12,29 +12,470 @@ forums](https://discuss.streamlit.io).
12
 
13
  In the meantime, below is an example of what you can do with just a few lines of code:
14
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
12
 
13
  In the meantime, below is an example of what you can do with just a few lines of code:
14
  """
15
+ import streamlit as st
16
+ import pandas as pd
17
+ import rag # Import the rag module
18
+ import os # Import os for file path handling
19
+
20
+ def main():
21
+ st.set_page_config(layout="wide", page_title="Communication Board")
22
+
23
+ # --- Session State Initialization ---
24
+ # Check specifically for 'assistant' to ensure it's initialized
25
+ if 'assistant' not in st.session_state:
26
+ st.session_state.current_page = "main"
27
+ st.session_state.show_custom_words = False
28
+ st.session_state.custom_words = []
29
+ st.session_state.text_size = 22
30
+ st.session_state.theme = "Default"
31
+ st.session_state.speech_rate = 1.0
32
+ st.session_state.voice_option = "Default Voice"
33
+ st.session_state.messages = []
34
+ st.session_state.assistant = None
35
+ # Initialize text_output only if assistant is not initialized, assuming they go together
36
+ st.session_state.text_output = ""
37
+
38
+ # --- Theme Colors ---
39
+ theme_colors = {
40
+ "Default": {
41
+ "bg": "#FFFFFF", "text": "#000000",
42
+ "pronoun": "#FFFF99", "verb": "#CCFFCC",
43
+ "question": "#CCCCFF", "common": "#FFCC99",
44
+ "preposition": "#99CCFF", "descriptive": "#CCFF99",
45
+ "misc": "#FFB6C1"
46
+ },
47
+ "High Contrast": {
48
+ "bg": "#FFFFFF", "text": "#000000",
49
+ "pronoun": "#FFFF00", "verb": "#00FF00",
50
+ "question": "#0000FF", "common": "#FF6600",
51
+ "preposition": "#00CCFF", "descriptive": "#66FF33",
52
+ "misc": "#FF3366"
53
+ },
54
+ "Pastel": {
55
+ "bg": "#F8F8FF", "text": "#333333",
56
+ "pronoun": "#FFEFD5", "verb": "#E0FFFF",
57
+ "question": "#D8BFD8", "common": "#FFE4B5",
58
+ "preposition": "#B0E0E6", "descriptive": "#F0FFF0",
59
+ "misc": "#FFF0F5"
60
+ },
61
+ "Dark Mode": {
62
+ "bg": "#333333", "text": "#FFFFFF",
63
+ "pronoun": "#8B8B00", "verb": "#006400",
64
+ "question": "#00008B", "common": "#8B4500",
65
+ "preposition": "#00688B", "descriptive": "#698B22",
66
+ "misc": "#8B1A1A"
67
+ }
68
+ }
69
+ colors = theme_colors[st.session_state.theme]
70
+
71
+ # --- Helper Function to Initialize Assistant (Adapted from previous main.py) ---
72
+ @st.cache_resource # Cache the assistant
73
+ def initialize_assistant(doc_path):
74
+ """Initializes the AACAssistant."""
75
+ # Create a dummy document if it doesn't exist for demonstration
76
+ if not os.path.exists(doc_path):
77
+ st.sidebar.warning(f"Doc '{os.path.basename(doc_path)}' not found. Creating dummy.")
78
+ try:
79
+ with open(doc_path, "w") as f:
80
+ f.write("""
81
+ I grew up in Seattle and love the rain.
82
+ My favorite hobby is playing chess.
83
+ I have a dog named Max.
84
+ I studied computer science.
85
+ I enjoy sci-fi movies.
86
+ """)
87
+ except Exception as e:
88
+ st.sidebar.error(f"Failed to create dummy doc: {e}")
89
+ return None
90
+ try:
91
+ assistant = rag.AACAssistant(doc_path)
92
+ st.sidebar.success("AAC Assistant Initialized.")
93
+ return assistant
94
+ except Exception as e:
95
+ st.sidebar.error(f"Error initializing AAC Assistant: {e}")
96
+ st.sidebar.error("Ensure Ollama/LM Studio running.")
97
+ return None
98
+
99
+ DEFAULT_DOCUMENT_PATH = "aac_user_experiences.txt"
100
+
101
+ # --- CSS Styling ---
102
+ css = f"""
103
+ <style>
104
+ .big-font {{
105
+ font-size:{st.session_state.text_size}px !important;
106
+ text-align: center;
107
+ }}
108
+ .output-box {{
109
+ border: 2px solid #ddd;
110
+ border-radius: 5px;
111
+ padding: 15px;
112
+ min-height: 100px;
113
+ background-color: white;
114
+ margin-bottom: 15px;
115
+ font-size: {st.session_state.text_size}px;
116
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
117
+ }}
118
+ div[data-testid="stHorizontalBlock"] {{
119
+ gap: 5px;
120
+ }}
121
+ section[data-testid="stSidebar"] {{
122
+ width: 20rem;
123
+ background-color: {colors["bg"]};
124
+ }}
125
+ body {{
126
+ background-color: {colors["bg"]};
127
+ color: {colors["text"]};
128
+ }}
129
+ .stButton>button {{
130
+ width: 100%;
131
+ height: 60px;
132
+ font-size: {max(16, st.session_state.text_size - 6)}px;
133
+ font-weight: bold;
134
+ white-space: normal;
135
+ padding: 0px;
136
+ transition: transform 0.1s ease;
137
+ }}
138
+ .stButton>button:hover {{
139
+ filter: brightness(95%);
140
+ transform: scale(1.03);
141
+ box-shadow: 0 2px 3px rgba(0,0,0,0.1);
142
+ }}
143
+ .control-button {{
144
+ height: 80px !important;
145
+ font-size: {max(18, st.session_state.text_size - 4)}px !important;
146
+ }}
147
+ .btn-pronoun {{
148
+ background-color: {colors["pronoun"]} !important;
149
+ border: 1px solid #888 !important;
150
+ }}
151
+ .btn-verb {{
152
+ background-color: {colors["verb"]} !important;
153
+ border: 1px solid #888 !important;
154
+ }}
155
+ .btn-question {{
156
+ background-color: {colors["question"]} !important;
157
+ border: 1px solid #888 !important;
158
+ }}
159
+ .btn-common {{
160
+ background-color: {colors["common"]} !important;
161
+ border: 1px solid #888 !important;
162
+ }}
163
+ .btn-preposition {{
164
+ background-color: {colors["preposition"]} !important;
165
+ border: 1px solid #888 !important;
166
+ }}
167
+ .btn-descriptive {{
168
+ background-color: {colors["descriptive"]} !important;
169
+ border: 1px solid #888 !important;
170
+ }}
171
+ .btn-misc {{
172
+ background-color: {colors["misc"]} !important;
173
+ border: 1px solid #888 !important;
174
+ }}
175
+ /* Sidebar chat styling */
176
+ .sidebar .stChatMessage {{
177
+ background-color: {colors.get('bg', '#FFFFFF')}; /* Use theme background */
178
+ border-radius: 8px;
179
+ }}
180
+ </style>
181
+ """
182
+
183
+ # --- JS for Button Coloring (no delay, no setTimeout) ---
184
+ js = """
185
+ <script>
186
+ function colorButtons() {
187
+ const buttons = document.querySelectorAll('button[id^="key_"]');
188
+ buttons.forEach(button => {
189
+ const id = button.id;
190
+ const parts = id.split('_');
191
+ if (parts.length >= 4) {
192
+ const category = parts[3];
193
+ button.classList.add('btn-' + category);
194
+ }
195
+ });
196
+ }
197
+ document.addEventListener('DOMContentLoaded', colorButtons);
198
+ new MutationObserver(colorButtons).observe(document.body, { childList: true, subtree: true });
199
+ </script>
200
+ """
201
+
202
+ st.markdown(css, unsafe_allow_html=True)
203
+ st.markdown(js, unsafe_allow_html=True)
204
+
205
+ # --- Keyboard Layout ---
206
+ layout = [
207
+ [
208
+ {"word": "I", "category": "pronoun"},
209
+ {"word": "am", "category": "verb"},
210
+ {"word": "how", "category": "question"},
211
+ {"word": "what", "category": "question"},
212
+ {"word": "when", "category": "question"},
213
+ {"word": "where", "category": "question"},
214
+ {"word": "who", "category": "question"},
215
+ {"word": "why", "category": "question"},
216
+ {"word": "That", "category": "pronoun"},
217
+ {"word": "Please", "category": "common"}
218
+ ],
219
+ [
220
+ {"word": "me", "category": "pronoun"},
221
+ {"word": "are", "category": "verb"},
222
+ {"word": "is", "category": "verb"},
223
+ {"word": "was", "category": "verb"},
224
+ {"word": "will", "category": "verb"},
225
+ {"word": "help", "category": "verb"},
226
+ {"word": "need", "category": "verb"},
227
+ {"word": "want", "category": "verb"},
228
+ {"word": "thank you", "category": "common"},
229
+ {"word": "sorry", "category": "common"}
230
+ ],
231
+ [
232
+ {"word": "my", "category": "pronoun"},
233
+ {"word": "can", "category": "verb"},
234
+ {"word": "A", "category": "misc"},
235
+ {"word": "B", "category": "misc"},
236
+ {"word": "C", "category": "misc"},
237
+ {"word": "D", "category": "misc"},
238
+ {"word": "E", "category": "misc"},
239
+ {"word": "F", "category": "misc"},
240
+ {"word": "G", "category": "misc"},
241
+ {"word": "H", "category": "misc"}
242
+ ],
243
+ [
244
+ {"word": "it", "category": "pronoun"},
245
+ {"word": "did", "category": "verb"},
246
+ {"word": "letter_I", "category": "misc", "display": "I"},
247
+ {"word": "J", "category": "misc"},
248
+ {"word": "K", "category": "misc"},
249
+ {"word": "L", "category": "misc"},
250
+ {"word": "M", "category": "misc"},
251
+ {"word": "N", "category": "misc"},
252
+ {"word": "O", "category": "misc"},
253
+ {"word": "P", "category": "misc"}
254
+ ],
255
+ [
256
+ {"word": "they", "category": "pronoun"},
257
+ {"word": "do", "category": "verb"},
258
+ {"word": "Q", "category": "misc"},
259
+ {"word": "R", "category": "misc"},
260
+ {"word": "S", "category": "misc"},
261
+ {"word": "T", "category": "misc"},
262
+ {"word": "U", "category": "misc"},
263
+ {"word": "V", "category": "misc"},
264
+ {"word": "W", "category": "misc"},
265
+ {"word": "X", "category": "misc"}
266
+ ],
267
+ [
268
+ {"word": "we", "category": "pronoun"},
269
+ {"word": "Y", "category": "misc"},
270
+ {"word": "Z", "category": "misc"},
271
+ {"word": "1", "category": "misc"},
272
+ {"word": "2", "category": "misc"},
273
+ {"word": "3", "category": "misc"},
274
+ {"word": "4", "category": "misc"},
275
+ {"word": "5", "category": "misc"},
276
+ {"word": ".", "category": "misc"},
277
+ {"word": "?", "category": "misc"}
278
+ ],
279
+ [
280
+ {"word": "you", "category": "pronoun"},
281
+ {"word": "6", "category": "misc"},
282
+ {"word": "7", "category": "misc"},
283
+ {"word": "8", "category": "misc"},
284
+ {"word": "9", "category": "misc"},
285
+ {"word": "0", "category": "misc"},
286
+ {"word": "-", "category": "misc"},
287
+ {"word": "!", "category": "misc"},
288
+ {"word": ",", "category": "misc"},
289
+ {"word": "SPACE", "category": "misc"}
290
+ ],
291
+ [
292
+ {"word": "your", "category": "pronoun"},
293
+ {"word": "like", "category": "verb"},
294
+ {"word": "to", "category": "preposition"},
295
+ {"word": "with", "category": "preposition"},
296
+ {"word": "in", "category": "preposition"},
297
+ {"word": "the", "category": "misc"},
298
+ {"word": "and", "category": "misc"},
299
+ {"word": "but", "category": "misc"},
300
+ {"word": "not", "category": "descriptive"},
301
+ {"word": "yes", "category": "common"}
302
+ ]
303
+ ]
304
+
305
+ # --- Add Custom Words ---
306
+ if st.session_state.custom_words:
307
+ custom_row = []
308
+ for idx, word_info in enumerate(st.session_state.custom_words):
309
+ word = word_info["word"]
310
+ category = word_info["category"]
311
+ custom_row.append({"word": f"custom_{idx}_{word}", "display": word, "category": category})
312
+ if len(custom_row) == 10:
313
+ layout.append(custom_row)
314
+ custom_row = []
315
+ if custom_row:
316
+ while len(custom_row) < 10:
317
+ custom_row.append({"word": "", "category": "misc"})
318
+ layout.append(custom_row)
319
+
320
+ # --- Initialize Assistant ---
321
+ # Attempt initialization only once or if it failed previously
322
+ if st.session_state.assistant is None: # This check is now safe
323
+ st.session_state.assistant = initialize_assistant(DEFAULT_DOCUMENT_PATH)
324
+
325
+ # --- Output Box (move to top, before keyboard) ---
326
+ st.title("Communication Board")
327
+ # Use st.text_area directly for input and display, bound to session state
328
+ st.session_state.text_output = st.text_area(
329
+ "Compose Message:", value=st.session_state.text_output, height=100, key="main_text_output"
330
+ )
331
+
332
+ # --- Keyboard Rendering (no delay, instant update) ---
333
+ send_action = False # Flag for SEND button
334
+ def add_to_output(word):
335
+ if word == "SPACE":
336
+ st.session_state.text_output += " "
337
+ elif word in [".", "?", "!", ",", "-"]:
338
+ st.session_state.text_output += word
339
+ elif word.isdigit() or (len(word) == 1 and word.isalpha()):
340
+ st.session_state.text_output += word
341
+ else:
342
+ if st.session_state.text_output and not st.session_state.text_output.endswith(" "):
343
+ st.session_state.text_output += " "
344
+ st.session_state.text_output += word
345
+
346
+ st.markdown("### Communication Keyboard")
347
+ for row_idx, row in enumerate(layout):
348
+ cols = st.columns(len(row))
349
+ for col_idx, item in enumerate(cols):
350
+ word_info = row[col_idx]
351
+ if "word" not in word_info or word_info["word"] == "":
352
+ continue
353
+ word = word_info["word"]
354
+ category = word_info["category"]
355
+ display = word_info.get("display", word)
356
+ key = f"key_{row_idx}_{col_idx}_{category}_{word}"
357
+ def make_callback(w=word, d=display):
358
+ def cb():
359
+ if w.startswith("custom_") or w.startswith("letter_"):
360
+ add_to_output(d)
361
+ else:
362
+ add_to_output(w)
363
+ return cb
364
+ with cols[col_idx]:
365
+ st.button(
366
+ display if word != "SPACE" else "␣",
367
+ key=key,
368
+ on_click=make_callback()
369
+ )
370
+
371
+ # --- Control Buttons ---
372
+ col1, col2, col3, col4 = st.columns(4)
373
+ with col1:
374
+ if st.button("CLEAR", key="clear_btn", help="Clear all text", use_container_width=True, type="primary"):
375
+ st.session_state.text_output = ""
376
+ st.rerun() # Rerun to reflect change
377
+ with col2:
378
+ if st.button("SPEAK", key="speak_btn", help="Speak the current text", use_container_width=True, type="primary"):
379
+ if st.session_state.text_output:
380
+ st.toast(f"Speaking: {st.session_state.text_output}", icon="🔊")
381
+ if st.button("⌫ DEL", key="backspace", help="Delete last character", use_container_width=True):
382
+ if st.session_state.text_output:
383
+ st.session_state.text_output = st.session_state.text_output[:-1]
384
+ with col4:
385
+ if st.button("⌫ WORD", key="backspace_word", help="Delete last word", use_container_width=True):
386
+ if st.session_state.text_output:
387
+ words = st.session_state.text_output.rstrip().split()
388
+ if words:
389
+ words.pop()
390
+ st.session_state.text_output = " ".join(words)
391
+ if words:
392
+ st.session_state.text_output += " "
393
+ with col3: # Use the 3rd column for SEND
394
+ if st.button("SEND", key="send_btn", help="Send message to assistant", use_container_width=True, type="primary"):
395
+ if st.session_state.text_output:
396
+ send_action = True # Set flag to process sending
397
+
398
+
399
+ # --- Settings Sidebar ---
400
+ with st.sidebar:
401
+ # --- Settings Section Commented Out ---
402
+ # st.title("Settings")
403
+ # st.subheader("Interface")
404
+ # theme_options = list(theme_colors.keys())
405
+ # theme_index = theme_options.index(st.session_state.theme)
406
+ # new_theme = st.selectbox("Theme", theme_options, index=theme_index)
407
+ # new_text_size = st.slider("Text Size", 12, 36, st.session_state.text_size)
408
+ # if st.button("Apply Settings Changes", type="primary"):
409
+ # changed = False
410
+ # if new_theme != st.session_state.theme:
411
+ # st.session_state.theme = new_theme
412
+ # changed = True
413
+ # if new_text_size != st.session_state.text_size:
414
+ # st.session_state.text_size = new_text_size
415
+ # changed = True
416
+ # if changed:
417
+ # st.rerun()
418
+ # with st.expander("Speech Settings"):
419
+ # speech_rate = st.slider("Rate", 0.5, 2.0, st.session_state.speech_rate, 0.1)
420
+ # voice_options = ["Default Voice", "Female Voice", "Male Voice", "Child Voice"]
421
+ # voice_index = voice_options.index(st.session_state.voice_option)
422
+ # voice = st.selectbox("Voice", voice_options, index=voice_index)
423
+ # if st.button("Apply Speech Settings"):
424
+ # st.session_state.speech_rate = speech_rate
425
+ # st.session_state.voice_option = voice
426
+ # st.subheader("Custom Words")
427
+ # with st.expander("Add New Word"):
428
+ # word = st.text_input("Word")
429
+ # cat = st.selectbox("Category", list(colors.keys()))
430
+ # col1, col2 = st.columns(2)
431
+ # with col1:
432
+ # if st.button("Add", key="add_word"):
433
+ # if word and cat:
434
+ # st.session_state.custom_words.append({"word": word, "category": cat})
435
+ # st.success(f"Added '{word}'")
436
+ # st.rerun()
437
+ # if st.session_state.custom_words:
438
+ # st.write("Current custom words:")
439
+ # words_df = pd.DataFrame([
440
+ # {"Word": w["word"], "Category": w["category"]}
441
+ # for w in st.session_state.custom_words
442
+ # ])
443
+ # st.dataframe(words_df, hide_index=True)
444
+ # with col2:
445
+ # if st.button("Clear All", key="clear_words"):
446
+ # st.session_state.custom_words = []
447
+ # st.success("Words cleared")
448
+ # st.rerun()
449
+ # --- End of Settings Section Commented Out ---
450
+
451
+ st.divider()
452
+ # --- Chat History Display in Sidebar ---
453
+ st.subheader("Conversation")
454
+ chat_container = st.container(height=400) # Fixed height container
455
+ with chat_container:
456
+ for message in st.session_state.messages:
457
+ with st.chat_message(message["role"]):
458
+ st.markdown(message["content"])
459
+
460
+ # --- Process SEND Action ---
461
+ if send_action and st.session_state.assistant:
462
+ user_message = st.session_state.text_output
463
+ st.session_state.messages.append({"role": "user", "content": user_message})
464
+
465
+ # Process with AACAssistant
466
+ try:
467
+ # Get the response from the AACAssistant
468
+ response = st.session_state.assistant.process_query(user_message)
469
+ # Add assistant response to chat history
470
+ st.session_state.messages.append({"role": "assistant", "content": response})
471
+ except Exception as e:
472
+ error_message = f"An error occurred: {e}"
473
+ st.error(error_message) # Show error in main area
474
+ st.session_state.messages.append({"role": "assistant", "content": f"*Error processing: {error_message}*"})
475
+
476
+ # Clear the board's text area after sending
477
+ st.session_state.text_output = ""
478
+ st.rerun() # Rerun to update chat and clear board
479
 
480
+ if __name__ == "__main__":
481
+ main()