uumerrr684 commited on
Commit
173a005
Β·
verified Β·
1 Parent(s): 8995b4b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +513 -131
app.py CHANGED
@@ -1,179 +1,561 @@
 
 
1
  import requests
2
- import os
3
  import json
4
- import streamlit as st
5
 
6
  # Page configuration
7
  st.set_page_config(
8
  page_title="AI Assistant",
9
- page_icon="πŸ’¬",
10
- initial_sidebar_state="collapsed"
 
11
  )
12
 
13
- # White background + chat message positioning
14
  st.markdown("""
15
  <style>
 
16
  .stApp {
 
 
 
 
 
 
 
 
 
 
 
 
17
  background: white;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  }
19
 
20
- .main .block-container {
21
- max-width: 800px;
 
 
 
 
22
  }
23
 
24
- #MainMenu {visibility: hidden;}
25
- footer {visibility: hidden;}
26
- header {visibility: hidden;}
27
- .stDeployButton {display: none;}
28
 
29
- /* User messages on the right - keep original colors */
30
- .stChatMessage:nth-child(odd) {
31
- flex-direction: row-reverse !important;
32
- margin-left: 20% !important;
33
- margin-right: 0% !important;
 
 
 
 
34
  }
35
 
36
- /* AI messages on the left - keep original colors */
37
- .stChatMessage:nth-child(even) {
38
- margin-right: 20% !important;
39
- margin-left: 0% !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  }
41
 
42
- /* Hide chat avatars */
43
- .stChatMessage img {
44
- display: none !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  }
46
  </style>
47
  """, unsafe_allow_html=True)
48
 
49
  # Initialize session state
50
- if "messages" not in st.session_state:
51
  st.session_state.messages = []
 
 
 
 
 
 
52
 
53
- # Get API key
54
- OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY")
 
 
 
 
 
 
 
 
 
 
 
55
 
56
- @st.cache_data(ttl=300)
57
- def check_api_status():
58
- if not OPENROUTER_API_KEY:
59
- return "No API Key"
60
- try:
61
- url = "https://openrouter.ai/api/v1/models"
62
- headers = {"Authorization": f"Bearer {OPENROUTER_API_KEY}"}
63
- response = requests.get(url, headers=headers, timeout=5)
64
- return "Connected" if response.status_code == 200 else "Error"
65
- except:
66
- return "Error"
67
-
68
- def get_ai_response(messages, model="openai/gpt-3.5-turbo"):
69
- if not OPENROUTER_API_KEY:
70
- return "No API key found. Please add OPENROUTER_API_KEY to environment variables."
71
-
72
- url = "https://openrouter.ai/api/v1/chat/completions"
73
- headers = {
74
- "Content-Type": "application/json",
75
- "Authorization": f"Bearer {OPENROUTER_API_KEY}"
76
- }
77
-
78
- api_messages = [{"role": "system", "content": "You are a helpful AI assistant. Provide clear and helpful responses."}]
79
- api_messages.extend(messages)
80
-
81
- data = {
82
- "model": model,
83
- "messages": api_messages,
84
- "stream": True,
85
- "max_tokens": 1000,
86
- "temperature": 0.7
87
- }
88
-
89
  try:
90
- response = requests.post(url, headers=headers, json=data, stream=True, timeout=30)
91
- response.raise_for_status()
 
 
 
 
 
 
 
 
 
 
 
92
 
93
- full_response = ""
94
- for line in response.iter_lines():
95
- if line:
96
- line_str = line.decode('utf-8')
97
- if line_str.startswith('data: '):
98
- line_str = line_str[6:]
99
- if line_str.strip() == '[DONE]':
100
- break
101
- try:
102
- data = json.loads(line_str)
103
- if 'choices' in data and len(data['choices']) > 0:
104
- delta = data['choices'][0].get('delta', {})
105
- if 'content' in delta:
106
- full_response += delta['content']
107
- yield full_response
108
- except json.JSONDecodeError:
109
- continue
 
110
  except Exception as e:
111
- yield f"Sorry, I encountered an error. Please try again."
112
-
113
- # Header
114
- st.title("AI Assistant")
115
- st.caption("Ask me anything")
116
 
117
  # Sidebar
118
  with st.sidebar:
119
- st.header("Settings")
120
 
121
- # API Status
122
- status = check_api_status()
123
- if status == "Connected":
124
- st.success("API Connected")
125
- elif status == "No API Key":
126
- st.error("No API Key")
127
- else:
128
- st.warning("Connection Issue")
129
 
130
- st.divider()
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
  # Model selection
133
- models = [
134
- "openai/gpt-3.5-turbo",
135
- "openai/gpt-4",
136
- "anthropic/claude-3-haiku",
137
- "google/gemini-pro"
138
- ]
 
 
139
 
140
- selected_model = st.selectbox("Model", models, index=0)
 
141
 
142
- st.divider()
143
 
144
- # Controls
145
- if st.button("Clear Chat", use_container_width=True):
146
  st.session_state.messages = []
147
  st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
 
149
- # Show welcome message when no messages
150
- if not st.session_state.messages:
151
- st.info("How can I help you today?")
152
-
153
- # Display chat messages
154
- for message in st.session_state.messages:
155
- with st.chat_message(message["role"]):
156
- st.markdown(message["content"])
157
 
158
- # Chat input
159
- if prompt := st.chat_input("Ask anything..."):
160
- # Add user message
161
- st.session_state.messages.append({"role": "user", "content": prompt})
162
-
163
- # Display user message
164
- with st.chat_message("user"):
165
- st.markdown(prompt)
166
-
167
- # Get AI response
168
- with st.chat_message("assistant"):
169
- placeholder = st.empty()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
- full_response = ""
172
- for response in get_ai_response(st.session_state.messages, selected_model):
173
- full_response = response
174
- placeholder.markdown(full_response + "β–Œ")
 
 
 
175
 
176
- placeholder.markdown(full_response)
177
-
178
- # Add AI response to messages
179
- st.session_state.messages.append({"role": "assistant", "content": full_response})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import time
3
  import requests
 
4
  import json
5
+ from datetime import datetime
6
 
7
  # Page configuration
8
  st.set_page_config(
9
  page_title="AI Assistant",
10
+ page_icon="πŸ€–",
11
+ layout="wide",
12
+ initial_sidebar_state="expanded"
13
  )
14
 
15
+ # Custom CSS to match the React design
16
  st.markdown("""
17
  <style>
18
+ /* Main background gradient */
19
  .stApp {
20
+ background: linear-gradient(135deg, #EEF2FF 0%, #E0E7FF 100%);
21
+ }
22
+
23
+ /* Sidebar styling */
24
+ .css-1d391kg, [data-testid="stSidebar"] {
25
+ background-color: white;
26
+ border-right: 1px solid #E5E7EB;
27
+ box-shadow: 2px 0 10px rgba(0,0,0,0.05);
28
+ }
29
+
30
+ /* Header styling */
31
+ .header-container {
32
  background: white;
33
+ border-bottom: 1px solid #E5E7EB;
34
+ padding: 1rem;
35
+ margin: -1rem -1rem 1rem -1rem;
36
+ display: flex;
37
+ align-items: center;
38
+ gap: 12px;
39
+ }
40
+
41
+ .header-avatar {
42
+ width: 40px;
43
+ height: 40px;
44
+ background: linear-gradient(135deg, #3B82F6 0%, #9333EA 100%);
45
+ border-radius: 50%;
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: center;
49
+ color: white;
50
+ font-size: 20px;
51
+ }
52
+
53
+ .header-text h1 {
54
+ margin: 0;
55
+ font-size: 1.25rem;
56
+ font-weight: 600;
57
+ color: #1F2937;
58
+ }
59
+
60
+ .header-text p {
61
+ margin: 0;
62
+ font-size: 0.875rem;
63
+ color: #6B7280;
64
+ }
65
+
66
+ /* Message styling */
67
+ .user-message {
68
+ background-color: #3B82F6;
69
+ color: white;
70
+ padding: 0.75rem 1rem;
71
+ border-radius: 12px;
72
+ border-bottom-right-radius: 4px;
73
+ margin: 0.5rem 0;
74
+ max-width: 70%;
75
+ float: right;
76
+ clear: both;
77
+ }
78
+
79
+ .assistant-message {
80
+ background-color: white;
81
+ color: #1F2937;
82
+ padding: 0.75rem 1rem;
83
+ border: 1px solid #E5E7EB;
84
+ border-radius: 12px;
85
+ border-bottom-left-radius: 4px;
86
+ margin: 0.5rem 0;
87
+ max-width: 70%;
88
+ float: left;
89
+ clear: both;
90
  }
91
 
92
+ /* Avatar styling */
93
+ .message-container {
94
+ display: flex;
95
+ align-items: flex-start;
96
+ margin: 1rem 0;
97
+ gap: 12px;
98
  }
99
 
100
+ .message-container.user {
101
+ flex-direction: row-reverse;
102
+ }
 
103
 
104
+ .avatar {
105
+ width: 32px;
106
+ height: 32px;
107
+ border-radius: 50%;
108
+ display: flex;
109
+ align-items: center;
110
+ justify-content: center;
111
+ flex-shrink: 0;
112
+ font-size: 14px;
113
  }
114
 
115
+ .avatar.user {
116
+ background-color: #3B82F6;
117
+ color: white;
118
+ }
119
+
120
+ .avatar.assistant {
121
+ background-color: #E5E7EB;
122
+ color: #6B7280;
123
+ }
124
+
125
+ /* Welcome screen styling */
126
+ .welcome-container {
127
+ text-align: center;
128
+ padding: 3rem 0;
129
+ }
130
+
131
+ .welcome-icon {
132
+ width: 64px;
133
+ height: 64px;
134
+ background: linear-gradient(135deg, #3B82F6 0%, #9333EA 100%);
135
+ border-radius: 50%;
136
+ display: flex;
137
+ align-items: center;
138
+ justify-content: center;
139
+ margin: 0 auto 1rem;
140
+ color: white;
141
+ font-size: 32px;
142
+ }
143
+
144
+ .example-prompt {
145
+ background: white;
146
+ border: 1px solid #E5E7EB;
147
+ border-radius: 8px;
148
+ padding: 0.75rem;
149
+ margin: 0.5rem;
150
+ cursor: pointer;
151
+ transition: all 0.2s;
152
+ text-align: left;
153
  }
154
 
155
+ .example-prompt:hover {
156
+ border-color: #93C5FD;
157
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
158
+ }
159
+
160
+ /* Input area styling */
161
+ .stTextArea textarea {
162
+ border-radius: 8px !important;
163
+ border: 1px solid #D1D5DB !important;
164
+ padding: 0.75rem !important;
165
+ }
166
+
167
+ .stTextArea textarea:focus {
168
+ border-color: #3B82F6 !important;
169
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1) !important;
170
+ }
171
+
172
+ /* Button styling */
173
+ .stButton button {
174
+ background: linear-gradient(135deg, #3B82F6 0%, #6366F1 100%);
175
+ color: white;
176
+ border: none;
177
+ border-radius: 8px;
178
+ padding: 0.5rem 1rem;
179
+ font-weight: 500;
180
+ transition: all 0.2s;
181
+ }
182
+
183
+ .stButton button:hover {
184
+ background: linear-gradient(135deg, #2563EB 0%, #4F46E5 100%);
185
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
186
+ }
187
+
188
+ /* Status indicator */
189
+ .status-indicator {
190
+ display: flex;
191
+ align-items: center;
192
+ gap: 8px;
193
+ font-size: 0.875rem;
194
+ margin-top: 1rem;
195
+ }
196
+
197
+ .status-indicator.ready {
198
+ color: #10B981;
199
+ }
200
+
201
+ .status-indicator.error {
202
+ color: #EF4444;
203
+ }
204
+
205
+ .status-dot {
206
+ width: 8px;
207
+ height: 8px;
208
+ border-radius: 50%;
209
+ animation: pulse 2s infinite;
210
+ }
211
+
212
+ .status-dot.ready {
213
+ background-color: #10B981;
214
+ }
215
+
216
+ .status-dot.error {
217
+ background-color: #EF4444;
218
+ }
219
+
220
+ @keyframes pulse {
221
+ 0%, 100% {
222
+ opacity: 1;
223
+ }
224
+ 50% {
225
+ opacity: 0.5;
226
+ }
227
+ }
228
+
229
+ /* Loading animation */
230
+ .loading-dots {
231
+ display: flex;
232
+ gap: 4px;
233
+ padding: 0.75rem 1rem;
234
+ background: white;
235
+ border: 1px solid #E5E7EB;
236
+ border-radius: 12px;
237
+ border-bottom-left-radius: 4px;
238
+ width: fit-content;
239
+ }
240
+
241
+ .loading-dot {
242
+ width: 8px;
243
+ height: 8px;
244
+ background-color: #9CA3AF;
245
+ border-radius: 50%;
246
+ animation: bounce 1.4s infinite ease-in-out both;
247
+ }
248
+
249
+ .loading-dot:nth-child(1) {
250
+ animation-delay: -0.32s;
251
+ }
252
+
253
+ .loading-dot:nth-child(2) {
254
+ animation-delay: -0.16s;
255
+ }
256
+
257
+ @keyframes bounce {
258
+ 0%, 80%, 100% {
259
+ transform: scale(0);
260
+ }
261
+ 40% {
262
+ transform: scale(1);
263
+ }
264
+ }
265
+
266
+ /* API Key input styling */
267
+ .stTextInput input {
268
+ border-radius: 8px !important;
269
+ border: 1px solid #D1D5DB !important;
270
+ padding: 0.5rem !important;
271
+ font-family: monospace !important;
272
+ }
273
+
274
+ .api-key-section {
275
+ background: #FEF3C7;
276
+ border: 1px solid #FCD34D;
277
+ border-radius: 8px;
278
+ padding: 1rem;
279
+ margin-bottom: 1rem;
280
+ }
281
+
282
+ .api-key-section h4 {
283
+ color: #92400E;
284
+ margin-bottom: 0.5rem;
285
+ }
286
+
287
+ .api-key-section p {
288
+ color: #78350F;
289
+ font-size: 0.875rem;
290
+ margin-bottom: 0.5rem;
291
  }
292
  </style>
293
  """, unsafe_allow_html=True)
294
 
295
  # Initialize session state
296
+ if 'messages' not in st.session_state:
297
  st.session_state.messages = []
298
+ if 'selected_model' not in st.session_state:
299
+ st.session_state.selected_model = 'openai/gpt-3.5-turbo'
300
+ if 'input_text' not in st.session_state:
301
+ st.session_state.input_text = ""
302
+ if 'api_key' not in st.session_state:
303
+ st.session_state.api_key = ""
304
 
305
+ # Model options
306
+ models = {
307
+ 'openai/gpt-3.5-turbo': 'GPT-3.5 Turbo',
308
+ 'openai/gpt-4': 'GPT-4',
309
+ 'openai/gpt-4-turbo': 'GPT-4 Turbo',
310
+ 'anthropic/claude-3-haiku': 'Claude 3 Haiku',
311
+ 'anthropic/claude-3-opus': 'Claude 3 Opus',
312
+ 'anthropic/claude-3-sonnet': 'Claude 3 Sonnet',
313
+ 'google/gemini-pro': 'Gemini Pro',
314
+ 'google/gemini-pro-1.5': 'Gemini Pro 1.5',
315
+ 'meta-llama/llama-3-70b-instruct': 'Llama 3 70B',
316
+ 'mistralai/mixtral-8x7b-instruct': 'Mixtral 8x7B'
317
+ }
318
 
319
+ def call_openrouter_api(messages, model, api_key):
320
+ """Call OpenRouter API with the given messages and model."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  try:
322
+ headers = {
323
+ "Authorization": f"Bearer {api_key}",
324
+ "HTTP-Referer": "http://localhost:8501", # Optional, can be your app URL
325
+ "X-Title": "AI Assistant", # Optional, shows in OpenRouter dashboard
326
+ "Content-Type": "application/json"
327
+ }
328
+
329
+ data = {
330
+ "model": model,
331
+ "messages": messages,
332
+ "temperature": 0.7,
333
+ "max_tokens": 1000
334
+ }
335
 
336
+ response = requests.post(
337
+ "https://openrouter.ai/api/v1/chat/completions",
338
+ headers=headers,
339
+ json=data,
340
+ timeout=30
341
+ )
342
+
343
+ if response.status_code == 200:
344
+ result = response.json()
345
+ return result['choices'][0]['message']['content'], None
346
+ else:
347
+ error_msg = f"API Error: {response.status_code} - {response.text}"
348
+ return None, error_msg
349
+
350
+ except requests.exceptions.Timeout:
351
+ return None, "Request timed out. Please try again."
352
+ except requests.exceptions.RequestException as e:
353
+ return None, f"Connection error: {str(e)}"
354
  except Exception as e:
355
+ return None, f"Unexpected error: {str(e)}"
 
 
 
 
356
 
357
  # Sidebar
358
  with st.sidebar:
359
+ st.markdown("### βš™οΈ Settings")
360
 
361
+ # API Key Section
362
+ st.markdown("""
363
+ <div class="api-key-section">
364
+ <h4>πŸ”‘ OpenRouter API Key</h4>
365
+ <p>Get your API key from <a href="https://openrouter.ai/keys" target="_blank">openrouter.ai/keys</a></p>
366
+ </div>
367
+ """, unsafe_allow_html=True)
 
368
 
369
+ api_key_input = st.text_input(
370
+ "API Key",
371
+ type="password",
372
+ value=st.session_state.api_key,
373
+ placeholder="sk-or-v1-...",
374
+ help="Your OpenRouter API key for accessing AI models",
375
+ label_visibility="collapsed"
376
+ )
377
+
378
+ if api_key_input:
379
+ st.session_state.api_key = api_key_input
380
+
381
+ st.markdown("---")
382
 
383
  # Model selection
384
+ st.markdown("### πŸ€– AI Model")
385
+ st.session_state.selected_model = st.selectbox(
386
+ "Select Model",
387
+ options=list(models.keys()),
388
+ format_func=lambda x: models[x],
389
+ key="model_select",
390
+ label_visibility="collapsed"
391
+ )
392
 
393
+ # Model info
394
+ st.info(f"πŸ“Š Using: {models[st.session_state.selected_model]}")
395
 
396
+ st.markdown("---")
397
 
398
+ # Clear chat button
399
+ if st.button("πŸ—‘οΈ Clear Chat", use_container_width=True):
400
  st.session_state.messages = []
401
  st.rerun()
402
+
403
+ # API Status
404
+ if st.session_state.api_key:
405
+ st.markdown("""
406
+ <div class="status-indicator ready">
407
+ <div class="status-dot ready"></div>
408
+ <span>API Ready</span>
409
+ </div>
410
+ """, unsafe_allow_html=True)
411
+ else:
412
+ st.markdown("""
413
+ <div class="status-indicator error">
414
+ <div class="status-dot error"></div>
415
+ <span>API Key Required</span>
416
+ </div>
417
+ """, unsafe_allow_html=True)
418
 
419
+ # Main chat area
420
+ col1, col2, col3 = st.columns([1, 6, 1])
 
 
 
 
 
 
421
 
422
+ with col2:
423
+ # Header
424
+ st.markdown(f"""
425
+ <div class="header-container">
426
+ <div class="header-avatar">πŸ€–</div>
427
+ <div class="header-text">
428
+ <h1>AI Assistant</h1>
429
+ <p>Powered by {models[st.session_state.selected_model]}</p>
430
+ </div>
431
+ </div>
432
+ """, unsafe_allow_html=True)
433
+
434
+ # Check if API key is set
435
+ if not st.session_state.api_key:
436
+ st.warning("⚠️ Please enter your OpenRouter API key in the sidebar to start chatting.")
437
+ st.markdown("""
438
+ ### How to get started:
439
+ 1. Go to [OpenRouter](https://openrouter.ai/keys) and sign up/login
440
+ 2. Create a new API key
441
+ 3. Paste it in the sidebar
442
+ 4. Start chatting with your preferred AI model!
443
+ """)
444
+
445
+ # Message container
446
+ messages_container = st.container()
447
+
448
+ # Welcome screen or messages
449
+ if not st.session_state.messages:
450
+ with messages_container:
451
+ st.markdown("""
452
+ <div class="welcome-container">
453
+ <div class="welcome-icon">⚑</div>
454
+ <h2 style="color: #1F2937; margin-bottom: 0.5rem;">Welcome to AI Assistant</h2>
455
+ <p style="color: #6B7280; margin-bottom: 1.5rem;">Start a conversation or try one of these examples:</p>
456
+ </div>
457
+ """, unsafe_allow_html=True)
458
+
459
+ # Example prompts
460
+ example_prompts = [
461
+ "Help me write a professional email",
462
+ "Explain quantum computing in simple terms",
463
+ "Give me 5 creative writing prompts",
464
+ "What are the latest web development trends?"
465
+ ]
466
+
467
+ cols = st.columns(2)
468
+ for i, prompt in enumerate(example_prompts):
469
+ with cols[i % 2]:
470
+ if st.button(prompt, key=f"example_{i}", use_container_width=True):
471
+ st.session_state.input_text = prompt
472
+ st.rerun()
473
+ else:
474
+ # Display messages
475
+ with messages_container:
476
+ for message in st.session_state.messages:
477
+ if message["role"] == "user":
478
+ st.markdown(f"""
479
+ <div class="message-container user">
480
+ <div class="avatar user">πŸ‘€</div>
481
+ <div class="user-message">{message["content"]}</div>
482
+ </div>
483
+ """, unsafe_allow_html=True)
484
+ else:
485
+ st.markdown(f"""
486
+ <div class="message-container">
487
+ <div class="avatar assistant">πŸ€–</div>
488
+ <div class="assistant-message">{message["content"]}</div>
489
+ </div>
490
+ """, unsafe_allow_html=True)
491
+
492
+ # Input area
493
+ st.markdown("<br>", unsafe_allow_html=True)
494
+
495
+ input_col1, input_col2 = st.columns([6, 1])
496
+
497
+ with input_col1:
498
+ user_input = st.text_area(
499
+ "Type your message here...",
500
+ key="user_input",
501
+ height=70,
502
+ value=st.session_state.input_text,
503
+ label_visibility="collapsed",
504
+ disabled=not st.session_state.api_key
505
+ )
506
+
507
+ with input_col2:
508
+ send_button = st.button(
509
+ "πŸ“€ Send",
510
+ use_container_width=True,
511
+ disabled=not st.session_state.api_key
512
+ )
513
+
514
+ # Handle send
515
+ if send_button and user_input and st.session_state.api_key:
516
+ # Add user message
517
+ st.session_state.messages.append({"role": "user", "content": user_input})
518
 
519
+ # Prepare messages for API
520
+ api_messages = []
521
+ for msg in st.session_state.messages:
522
+ api_messages.append({
523
+ "role": msg["role"],
524
+ "content": msg["content"]
525
+ })
526
 
527
+ # Show loading animation
528
+ with messages_container:
529
+ with st.spinner("Thinking..."):
530
+ # Call OpenRouter API
531
+ response_content, error = call_openrouter_api(
532
+ api_messages,
533
+ st.session_state.selected_model,
534
+ st.session_state.api_key
535
+ )
536
+
537
+ if error:
538
+ st.error(f"❌ {error}")
539
+ # Add error message to chat
540
+ error_response = f"Sorry, I encountered an error: {error}\n\nPlease check your API key and try again."
541
+ st.session_state.messages.append({"role": "assistant", "content": error_response})
542
+ else:
543
+ # Add assistant response
544
+ st.session_state.messages.append({"role": "assistant", "content": response_content})
545
+
546
+ # Clear input
547
+ st.session_state.input_text = ""
548
+ st.rerun()
549
+
550
+ # Auto-scroll to bottom (Streamlit does this automatically)
551
+ if st.session_state.messages:
552
+ st.markdown("<script>window.scrollTo(0, document.body.scrollHeight);</script>", unsafe_allow_html=True)
553
+
554
+ # Footer info
555
+ with col2:
556
+ if st.session_state.api_key:
557
+ st.markdown("---")
558
+ st.caption("Connected to OpenRouter API | Chat powered by " + models[st.session_state.selected_model])
559
+ else:
560
+ st.markdown("---")
561
+ st.caption("Not connected - Please add your OpenRouter API key in the sidebar")