danf commited on
Commit
d65aec2
·
unverified ·
1 Parent(s): a4bdb9d

Bilingual UI

Browse files

Model answers in English when the UI is in English, and vice versa.

Files changed (1) hide show
  1. src/streamlit_app.py +166 -149
src/streamlit_app.py CHANGED
@@ -35,105 +35,7 @@ def convert_latex_brackets_to_dollars(text):
35
  return text
36
 
37
 
38
- # Add RTL CSS styling for Hebrew support
39
- st.markdown(
40
- """
41
- <style>
42
- /* RTL support for specific text elements - avoid global .stMarkdown RTL */
43
- .stText, .stTextArea textarea, .stTextArea label, .stSelectbox select, .stSelectbox label, .stSelectbox div {
44
- direction: rtl;
45
- text-align: right;
46
- }
47
-
48
- /* Chat messages styling for RTL */
49
- .stChatMessage {
50
- direction: rtl;
51
- text-align: right;
52
- }
53
-
54
- /* Title alignment - more specific selectors */
55
- h1, .stTitle, [data-testid="stHeader"] h1 {
56
- direction: rtl !important;
57
- text-align: right !important;
58
- }
59
-
60
- /* Apply RTL only to text content, not math */
61
- .stMarkdown p:not(:has(.MathJax)):not(:has(mjx-container)):not(:has(.katex)) {
62
- direction: rtl;
63
- text-align: right;
64
- unicode-bidi: plaintext;
65
- }
66
-
67
- /* Code blocks should remain LTR */
68
- .stMarkdown code, .stMarkdown pre {
69
- direction: ltr !important;
70
- text-align: left !important;
71
- display: inline-block;
72
- }
73
-
74
- /* Details/summary styling for RTL */
75
- details {
76
- direction: rtl;
77
- text-align: right;
78
- }
79
-
80
- /* Button alignment */
81
- .stButton button {
82
- direction: rtl;
83
- }
84
-
85
- /* Ensure LaTeX/Math rendering works normally - comprehensive selectors */
86
- .MathJax,
87
- .MathJax_Display,
88
- mjx-container,
89
- .katex,
90
- .katex-display,
91
- [data-testid="stMarkdownContainer"] .MathJax,
92
- [data-testid="stMarkdownContainer"] .MathJax_Display,
93
- [data-testid="stMarkdownContainer"] mjx-container,
94
- [data-testid="stMarkdownContainer"] .katex,
95
- [data-testid="stMarkdownContainer"] .katex-display,
96
- .stMarkdown .MathJax,
97
- .stMarkdown .MathJax_Display,
98
- .stMarkdown mjx-container,
99
- .stMarkdown .katex,
100
- .stMarkdown .katex-display {
101
- direction: ltr !important;
102
- text-align: center !important;
103
- unicode-bidi: normal !important;
104
- }
105
-
106
- /* Inline math should be LTR but inline */
107
- mjx-container[display="false"],
108
- .katex:not(.katex-display),
109
- .MathJax:not(.MathJax_Display) {
110
- direction: ltr !important;
111
- text-align: left !important;
112
- display: inline !important;
113
- unicode-bidi: normal !important;
114
- }
115
-
116
- /* Block/display math should be centered */
117
- mjx-container[display="true"],
118
- .katex-display,
119
- .MathJax_Display {
120
- direction: ltr !important;
121
- text-align: center !important;
122
- display: block !important;
123
- margin: 1em auto !important;
124
- unicode-bidi: normal !important;
125
- }
126
-
127
- /* For custom RTL wrappers */
128
- .rtl-text {
129
- direction: rtl;
130
- text-align: right;
131
- unicode-bidi: plaintext;
132
- }
133
- </style>
134
- """,
135
- unsafe_allow_html=True,
136
- )
137
 
138
 
139
  @st.cache_resource
@@ -155,10 +57,15 @@ def get_client():
155
 
156
  client = get_client()
157
 
158
- st.title("מתמטיבוט 🧮")
159
-
160
- st.markdown("""
161
 
 
 
 
 
 
162
  ברוכים הבאים לדמו! 💡 כאן תוכלו להתרשם **ממודל השפה החדש** שלנו; מודל בגודל 4 מיליארד פרמטרים שאומן לענות על שאלות מתמטיות בעברית, על המחשב שלכם, ללא חיבור לרשת.
163
 
164
  קישור למודל, פרטים נוספים, יצירת קשר ותנאי שימוש:
@@ -166,39 +73,156 @@ st.markdown("""
166
  https://huggingface.co/Intel/hebrew-math-tutor-v1
167
 
168
  -----
169
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
  if "chat_history" not in st.session_state:
172
  st.session_state.chat_history = []
173
 
174
  # Predefined options
175
- predefined_options = [
176
- "שאלה חדשה...",
177
- " מהו סכום הסדרה הבאה: 1 + 1/2 + 1/4 + 1/8 + ...",
178
- "פתח את הביטוי: (a-b)^4",
179
- "פתרו את המשוואה הבאה: sin(2x) = 0.5",
180
- ]
181
 
182
  # Dropdown for predefined options
183
- selected_option = st.selectbox("בחרו שאלה מוכנה או צרו שאלה חדשה:", predefined_options)
184
 
185
  # Text area for input
186
- if selected_option == "שאלה חדשה...":
187
  user_input = st.text_area(
188
- "שאלה:", height=100, key="user_input", placeholder="הזינו את השאלה כאן..."
189
  )
190
  else:
191
- user_input = st.text_area("שאלה:", height=100, key="user_input", value=selected_option)
192
-
193
- # Add reset button next to Send button
194
- col1, col2 = st.columns([8, 4])
195
- with col2:
196
- send_clicked = st.button("שלח", type="primary", use_container_width=True) and user_input.strip()
197
- with col1:
198
- if st.button("שיחה חדשה", type="secondary", use_container_width=True):
199
  st.session_state.chat_history = []
200
  st.rerun()
201
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  if send_clicked:
203
  st.session_state.chat_history.append(("user", user_input))
204
 
@@ -207,11 +231,17 @@ if send_clicked:
207
  message_placeholder = st.empty()
208
  full_response = ""
209
 
210
- # System prompt - not visible in UI but guides the model
211
- system_prompt = """\
 
212
  You are a helpful AI assistant specialized in mathematics and problem-solving who can answer math questions with the correct answer.
213
  Answer shortly, not more than 500 tokens, but outline the process step by step.
214
  Answer ONLY in Hebrew!
 
 
 
 
 
215
  """
216
 
217
  # Create messages in proper chat format
@@ -249,8 +279,8 @@ Answer ONLY in Hebrew!
249
  # compatible with OpenAI response structure
250
  delta = chunk.choices[0].delta.content
251
  except Exception:
252
- # fallback for older/other shapes
253
- delta = chunk.get("text", "HI ")
254
 
255
  if not delta:
256
  continue
@@ -268,22 +298,17 @@ Answer ONLY in Hebrew!
268
  thinking_text = (
269
  thinking_content.replace("<think>", "").replace("</think>", "").strip()
270
  )
271
- display_content = f"""
272
- <details dir="rtl" style="text-align: right;">
273
- <summary>🤔 <em>לחץ כדי לראות את תהליך החשיבה</em></summary>
274
- <div style="white-space: pre-wrap; margin: 10px 0; direction: rtl; text-align: right;">
275
- {thinking_text}
276
- </div>
277
- </details>
278
-
279
- """
280
  message_placeholder.markdown(display_content + "▌", unsafe_allow_html=True)
281
  else:
282
  dots = "." * ((len(thinking_content) // 10) % 6)
 
283
  thinking_indicator = f"""
284
- <div dir="rtl" style="padding: 10px; background-color: #f0f2f6; border-radius: 10px; border-right: 4px solid #1f77b4; text-align: right;">
285
  <p style="margin: 0; color: #1f77b4; font-style: italic;">
286
- 🤔 חושב{dots}
287
  </p>
288
  </div>
289
  """
@@ -293,9 +318,7 @@ Answer ONLY in Hebrew!
293
  final_answer += delta
294
  converted_answer = convert_latex_brackets_to_dollars(final_answer)
295
  message_placeholder.markdown(
296
- "🤔 *תהליך החשיבה הושלם, מכין תשובה...*\n\n**📝 תשובה סופית:**\n\n"
297
- + converted_answer
298
- + "▌",
299
  unsafe_allow_html=True,
300
  )
301
  except Exception as e:
@@ -307,18 +330,12 @@ Answer ONLY in Hebrew!
307
  thinking_text = thinking_content.replace("<think>", "").replace("</think>", "").strip()
308
  message_placeholder.empty()
309
  with message_placeholder.container():
310
- thinking_html = f"""
311
- <details dir="rtl" style="text-align: right;">
312
- <summary>🤔 <em>לחץ כדי לראות את תהליך החשיבה</em></summary>
313
- <div style="white-space: pre-wrap; margin: 10px 0; direction: rtl; text-align: right;">
314
- {thinking_text}
315
- </div>
316
- </details>
317
-
318
- """
319
  st.markdown(thinking_html, unsafe_allow_html=True)
320
  st.markdown(
321
- '<div dir="rtl" style="text-align: right; margin: 10px 0;"><strong>📝 תשובה סופית:</strong></div>',
322
  unsafe_allow_html=True,
323
  )
324
  converted_answer = convert_latex_brackets_to_dollars(final_answer or full_response)
@@ -327,4 +344,4 @@ Answer ONLY in Hebrew!
327
  converted_response = convert_latex_brackets_to_dollars(final_answer or full_response)
328
  message_placeholder.markdown(converted_response, unsafe_allow_html=True)
329
 
330
- st.session_state.chat_history.append(("assistant", final_answer or full_response))
 
35
  return text
36
 
37
 
38
+ # (CSS injection moved below and applied conditionally based on st.session_state.lang)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
 
41
  @st.cache_resource
 
57
 
58
  client = get_client()
59
 
60
+ # Language toggle state: 'he' (Hebrew) or 'en' (English)
61
+ if "lang" not in st.session_state:
62
+ st.session_state.lang = "he"
63
 
64
+ # Localized UI strings
65
+ labels = {
66
+ "he": {
67
+ "title": "מתמטיבוט 🧮",
68
+ "intro": """
69
  ברוכים הבאים לדמו! 💡 כאן תוכלו להתרשם **ממודל השפה החדש** שלנו; מודל בגודל 4 מיליארד פרמטרים שאומן לענות על שאלות מתמטיות בעברית, על המחשב שלכם, ללא חיבור לרשת.
70
 
71
  קישור למודל, פרטים נוספים, יצירת קשר ותנאי שימוש:
 
73
  https://huggingface.co/Intel/hebrew-math-tutor-v1
74
 
75
  -----
76
+ """,
77
+ "select_label": "בחרו שאלה מוכנה או צרו שאלה חדשה:",
78
+ "new_question": "שאלה חדשה...",
79
+ "text_label": "שאלה:",
80
+ "placeholder": "הזינו את השאלה כאן...",
81
+ "send": "שלח",
82
+ "reset": "שיחה חדשה",
83
+ "toggle_to": "English 🇬🇧",
84
+ "predefined": [
85
+ "שאלה חדשה...",
86
+ " מהו סכום הסדרה הבאה: 1 + 1/2 + 1/4 + 1/8 + ...",
87
+ "פתח את הביטוי: (a-b)^4",
88
+ "פתרו את המשוואה הבאה: sin(2x) = 0.5",
89
+ ],
90
+ },
91
+ "en": {
92
+ "title": "MathBot 🧮",
93
+ "intro": """
94
+ Welcome to the demo! 💡 Here you can try our **new language model** — a 4-billion-parameter model trained to answer math questions in Hebrew while maintaining its English capabilities. It runs locally on your machine without requiring an internet connection.
95
+
96
+ For the model page and more details see:
97
+
98
+ https://huggingface.co/Intel/hebrew-math-tutor-v1
99
+
100
+ -----
101
+ """,
102
+ "select_label": "Choose a prepared question or create a new one:",
103
+ "new_question": "New question...",
104
+ "text_label": "Question:",
105
+ "placeholder": "Type your question here...",
106
+ "send": "Send",
107
+ "reset": "New Conversation",
108
+ "toggle_to": "עברית 🇮🇱",
109
+ "predefined": [
110
+ "New question...",
111
+ "What is the sum of the series: 1 + 1/2 + 1/4 + 1/8 + ...",
112
+ "Expand the expression: (a-b)^4",
113
+ "Solve the equation: sin(2x) = 0.5",
114
+ ],
115
+ },
116
+ }
117
+
118
+ L = labels[st.session_state.lang]
119
+
120
+ # Inject language-specific CSS so alignment follows the current UI language
121
+ if st.session_state.lang == "he":
122
+ st.markdown(
123
+ """
124
+ <style>
125
+ /* RTL: apply to Streamlit content */
126
+ .stText, .stTextArea textarea, .stTextArea label, .stSelectbox select, .stSelectbox label, .stSelectbox div,
127
+ select, option, [data-testid="stSelectbox"] select, [data-testid="stSelectbox"] option, .stSelectbox > div, .stSelectbox > div > div,
128
+ .stSelectbox [role="listbox"], .stSelectbox [role="option"], div[role="listbox"], div[role="option"] {
129
+ direction: rtl !important;
130
+ text-align: right !important;
131
+ }
132
+ .stChatMessage { direction: rtl !important; text-align: right !important; }
133
+ h1, .stTitle, [data-testid="stHeader"] h1 { direction: rtl !important; text-align: right !important; }
134
+ .stMarkdown p:not(:has(.MathJax)):not(:has(mjx-container)):not(:has(.katex)) { direction: rtl !important; text-align: right !important; }
135
+ .stMarkdown code, .stMarkdown pre { direction: ltr !important; text-align: left !important; }
136
+ .stButton button { direction: rtl !important; }
137
+ </style>
138
+ """,
139
+ unsafe_allow_html=True,
140
+ )
141
+ else:
142
+ # Ensure default LTR for English mode (override any residual RTL rules)
143
+ st.markdown(
144
+ """
145
+ <style>
146
+ .stText, .stTextArea textarea, .stTextArea label, .stSelectbox select, .stSelectbox label, .stSelectbox div,
147
+ select, option, [data-testid="stSelectbox"] select, [data-testid="stSelectbox"] option, .stSelectbox > div, .stSelectbox > div > div,
148
+ .stSelectbox [role="listbox"], .stSelectbox [role="option"], div[role="listbox"], div[role="option"] {
149
+ direction: ltr !important;
150
+ text-align: left !important;
151
+ }
152
+ .stChatMessage { direction: ltr !important; text-align: left !important; }
153
+ h1, .stTitle, [data-testid="stHeader"] h1 { direction: ltr !important; text-align: left !important; }
154
+ .stMarkdown code, .stMarkdown pre { direction: ltr !important; text-align: left !important; }
155
+ .stButton button { direction: ltr !important; }
156
+ </style>
157
+ """,
158
+ unsafe_allow_html=True,
159
+ )
160
+
161
+ # Localized strings/templates for thinking/details and final answer
162
+ if st.session_state.lang == "he":
163
+ _dir = "rtl"
164
+ _align = "right"
165
+ _summary_text = "לחץ כדי לראות את תהליך החשיבה"
166
+ _thinking_prefix = "🤔 חושב"
167
+ _thinking_done = "🤔 *תהליך החשיבה הושלם, מכין תשובה...*"
168
+ _final_label = "📝 תשובה סופית:"
169
+ else:
170
+ _dir = "ltr"
171
+ _align = "left"
172
+ _summary_text = "Click to view the thinking process"
173
+ _thinking_prefix = "🤔 Thinking"
174
+ _thinking_done = "🤔 *Thinking complete, preparing answer...*"
175
+ _final_label = "📝 Final answer:"
176
+
177
+ # Helper HTML template for the collapsible thinking/details block
178
+ _details_template = (
179
+ '<details dir="{dir}" style="text-align: {align};">'
180
+ "<summary>🤔 <em>{summary}</em></summary>"
181
+ '<div style="white-space: pre-wrap; margin: 10px 0; direction: {dir}; text-align: {align};">{content}</div>'
182
+ "</details>"
183
+ )
184
+
185
+ st.title(L["title"])
186
+
187
+ st.markdown(L["intro"])
188
 
189
  if "chat_history" not in st.session_state:
190
  st.session_state.chat_history = []
191
 
192
  # Predefined options
193
+ predefined_options = L["predefined"]
 
 
 
 
 
194
 
195
  # Dropdown for predefined options
196
+ selected_option = st.selectbox(L["select_label"], predefined_options)
197
 
198
  # Text area for input
199
+ if selected_option == L["new_question"]:
200
  user_input = st.text_area(
201
+ L["text_label"], height=100, key="user_input", placeholder=L["placeholder"]
202
  )
203
  else:
204
+ user_input = st.text_area(L["text_label"], height=100, key="user_input", value=selected_option)
205
+
206
+ # Buttons layout: Reset | Language Toggle | Send
207
+ col_left, col_mid, col_right = st.columns([4, 2, 4])
208
+
209
+ with col_left:
210
+ if st.button(L["reset"], type="secondary", use_container_width=True):
 
211
  st.session_state.chat_history = []
212
  st.rerun()
213
 
214
+ with col_mid:
215
+ # Button shows the language to switch TO (e.g. 'English' when current is Hebrew)
216
+ if st.button(L["toggle_to"], use_container_width=True):
217
+ st.session_state.lang = "en" if st.session_state.lang == "he" else "he"
218
+ st.rerun()
219
+
220
+ with col_right:
221
+ # Guard against None from text_area and ensure non-empty trimmed input
222
+ send_clicked = st.button(L["send"], type="primary", use_container_width=True) and (
223
+ user_input and user_input.strip()
224
+ )
225
+
226
  if send_clicked:
227
  st.session_state.chat_history.append(("user", user_input))
228
 
 
231
  message_placeholder = st.empty()
232
  full_response = ""
233
 
234
+ # System prompt - adapt to UI language; do not force Hebrew when UI is English
235
+ if st.session_state.lang == "he":
236
+ system_prompt = """\
237
  You are a helpful AI assistant specialized in mathematics and problem-solving who can answer math questions with the correct answer.
238
  Answer shortly, not more than 500 tokens, but outline the process step by step.
239
  Answer ONLY in Hebrew!
240
+ """
241
+ else:
242
+ system_prompt = """\
243
+ You are a helpful AI assistant specialized in mathematics and problem-solving who can answer math questions with the correct answer.
244
+ Answer shortly, not more than 500 tokens, but outline the process step by step.
245
  """
246
 
247
  # Create messages in proper chat format
 
279
  # compatible with OpenAI response structure
280
  delta = chunk.choices[0].delta.content
281
  except Exception:
282
+ # fallback for older/other shapes; use getattr to avoid dict-specific calls
283
+ delta = getattr(chunk, "text", None) or "HI "
284
 
285
  if not delta:
286
  continue
 
298
  thinking_text = (
299
  thinking_content.replace("<think>", "").replace("</think>", "").strip()
300
  )
301
+ display_content = _details_template.format(
302
+ dir=_dir, align=_align, summary=_summary_text, content=thinking_text
303
+ )
 
 
 
 
 
 
304
  message_placeholder.markdown(display_content + "▌", unsafe_allow_html=True)
305
  else:
306
  dots = "." * ((len(thinking_content) // 10) % 6)
307
+ # thinking indicator
308
  thinking_indicator = f"""
309
+ <div dir="{_dir}" style="padding: 10px; background-color: #f0f2f6; border-radius: 10px; border-right: 4px solid #1f77b4; text-align: {_align};">
310
  <p style="margin: 0; color: #1f77b4; font-style: italic;">
311
+ {_thinking_prefix}{dots}
312
  </p>
313
  </div>
314
  """
 
318
  final_answer += delta
319
  converted_answer = convert_latex_brackets_to_dollars(final_answer)
320
  message_placeholder.markdown(
321
+ f"{_thinking_done}\n\n**{_final_label}**\n\n" + converted_answer + "▌",
 
 
322
  unsafe_allow_html=True,
323
  )
324
  except Exception as e:
 
330
  thinking_text = thinking_content.replace("<think>", "").replace("</think>", "").strip()
331
  message_placeholder.empty()
332
  with message_placeholder.container():
333
+ thinking_html = _details_template.format(
334
+ dir=_dir, align=_align, summary=_summary_text, content=thinking_text
335
+ )
 
 
 
 
 
 
336
  st.markdown(thinking_html, unsafe_allow_html=True)
337
  st.markdown(
338
+ f'<div dir="{_dir}" style="text-align: {_align}; margin: 10px 0;"><strong>{_final_label}</strong></div>',
339
  unsafe_allow_html=True,
340
  )
341
  converted_answer = convert_latex_brackets_to_dollars(final_answer or full_response)
 
344
  converted_response = convert_latex_brackets_to_dollars(final_answer or full_response)
345
  message_placeholder.markdown(converted_response, unsafe_allow_html=True)
346
 
347
+ st.session_state.chat_history.append(("assistant", final_answer or full_response))