Keeby-smilyai commited on
Commit
e34308d
·
verified ·
1 Parent(s): dbe4e96

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +971 -300
app.py CHANGED
@@ -806,10 +806,11 @@ def generate_response_stream(prompt, temperature=0.7, backend=None, max_tokens=2
806
  final_tokens_per_sec = tokens_generated / elapsed if elapsed > 0 else 0
807
  yield "", False, final_tokens_per_sec, final_tokens_per_sec, False
808
 
809
-
810
- # PART 3 - Multi-Page Website UI (No Backend Changes)
811
 
812
  import secrets
 
 
813
 
814
  # Global session storage (unchanged from original)
815
  active_sessions = {}
@@ -849,46 +850,69 @@ if __name__ == "__main__":
849
  import gradio as gr
850
 
851
  custom_css = """
852
- /* Modern Website Styling */
853
- .app-container { max-width: 1400px; margin: 0 auto; }
 
 
 
 
 
 
 
 
 
 
854
 
855
  /* Navigation Bar */
856
  .nav-bar {
857
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
858
- padding: 16px 32px;
859
  border-radius: 12px;
860
- margin-bottom: 24px;
861
  display: flex;
862
  justify-content: space-between;
863
  align-items: center;
864
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
 
 
 
865
  }
866
- .nav-left { display: flex; align-items: center; gap: 24px; }
867
- .nav-brand { font-size: 24px; font-weight: 700; color: white; }
868
- .nav-links { display: flex; gap: 16px; }
869
- .nav-link {
870
  color: white;
871
- padding: 8px 16px;
872
- border-radius: 8px;
873
- cursor: pointer;
874
- transition: all 0.2s;
875
- background: rgba(255,255,255,0.1);
 
 
 
 
 
 
 
 
 
 
876
  }
877
- .nav-link:hover { background: rgba(255,255,255,0.2); transform: translateY(-2px); }
878
- .nav-link.active { background: rgba(255,255,255,0.3); font-weight: 600; }
879
- .nav-right { display: flex; align-items: center; gap: 16px; }
880
- .user-greeting { color: white; font-weight: 500; font-size: 15px; }
881
 
882
  /* Plan Badge */
883
  .plan-badge {
884
  display: inline-block;
885
- padding: 6px 14px;
886
- border-radius: 16px;
887
- font-size: 12px;
888
  font-weight: 700;
889
- margin-left: 8px;
890
  text-transform: uppercase;
891
  letter-spacing: 0.5px;
 
 
 
 
 
892
  }
893
  .plan-free { background: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 100%); color: #3730a3; }
894
  .plan-plus { background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); color: #1e40af; }
@@ -899,18 +923,23 @@ if __name__ == "__main__":
899
 
900
  /* Auth Page */
901
  .auth-container {
902
- max-width: 480px;
903
- margin: 60px auto;
904
  background: white;
905
- padding: 40px;
906
  border-radius: 16px;
907
- box-shadow: 0 8px 24px rgba(0,0,0,0.1);
 
 
 
 
 
908
  }
909
  .auth-title {
910
- font-size: 32px;
911
  font-weight: 700;
912
  text-align: center;
913
- margin-bottom: 8px;
914
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
915
  -webkit-background-clip: text;
916
  -webkit-text-fill-color: transparent;
@@ -918,71 +947,186 @@ if __name__ == "__main__":
918
  .auth-subtitle {
919
  text-align: center;
920
  color: #6b7280;
921
- margin-bottom: 32px;
922
- font-size: 14px;
923
  }
924
 
925
  /* Chat Interface */
 
 
 
 
926
  .chat-container {
927
  height: 520px;
928
  overflow-y: auto;
929
- padding: 24px;
930
  background: #f9fafb;
931
  border: 1px solid #e5e7eb;
932
  border-radius: 12px;
933
- margin-bottom: 16px;
 
934
  }
 
 
 
 
 
935
  .user-message {
936
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
937
  color: white;
938
- padding: 16px 20px;
939
- margin: 12px 0;
940
  border-radius: 16px 16px 4px 16px;
941
- max-width: 80%;
942
  margin-left: auto;
 
 
943
  }
944
  .assistant-message {
945
  background: white;
946
- padding: 16px 20px;
947
- margin: 12px 0;
948
  border-radius: 16px 16px 16px 4px;
949
- border-left: 4px solid #10a37f;
950
- max-width: 80%;
951
- box-shadow: 0 2px 8px rgba(0,0,0,0.05);
 
 
 
 
 
 
 
 
 
 
 
 
 
952
  }
953
- .message-content { color: #353740; line-height: 1.7; font-size: 15px; }
954
  .user-message .message-content { color: white; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
955
  .thinking-content {
956
  color: #6b7280;
957
  font-style: italic;
958
  border-left: 3px solid #d1d5db;
959
  padding-left: 12px;
960
- margin: 12px 0;
961
  background: #f9fafb;
962
- padding: 12px;
963
- border-radius: 8px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
964
  }
965
 
966
  /* Input Area */
 
 
 
 
 
 
 
 
 
 
 
 
967
  .input-row {
968
  display: flex;
969
- gap: 12px;
970
  align-items: flex-end;
971
  }
972
  .circular-btn {
973
- width: 52px !important;
974
- height: 52px !important;
975
- min-width: 52px !important;
976
  border-radius: 50% !important;
977
  padding: 0 !important;
978
- font-size: 22px !important;
979
  box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
980
  transition: all 0.2s ease !important;
981
  border: none !important;
982
  }
983
  .circular-btn:hover:not(:disabled) {
984
  transform: scale(1.08) !important;
985
- box-shadow: 0 6px 16px rgba(0,0,0,0.2) !important;
 
 
 
986
  }
987
  .send-btn {
988
  background: linear-gradient(135deg, #10a37f 0%, #0d8c6c 100%) !important;
@@ -991,137 +1135,251 @@ if __name__ == "__main__":
991
  background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%) !important;
992
  }
993
 
 
 
 
 
 
 
 
 
994
  /* Sidebar/Limits Panel */
995
  .limits-panel {
996
  background: white;
997
  border: 1px solid #e5e7eb;
998
  border-radius: 12px;
999
- padding: 20px;
1000
- margin-bottom: 16px;
1001
  box-shadow: 0 2px 8px rgba(0,0,0,0.05);
1002
  }
 
 
 
 
 
 
 
 
 
1003
  .limit-item {
1004
  display: flex;
1005
  justify-content: space-between;
1006
- padding: 12px 0;
1007
  border-bottom: 1px solid #f3f4f6;
1008
  align-items: center;
1009
  }
1010
  .limit-item:last-child { border-bottom: none; }
1011
- .limit-exceeded { color: #dc2626; font-weight: 600; }
1012
- .limit-ok { color: #059669; font-weight: 500; }
1013
- .limit-warning { color: #f59e0b; font-weight: 600; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1014
 
1015
  /* Plans Section */
1016
  .plans-grid {
1017
  display: grid;
1018
- grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
1019
- gap: 24px;
1020
- margin-top: 24px;
1021
  }
1022
  .plan-card {
1023
  background: white;
1024
  border: 2px solid #e5e7eb;
1025
- border-radius: 16px;
1026
- padding: 28px;
1027
  transition: all 0.3s;
1028
  position: relative;
1029
  overflow: hidden;
1030
  }
1031
  .plan-card:hover {
1032
- transform: translateY(-4px);
1033
- box-shadow: 0 12px 24px rgba(0,0,0,0.12);
1034
  border-color: #667eea;
1035
  }
1036
  .plan-card.featured {
1037
  border: 3px solid #667eea;
1038
- box-shadow: 0 8px 24px rgba(102, 126, 234, 0.2);
 
1039
  }
1040
  .plan-card.featured::before {
1041
  content: '⭐ POPULAR';
1042
  position: absolute;
1043
- top: 16px;
1044
- right: -32px;
1045
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1046
  color: white;
1047
- padding: 4px 40px;
1048
- font-size: 11px;
1049
  font-weight: 700;
1050
  letter-spacing: 1px;
1051
  transform: rotate(45deg);
1052
  }
1053
  .plan-name {
1054
- font-size: 24px;
1055
  font-weight: 700;
1056
- margin-bottom: 8px;
1057
  color: #1f2937;
1058
  }
1059
  .plan-price {
1060
- font-size: 14px;
1061
  color: #6b7280;
1062
- margin-bottom: 20px;
1063
  }
1064
  .plan-features {
1065
  list-style: none;
1066
  padding: 0;
1067
- margin: 20px 0;
1068
  }
1069
  .plan-features li {
1070
- padding: 8px 0;
1071
  color: #4b5563;
1072
- font-size: 14px;
1073
  }
1074
  .plan-features li::before {
1075
  content: '✓ ';
1076
  color: #10a37f;
1077
  font-weight: 700;
1078
- margin-right: 8px;
1079
  }
1080
 
1081
- /* Admin Dashboard */
1082
- .admin-dashboard {
1083
- background: white;
1084
- border-radius: 12px;
1085
- padding: 24px;
1086
- box-shadow: 0 2px 8px rgba(0,0,0,0.05);
 
 
 
 
 
 
 
 
1087
  }
1088
- .dashboard-header {
1089
- font-size: 24px;
1090
- font-weight: 700;
1091
- margin-bottom: 24px;
1092
- color: #1f2937;
 
 
 
 
 
 
 
1093
  }
1094
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1095
  /* Settings Panel */
1096
  .settings-panel {
1097
  background: white;
1098
  border: 1px solid #e5e7eb;
1099
  border-radius: 12px;
1100
- padding: 20px;
1101
- margin-bottom: 16px;
1102
  }
1103
 
1104
- /* Speed indicator */
1105
- .speed-indicator {
1106
- text-align: center;
1107
- padding: 12px;
1108
- background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
1109
- border-radius: 8px;
1110
- font-weight: 600;
1111
- color: #166534;
1112
- margin-bottom: 12px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1113
  }
1114
 
1115
  /* Responsive */
 
 
 
 
1116
  @media (max-width: 768px) {
1117
- .nav-bar { flex-direction: column; gap: 16px; }
1118
  .nav-left, .nav-right { width: 100%; justify-content: center; }
1119
  .chat-container { height: 400px; }
1120
  .plans-grid { grid-template-columns: 1fr; }
 
1121
  }
 
 
 
 
1122
  """
1123
 
1124
- # User greeting variations
1125
  def get_greeting(username):
1126
  import random
1127
  greetings = [
@@ -1130,16 +1388,51 @@ if __name__ == "__main__":
1130
  f"Hi {username}! 🚀",
1131
  f"Hello {username}! 😊",
1132
  f"Great to see you, {username}! 🎉",
1133
- f"What's up, {username}? 💫"
 
 
1134
  ]
1135
  return random.choice(greetings)
1136
 
1137
- # Format message HTML
1138
- def format_message_html(role, content, show_thinking=True):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1139
  role_class = "user-message" if role == "user" else "assistant-message"
1140
  thinking = ""
1141
  answer = ""
1142
 
 
1143
  if "<think>" in content:
1144
  parts = content.split("<think>", 1)
1145
  before_think = parts[0].strip()
@@ -1161,39 +1454,64 @@ if __name__ == "__main__":
1161
  else:
1162
  answer = content
1163
 
1164
- html = f'<div class="{role_class}"><div class="message-content">'
 
 
 
 
1165
  if thinking and show_thinking:
1166
- html += f'<div class="thinking-content">💭 {thinking}</div>'
 
1167
  if answer:
1168
  html += f'<div>{answer}</div>'
 
 
 
 
 
 
 
 
 
 
1169
  html += '</div></div>'
1170
  return html
1171
 
1172
  def render_history(history, show_thinking):
 
 
 
 
 
 
 
 
 
1173
  html = ""
1174
- for msg in history:
1175
- html += format_message_html(msg["role"], msg["content"], show_thinking)
1176
  return html
1177
 
1178
  def render_limits_panel(user_data):
1179
  if not user_data or 'user_id' not in user_data:
1180
  return ""
 
1181
  info = get_user_limits_info(user_data['user_id'])
1182
  if not info:
1183
  return ""
1184
 
1185
  plan_class = f"plan-{info['plan'].lower()}"
 
1186
  html = f'''<div class="limits-panel">
1187
- <div style="font-weight: 700; margin-bottom: 16px; font-size: 18px; color: #1f2937;">
1188
- Usage Limits
1189
- <span class="plan-badge {plan_class}">{info["plan"]}</span>
1190
  </div>
1191
- <div style="font-size: 13px; color: #6b7280; margin-bottom: 16px; padding: 8px; background: #f9fafb; border-radius: 6px;">
1192
- Resets in: <strong>{info["reset_in"]}</strong>
1193
  </div>'''
1194
 
1195
  models_info = [
1196
- ('Nano ⚡⚡', info['nano_used'], info['nano_limit']),
1197
  ('Mini 🚀', info['mini_used'], info['mini_limit']),
1198
  ('Fast ⚡', info['fast_used'], info['fast_limit']),
1199
  ('Large 💎', info['large_used'], info['large_limit'])
@@ -1201,198 +1519,394 @@ if __name__ == "__main__":
1201
 
1202
  for model_name, used, limit in models_info:
1203
  if limit == -1:
1204
- status = f'<span class="limit-ok">{used} / ∞</span>'
 
 
 
1205
  else:
 
1206
  remaining = limit - used
1207
  if remaining <= 0:
1208
- status = f'<span class="limit-exceeded">{used}/{limit}</span>'
 
 
1209
  elif remaining <= 2:
1210
- status = f'<span class="limit-warning">{used}/{limit}</span>'
 
 
1211
  else:
1212
- status = f'<span class="limit-ok">{used}/{limit}</span>'
1213
- html += f'<div class="limit-item"><span style="font-weight: 500;">{model_name}</span>{status}</div>'
 
 
 
 
 
 
 
 
 
 
 
1214
 
1215
  html += '</div>'
1216
  return html
1217
 
1218
- with gr.Blocks(css=custom_css, theme=gr.themes.Soft(primary_hue="slate")) as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1219
 
1220
- # State to track current page and session
1221
- current_page = gr.State("auth") # auth, chat, admin
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1222
  session_code = gr.State("")
1223
  user_data = gr.State(None)
 
1224
 
1225
  # Navigation Bar
1226
  with gr.Row(elem_classes="nav-bar"):
1227
  with gr.Column(scale=1, elem_classes="nav-left"):
1228
- gr.HTML('<div class="nav-brand">🤖 SAM-X-1</div>')
1229
  with gr.Column(scale=2, elem_classes="nav-right"):
1230
  user_greeting = gr.HTML('<div class="user-greeting">Please sign in</div>')
1231
- upgrade_nav_btn = gr.Button("⭐ Upgrade", size="sm", visible=False)
1232
- logout_nav_btn = gr.Button("🚪 Logout", size="sm", visible=False)
 
1233
 
1234
  # AUTH PAGE
1235
  with gr.Group(visible=True) as auth_page:
1236
  with gr.Column(elem_classes="auth-container"):
1237
  gr.HTML('<div class="auth-title">Welcome to SAM-X-1</div>')
1238
- gr.HTML('<div class="auth-subtitle">Sign in or create a new account</div>')
1239
 
1240
- auth_username = gr.Textbox(label="Username", placeholder="Enter your username")
1241
- auth_password = gr.Textbox(label="Password", type="password", placeholder="Enter your password")
1242
- auth_email = gr.Textbox(label="Email (optional, for new accounts)", placeholder="[email protected]")
 
 
 
 
 
 
 
 
 
 
 
 
1243
 
1244
  auth_btn = gr.Button("Continue →", variant="primary", size="lg")
1245
  auth_msg = gr.Markdown("")
 
 
 
 
 
 
 
1246
 
1247
  # CHAT PAGE
1248
  with gr.Group(visible=False) as chat_page:
1249
- with gr.Row():
1250
- with gr.Column(scale=3):
1251
- chat_html = gr.HTML(value='<div class="chat-container"></div>')
 
1252
 
1253
- with gr.Row(elem_classes="input-row"):
1254
- msg_input = gr.Textbox(
1255
- placeholder="Ask me anything...",
1256
- show_label=False,
1257
- scale=10,
1258
- lines=1
1259
- )
1260
- send_btn = gr.Button("▶", elem_classes=["circular-btn", "send-btn"])
1261
- stop_btn = gr.Button("⏹", elem_classes=["circular-btn", "stop-btn"], visible=False)
1262
 
1263
- speed_display = gr.HTML('<div class="speed-indicator">⚡ Ready</div>')
 
 
 
 
 
 
 
 
 
 
 
 
 
1264
 
1265
  with gr.Row():
1266
- clear_btn = gr.Button("🗑️ Clear", size="sm")
1267
  new_chat_btn = gr.Button("➕ New Chat", size="sm", variant="primary")
 
1268
 
1269
- with gr.Column(scale=1):
 
1270
  limits_display = gr.HTML("")
1271
 
1272
  with gr.Accordion("⚙️ Settings", open=False, elem_classes="settings-panel"):
1273
  model_selector = gr.Dropdown(
1274
  choices=["🤖 Auto (Recommended)"],
1275
  value="🤖 Auto (Recommended)",
1276
- label="Model Selection"
 
1277
  )
1278
  max_tokens_slider = gr.Slider(
1279
  minimum=64, maximum=512, value=256, step=64,
1280
- label="Max Tokens"
 
1281
  )
1282
  temperature_slider = gr.Slider(
1283
  minimum=0.0, maximum=2.0, value=0.7, step=0.1,
1284
- label="Temperature"
 
1285
  )
1286
  show_thinking_checkbox = gr.Checkbox(
1287
- label="Show Thinking Process",
1288
- value=True
 
1289
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1290
 
1291
- # UPGRADE PAGE (Plans)
1292
  with gr.Group(visible=False) as upgrade_page:
1293
- gr.HTML('<div class="dashboard-header" style="text-align: center;">Choose Your Plan</div>')
1294
- gr.Markdown("**Select the perfect plan for your needs**", elem_classes="text-center")
 
 
 
 
 
 
 
 
1295
 
1296
- with gr.Column(elem_classes="plans-grid"):
1297
- gr.HTML('''
1298
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; margin-top: 24px;">
1299
- <div class="plan-card">
1300
- <div class="plan-name">Free</div>
1301
- <div class="plan-price">Perfect for getting started</div>
1302
- <ul class="plan-features">
1303
- <li>Nano: Unlimited messages</li>
1304
- <li>Mini: Unlimited messages</li>
1305
- <li>Fast: 10 messages/3h</li>
1306
- <li>Large: 8 messages/3h</li>
1307
- <li>Auto model selection</li>
1308
- <li>256 max tokens</li>
1309
- </ul>
1310
- </div>
1311
-
1312
- <div class="plan-card featured">
1313
- <div class="plan-name">Plus ⭐</div>
1314
- <div class="plan-price">Great for power users</div>
1315
- <ul class="plan-features">
1316
- <li>Everything in Free</li>
1317
- <li>Fast: Unlimited messages</li>
1318
- <li>Large: 20 messages/3h</li>
1319
- <li>Manual model selection</li>
1320
- <li>384 max tokens</li>
1321
- <li>Priority support</li>
1322
- </ul>
1323
- </div>
1324
-
1325
- <div class="plan-card">
1326
- <div class="plan-name">Pro 💎</div>
1327
- <div class="plan-price">For professionals</div>
1328
- <ul class="plan-features">
1329
- <li>Everything in Plus</li>
1330
- <li>All models unlimited</li>
1331
- <li>512 max tokens</li>
1332
- <li>Fastest reset (3h)</li>
1333
- <li>24/7 premium support</li>
1334
- <li>Early feature access</li>
1335
- </ul>
1336
- </div>
1337
  </div>
1338
- ''')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1339
 
1340
- upgrade_plan_choice = gr.Radio(
1341
- choices=["plus", "pro", "explore", "Research"],
1342
- label="Select Plan to Request",
1343
- value="plus"
1344
- )
1345
- upgrade_reason = gr.Textbox(
1346
- label="Why do you need this upgrade?",
1347
- placeholder="Tell us about your use case...",
1348
- lines=3
1349
- )
1350
- submit_upgrade_btn = gr.Button("Submit Upgrade Request", variant="primary", size="lg")
1351
- upgrade_msg = gr.Markdown("")
1352
- back_to_chat_btn = gr.Button(" Back to Chat", size="sm")
 
 
 
 
 
 
1353
 
1354
  # ADMIN PAGE
1355
  with gr.Group(visible=False) as admin_page:
1356
- with gr.Column(elem_classes="admin-dashboard"):
1357
- gr.HTML('<div class="dashboard-header">👨���💼 Admin Dashboard</div>')
1358
-
1359
- with gr.Tabs():
1360
- with gr.Tab("👥 Users"):
1361
- users_table = gr.Dataframe(
1362
- headers=["ID", "Username", "Email", "Plan", "Created", "Admin"]
1363
- )
1364
- refresh_users_btn = gr.Button("🔄 Refresh", size="sm")
1365
-
1366
- with gr.Row():
1367
- admin_username = gr.Textbox(label="Username", scale=2)
1368
- admin_new_plan = gr.Dropdown(
1369
- choices=["free", "plus", "pro", "explore", "Research", "VIP"],
1370
- label="New Plan",
1371
- scale=1
1372
- )
1373
- update_plan_btn = gr.Button("Update", variant="primary", scale=1)
1374
- admin_msg = gr.Markdown("")
1375
 
1376
- with gr.Tab("📋 Requests"):
1377
- requests_table = gr.Dataframe(
1378
- headers=["ID", "Username", "Plan", "Reason", "Date"]
 
 
 
 
 
1379
  )
1380
- refresh_requests_btn = gr.Button("🔄 Refresh", size="sm")
1381
-
1382
- with gr.Row():
1383
- request_id_input = gr.Number(label="Request ID", precision=0)
1384
- approve_req_btn = gr.Button("✅ Approve", variant="primary")
1385
- deny_req_btn = gr.Button(" Deny", variant="stop")
1386
- request_msg = gr.Markdown("")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1387
 
1388
  # ==================== EVENT HANDLERS ====================
1389
 
1390
  def handle_auth(username, password, email):
1391
- """Unified auth handler - auto signup if new user"""
1392
  if len(username) < 3:
1393
- return None, "❌ Username must be at least 3 characters", gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
 
 
 
 
1394
  if len(password) < 6:
1395
- return None, "❌ Password must be at least 6 characters", gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
 
 
 
 
1396
 
1397
  # Try login first
1398
  success, data = authenticate_user(username, password)
@@ -1404,15 +1918,34 @@ if __name__ == "__main__":
1404
  # Auto-login after signup
1405
  success, data = authenticate_user(username, password)
1406
  if not success:
1407
- return None, "❌ Account created but login failed", gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
 
 
 
 
1408
  else:
1409
- return None, f"❌ {message}", gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
 
 
 
 
1410
 
1411
- # Generate session code
 
 
 
 
1412
  code = create_session(data)
1413
 
1414
  # Get user info
1415
  info = get_user_limits_info(data['user_id'])
 
 
 
 
 
 
 
1416
  plan_class = f"plan-{info['plan'].lower()}"
1417
  greeting_html = f'<div class="user-greeting">{get_greeting(username)} <span class="plan-badge {plan_class}">{info["plan"]}</span></div>'
1418
 
@@ -1424,18 +1957,22 @@ if __name__ == "__main__":
1424
 
1425
  limits_html = render_limits_panel(data)
1426
 
 
 
1427
  return (
1428
  code,
1429
- f"✅ Welcome, {username}!",
 
1430
  gr.update(visible=False), # auth_page
1431
  gr.update(visible=True), # chat_page
1432
  gr.update(visible=data.get('is_admin', False)), # admin_page
1433
  greeting_html,
1434
  gr.update(visible=True), # upgrade_nav_btn
1435
  gr.update(visible=True), # logout_nav_btn
1436
- model_choices,
1437
- info['max_tokens'],
1438
- limits_html
 
1439
  )
1440
 
1441
  def show_upgrade_page():
@@ -1449,6 +1986,8 @@ if __name__ == "__main__":
1449
  invalidate_session(code)
1450
  return (
1451
  "",
 
 
1452
  gr.update(visible=True), # auth_page
1453
  gr.update(visible=False), # chat_page
1454
  gr.update(visible=False), # admin_page
@@ -1460,51 +1999,57 @@ if __name__ == "__main__":
1460
  ""
1461
  )
1462
 
1463
- def send_message_handler(message, show_thinking, temperature, model_choice, max_tokens, code):
1464
  global stop_generation
1465
  stop_generation.clear()
1466
 
1467
  if not code:
1468
- return "", "", '<div class="speed-indicator" style="background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%); color: #991b1b;">❌ Session expired</div>', gr.update(), gr.update(), ""
 
1469
 
1470
  data = validate_session(code)
1471
  if not data:
1472
- return "", "", '<div class="speed-indicator" style="background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%); color: #991b1b;">❌ Session expired</div>', gr.update(), gr.update(), ""
 
1473
 
1474
  if not message.strip():
1475
- return "", "", '<div class="speed-indicator">⚡ Ready</div>', gr.update(), gr.update(), ""
1476
 
1477
  info = get_user_limits_info(data['user_id'])
1478
 
1479
- # Model selection logic
1480
  if model_choice == "🤖 Auto (Recommended)" or not info['can_choose_model']:
1481
  user_available = get_available_models_for_user(data['user_id'])
1482
  if not user_available:
1483
- return "", "", '<div class="speed-indicator" style="background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%); color: #991b1b;">❌ No models available</div>', gr.update(), gr.update(), render_limits_panel(data)
 
1484
  backend = select_model_auto(message, available_models, user_available)
1485
  if not backend:
1486
- return "", "", '<div class="speed-indicator" style="background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%); color: #991b1b;">❌ Could not select model</div>', gr.update(), gr.update(), render_limits_panel(data)
 
1487
  model_name = backend.get_name()
1488
  else:
1489
  model_name = model_choice
1490
  can_use, msg = can_use_model(data['user_id'], model_name)
1491
  if not can_use:
1492
- return "", "", f'<div class="speed-indicator" style="background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%); color: #991b1b;">❌ {msg}</div>', gr.update(), gr.update(), render_limits_panel(data)
 
1493
  backend = available_models[model_name]
1494
 
1495
  # Final check
1496
  can_use, msg = can_use_model(data['user_id'], model_name)
1497
  if not can_use:
1498
- return "", "", f'<div class="speed-indicator" style="background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%); color: #991b1b;">❌ {msg}</div>', gr.update(), gr.update(), render_limits_panel(data)
 
1499
 
1500
  # Increment usage
1501
  increment_model_usage(data['user_id'], model_name)
1502
 
1503
- yield "", "", f'<div class="speed-indicator" style="background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); color: #1e40af;">⚡ Using {model_name}...</div>', gr.update(interactive=False), gr.update(visible=True), render_limits_panel(data)
1504
-
1505
- history = [{"role": "user", "content": message}]
1506
- yield "", render_history(history, show_thinking), '<div class="speed-indicator" style="background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); color: #1e40af;">⚡ Generating...</div>', gr.update(interactive=False), gr.update(visible=True), render_limits_panel(data)
1507
 
 
1508
  prompt = f"User: {message}\nSam: <think>"
1509
  history.append({"role": "assistant", "content": "<think>"})
1510
 
@@ -1525,18 +2070,31 @@ if __name__ == "__main__":
1525
  history[-1]["content"] += new_chunk
1526
 
1527
  last_speed = avg_speed
1528
- yield "", render_history(history, show_thinking), f'<div class="speed-indicator" style="background: linear-gradient(135deg, #dcfce7 0%, #bbf7d0 100%); color: #166534;">⚡ {tokens_per_sec:.1f} tok/s</div>', gr.update(interactive=False), gr.update(visible=True), render_limits_panel(data)
 
 
 
 
 
1529
 
1530
- final_html = f'<div class="speed-indicator" style="background: linear-gradient(135deg, {"#fee2e2 0%, #fecaca 100%); color: #991b1b;" if was_stopped else "#dcfce7 0%, #bbf7d0 100%); color: #166534;"}>{"🛑 Stopped" if was_stopped else "✅ Done"} - {last_speed:.1f} tok/s</div>'
1531
- yield "", render_history(history, show_thinking), final_html, gr.update(interactive=True), gr.update(visible=False), render_limits_panel(data)
1532
 
1533
  def stop_generation_handler():
1534
  global stop_generation
1535
  stop_generation.set()
1536
- return '<div class="speed-indicator" style="background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%); color: #991b1b;">🛑 Stopping...</div>', gr.update(interactive=False), gr.update(visible=False)
 
 
 
 
1537
 
1538
- def clear_chat():
1539
- return '<div class="chat-container"></div>', '<div class="speed-indicator">⚡ Ready</div>', gr.update(interactive=True), gr.update(visible=False)
 
 
 
 
 
1540
 
1541
  def submit_upgrade_request(code, plan, reason):
1542
  if not code:
@@ -1547,10 +2105,12 @@ if __name__ == "__main__":
1547
  return "❌ Session expired"
1548
 
1549
  if not reason.strip():
1550
- return "❌ Please provide a reason"
1551
 
1552
  success, msg = request_upgrade(data['user_id'], plan, reason)
1553
- return f"{'✅' if success else '❌'} {msg}"
 
 
1554
 
1555
  def load_all_users():
1556
  users = get_all_users()
@@ -1574,7 +2134,7 @@ if __name__ == "__main__":
1574
  req[0],
1575
  req[1],
1576
  req[2],
1577
- req[3],
1578
  req[4][:10] if req[4] else "N/A"
1579
  ])
1580
  return formatted
@@ -1583,60 +2143,63 @@ if __name__ == "__main__":
1583
  if not username or not new_plan:
1584
  return "❌ Please fill all fields"
1585
  success, msg = update_user_plan(username, new_plan)
1586
- return f"{'✅' if success else '❌'} {msg}"
 
 
1587
 
1588
  def admin_approve_request_handler(request_id):
1589
  if not request_id:
1590
- return "❌ Please enter request ID"
1591
  success, msg = approve_request(int(request_id))
1592
- return f"{'✅' if success else '❌'} {msg}"
 
 
1593
 
1594
  def admin_deny_request_handler(request_id):
1595
  if not request_id:
1596
- return "❌ Please enter request ID"
1597
  success, msg = deny_request(int(request_id))
1598
- return f"{'✅' if success else '❌'} {msg}"
 
 
1599
 
1600
  # ==================== WIRE UP EVENTS ====================
1601
 
1602
  # Auth
1603
  auth_outputs = [
1604
- session_code, auth_msg, auth_page, chat_page, admin_page,
1605
  user_greeting, upgrade_nav_btn, logout_nav_btn,
1606
- model_selector, max_tokens_slider, limits_display
1607
  ]
1608
- auth_btn.click(
1609
- handle_auth,
1610
- [auth_username, auth_password, auth_email],
1611
- auth_outputs
1612
- )
1613
 
1614
  # Navigation
1615
  upgrade_nav_btn.click(show_upgrade_page, outputs=[chat_page, upgrade_page])
1616
  back_to_chat_btn.click(back_to_chat, outputs=[chat_page, upgrade_page])
1617
 
1618
  logout_outputs = [
1619
- session_code, auth_page, chat_page, admin_page, upgrade_page,
1620
  user_greeting, upgrade_nav_btn, logout_nav_btn, chat_html, limits_display
1621
  ]
1622
  logout_nav_btn.click(handle_logout, [session_code], logout_outputs)
1623
 
1624
  # Chat
1625
- send_outputs = [msg_input, chat_html, speed_display, send_btn, stop_btn, limits_display]
1626
  send_btn.click(
1627
  send_message_handler,
1628
- [msg_input, show_thinking_checkbox, temperature_slider, model_selector, max_tokens_slider, session_code],
1629
  send_outputs
1630
  )
1631
  msg_input.submit(
1632
  send_message_handler,
1633
- [msg_input, show_thinking_checkbox, temperature_slider, model_selector, max_tokens_slider, session_code],
1634
  send_outputs
1635
  )
1636
 
1637
  stop_btn.click(stop_generation_handler, outputs=[speed_display, send_btn, stop_btn])
1638
- clear_btn.click(clear_chat, outputs=[chat_html, speed_display, send_btn, stop_btn])
1639
- new_chat_btn.click(clear_chat, outputs=[chat_html, speed_display, send_btn, stop_btn])
1640
 
1641
  # Upgrade
1642
  submit_upgrade_btn.click(
@@ -1655,27 +2218,135 @@ if __name__ == "__main__":
1655
  # Footer
1656
  gr.Markdown("""
1657
  ---
1658
- ### 🚀 Getting Started
1659
 
1660
- 1. **Enter your username and password** - We'll automatically create an account if you're new!
1661
- 2. **Start chatting** - Ask anything and SAM-X-1 will help you
1662
- 3. **Need more?** - Click the ⭐ Upgrade button to see plans and request an upgrade
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1663
 
1664
- ### 📊 Quick Plan Overview
1665
 
1666
- - **Free**: Nano & Mini unlimited, Fast 10/3h, Large 8/3h
1667
- - **Plus**: Fast unlimited, Large 20/3h + manual model selection
1668
- - **Pro**: Everything unlimited + 512 tokens + priority support
 
 
 
 
 
 
1669
 
1670
- ### 💡 Tips
1671
 
1672
- - Use **Auto mode** for best results - SAM will pick the right model for you
1673
- - Watch the **limits panel** on the right to track your usage
1674
- - Enable **Show Thinking** to see SAM's reasoning process
1675
 
1676
  ---
1677
 
1678
- Made with ❤️ by the SAM-X-1 Team | Powered by Keras & TensorFlow
 
 
 
 
 
 
1679
  """)
1680
 
1681
- demo.launch(debug=True, share=False, server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
 
 
 
806
  final_tokens_per_sec = tokens_generated / elapsed if elapsed > 0 else 0
807
  yield "", False, final_tokens_per_sec, final_tokens_per_sec, False
808
 
809
+ # PART 3 - Production-Grade Multi-Page UI (No Backend Changes)
 
810
 
811
  import secrets
812
+ import json
813
+ from datetime import datetime
814
 
815
  # Global session storage (unchanged from original)
816
  active_sessions = {}
 
850
  import gradio as gr
851
 
852
  custom_css = """
853
+ /* Modern Production-Grade Styling */
854
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
855
+
856
+ * { font-family: 'Inter', sans-serif; }
857
+
858
+ .app-container { max-width: 1600px; margin: 0 auto; }
859
+
860
+ /* Dark Mode Support */
861
+ .dark-mode { background: #1a1a1a; color: #e5e5e5; }
862
+ .dark-mode .nav-bar { background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); }
863
+ .dark-mode .chat-container { background: #2d3748; border-color: #4a5568; }
864
+ .dark-mode .assistant-message { background: #374151; border-color: #10a37f; }
865
 
866
  /* Navigation Bar */
867
  .nav-bar {
868
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
869
+ padding: 14px 28px;
870
  border-radius: 12px;
871
+ margin-bottom: 20px;
872
  display: flex;
873
  justify-content: space-between;
874
  align-items: center;
875
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
876
+ position: sticky;
877
+ top: 0;
878
+ z-index: 100;
879
  }
880
+ .nav-left { display: flex; align-items: center; gap: 20px; }
881
+ .nav-brand {
882
+ font-size: 22px;
883
+ font-weight: 700;
884
  color: white;
885
+ display: flex;
886
+ align-items: center;
887
+ gap: 8px;
888
+ }
889
+ .nav-right { display: flex; align-items: center; gap: 12px; }
890
+ .user-greeting {
891
+ color: white;
892
+ font-weight: 500;
893
+ font-size: 14px;
894
+ display: flex;
895
+ align-items: center;
896
+ gap: 8px;
897
+ padding: 6px 12px;
898
+ background: rgba(255,255,255,0.15);
899
+ border-radius: 20px;
900
  }
 
 
 
 
901
 
902
  /* Plan Badge */
903
  .plan-badge {
904
  display: inline-block;
905
+ padding: 4px 10px;
906
+ border-radius: 12px;
907
+ font-size: 10px;
908
  font-weight: 700;
 
909
  text-transform: uppercase;
910
  letter-spacing: 0.5px;
911
+ animation: badge-glow 2s ease-in-out infinite;
912
+ }
913
+ @keyframes badge-glow {
914
+ 0%, 100% { box-shadow: 0 0 5px rgba(255,255,255,0.3); }
915
+ 50% { box-shadow: 0 0 15px rgba(255,255,255,0.5); }
916
  }
917
  .plan-free { background: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 100%); color: #3730a3; }
918
  .plan-plus { background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); color: #1e40af; }
 
923
 
924
  /* Auth Page */
925
  .auth-container {
926
+ max-width: 440px;
927
+ margin: 40px auto;
928
  background: white;
929
+ padding: 36px;
930
  border-radius: 16px;
931
+ box-shadow: 0 10px 40px rgba(0,0,0,0.1);
932
+ animation: slideUp 0.4s ease-out;
933
+ }
934
+ @keyframes slideUp {
935
+ from { opacity: 0; transform: translateY(20px); }
936
+ to { opacity: 1; transform: translateY(0); }
937
  }
938
  .auth-title {
939
+ font-size: 28px;
940
  font-weight: 700;
941
  text-align: center;
942
+ margin-bottom: 6px;
943
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
944
  -webkit-background-clip: text;
945
  -webkit-text-fill-color: transparent;
 
947
  .auth-subtitle {
948
  text-align: center;
949
  color: #6b7280;
950
+ margin-bottom: 28px;
951
+ font-size: 13px;
952
  }
953
 
954
  /* Chat Interface */
955
+ .chat-layout { display: flex; gap: 20px; }
956
+ .chat-main { flex: 1; min-width: 0; }
957
+ .chat-sidebar { width: 320px; flex-shrink: 0; }
958
+
959
  .chat-container {
960
  height: 520px;
961
  overflow-y: auto;
962
+ padding: 20px;
963
  background: #f9fafb;
964
  border: 1px solid #e5e7eb;
965
  border-radius: 12px;
966
+ margin-bottom: 12px;
967
+ scroll-behavior: smooth;
968
  }
969
+ .chat-container::-webkit-scrollbar { width: 6px; }
970
+ .chat-container::-webkit-scrollbar-track { background: transparent; }
971
+ .chat-container::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }
972
+ .chat-container::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
973
+
974
  .user-message {
975
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
976
  color: white;
977
+ padding: 14px 18px;
978
+ margin: 10px 0;
979
  border-radius: 16px 16px 4px 16px;
980
+ max-width: 75%;
981
  margin-left: auto;
982
+ box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
983
+ animation: messageSlideIn 0.3s ease-out;
984
  }
985
  .assistant-message {
986
  background: white;
987
+ padding: 14px 18px;
988
+ margin: 10px 0;
989
  border-radius: 16px 16px 16px 4px;
990
+ border-left: 3px solid #10a37f;
991
+ max-width: 75%;
992
+ box-shadow: 0 2px 8px rgba(0,0,0,0.06);
993
+ animation: messageSlideIn 0.3s ease-out;
994
+ position: relative;
995
+ }
996
+ @keyframes messageSlideIn {
997
+ from { opacity: 0; transform: translateY(10px); }
998
+ to { opacity: 1; transform: translateY(0); }
999
+ }
1000
+
1001
+ .message-content {
1002
+ color: #353740;
1003
+ line-height: 1.6;
1004
+ font-size: 14px;
1005
+ word-wrap: break-word;
1006
  }
 
1007
  .user-message .message-content { color: white; }
1008
+
1009
+ /* Markdown Styling */
1010
+ .message-content code {
1011
+ background: #f3f4f6;
1012
+ padding: 2px 6px;
1013
+ border-radius: 4px;
1014
+ font-family: 'Courier New', monospace;
1015
+ font-size: 13px;
1016
+ }
1017
+ .message-content pre {
1018
+ background: #1f2937;
1019
+ color: #e5e7eb;
1020
+ padding: 12px;
1021
+ border-radius: 8px;
1022
+ overflow-x: auto;
1023
+ margin: 8px 0;
1024
+ position: relative;
1025
+ }
1026
+ .message-content pre code {
1027
+ background: transparent;
1028
+ padding: 0;
1029
+ color: inherit;
1030
+ }
1031
+ .message-content ul, .message-content ol {
1032
+ margin: 8px 0;
1033
+ padding-left: 20px;
1034
+ }
1035
+ .message-content li { margin: 4px 0; }
1036
+ .message-content strong { font-weight: 600; }
1037
+ .message-content em { font-style: italic; }
1038
+ .message-content a { color: #667eea; text-decoration: underline; }
1039
+
1040
+ /* Code Copy Button */
1041
+ .copy-button {
1042
+ position: absolute;
1043
+ top: 8px;
1044
+ right: 8px;
1045
+ background: rgba(255,255,255,0.1);
1046
+ border: 1px solid rgba(255,255,255,0.2);
1047
+ color: white;
1048
+ padding: 4px 8px;
1049
+ border-radius: 4px;
1050
+ font-size: 11px;
1051
+ cursor: pointer;
1052
+ opacity: 0;
1053
+ transition: all 0.2s;
1054
+ }
1055
+ .assistant-message:hover .copy-button { opacity: 1; }
1056
+ .copy-button:hover { background: rgba(255,255,255,0.2); }
1057
+
1058
  .thinking-content {
1059
  color: #6b7280;
1060
  font-style: italic;
1061
  border-left: 3px solid #d1d5db;
1062
  padding-left: 12px;
1063
+ margin: 10px 0;
1064
  background: #f9fafb;
1065
+ padding: 10px 12px;
1066
+ border-radius: 6px;
1067
+ font-size: 13px;
1068
+ }
1069
+
1070
+ /* Message Actions */
1071
+ .message-actions {
1072
+ display: flex;
1073
+ gap: 8px;
1074
+ margin-top: 8px;
1075
+ opacity: 0;
1076
+ transition: opacity 0.2s;
1077
+ }
1078
+ .assistant-message:hover .message-actions { opacity: 1; }
1079
+ .action-btn {
1080
+ background: #f3f4f6;
1081
+ border: 1px solid #e5e7eb;
1082
+ padding: 4px 10px;
1083
+ border-radius: 6px;
1084
+ font-size: 12px;
1085
+ cursor: pointer;
1086
+ transition: all 0.2s;
1087
+ color: #6b7280;
1088
+ }
1089
+ .action-btn:hover {
1090
+ background: #e5e7eb;
1091
+ color: #374151;
1092
+ transform: translateY(-1px);
1093
  }
1094
 
1095
  /* Input Area */
1096
+ .input-container {
1097
+ background: white;
1098
+ border: 2px solid #e5e7eb;
1099
+ border-radius: 12px;
1100
+ padding: 4px;
1101
+ transition: all 0.2s;
1102
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
1103
+ }
1104
+ .input-container:focus-within {
1105
+ border-color: #667eea;
1106
+ box-shadow: 0 4px 16px rgba(102, 126, 234, 0.2);
1107
+ }
1108
  .input-row {
1109
  display: flex;
1110
+ gap: 8px;
1111
  align-items: flex-end;
1112
  }
1113
  .circular-btn {
1114
+ width: 46px !important;
1115
+ height: 46px !important;
1116
+ min-width: 46px !important;
1117
  border-radius: 50% !important;
1118
  padding: 0 !important;
1119
+ font-size: 20px !important;
1120
  box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
1121
  transition: all 0.2s ease !important;
1122
  border: none !important;
1123
  }
1124
  .circular-btn:hover:not(:disabled) {
1125
  transform: scale(1.08) !important;
1126
+ box-shadow: 0 6px 16px rgba(0,0,0,0.25) !important;
1127
+ }
1128
+ .circular-btn:active:not(:disabled) {
1129
+ transform: scale(0.95) !important;
1130
  }
1131
  .send-btn {
1132
  background: linear-gradient(135deg, #10a37f 0%, #0d8c6c 100%) !important;
 
1135
  background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%) !important;
1136
  }
1137
 
1138
+ /* Token Counter */
1139
+ .token-counter {
1140
+ font-size: 11px;
1141
+ color: #9ca3af;
1142
+ text-align: right;
1143
+ padding: 4px 8px;
1144
+ }
1145
+
1146
  /* Sidebar/Limits Panel */
1147
  .limits-panel {
1148
  background: white;
1149
  border: 1px solid #e5e7eb;
1150
  border-radius: 12px;
1151
+ padding: 18px;
1152
+ margin-bottom: 14px;
1153
  box-shadow: 0 2px 8px rgba(0,0,0,0.05);
1154
  }
1155
+ .limit-header {
1156
+ font-weight: 700;
1157
+ margin-bottom: 14px;
1158
+ font-size: 16px;
1159
+ color: #1f2937;
1160
+ display: flex;
1161
+ justify-content: space-between;
1162
+ align-items: center;
1163
+ }
1164
  .limit-item {
1165
  display: flex;
1166
  justify-content: space-between;
1167
+ padding: 10px 0;
1168
  border-bottom: 1px solid #f3f4f6;
1169
  align-items: center;
1170
  }
1171
  .limit-item:last-child { border-bottom: none; }
1172
+ .limit-label {
1173
+ font-size: 13px;
1174
+ color: #6b7280;
1175
+ font-weight: 500;
1176
+ }
1177
+ .limit-value {
1178
+ font-size: 13px;
1179
+ font-weight: 600;
1180
+ }
1181
+ .limit-exceeded { color: #dc2626; }
1182
+ .limit-ok { color: #059669; }
1183
+ .limit-warning { color: #f59e0b; }
1184
+
1185
+ /* Progress Bar */
1186
+ .progress-bar {
1187
+ height: 6px;
1188
+ background: #f3f4f6;
1189
+ border-radius: 3px;
1190
+ overflow: hidden;
1191
+ margin-top: 6px;
1192
+ }
1193
+ .progress-fill {
1194
+ height: 100%;
1195
+ background: linear-gradient(90deg, #10a37f 0%, #059669 100%);
1196
+ transition: width 0.3s ease;
1197
+ border-radius: 3px;
1198
+ }
1199
+ .progress-fill.warning { background: linear-gradient(90deg, #f59e0b 0%, #ea580c 100%); }
1200
+ .progress-fill.danger { background: linear-gradient(90deg, #ef4444 0%, #dc2626 100%); }
1201
 
1202
  /* Plans Section */
1203
  .plans-grid {
1204
  display: grid;
1205
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
1206
+ gap: 20px;
1207
+ margin-top: 20px;
1208
  }
1209
  .plan-card {
1210
  background: white;
1211
  border: 2px solid #e5e7eb;
1212
+ border-radius: 14px;
1213
+ padding: 24px;
1214
  transition: all 0.3s;
1215
  position: relative;
1216
  overflow: hidden;
1217
  }
1218
  .plan-card:hover {
1219
+ transform: translateY(-6px);
1220
+ box-shadow: 0 12px 28px rgba(0,0,0,0.15);
1221
  border-color: #667eea;
1222
  }
1223
  .plan-card.featured {
1224
  border: 3px solid #667eea;
1225
+ box-shadow: 0 8px 24px rgba(102, 126, 234, 0.25);
1226
+ transform: scale(1.02);
1227
  }
1228
  .plan-card.featured::before {
1229
  content: '⭐ POPULAR';
1230
  position: absolute;
1231
+ top: 14px;
1232
+ right: -28px;
1233
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1234
  color: white;
1235
+ padding: 3px 36px;
1236
+ font-size: 10px;
1237
  font-weight: 700;
1238
  letter-spacing: 1px;
1239
  transform: rotate(45deg);
1240
  }
1241
  .plan-name {
1242
+ font-size: 22px;
1243
  font-weight: 700;
1244
+ margin-bottom: 6px;
1245
  color: #1f2937;
1246
  }
1247
  .plan-price {
1248
+ font-size: 13px;
1249
  color: #6b7280;
1250
+ margin-bottom: 18px;
1251
  }
1252
  .plan-features {
1253
  list-style: none;
1254
  padding: 0;
1255
+ margin: 16px 0;
1256
  }
1257
  .plan-features li {
1258
+ padding: 6px 0;
1259
  color: #4b5563;
1260
+ font-size: 13px;
1261
  }
1262
  .plan-features li::before {
1263
  content: '✓ ';
1264
  color: #10a37f;
1265
  font-weight: 700;
1266
+ margin-right: 6px;
1267
  }
1268
 
1269
+ /* Speed Indicator */
1270
+ .speed-indicator {
1271
+ text-align: center;
1272
+ padding: 10px;
1273
+ background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
1274
+ border-radius: 8px;
1275
+ font-weight: 600;
1276
+ color: #166534;
1277
+ margin-bottom: 10px;
1278
+ font-size: 13px;
1279
+ display: flex;
1280
+ align-items: center;
1281
+ justify-content: center;
1282
+ gap: 8px;
1283
  }
1284
+ .speed-indicator.generating {
1285
+ background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%);
1286
+ color: #1e40af;
1287
+ animation: pulse 2s ease-in-out infinite;
1288
+ }
1289
+ @keyframes pulse {
1290
+ 0%, 100% { opacity: 1; }
1291
+ 50% { opacity: 0.8; }
1292
+ }
1293
+ .speed-indicator.error {
1294
+ background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%);
1295
+ color: #991b1b;
1296
  }
1297
 
1298
+ /* Toast Notifications */
1299
+ .toast {
1300
+ position: fixed;
1301
+ top: 80px;
1302
+ right: 20px;
1303
+ background: white;
1304
+ padding: 12px 18px;
1305
+ border-radius: 8px;
1306
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
1307
+ display: flex;
1308
+ align-items: center;
1309
+ gap: 10px;
1310
+ z-index: 1000;
1311
+ animation: toastSlide 0.3s ease-out;
1312
+ }
1313
+ @keyframes toastSlide {
1314
+ from { transform: translateX(400px); opacity: 0; }
1315
+ to { transform: translateX(0); opacity: 1; }
1316
+ }
1317
+ .toast.success { border-left: 4px solid #10a37f; }
1318
+ .toast.error { border-left: 4px solid #ef4444; }
1319
+ .toast.info { border-left: 4px solid #3b82f6; }
1320
+
1321
  /* Settings Panel */
1322
  .settings-panel {
1323
  background: white;
1324
  border: 1px solid #e5e7eb;
1325
  border-radius: 12px;
1326
+ padding: 16px;
1327
+ margin-bottom: 14px;
1328
  }
1329
 
1330
+ /* Keyboard Shortcuts */
1331
+ .kbd {
1332
+ display: inline-block;
1333
+ padding: 2px 6px;
1334
+ background: #f3f4f6;
1335
+ border: 1px solid #d1d5db;
1336
+ border-radius: 4px;
1337
+ font-family: monospace;
1338
+ font-size: 11px;
1339
+ color: #4b5563;
1340
+ }
1341
+
1342
+ /* Empty State */
1343
+ .empty-state {
1344
+ text-align: center;
1345
+ padding: 60px 20px;
1346
+ color: #9ca3af;
1347
+ }
1348
+ .empty-state-icon { font-size: 48px; margin-bottom: 16px; opacity: 0.5; }
1349
+ .empty-state-title { font-size: 18px; font-weight: 600; color: #6b7280; margin-bottom: 8px; }
1350
+ .empty-state-subtitle { font-size: 14px; color: #9ca3af; }
1351
+
1352
+ /* Skeleton Loader */
1353
+ .skeleton {
1354
+ background: linear-gradient(90deg, #f3f4f6 25%, #e5e7eb 50%, #f3f4f6 75%);
1355
+ background-size: 200% 100%;
1356
+ animation: shimmer 1.5s infinite;
1357
+ border-radius: 4px;
1358
+ }
1359
+ @keyframes shimmer {
1360
+ 0% { background-position: 200% 0; }
1361
+ 100% { background-position: -200% 0; }
1362
  }
1363
 
1364
  /* Responsive */
1365
+ @media (max-width: 1024px) {
1366
+ .chat-layout { flex-direction: column; }
1367
+ .chat-sidebar { width: 100%; }
1368
+ }
1369
  @media (max-width: 768px) {
1370
+ .nav-bar { flex-direction: column; gap: 12px; padding: 12px 16px; }
1371
  .nav-left, .nav-right { width: 100%; justify-content: center; }
1372
  .chat-container { height: 400px; }
1373
  .plans-grid { grid-template-columns: 1fr; }
1374
+ .user-message, .assistant-message { max-width: 90%; }
1375
  }
1376
+
1377
+ /* Smooth Transitions */
1378
+ * { transition: background-color 0.2s, border-color 0.2s, color 0.2s; }
1379
+ button { transition: all 0.2s !important; }
1380
  """
1381
 
1382
+ # Greeting variations
1383
  def get_greeting(username):
1384
  import random
1385
  greetings = [
 
1388
  f"Hi {username}! 🚀",
1389
  f"Hello {username}! 😊",
1390
  f"Great to see you, {username}! 🎉",
1391
+ f"What's up, {username}? 💫",
1392
+ f"Howdy, {username}! 🤠",
1393
+ f"Yo {username}! 🔥"
1394
  ]
1395
  return random.choice(greetings)
1396
 
1397
+ # Markdown rendering (simple version)
1398
+ def render_markdown(text):
1399
+ """Simple markdown rendering for common patterns"""
1400
+ import re
1401
+
1402
+ # Code blocks
1403
+ text = re.sub(r'```(\w+)?\n(.*?)```', r'<pre><code class="\1">\2</code><button class="copy-button" onclick="copyCode(this)">Copy</button></pre>', text, flags=re.DOTALL)
1404
+
1405
+ # Inline code
1406
+ text = re.sub(r'`([^`]+)`', r'<code>\1</code>', text)
1407
+
1408
+ # Bold
1409
+ text = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', text)
1410
+ text = re.sub(r'__(.+?)__', r'<strong>\1</strong>', text)
1411
+
1412
+ # Italic
1413
+ text = re.sub(r'\*(.+?)\*', r'<em>\1</em>', text)
1414
+ text = re.sub(r'_(.+?)_', r'<em>\1</em>', text)
1415
+
1416
+ # Links
1417
+ text = re.sub(r'\[([^\]]+)\]\(([^\)]+)\)', r'<a href="\2" target="_blank">\1</a>', text)
1418
+
1419
+ # Lists
1420
+ text = re.sub(r'^- (.+)$', r'<li>\1</li>', text, flags=re.MULTILINE)
1421
+ text = re.sub(r'^(\d+)\. (.+)$', r'<li>\2</li>', text, flags=re.MULTILINE)
1422
+ text = re.sub(r'(<li>.*?</li>\n?)+', r'<ul>\g<0></ul>', text, flags=re.DOTALL)
1423
+
1424
+ # Line breaks
1425
+ text = text.replace('\n', '<br>')
1426
+
1427
+ return text
1428
+
1429
+ # Format message HTML with markdown and actions
1430
+ def format_message_html(role, content, show_thinking=True, message_id=None):
1431
  role_class = "user-message" if role == "user" else "assistant-message"
1432
  thinking = ""
1433
  answer = ""
1434
 
1435
+ # Extract thinking
1436
  if "<think>" in content:
1437
  parts = content.split("<think>", 1)
1438
  before_think = parts[0].strip()
 
1454
  else:
1455
  answer = content
1456
 
1457
+ # Render markdown
1458
+ answer = render_markdown(answer)
1459
+
1460
+ html = f'<div class="{role_class}" id="msg-{message_id}"><div class="message-content">'
1461
+
1462
  if thinking and show_thinking:
1463
+ html += f'<div class="thinking-content">💭 {render_markdown(thinking)}</div>'
1464
+
1465
  if answer:
1466
  html += f'<div>{answer}</div>'
1467
+
1468
+ # Add message actions for assistant messages
1469
+ if role == "assistant":
1470
+ html += '''
1471
+ <div class="message-actions">
1472
+ <button class="action-btn" onclick="copyMessage(this)">📋 Copy</button>
1473
+ <button class="action-btn" onclick="regenerateResponse(this)">🔄 Regenerate</button>
1474
+ </div>
1475
+ '''
1476
+
1477
  html += '</div></div>'
1478
  return html
1479
 
1480
  def render_history(history, show_thinking):
1481
+ if not history:
1482
+ return '''
1483
+ <div class="empty-state">
1484
+ <div class="empty-state-icon">💬</div>
1485
+ <div class="empty-state-title">No messages yet</div>
1486
+ <div class="empty-state-subtitle">Start a conversation by typing below</div>
1487
+ </div>
1488
+ '''
1489
+
1490
  html = ""
1491
+ for idx, msg in enumerate(history):
1492
+ html += format_message_html(msg["role"], msg["content"], show_thinking, idx)
1493
  return html
1494
 
1495
  def render_limits_panel(user_data):
1496
  if not user_data or 'user_id' not in user_data:
1497
  return ""
1498
+
1499
  info = get_user_limits_info(user_data['user_id'])
1500
  if not info:
1501
  return ""
1502
 
1503
  plan_class = f"plan-{info['plan'].lower()}"
1504
+
1505
  html = f'''<div class="limits-panel">
1506
+ <div class="limit-header">
1507
+ <span>Usage Limits <span class="plan-badge {plan_class}">{info["plan"]}</span></span>
 
1508
  </div>
1509
+ <div style="font-size: 12px; color: #6b7280; margin-bottom: 14px; padding: 8px; background: #f9fafb; border-radius: 6px; text-align: center;">
1510
+ ⏰ <strong>{info["reset_in"]}</strong> until reset
1511
  </div>'''
1512
 
1513
  models_info = [
1514
+ ('Nano ', info['nano_used'], info['nano_limit']),
1515
  ('Mini 🚀', info['mini_used'], info['mini_limit']),
1516
  ('Fast ⚡', info['fast_used'], info['fast_limit']),
1517
  ('Large 💎', info['large_used'], info['large_limit'])
 
1519
 
1520
  for model_name, used, limit in models_info:
1521
  if limit == -1:
1522
+ percentage = 0
1523
+ status_class = "limit-ok"
1524
+ status_text = f'{used} / ∞'
1525
+ bar_class = ""
1526
  else:
1527
+ percentage = min((used / limit * 100), 100)
1528
  remaining = limit - used
1529
  if remaining <= 0:
1530
+ status_class = "limit-exceeded"
1531
+ status_text = f'{used}/{limit}'
1532
+ bar_class = "danger"
1533
  elif remaining <= 2:
1534
+ status_class = "limit-warning"
1535
+ status_text = f'{used}/{limit}'
1536
+ bar_class = "warning"
1537
  else:
1538
+ status_class = "limit-ok"
1539
+ status_text = f'{used}/{limit}'
1540
+ bar_class = ""
1541
+
1542
+ html += f'''
1543
+ <div class="limit-item">
1544
+ <span class="limit-label">{model_name}</span>
1545
+ <span class="limit-value {status_class}">{status_text}</span>
1546
+ </div>
1547
+ <div class="progress-bar">
1548
+ <div class="progress-fill {bar_class}" style="width: {percentage}%"></div>
1549
+ </div>
1550
+ '''
1551
 
1552
  html += '</div>'
1553
  return html
1554
 
1555
+ with gr.Blocks(css=custom_css, title="SAM-X-1 AI Chat", theme=gr.themes.Soft(primary_hue="slate")) as demo:
1556
+
1557
+ # JavaScript for interactive features
1558
+ gr.HTML("""
1559
+ <script>
1560
+ function copyCode(button) {
1561
+ const pre = button.parentElement;
1562
+ const code = pre.querySelector('code').textContent;
1563
+ navigator.clipboard.writeText(code).then(() => {
1564
+ button.textContent = 'Copied!';
1565
+ setTimeout(() => button.textContent = 'Copy', 2000);
1566
+ });
1567
+ }
1568
+
1569
+ function copyMessage(button) {
1570
+ const messageDiv = button.closest('.assistant-message');
1571
+ const content = messageDiv.querySelector('.message-content').textContent;
1572
+ navigator.clipboard.writeText(content).then(() => {
1573
+ showToast('Message copied!', 'success');
1574
+ });
1575
+ }
1576
 
1577
+ function regenerateResponse(button) {
1578
+ showToast('Regeneration feature coming soon!', 'info');
1579
+ }
1580
+
1581
+ function showToast(message, type = 'info') {
1582
+ const toast = document.createElement('div');
1583
+ toast.className = `toast ${type}`;
1584
+ toast.innerHTML = `
1585
+ <span>${type === 'success' ? '✓' : type === 'error' ? '✗' : 'ℹ'}</span>
1586
+ <span>${message}</span>
1587
+ `;
1588
+ document.body.appendChild(toast);
1589
+ setTimeout(() => toast.remove(), 3000);
1590
+ }
1591
+
1592
+ // Keyboard shortcuts
1593
+ document.addEventListener('keydown', function(e) {
1594
+ // Ctrl/Cmd + K for search (future feature)
1595
+ if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
1596
+ e.preventDefault();
1597
+ showToast('Search coming soon!', 'info');
1598
+ }
1599
+
1600
+ // Esc to stop generation
1601
+ if (e.key === 'Escape') {
1602
+ const stopBtn = document.querySelector('.stop-btn');
1603
+ if (stopBtn && !stopBtn.disabled) stopBtn.click();
1604
+ }
1605
+ });
1606
+
1607
+ // Auto-scroll chat to bottom
1608
+ function scrollChatToBottom() {
1609
+ const chatContainer = document.querySelector('.chat-container');
1610
+ if (chatContainer) {
1611
+ chatContainer.scrollTop = chatContainer.scrollHeight;
1612
+ }
1613
+ }
1614
+
1615
+ // Call after messages update
1616
+ setInterval(scrollChatToBottom, 500);
1617
+ </script>
1618
+ """)
1619
+
1620
+ # State management
1621
  session_code = gr.State("")
1622
  user_data = gr.State(None)
1623
+ chat_history = gr.State([])
1624
 
1625
  # Navigation Bar
1626
  with gr.Row(elem_classes="nav-bar"):
1627
  with gr.Column(scale=1, elem_classes="nav-left"):
1628
+ gr.HTML('<div class="nav-brand">🤖 SAM-X-1 <span style="font-size: 12px; opacity: 0.8; font-weight: 400;">v3.0</span></div>')
1629
  with gr.Column(scale=2, elem_classes="nav-right"):
1630
  user_greeting = gr.HTML('<div class="user-greeting">Please sign in</div>')
1631
+ with gr.Row():
1632
+ upgrade_nav_btn = gr.Button(" Upgrade", size="sm", visible=False)
1633
+ logout_nav_btn = gr.Button("🚪 Logout", size="sm", visible=False)
1634
 
1635
  # AUTH PAGE
1636
  with gr.Group(visible=True) as auth_page:
1637
  with gr.Column(elem_classes="auth-container"):
1638
  gr.HTML('<div class="auth-title">Welcome to SAM-X-1</div>')
1639
+ gr.HTML('<div class="auth-subtitle">Sign in or create account • Auto-detects new users</div>')
1640
 
1641
+ auth_username = gr.Textbox(
1642
+ label="Username",
1643
+ placeholder="Enter your username",
1644
+ elem_id="auth-username"
1645
+ )
1646
+ auth_password = gr.Textbox(
1647
+ label="Password",
1648
+ type="password",
1649
+ placeholder="Enter your password",
1650
+ elem_id="auth-password"
1651
+ )
1652
+ auth_email = gr.Textbox(
1653
+ label="Email (optional, for new accounts)",
1654
+ placeholder="[email protected]"
1655
+ )
1656
 
1657
  auth_btn = gr.Button("Continue →", variant="primary", size="lg")
1658
  auth_msg = gr.Markdown("")
1659
+
1660
+ gr.Markdown("""
1661
+ <div style="text-align: center; margin-top: 20px; font-size: 12px; color: #9ca3af;">
1662
+ <p>🔐 Secure authentication • 🆓 Free tier available</p>
1663
+ <p>Press <span class="kbd">Enter</span> to continue</p>
1664
+ </div>
1665
+ """)
1666
 
1667
  # CHAT PAGE
1668
  with gr.Group(visible=False) as chat_page:
1669
+ with gr.Row(elem_classes="chat-layout"):
1670
+ # Main Chat Area
1671
+ with gr.Column(elem_classes="chat-main"):
1672
+ chat_html = gr.HTML(value='')
1673
 
1674
+ speed_display = gr.HTML('<div class="speed-indicator">⚡ Ready to chat</div>')
 
 
 
 
 
 
 
 
1675
 
1676
+ with gr.Column(elem_classes="input-container"):
1677
+ with gr.Row(elem_classes="input-row"):
1678
+ msg_input = gr.Textbox(
1679
+ placeholder="Ask me anything... (Shift+Enter for new line)",
1680
+ show_label=False,
1681
+ scale=10,
1682
+ lines=1,
1683
+ max_lines=5,
1684
+ elem_id="chat-input"
1685
+ )
1686
+ send_btn = gr.Button("▶", elem_classes=["circular-btn", "send-btn"])
1687
+ stop_btn = gr.Button("⏹", elem_classes=["circular-btn", "stop-btn"], visible=False)
1688
+
1689
+ token_counter = gr.HTML('<div class="token-counter">0 / 256 tokens</div>')
1690
 
1691
  with gr.Row():
1692
+ clear_btn = gr.Button("🗑️ Clear Chat", size="sm")
1693
  new_chat_btn = gr.Button("➕ New Chat", size="sm", variant="primary")
1694
+ export_btn = gr.Button("📥 Export", size="sm")
1695
 
1696
+ # Sidebar
1697
+ with gr.Column(elem_classes="chat-sidebar"):
1698
  limits_display = gr.HTML("")
1699
 
1700
  with gr.Accordion("⚙️ Settings", open=False, elem_classes="settings-panel"):
1701
  model_selector = gr.Dropdown(
1702
  choices=["🤖 Auto (Recommended)"],
1703
  value="🤖 Auto (Recommended)",
1704
+ label="Model Selection",
1705
+ info="AI picks the best model"
1706
  )
1707
  max_tokens_slider = gr.Slider(
1708
  minimum=64, maximum=512, value=256, step=64,
1709
+ label="Max Tokens",
1710
+ info="Response length limit"
1711
  )
1712
  temperature_slider = gr.Slider(
1713
  minimum=0.0, maximum=2.0, value=0.7, step=0.1,
1714
+ label="Temperature",
1715
+ info="Creativity level"
1716
  )
1717
  show_thinking_checkbox = gr.Checkbox(
1718
+ label="💭 Show Thinking Process",
1719
+ value=True,
1720
+ info="See AI's reasoning"
1721
  )
1722
+
1723
+ with gr.Accordion("ℹ️ Tips & Shortcuts", open=False):
1724
+ gr.Markdown("""
1725
+ ### Keyboard Shortcuts
1726
+ - <span class="kbd">Enter</span> - Send message
1727
+ - <span class="kbd">Shift+Enter</span> - New line
1728
+ - <span class="kbd">Esc</span> - Stop generation
1729
+ - <span class="kbd">Ctrl+K</span> - Search (soon)
1730
+
1731
+ ### Tips
1732
+ - Be specific in your questions
1733
+ - Use markdown for formatting
1734
+ - Auto mode picks the best model
1735
+ - Check limits panel regularly
1736
+ """)
1737
 
1738
+ # UPGRADE PAGE
1739
  with gr.Group(visible=False) as upgrade_page:
1740
+ gr.HTML('''
1741
+ <div style="text-align: center; margin-bottom: 32px;">
1742
+ <div style="font-size: 32px; font-weight: 700; margin-bottom: 8px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent;">
1743
+ Choose Your Plan
1744
+ </div>
1745
+ <div style="font-size: 16px; color: #6b7280;">
1746
+ Unlock more power and flexibility
1747
+ </div>
1748
+ </div>
1749
+ ''')
1750
 
1751
+ gr.HTML('''
1752
+ <div class="plans-grid">
1753
+ <div class="plan-card">
1754
+ <div class="plan-name">Free 🆓</div>
1755
+ <div class="plan-price">Perfect for getting started</div>
1756
+ <ul class="plan-features">
1757
+ <li>Nano: Unlimited messages</li>
1758
+ <li>Mini: Unlimited messages</li>
1759
+ <li>Fast: 10 messages/3h</li>
1760
+ <li>Large: 8 messages/3h</li>
1761
+ <li>Auto model selection</li>
1762
+ <li>256 max tokens</li>
1763
+ <li>Community support</li>
1764
+ </ul>
1765
+ </div>
1766
+
1767
+ <div class="plan-card featured">
1768
+ <div class="plan-name">Plus ⭐</div>
1769
+ <div class="plan-price">Great for power users</div>
1770
+ <ul class="plan-features">
1771
+ <li>Everything in Free</li>
1772
+ <li>Fast: Unlimited messages</li>
1773
+ <li>Large: 20 messages/3h</li>
1774
+ <li>Manual model selection</li>
1775
+ <li>384 max tokens</li>
1776
+ <li>Priority support</li>
1777
+ <li>Advanced settings</li>
1778
+ </ul>
 
 
 
 
 
 
 
 
 
 
 
 
 
1779
  </div>
1780
+
1781
+ <div class="plan-card">
1782
+ <div class="plan-name">Pro 💎</div>
1783
+ <div class="plan-price">For professionals</div>
1784
+ <ul class="plan-features">
1785
+ <li>Everything in Plus</li>
1786
+ <li>All models unlimited</li>
1787
+ <li>512 max tokens</li>
1788
+ <li>Fastest reset (3h)</li>
1789
+ <li>24/7 premium support</li>
1790
+ <li>Early feature access</li>
1791
+ <li>API access (soon)</li>
1792
+ </ul>
1793
+ </div>
1794
+
1795
+ <div class="plan-card">
1796
+ <div class="plan-name">Research 🔬</div>
1797
+ <div class="plan-price">For researchers & educators</div>
1798
+ <ul class="plan-features">
1799
+ <li>Everything in Pro</li>
1800
+ <li>Extended limits (1000+ msgs)</li>
1801
+ <li>1024 max tokens</li>
1802
+ <li>Batch processing</li>
1803
+ <li>Custom fine-tuning</li>
1804
+ <li>Dedicated support</li>
1805
+ <li>Academic discount</li>
1806
+ </ul>
1807
+ </div>
1808
+ </div>
1809
+ ''')
1810
 
1811
+ gr.Markdown("### 📝 Request an Upgrade")
1812
+ gr.Markdown("Fill out the form below and an admin will review your request within 24 hours.")
1813
+
1814
+ with gr.Row():
1815
+ with gr.Column(scale=2):
1816
+ upgrade_plan_choice = gr.Radio(
1817
+ choices=["plus", "pro", "explore", "Research"],
1818
+ label="Select Plan",
1819
+ value="plus"
1820
+ )
1821
+ upgrade_reason = gr.Textbox(
1822
+ label="Why do you need this upgrade?",
1823
+ placeholder="Tell us about your use case, what you're building, or why you need more access...",
1824
+ lines=4
1825
+ )
1826
+ with gr.Row():
1827
+ submit_upgrade_btn = gr.Button("Submit Request 📨", variant="primary", size="lg", scale=2)
1828
+ back_to_chat_btn = gr.Button("← Back to Chat", size="lg", scale=1)
1829
+ upgrade_msg = gr.Markdown("")
1830
 
1831
  # ADMIN PAGE
1832
  with gr.Group(visible=False) as admin_page:
1833
+ gr.HTML('''
1834
+ <div style="text-align: center; margin-bottom: 24px;">
1835
+ <div style="font-size: 28px; font-weight: 700; color: #1f2937;">
1836
+ 👨‍💼 Admin Dashboard
1837
+ </div>
1838
+ </div>
1839
+ ''')
1840
+
1841
+ with gr.Tabs():
1842
+ with gr.Tab("👥 User Management"):
1843
+ with gr.Row():
1844
+ refresh_users_btn = gr.Button("🔄 Refresh Users", size="sm")
1845
+
1846
+ users_table = gr.Dataframe(
1847
+ headers=["ID", "Username", "Email", "Plan", "Created", "Admin"],
1848
+ wrap=True
1849
+ )
 
 
1850
 
1851
+ gr.Markdown("### ✏️ Update User Plan")
1852
+ with gr.Row():
1853
+ admin_username = gr.Textbox(label="Username", scale=2, placeholder="username")
1854
+ admin_new_plan = gr.Dropdown(
1855
+ choices=["free", "plus", "pro", "explore", "Research", "VIP"],
1856
+ label="New Plan",
1857
+ value="free",
1858
+ scale=1
1859
  )
1860
+ update_plan_btn = gr.Button("Update Plan", variant="primary", scale=1)
1861
+ admin_msg = gr.Markdown("")
1862
+
1863
+ with gr.Tab("📋 Upgrade Requests"):
1864
+ with gr.Row():
1865
+ refresh_requests_btn = gr.Button("🔄 Refresh Requests", size="sm")
1866
+
1867
+ requests_table = gr.Dataframe(
1868
+ headers=["ID", "Username", "Plan", "Reason", "Date"],
1869
+ wrap=True
1870
+ )
1871
+
1872
+ gr.Markdown("### 🔍 Review Request")
1873
+ request_id_input = gr.Number(
1874
+ label="Request ID (from table above)",
1875
+ precision=0,
1876
+ minimum=1,
1877
+ info="Enter the ID number from the first column"
1878
+ )
1879
+ with gr.Row():
1880
+ approve_req_btn = gr.Button("✅ Approve Request", variant="primary", size="lg")
1881
+ deny_req_btn = gr.Button("❌ Deny Request", variant="stop", size="lg")
1882
+ request_msg = gr.Markdown("")
1883
+
1884
+ with gr.Tab("📊 Analytics (Coming Soon)"):
1885
+ gr.Markdown("""
1886
+ ### 📈 Usage Statistics
1887
+ - Total users: Coming soon
1888
+ - Active users (24h): Coming soon
1889
+ - Total messages: Coming soon
1890
+ - Most used model: Coming soon
1891
+ - Average tokens/message: Coming soon
1892
+ """)
1893
 
1894
  # ==================== EVENT HANDLERS ====================
1895
 
1896
  def handle_auth(username, password, email):
1897
+ """Unified auth - auto signup if new, FIX: Handle both 'id' and 'user_id'"""
1898
  if len(username) < 3:
1899
+ return (
1900
+ None, None, "❌ Username must be at least 3 characters",
1901
+ gr.update(), gr.update(), gr.update(), gr.update(),
1902
+ gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), ""
1903
+ )
1904
  if len(password) < 6:
1905
+ return (
1906
+ None, None, "❌ Password must be at least 6 characters",
1907
+ gr.update(), gr.update(), gr.update(), gr.update(),
1908
+ gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), ""
1909
+ )
1910
 
1911
  # Try login first
1912
  success, data = authenticate_user(username, password)
 
1918
  # Auto-login after signup
1919
  success, data = authenticate_user(username, password)
1920
  if not success:
1921
+ return (
1922
+ None, None, "❌ Account created but login failed",
1923
+ gr.update(), gr.update(), gr.update(), gr.update(),
1924
+ gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), ""
1925
+ )
1926
  else:
1927
+ return (
1928
+ None, None, f"❌ {message}",
1929
+ gr.update(), gr.update(), gr.update(), gr.update(),
1930
+ gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), ""
1931
+ )
1932
 
1933
+ # FIX: Normalize data to always have 'user_id'
1934
+ if 'id' in data and 'user_id' not in data:
1935
+ data['user_id'] = data['id']
1936
+
1937
+ # Generate session
1938
  code = create_session(data)
1939
 
1940
  # Get user info
1941
  info = get_user_limits_info(data['user_id'])
1942
+ if not info:
1943
+ return (
1944
+ None, None, "❌ Could not load user info",
1945
+ gr.update(), gr.update(), gr.update(), gr.update(),
1946
+ gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), ""
1947
+ )
1948
+
1949
  plan_class = f"plan-{info['plan'].lower()}"
1950
  greeting_html = f'<div class="user-greeting">{get_greeting(username)} <span class="plan-badge {plan_class}">{info["plan"]}</span></div>'
1951
 
 
1957
 
1958
  limits_html = render_limits_panel(data)
1959
 
1960
+ empty_chat = render_history([], True)
1961
+
1962
  return (
1963
  code,
1964
+ data,
1965
+ f"✅ Welcome, **{username}**! Your session is active.",
1966
  gr.update(visible=False), # auth_page
1967
  gr.update(visible=True), # chat_page
1968
  gr.update(visible=data.get('is_admin', False)), # admin_page
1969
  greeting_html,
1970
  gr.update(visible=True), # upgrade_nav_btn
1971
  gr.update(visible=True), # logout_nav_btn
1972
+ gr.update(choices=model_choices, value="🤖 Auto (Recommended)"),
1973
+ gr.update(maximum=info['max_tokens'], value=min(256, info['max_tokens'])),
1974
+ limits_html,
1975
+ empty_chat
1976
  )
1977
 
1978
  def show_upgrade_page():
 
1986
  invalidate_session(code)
1987
  return (
1988
  "",
1989
+ None,
1990
+ [],
1991
  gr.update(visible=True), # auth_page
1992
  gr.update(visible=False), # chat_page
1993
  gr.update(visible=False), # admin_page
 
1999
  ""
2000
  )
2001
 
2002
+ def send_message_handler(message, history, show_thinking, temperature, model_choice, max_tokens, code):
2003
  global stop_generation
2004
  stop_generation.clear()
2005
 
2006
  if not code:
2007
+ error_html = '<div class="speed-indicator error">❌ Session expired - please sign in again</div>'
2008
+ return "", history, "", error_html, gr.update(), gr.update(), ""
2009
 
2010
  data = validate_session(code)
2011
  if not data:
2012
+ error_html = '<div class="speed-indicator error">❌ Session expired - please sign in again</div>'
2013
+ return "", history, "", error_html, gr.update(), gr.update(), ""
2014
 
2015
  if not message.strip():
2016
+ return "", history, "", '<div class="speed-indicator">⚡ Ready to chat</div>', gr.update(), gr.update(), render_limits_panel(data)
2017
 
2018
  info = get_user_limits_info(data['user_id'])
2019
 
2020
+ # Model selection
2021
  if model_choice == "🤖 Auto (Recommended)" or not info['can_choose_model']:
2022
  user_available = get_available_models_for_user(data['user_id'])
2023
  if not user_available:
2024
+ error_html = '<div class="speed-indicator error">❌ No models available (limits reached)</div>'
2025
+ return "", history, "", error_html, gr.update(), gr.update(), render_limits_panel(data)
2026
  backend = select_model_auto(message, available_models, user_available)
2027
  if not backend:
2028
+ error_html = '<div class="speed-indicator error">❌ Could not select model</div>'
2029
+ return "", history, "", error_html, gr.update(), gr.update(), render_limits_panel(data)
2030
  model_name = backend.get_name()
2031
  else:
2032
  model_name = model_choice
2033
  can_use, msg = can_use_model(data['user_id'], model_name)
2034
  if not can_use:
2035
+ error_html = f'<div class="speed-indicator error">❌ {msg}</div>'
2036
+ return "", history, "", error_html, gr.update(), gr.update(), render_limits_panel(data)
2037
  backend = available_models[model_name]
2038
 
2039
  # Final check
2040
  can_use, msg = can_use_model(data['user_id'], model_name)
2041
  if not can_use:
2042
+ error_html = f'<div class="speed-indicator error">❌ {msg}</div>'
2043
+ return "", history, "", error_html, gr.update(), gr.update(), render_limits_panel(data)
2044
 
2045
  # Increment usage
2046
  increment_model_usage(data['user_id'], model_name)
2047
 
2048
+ # Add user message
2049
+ history.append({"role": "user", "content": message})
2050
+ yield "", history, render_history(history, show_thinking), f'<div class="speed-indicator generating">⚡ Using {model_name}...</div>', gr.update(interactive=False), gr.update(visible=True), render_limits_panel(data)
 
2051
 
2052
+ # Start generation
2053
  prompt = f"User: {message}\nSam: <think>"
2054
  history.append({"role": "assistant", "content": "<think>"})
2055
 
 
2070
  history[-1]["content"] += new_chunk
2071
 
2072
  last_speed = avg_speed
2073
+ yield "", history, render_history(history, show_thinking), f'<div class="speed-indicator generating">⚡ {tokens_per_sec:.1f} tok/s</div>', gr.update(interactive=False), gr.update(visible=True), render_limits_panel(data)
2074
+
2075
+ if was_stopped:
2076
+ final_html = f'<div class="speed-indicator error">🛑 Stopped - {last_speed:.1f} tok/s</div>'
2077
+ else:
2078
+ final_html = f'<div class="speed-indicator">✅ Done - {last_speed:.1f} tok/s</div>'
2079
 
2080
+ yield "", history, render_history(history, show_thinking), final_html, gr.update(interactive=True), gr.update(visible=False), render_limits_panel(data)
 
2081
 
2082
  def stop_generation_handler():
2083
  global stop_generation
2084
  stop_generation.set()
2085
+ return '<div class="speed-indicator error">🛑 Stopping...</div>', gr.update(interactive=False), gr.update(visible=False)
2086
+
2087
+ def clear_chat(history):
2088
+ empty = render_history([], True)
2089
+ return [], empty, '<div class="speed-indicator">⚡ Ready to chat</div>', gr.update(interactive=True), gr.update(visible=False)
2090
 
2091
+ def export_chat(history):
2092
+ # Simple export as text
2093
+ text = ""
2094
+ for msg in history:
2095
+ role = "You" if msg["role"] == "user" else "SAM-X-1"
2096
+ text += f"{role}: {msg['content']}\n\n"
2097
+ return text
2098
 
2099
  def submit_upgrade_request(code, plan, reason):
2100
  if not code:
 
2105
  return "❌ Session expired"
2106
 
2107
  if not reason.strip():
2108
+ return "❌ Please provide a reason for your upgrade request"
2109
 
2110
  success, msg = request_upgrade(data['user_id'], plan, reason)
2111
+ if success:
2112
+ return f"✅ {msg}\n\nAn admin will review your request within 24 hours. You'll be notified via email if provided."
2113
+ return f"❌ {msg}"
2114
 
2115
  def load_all_users():
2116
  users = get_all_users()
 
2134
  req[0],
2135
  req[1],
2136
  req[2],
2137
+ req[3][:100] + "..." if len(req[3]) > 100 else req[3],
2138
  req[4][:10] if req[4] else "N/A"
2139
  ])
2140
  return formatted
 
2143
  if not username or not new_plan:
2144
  return "❌ Please fill all fields"
2145
  success, msg = update_user_plan(username, new_plan)
2146
+ if success:
2147
+ return f"✅ {msg}\n\nThe user's limits have been reset and they now have access to {new_plan} features."
2148
+ return f"❌ {msg}"
2149
 
2150
  def admin_approve_request_handler(request_id):
2151
  if not request_id:
2152
+ return "❌ Please enter a request ID"
2153
  success, msg = approve_request(int(request_id))
2154
+ if success:
2155
+ return f"✅ {msg}\n\nThe user has been upgraded and can now access their new plan features."
2156
+ return f"❌ {msg}"
2157
 
2158
  def admin_deny_request_handler(request_id):
2159
  if not request_id:
2160
+ return "❌ Please enter a request ID"
2161
  success, msg = deny_request(int(request_id))
2162
+ if success:
2163
+ return f"✅ {msg}\n\nThe request has been marked as denied."
2164
+ return f"❌ {msg}"
2165
 
2166
  # ==================== WIRE UP EVENTS ====================
2167
 
2168
  # Auth
2169
  auth_outputs = [
2170
+ session_code, user_data, auth_msg, auth_page, chat_page, admin_page,
2171
  user_greeting, upgrade_nav_btn, logout_nav_btn,
2172
+ model_selector, max_tokens_slider, limits_display, chat_html
2173
  ]
2174
+ auth_btn.click(handle_auth, [auth_username, auth_password, auth_email], auth_outputs)
2175
+ auth_password.submit(handle_auth, [auth_username, auth_password, auth_email], auth_outputs)
 
 
 
2176
 
2177
  # Navigation
2178
  upgrade_nav_btn.click(show_upgrade_page, outputs=[chat_page, upgrade_page])
2179
  back_to_chat_btn.click(back_to_chat, outputs=[chat_page, upgrade_page])
2180
 
2181
  logout_outputs = [
2182
+ session_code, user_data, chat_history, auth_page, chat_page, admin_page, upgrade_page,
2183
  user_greeting, upgrade_nav_btn, logout_nav_btn, chat_html, limits_display
2184
  ]
2185
  logout_nav_btn.click(handle_logout, [session_code], logout_outputs)
2186
 
2187
  # Chat
2188
+ send_outputs = [msg_input, chat_history, chat_html, speed_display, send_btn, stop_btn, limits_display]
2189
  send_btn.click(
2190
  send_message_handler,
2191
+ [msg_input, chat_history, show_thinking_checkbox, temperature_slider, model_selector, max_tokens_slider, session_code],
2192
  send_outputs
2193
  )
2194
  msg_input.submit(
2195
  send_message_handler,
2196
+ [msg_input, chat_history, show_thinking_checkbox, temperature_slider, model_selector, max_tokens_slider, session_code],
2197
  send_outputs
2198
  )
2199
 
2200
  stop_btn.click(stop_generation_handler, outputs=[speed_display, send_btn, stop_btn])
2201
+ clear_btn.click(clear_chat, [chat_history], [chat_history, chat_html, speed_display, send_btn, stop_btn])
2202
+ new_chat_btn.click(clear_chat, [chat_history], [chat_history, chat_html, speed_display, send_btn, stop_btn])
2203
 
2204
  # Upgrade
2205
  submit_upgrade_btn.click(
 
2218
  # Footer
2219
  gr.Markdown("""
2220
  ---
 
2221
 
2222
+ <div style="text-align: center; padding: 40px 20px; background: linear-gradient(135deg, #f9fafb 0%, #f3f4f6 100%); border-radius: 12px; margin-top: 40px;">
2223
+ <div style="font-size: 24px; font-weight: 700; margin-bottom: 16px; color: #1f2937;">
2224
+ 🚀 Powered by SAM-X-1 Technology
2225
+ </div>
2226
+ <div style="font-size: 14px; color: #6b7280; margin-bottom: 24px;">
2227
+ State-of-the-art AI models built with Keras & TensorFlow
2228
+ </div>
2229
+
2230
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-top: 32px;">
2231
+ <div>
2232
+ <div style="font-size: 28px; font-weight: 700; color: #667eea;">4</div>
2233
+ <div style="font-size: 12px; color: #6b7280; text-transform: uppercase;">AI Models</div>
2234
+ </div>
2235
+ <div>
2236
+ <div style="font-size: 28px; font-weight: 700; color: #10a37f;">Fast</div>
2237
+ <div style="font-size: 12px; color: #6b7280; text-transform: uppercase;">Response Time</div>
2238
+ </div>
2239
+ <div>
2240
+ <div style="font-size: 28px; font-weight: 700; color: #f59e0b;">24/7</div>
2241
+ <div style="font-size: 12px; color: #6b7280; text-transform: uppercase;">Availability</div>
2242
+ </div>
2243
+ <div>
2244
+ <div style="font-size: 28px; font-weight: 700; color: #ef4444;">Free</div>
2245
+ <div style="font-size: 12px; color: #6b7280; text-transform: uppercase;">Tier Available</div>
2246
+ </div>
2247
+ </div>
2248
+ </div>
2249
+
2250
+ ### 🎯 Quick Start Guide
2251
+
2252
+ **New Users:**
2253
+ 1. Enter your username and password above
2254
+ 2. We'll automatically create your account
2255
+ 3. Start chatting immediately with unlimited Nano & Mini models!
2256
+
2257
+ **Tips for Best Results:**
2258
+ - 💡 Use **Auto mode** - SAM intelligently selects the best model for your task
2259
+ - 🔄 Enable **Show Thinking** to understand SAM's reasoning process
2260
+ - 📊 Monitor the **limits panel** to track your usage
2261
+ - ⚙️ Adjust **temperature** for creativity (0.7 is balanced)
2262
+
2263
+ ### 📊 Model Comparison
2264
+
2265
+ | Model | Speed | Quality | Best For |
2266
+ |-------|-------|---------|----------|
2267
+ | **Nano ⚡⚡** | Fastest | Good | Quick questions, simple tasks |
2268
+ | **Mini 🚀** | Fast | Great | General conversations, explanations |
2269
+ | **Fast ⚡** | Balanced | Excellent | Complex questions, coding |
2270
+ | **Large 💎** | Thorough | Premium | Deep analysis, research |
2271
+
2272
+ ### 💎 Upgrade Benefits
2273
+
2274
+ **Plus Plan ($):**
2275
+ - Fast model unlimited
2276
+ - Large model 20 msgs/3h
2277
+ - Manual model selection
2278
+ - 384 max tokens
2279
+ - Priority support
2280
+
2281
+ **Pro Plan ($):**
2282
+ - All models unlimited
2283
+ - 512 max tokens
2284
+ - Fastest limits reset
2285
+ - Premium support
2286
+ - Early features
2287
+
2288
+ ### 🔐 Privacy & Security
2289
+
2290
+ - 🛡️ Secure authentication with session codes
2291
+ - 🔒 Passwords are properly hashed (SHA256 → upgrading to bcrypt soon)
2292
+ - 📊 Usage tracking for fair limits
2293
+ - 🚫 We never share your data
2294
+ - ⚖️ Rate limiting prevents abuse
2295
+
2296
+ ### 🆘 Need Help?
2297
+
2298
+ - **Questions?** Check the Tips & Shortcuts in the sidebar
2299
+ - **Upgrade?** Click the ⭐ Upgrade button in the nav bar
2300
+ - **Issues?** Contact admin or report via feedback
2301
+ - **Shortcuts?** Press <span class="kbd">Ctrl+K</span> for keyboard shortcuts
2302
+
2303
+ ### 🎨 Features Included
2304
+
2305
+ ✅ **Markdown rendering** - Format your messages with bold, italic, code blocks
2306
+ ✅ **Syntax highlighting** - Beautiful code display
2307
+ ✅ **Copy buttons** - One-click copy for code and messages
2308
+ ✅ **Auto-scroll** - Chat automatically scrolls to latest message
2309
+ ✅ **Keyboard shortcuts** - Fast navigation and actions
2310
+ ✅ **Responsive design** - Works on desktop, tablet, and mobile
2311
+ ✅ **Dark mode ready** - Easy on the eyes (coming soon)
2312
+ ✅ **Progress tracking** - Visual usage indicators
2313
+ ✅ **Toast notifications** - Subtle feedback for actions
2314
+ ✅ **Empty states** - Helpful when starting new chats
2315
+ ✅ **Smooth animations** - Polished user experience
2316
 
2317
+ ### 🚀 Coming Soon
2318
 
2319
+ - 🌙 Dark mode toggle
2320
+ - 💾 Conversation history & search
2321
+ - 📁 Export to PDF/Markdown
2322
+ - 🎤 Voice input/output
2323
+ - 🔗 Share conversations
2324
+ - 📱 Mobile app
2325
+ - 🤝 Team workspaces
2326
+ - 📊 Advanced analytics
2327
+ - 🔌 API access
2328
 
2329
+ ### 📜 Legal
2330
 
2331
+ By using SAM-X-1, you agree to our Terms of Service and Privacy Policy.
2332
+ © 2025 SAM-X-1 Labs Built with ❤️ and AI
 
2333
 
2334
  ---
2335
 
2336
+ <div style="text-align: center; padding: 20px; color: #9ca3af; font-size: 12px;">
2337
+ <p><strong>SAM-X-1 v3.0</strong> • Production-Grade AI Chat Interface</p>
2338
+ <p>Powered by TensorFlow • Keras • Gradio • Python</p>
2339
+ <p style="margin-top: 12px;">
2340
+ Made with 🤖 by AI researchers, for AI enthusiasts
2341
+ </p>
2342
+ </div>
2343
  """)
2344
 
2345
+ demo.launch(
2346
+ debug=True,
2347
+ share=False,
2348
+ server_name="0.0.0.0",
2349
+ server_port=7860,
2350
+ favicon_path=None,
2351
+ show_error=True
2352
+ )