Ezzio11 commited on
Commit
e17743f
·
verified ·
1 Parent(s): ac6b931

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +193 -254
src/streamlit_app.py CHANGED
@@ -7,12 +7,6 @@ import numpy as np
7
  from urllib.parse import quote
8
  from PIL import Image
9
  from io import BytesIO
10
- import uuid
11
- import os
12
-
13
- # Environment configuration
14
- os.environ["STREAMLIT_SERVER_ENABLE_STATIC"] = "false"
15
- os.environ["STREAMLIT_SERVER_ENABLE_WEBSOCKET_COMPRESSION"] = "false"
16
 
17
  # Supabase and OpenRouter configurations
18
  OR_API_KEY = "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"
@@ -24,23 +18,24 @@ DB_API_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZ
24
  supabase = create_client(PROJECT_URL, DB_API_KEY)
25
 
26
  portfolio_faq = {
27
- "who are you": "I'm X.A.N.E. — Ezz Eldin Ahmed's assistant. He's a statistics major passionate about data science, automation, and machine learning. He built me using Python, Streamlit, and Django.",
28
- "what do you do": "I support users by answering questions about Ezz Eldin's work, skills, and projects. Think of me as a smart, interactive portfolio guide.",
29
  "skills": "Ezz is skilled in Python, R, SQL, statistical modeling, automation, and full-stack development with Streamlit and Django. He also works with Supabase, Excel, and Figma.",
30
- "projects": "His projects include a regression tool, time series forecaster, OCR scanner, AI chatbot (that's me), and a custom platform replacing many third-party tools.",
31
  "tools": "He primarily uses Python, Streamlit, Django, and Supabase. He also works with Excel, R, and Figma — and is currently exploring Power BI.",
32
  "education": "Ezz studies Statistics and Economics at the Faculty of Economics and Political Science — blending theory, data, and real-world application.",
33
- "experience": "He's coordinated a data science scholarship with EMAM, co-founded a research center, and led student-driven tools for learning and analytics.",
34
  "favorite project": "His favorite project is this portfolio — a central hub for his tools, chatbot, and regression models, all seamlessly embedded into one platform.",
35
- "what does xane stand for": "X.A.N.E. stands for: eXtended Artificial Neural Entity. I'm more than code — I'm a part of his creative process.",
36
- "how can i reach him": "You can reach Ezz via LinkedIn or the contact form on this website. He's always open to opportunities and collaboration.",
37
  "can i see the source code": "Some projects are public on his GitHub, while others are private or under development. You can ask about a specific project.",
38
- "is this chatbot ai-powered": "Yes, partially. I'm built on a rule-based system with optional LLM integration for advanced answers and search tasks.",
39
- "what's special about this site": "Unlike typical portfolios, this site is dynamic — combining tools, models, and a living assistant into one seamless interface.",
40
  "why streamlit": "Because it allows rapid, elegant development of interactive apps — perfect for building tools quickly without compromising UX.",
41
- "what's next": "More ML engineering projects, improved explainability using SHAP/SHAPASH, and diving deeper into NLP and generative AI."
42
  }
43
 
 
44
  def chatbot(prompt):
45
  headers = {"Authorization": f"Bearer {OR_API_KEY}", "Content-Type": "application/json"}
46
  payload = {"model": MODEL, "messages": prompt}
@@ -67,35 +62,32 @@ def fallback_pollinations(message):
67
  return f"Text fallback failed: {e}"
68
 
69
  def save_memory(chat_name, role, content):
 
70
  data = {
71
  "chat_name": chat_name,
72
  "role": role,
73
- "content": content,
74
- "session_id": st.session_state.xane_id
75
  }
76
  supabase.table("chats").insert(data).execute()
77
 
78
  def load_memory(chat_name):
79
- response = supabase.table("chats").select("*").match({
80
- "chat_name": chat_name,
81
- "session_id": st.session_state.xane_id
82
- }).execute()
83
  return response.data
84
 
85
  def load_all_memory():
86
- response = supabase.table("chats").select("chat_name").eq(
87
- "session_id", st.session_state.xane_id
88
- ).execute()
89
  chat_names = {row['chat_name'] for row in response.data}
90
- return {name: load_memory(name) for name in chat_names} or {"Default": []}
 
91
 
92
  def delete_chat(chat_name):
93
- supabase.table("chats").delete().match({
94
- "chat_name": chat_name,
95
- "session_id": st.session_state.xane_id
96
- }).execute()
97
 
98
  def gradual_display(text, placeholder):
 
99
  displayed_text = ""
100
  for char in text:
101
  displayed_text += char
@@ -109,243 +101,190 @@ def extract_pdf_text(uploaded_file):
109
  text += page.get_text()
110
  return text
111
 
112
- def convert_image_to_bytes(img):
113
- img_byte_arr = BytesIO()
114
- img.save(img_byte_arr, format='JPEG')
115
- return img_byte_arr.getvalue()
116
-
117
- welcome_messages = [
118
- "Greetings, warrior. Ready to unlock some secrets?",
119
- "Hello, I am XANE, your digital ninja assistant.",
120
- "Hey, apprentice. Ready to master the art of knowledge?",
121
- "XANE here. How can I assist you on your quest?",
122
- "Welcome, ninja. Let's crack the code together.",
123
- "Hi there, ready to unleash your inner ninja?",
124
- "Step into the dojo. Ask anything, learn everything.",
125
- "XANE at your service. What's your mission today?",
126
- "Greetings, young ninja. The path to insight awaits.",
127
- "Hey! Time to sharpen your skills and knowledge.",
128
- "Hello, ninja-in-training! How can I guide you?",
129
- "Welcome back, warrior. Let's conquer your questions.",
130
- "Greetings from the digital dojo. What's next on your path?",
131
- "Hey, warrior! Let's hack through your toughest problems.",
132
- "Welcome, ninja master in the making. What's your next move?"
133
- ]
134
-
135
- def render_chatbot_page():
136
- if "chatbot_initialized" not in st.session_state:
137
- st.session_state.chatbot_initialized = True
138
- st.session_state.chat_sessions = load_all_memory()
139
- st.session_state.current_chat = "Default"
140
- st.session_state.messages = st.session_state.chat_sessions.get(st.session_state.current_chat, [])
141
- st.session_state.show_greeting = True
142
  st.rerun()
143
 
144
- with st.sidebar:
145
- st.title("💬 Chats")
146
- chat_options = list(st.session_state.chat_sessions.keys()) + ["➕ New Chat"]
147
- selected_chat = st.selectbox("Choose a chat:", chat_options, key="chat_select")
148
-
149
- if selected_chat == " New Chat":
150
- new_chat_name = st.text_input("Enter chat name:", key="new_chat_input")
151
- if st.button("Create", key="create_chat_btn"):
152
- if new_chat_name and new_chat_name not in st.session_state.chat_sessions:
153
- st.session_state.chat_sessions[new_chat_name] = []
154
- st.session_state.current_chat = new_chat_name
155
- st.session_state.messages = []
156
- st.rerun()
157
- elif selected_chat != st.session_state.current_chat:
158
- st.session_state.current_chat = selected_chat
159
- st.session_state.messages = st.session_state.chat_sessions.get(selected_chat, [])
160
- st.rerun()
161
-
162
- if st.button("🧹 Clear Current Chat", key="clear_chat_btn"):
163
- delete_chat(st.session_state.current_chat)
164
- st.session_state.chat_sessions[st.session_state.current_chat] = []
165
- st.session_state.messages = []
166
- st.rerun()
167
-
168
- if st.session_state.show_greeting:
169
- greeting = np.random.choice(welcome_messages)
170
- st.header(greeting)
171
- st.session_state.show_greeting = False
172
 
173
  for msg in st.session_state.messages:
174
  with st.chat_message(msg['role']):
175
- st.markdown(msg['content'])
176
-
177
- chat_input = st.chat_input(
178
- "Ask me anything or upload files",
179
- key="chat_input",
180
- accept_file="multiple"
181
- )
182
-
183
- if chat_input:
184
- if chat_input.text and chat_input.text.strip():
185
- with st.chat_message("user"):
186
- st.markdown(chat_input.text)
187
-
188
- save_memory(st.session_state.current_chat, "user", chat_input.text)
189
- st.session_state.messages.append({"role": "user", "content": chat_input.text})
190
-
191
- if chat_input.files:
192
- with st.chat_message("user"):
193
- for uploaded_file in chat_input.files:
194
- if uploaded_file.type.startswith('image/'):
195
- st.image(uploaded_file)
196
- file_content = f"![Uploaded Image]({uploaded_file.name})"
197
- elif uploaded_file.type == "application/pdf":
198
- try:
199
- text = extract_pdf_text(uploaded_file)
200
- file_content = text
201
- st.warning(f"PDF file uploaded: {uploaded_file.name}")
202
- except Exception as e:
203
- st.error(f"Failed to extract PDF text: {e}")
204
- file_content = f"[PDF file: {uploaded_file.name}]"
205
- elif uploaded_file.type == "text/plain":
206
- try:
207
- text = uploaded_file.read().decode("utf-8")
208
- file_content = text
209
- st.warning(f"Text file uploaded: {uploaded_file.name}")
210
- except Exception as e:
211
- st.error(f"Failed to read text file: {e}")
212
- file_content = f"[Text file: {uploaded_file.name}]"
213
- else:
214
- st.warning(f"Unsupported file type: {uploaded_file.type}")
215
- continue
216
-
217
- save_memory(st.session_state.current_chat, "user", file_content)
218
- st.session_state.messages.append({"role": "user", "content": file_content})
219
-
220
- response = ""
221
- if chat_input.text and isinstance(chat_input.text, str) and chat_input.text.strip():
222
- for question, answer in portfolio_faq.items():
223
- if question.lower() in chat_input.text.lower():
224
- response = answer
225
- break
226
-
227
- if not response:
228
- with st.spinner("XANE is thinking... 🤖"):
229
- response = chatbot(st.session_state.messages)
230
-
231
- st.session_state.messages.append({"role": "assistant", "content": response})
232
- save_memory(st.session_state.current_chat, "assistant", response)
233
-
234
- with st.chat_message("assistant"):
235
- placeholder = st.empty()
236
- gradual_display(response, placeholder)
237
-
238
- st.session_state.chat_sessions[st.session_state.current_chat] = st.session_state.messages
239
-
240
- def render_image_generator_page():
241
- if "image_gen_initialized" not in st.session_state:
242
- st.session_state.image_gen_initialized = True
243
- st.session_state.image_params = {
244
- "prompt": "Mass Effect Citadel scene at dusk",
245
- "model": "flux",
246
- "width": 1024,
247
- "height": 1024,
248
- "seed": 42
249
- }
250
- st.rerun()
251
-
252
- with st.sidebar:
253
- st.title("🎨 Image Settings")
254
- st.session_state.image_params["prompt"] = st.text_area(
255
- "🖌️ Prompt",
256
- st.session_state.image_params["prompt"],
257
- height=100,
258
- key="img_prompt"
259
- )
260
- st.session_state.image_params["model"] = st.selectbox(
261
- "⚙️ Model",
262
- ["flux", "flux-pro", "flux-cablyai", "turbo"],
263
- index=["flux", "flux-pro", "flux-cablyai", "turbo"].index(st.session_state.image_params["model"]),
264
- key="img_model"
265
- )
266
- st.session_state.image_params["width"] = st.slider(
267
- "Width", 512, 1536, st.session_state.image_params["width"], 128,
268
- key="img_width"
269
- )
270
- st.session_state.image_params["height"] = st.slider(
271
- "Height", 512, 1536, st.session_state.image_params["height"], 128,
272
- key="img_height"
273
- )
274
- st.session_state.image_params["seed"] = st.number_input(
275
- "Seed (optional)",
276
- value=st.session_state.image_params["seed"],
277
- key="img_seed"
278
  )
279
 
280
- if st.button("✨ Generate Image", key="gen_img_btn"):
281
- generate_image()
282
-
283
- st.title("🎨 Pollinations – Free Multi-Model Generator")
284
-
285
- if "generated_image" in st.session_state:
286
- st.image(
287
- st.session_state.generated_image,
288
- caption=f"{st.session_state.image_params['model']} – {st.session_state.image_params['width']}×{st.session_state.image_params['height']}",
289
- use_container_width=True,
290
- key="display_img"
291
- )
292
- st.download_button(
293
- "📥 Download Image",
294
- data=convert_image_to_bytes(st.session_state.generated_image),
295
- file_name=f"pollinations_{st.session_state.image_params['model']}.jpg",
296
- mime="image/jpeg",
297
- key="dl_img_btn"
298
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
 
300
- def generate_image():
301
- params = st.session_state.image_params
302
- if not params["prompt"].strip():
303
- st.warning("Please enter a prompt")
304
- return
305
-
306
- prompt_enc = quote(params["prompt"].strip())
307
- url = (
308
- f"https://image.pollinations.ai/prompt/{prompt_enc}"
309
- f"?model={params['model']}"
310
- f"&width={params['width']}"
311
- f"&height={params['height']}"
312
- f"&seed={params['seed']}"
313
- )
314
-
315
- with st.spinner("Generating image..."):
316
- try:
317
- resp = requests.get(url, timeout=60)
318
- resp.raise_for_status()
319
- st.session_state.generated_image = Image.open(BytesIO(resp.content))
320
- st.rerun()
321
- except Exception as e:
322
- st.error(f"Image generation failed: {str(e)}")
323
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  def main():
325
  st.set_page_config("XANE - AI Assistant", layout="centered")
326
 
327
- if "xane_id" not in st.session_state:
328
- st.session_state.xane_id = str(uuid.uuid4())
329
-
330
- with st.sidebar:
331
- st.title("Navigation")
332
- page = st.radio("Go to", ["Chatbot", "Image Generator"], key="nav_radio")
333
-
334
- if "current_page" not in st.session_state:
335
- st.session_state.current_page = page
336
- elif st.session_state.current_page != page:
337
- keys_to_keep = ["xane_id", "current_page"]
338
- for key in list(st.session_state.keys()):
339
- if key not in keys_to_keep:
340
- del st.session_state[key]
341
- st.session_state.current_page = page
342
- st.rerun()
343
-
344
- with st.container():
345
- if page == "Chatbot":
346
- render_chatbot_page()
347
- elif page == "Image Generator":
348
- render_image_generator_page()
349
 
350
  if __name__ == '__main__':
351
  main()
 
7
  from urllib.parse import quote
8
  from PIL import Image
9
  from io import BytesIO
 
 
 
 
 
 
10
 
11
  # Supabase and OpenRouter configurations
12
  OR_API_KEY = "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"
 
18
  supabase = create_client(PROJECT_URL, DB_API_KEY)
19
 
20
  portfolio_faq = {
21
+ "who are you": "I'm X.A.N.E. — Ezz Eldin Ahmed's assistant. Hes a statistics major passionate about data science, automation, and machine learning. He built me using Python, Streamlit, and Django.",
22
+ "what do you do": "I support users by answering questions about Ezz Eldins work, skills, and projects. Think of me as a smart, interactive portfolio guide.",
23
  "skills": "Ezz is skilled in Python, R, SQL, statistical modeling, automation, and full-stack development with Streamlit and Django. He also works with Supabase, Excel, and Figma.",
24
+ "projects": "His projects include a regression tool, time series forecaster, OCR scanner, AI chatbot (thats me), and a custom platform replacing many third-party tools.",
25
  "tools": "He primarily uses Python, Streamlit, Django, and Supabase. He also works with Excel, R, and Figma — and is currently exploring Power BI.",
26
  "education": "Ezz studies Statistics and Economics at the Faculty of Economics and Political Science — blending theory, data, and real-world application.",
27
+ "experience": "Hes coordinated a data science scholarship with EMAM, co-founded a research center, and led student-driven tools for learning and analytics.",
28
  "favorite project": "His favorite project is this portfolio — a central hub for his tools, chatbot, and regression models, all seamlessly embedded into one platform.",
29
+ "what does xane stand for": "X.A.N.E. stands for: eXtended Artificial Neural Entity. Im more than code — Im a part of his creative process.",
30
+ "how can i reach him": "You can reach Ezz via LinkedIn or the contact form on this website. Hes always open to opportunities and collaboration.",
31
  "can i see the source code": "Some projects are public on his GitHub, while others are private or under development. You can ask about a specific project.",
32
+ "is this chatbot ai-powered": "Yes, partially. Im built on a rule-based system with optional LLM integration for advanced answers and search tasks.",
33
+ "whats special about this site": "Unlike typical portfolios, this site is dynamic — combining tools, models, and a living assistant into one seamless interface.",
34
  "why streamlit": "Because it allows rapid, elegant development of interactive apps — perfect for building tools quickly without compromising UX.",
35
+ "whats next": "More ML engineering projects, improved explainability using SHAP/SHAPASH, and diving deeper into NLP and generative AI."
36
  }
37
 
38
+ # Common functions
39
  def chatbot(prompt):
40
  headers = {"Authorization": f"Bearer {OR_API_KEY}", "Content-Type": "application/json"}
41
  payload = {"model": MODEL, "messages": prompt}
 
62
  return f"Text fallback failed: {e}"
63
 
64
  def save_memory(chat_name, role, content):
65
+ """Saves chat memory to the database."""
66
  data = {
67
  "chat_name": chat_name,
68
  "role": role,
69
+ "content": content
 
70
  }
71
  supabase.table("chats").insert(data).execute()
72
 
73
  def load_memory(chat_name):
74
+ """Loads chat memory for a specific chat."""
75
+ response = supabase.table("chats").select("*").eq("chat_name", chat_name).execute()
 
 
76
  return response.data
77
 
78
  def load_all_memory():
79
+ """Loads all chat sessions from the database."""
80
+ response = supabase.table("chats").select("chat_name").execute()
 
81
  chat_names = {row['chat_name'] for row in response.data}
82
+ chats = {name: load_memory(name) for name in chat_names}
83
+ return chats if chats else {"Default": []}
84
 
85
  def delete_chat(chat_name):
86
+ """Deletes chat history from the database."""
87
+ supabase.table("chats").delete().eq("chat_name", chat_name).execute()
 
 
88
 
89
  def gradual_display(text, placeholder):
90
+ """Displays text gradually."""
91
  displayed_text = ""
92
  for char in text:
93
  displayed_text += char
 
101
  text += page.get_text()
102
  return text
103
 
104
+ # Page 1: Chatbot
105
+ def chatbot_page():
106
+ st.sidebar.title("💬 Chats")
107
+
108
+ if st.sidebar.button("🧹 Clear Current Chat"):
109
+ delete_chat(st.session_state.current_chat)
110
+ st.session_state.chat_sessions[st.session_state.current_chat] = []
111
+ st.session_state.messages = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  st.rerun()
113
 
114
+ if "chat_sessions" not in st.session_state:
115
+ st.session_state.chat_sessions = load_all_memory()
116
+ if not st.session_state.chat_sessions:
117
+ st.session_state.chat_sessions = {"Default": []}
118
+
119
+ if "current_chat" not in st.session_state:
120
+ st.session_state.current_chat = "Default"
121
+
122
+ chat_options = list(st.session_state.chat_sessions.keys()) + ["➕ New Chat"]
123
+ selected_chat = st.sidebar.selectbox("Choose a chat:", chat_options)
124
+
125
+ if selected_chat == "➕ New Chat":
126
+ new_chat_name = st.sidebar.text_input("Enter chat name:")
127
+ if st.sidebar.button("Create"):
128
+ if new_chat_name and new_chat_name not in st.session_state.chat_sessions:
129
+ st.session_state.chat_sessions[new_chat_name] = []
130
+ st.session_state.current_chat = new_chat_name
131
+ st.rerun()
132
+ else:
133
+ st.session_state.current_chat = selected_chat
134
+
135
+ st.session_state.messages = st.session_state.chat_sessions[st.session_state.current_chat]
 
 
 
 
 
 
136
 
137
  for msg in st.session_state.messages:
138
  with st.chat_message(msg['role']):
139
+ st.markdown(msg['content'])
140
+
141
+ welcome = [
142
+ "Greetings, warrior. Ready to unlock some secrets?",
143
+ "Hello, I am XANE, your digital ninja assistant.",
144
+ "Hey, apprentice. Ready to master the art of knowledge?",
145
+ "XANE here. How can I assist you on your quest?",
146
+ "Welcome, ninja. Let’s crack the code together.",
147
+ "Hi there, ready to unleash your inner ninja?",
148
+ "Step into the dojo. Ask anything, learn everything.",
149
+ "XANE at your service. What’s your mission today?",
150
+ "Greetings, young ninja. The path to insight awaits.",
151
+ "Hey! Time to sharpen your skills and knowledge.",
152
+ "Hello, ninja-in-training! How can I guide you?",
153
+ "Welcome back, warrior. Let’s conquer your questions.",
154
+ "Greetings from the digital dojo. What’s next on your path?",
155
+ "Hey, warrior! Let’s hack through your toughest problems.",
156
+ "Welcome, ninja master in the making. What’s your next move?"
157
+ ]
158
+
159
+ # Pick one randomly
160
+ greeting = np.random.choice(welcome)
161
+
162
+ # Display as header
163
+ st.header(greeting)
164
+
165
+ def send_message():
166
+ chat_input = st.chat_input(
167
+ "Ask me anything or upload files",
168
+ key="chat_input",
169
+ max_chars=None,
170
+ accept_file="multiple",
171
+ file_type=["jpg", "jpeg", "png", "pdf", "txt"],
172
+ disabled=False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  )
174
 
175
+ if chat_input:
176
+ if chat_input.text and chat_input.text.strip():
177
+ with st.chat_message("user"):
178
+ st.markdown(chat_input.text)
179
+
180
+ save_memory(st.session_state.current_chat, "user", chat_input.text)
181
+ st.session_state.messages.append({"role": "user", "content": chat_input.text})
182
+
183
+ if chat_input.files:
184
+ with st.chat_message("user"):
185
+ for uploaded_file in chat_input.files:
186
+ if uploaded_file.type.startswith('image/'):
187
+ st.image(uploaded_file)
188
+ file_content = f"![Uploaded Image]({uploaded_file.name})"
189
+
190
+ elif uploaded_file.type == "application/pdf":
191
+ try:
192
+ uploaded_file.seek(0)
193
+ text = extract_pdf_text(uploaded_file)
194
+ file_content = text
195
+ st.warning(f"PDF file uploaded: {uploaded_file.name}")
196
+ except Exception as e:
197
+ st.error(f"Failed to extract PDF text: {e}")
198
+ file_content = f"[PDF file: {uploaded_file.name}]"
199
+
200
+ elif uploaded_file.type == "text/plain":
201
+ try:
202
+ uploaded_file.seek(0)
203
+ text = uploaded_file.read().decode("utf-8")
204
+ file_content = text
205
+ st.warning(f"Text file uploaded: {uploaded_file.name}")
206
+ except Exception as e:
207
+ st.error(f"Failed to read text file: {e}")
208
+ file_content = f"[Text file: {uploaded_file.name}]"
209
+
210
+ else:
211
+ st.warning(f"Unsupported file type: {uploaded_file.type}")
212
+ continue
213
+
214
+ save_memory(st.session_state.current_chat, "user", file_content)
215
+ st.session_state.messages.append({"role": "user", "content": file_content})
216
+
217
+ response = ""
218
+ if chat_input.text and isinstance(chat_input.text, str) and chat_input.text.strip():
219
+ for question, answer in portfolio_faq.items():
220
+ if question.lower() in chat_input.text.lower():
221
+ response = answer
222
+ break
223
+
224
+ if not response:
225
+ with st.spinner("XANE is thinking... 🤖"):
226
+ response = chatbot(st.session_state.messages)
227
+
228
+ st.session_state.messages.append({"role": "assistant", "content": response})
229
+ save_memory(st.session_state.current_chat, "assistant", response)
230
+
231
+ with st.chat_message("assistant"):
232
+ placeholder = st.empty()
233
+ gradual_display(response, placeholder)
234
+
235
+ st.session_state.chat_sessions[st.session_state.current_chat] = st.session_state.messages
236
+
237
+ send_message()
238
 
239
+ # Page 2: Image Generator
240
+ def image_generator_page():
241
+ st.title("🎨 Pollinations – Free Multi‑Model Generator")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
+ # Sidebar settings
244
+ with st.sidebar:
245
+ prompt = st.text_area("🖌️ Prompt", "Mass Effect Citadel scene at dusk", height=100)
246
+ model = st.selectbox("⚙️ Model", ["flux", "flux-pro", "flux-cablyai", "turbo"])
247
+ width = st.slider("Width", 512, 1536, 1024, 128)
248
+ height = st.slider("Height", 512, 1536, 1024, 128)
249
+ seed = st.number_input("Seed (optional)", value=42)
250
+
251
+ # Generate button
252
+ if st.button("✨ Generate Image"):
253
+ if not prompt.strip():
254
+ st.warning("Enter a prompt first.")
255
+ else:
256
+ prompt_enc = quote(prompt.strip())
257
+ url = (
258
+ f"https://image.pollinations.ai/prompt/{prompt_enc}"
259
+ f"?model={model}&width={width}&height={height}&seed={seed}"
260
+ )
261
+ with st.spinner("Generating image..."):
262
+ try:
263
+ resp = requests.get(url, timeout=60)
264
+ resp.raise_for_status()
265
+ img = Image.open(BytesIO(resp.content))
266
+ st.image(img, caption=f"{model} – {width}×{height}", use_container_width=True)
267
+ st.download_button(
268
+ "📥 Download Image",
269
+ data=resp.content,
270
+ file_name=f"pollinations_{model}.jpg",
271
+ mime="image/jpeg"
272
+ )
273
+ except Exception as e:
274
+ st.error(f"❌ Error generating image: {e}")
275
+
276
+ # Main app
277
  def main():
278
  st.set_page_config("XANE - AI Assistant", layout="centered")
279
 
280
+ # Sidebar navigation
281
+ st.sidebar.title("Navigation")
282
+ page = st.sidebar.radio("Go to", ["Chatbot", "Image Generator"])
283
+
284
+ if page == "Chatbot":
285
+ chatbot_page()
286
+ elif page == "Image Generator":
287
+ image_generator_page()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
 
289
  if __name__ == '__main__':
290
  main()