Thomas (Tom) Gardos commited on
Commit
85ab41f
·
unverified ·
2 Parent(s): 703c436 ab2ff67

Merge pull request #79 from DL4DS/regen_tokens

Browse files
code/app.py CHANGED
@@ -14,6 +14,8 @@ from modules.config.constants import (
14
  CHAINLIT_URL,
15
  GITHUB_REPO,
16
  DOCS_WEBSITE,
 
 
17
  )
18
  from fastapi.middleware.cors import CORSMiddleware
19
  from fastapi.staticfiles import StaticFiles
@@ -207,7 +209,9 @@ async def cooldown(request: Request):
207
  user_info = await get_user_info_from_cookie(request)
208
  user_details = await get_user_details(user_info["email"])
209
  current_datetime = get_time()
210
- cooldown, cooldown_end_time = check_user_cooldown(user_details, current_datetime)
 
 
211
  print(f"User in cooldown: {cooldown}")
212
  print(f"Cooldown end time: {cooldown_end_time}")
213
  if cooldown and "admin" not in get_user_role(user_info["email"]):
@@ -218,9 +222,11 @@ async def cooldown(request: Request):
218
  "username": user_info["email"],
219
  "role": get_user_role(user_info["email"]),
220
  "cooldown_end_time": cooldown_end_time,
 
221
  },
222
  )
223
  else:
 
224
  await update_user_info(user_details)
225
  await reset_tokens_for_user(user_details)
226
  return RedirectResponse("/post-signin")
@@ -236,15 +242,26 @@ async def post_signin(request: Request):
236
  user_details.metadata["last_login"] = current_datetime
237
  # if new user, set the number of tries
238
  if "tokens_left" not in user_details.metadata:
239
- await reset_tokens_for_user(user_details)
 
 
 
 
 
 
 
 
 
240
 
241
  if "last_message_time" in user_details.metadata and "admin" not in get_user_role(
242
  user_info["email"]
243
  ):
244
- cooldown, _ = check_user_cooldown(user_details, current_datetime)
245
  if cooldown:
 
246
  return RedirectResponse("/cooldown")
247
  else:
 
248
  await reset_tokens_for_user(user_details)
249
 
250
  if user_info:
@@ -259,6 +276,10 @@ async def post_signin(request: Request):
259
  "role": role,
260
  "jwt_token": jwt_token,
261
  "tokens_left": user_details.metadata["tokens_left"],
 
 
 
 
262
  },
263
  )
264
  return RedirectResponse("/")
@@ -309,6 +330,19 @@ async def logout(request: Request, response: Response):
309
  return response
310
 
311
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  mount_chainlit(app=app, target="main.py", path=CHAINLIT_PATH)
313
 
314
  if __name__ == "__main__":
 
14
  CHAINLIT_URL,
15
  GITHUB_REPO,
16
  DOCS_WEBSITE,
17
+ ALL_TIME_TOKENS_ALLOCATED,
18
+ TOKENS_LEFT,
19
  )
20
  from fastapi.middleware.cors import CORSMiddleware
21
  from fastapi.staticfiles import StaticFiles
 
209
  user_info = await get_user_info_from_cookie(request)
210
  user_details = await get_user_details(user_info["email"])
211
  current_datetime = get_time()
212
+ cooldown, cooldown_end_time = await check_user_cooldown(
213
+ user_details, current_datetime
214
+ )
215
  print(f"User in cooldown: {cooldown}")
216
  print(f"Cooldown end time: {cooldown_end_time}")
217
  if cooldown and "admin" not in get_user_role(user_info["email"]):
 
222
  "username": user_info["email"],
223
  "role": get_user_role(user_info["email"]),
224
  "cooldown_end_time": cooldown_end_time,
225
+ "tokens_left": user_details.metadata["tokens_left"],
226
  },
227
  )
228
  else:
229
+ user_details.metadata["in_cooldown"] = False
230
  await update_user_info(user_details)
231
  await reset_tokens_for_user(user_details)
232
  return RedirectResponse("/post-signin")
 
242
  user_details.metadata["last_login"] = current_datetime
243
  # if new user, set the number of tries
244
  if "tokens_left" not in user_details.metadata:
245
+ user_details.metadata["tokens_left"] = (
246
+ TOKENS_LEFT # set the number of tokens left for the new user
247
+ )
248
+ if "all_time_tokens_allocated" not in user_details.metadata:
249
+ user_details.metadata["all_time_tokens_allocated"] = (
250
+ ALL_TIME_TOKENS_ALLOCATED
251
+ )
252
+ if "in_cooldown" not in user_details.metadata:
253
+ user_details.metadata["in_cooldown"] = False
254
+ await update_user_info(user_details)
255
 
256
  if "last_message_time" in user_details.metadata and "admin" not in get_user_role(
257
  user_info["email"]
258
  ):
259
+ cooldown, _ = await check_user_cooldown(user_details, current_datetime)
260
  if cooldown:
261
+ user_details.metadata["in_cooldown"] = True
262
  return RedirectResponse("/cooldown")
263
  else:
264
+ user_details.metadata["in_cooldown"] = False
265
  await reset_tokens_for_user(user_details)
266
 
267
  if user_info:
 
276
  "role": role,
277
  "jwt_token": jwt_token,
278
  "tokens_left": user_details.metadata["tokens_left"],
279
+ "all_time_tokens_allocated": user_details.metadata[
280
+ "all_time_tokens_allocated"
281
+ ],
282
+ "total_tokens_allocated": ALL_TIME_TOKENS_ALLOCATED,
283
  },
284
  )
285
  return RedirectResponse("/")
 
330
  return response
331
 
332
 
333
+ @app.get("/get-tokens-left")
334
+ async def get_tokens_left(request: Request):
335
+ try:
336
+ user_info = await get_user_info_from_cookie(request)
337
+ user_details = await get_user_details(user_info["email"])
338
+ await reset_tokens_for_user(user_details)
339
+ tokens_left = user_details.metadata["tokens_left"]
340
+ return {"tokens_left": tokens_left}
341
+ except Exception as e:
342
+ print(f"Error getting tokens left: {e}")
343
+ return {"tokens_left": 0}
344
+
345
+
346
  mount_chainlit(app=app, target="main.py", path=CHAINLIT_PATH)
347
 
348
  if __name__ == "__main__":
code/main.py CHANGED
@@ -20,6 +20,8 @@ from modules.chat_processor.helpers import (
20
  update_user_info,
21
  get_time,
22
  check_user_cooldown,
 
 
23
  )
24
  import copy
25
  from typing import Optional
@@ -54,6 +56,24 @@ async def setup_data_layer():
54
  return data_layer
55
 
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  class Chatbot:
58
  def __init__(self, config):
59
  """
@@ -221,17 +241,18 @@ class Chatbot:
221
  "view_sources": llm_settings.get("view_sources"),
222
  "follow_up_questions": llm_settings.get("follow_up_questions"),
223
  }
 
224
  await cl.Message(
225
  author=SYSTEM,
226
  content="LLM settings have been updated. You can continue with your Query!",
227
- elements=[
228
- cl.Text(
229
- name="settings",
230
- display="side",
231
- content=json.dumps(settings_dict, indent=4),
232
- language="json",
233
- ),
234
- ],
235
  ).send()
236
 
237
  async def set_starters(self):
@@ -365,6 +386,10 @@ class Chatbot:
365
 
366
  # update user info with last message time
367
  user = cl.user_session.get("user")
 
 
 
 
368
 
369
  print("\n\n User Tokens Left: ", user.metadata["tokens_left"])
370
 
@@ -372,7 +397,9 @@ class Chatbot:
372
  # if not, return message saying they have run out of tokens
373
  if user.metadata["tokens_left"] <= 0 and "admin" not in user.metadata["role"]:
374
  current_datetime = get_time()
375
- cooldown, cooldown_end_time = check_user_cooldown(user, current_datetime)
 
 
376
  if cooldown:
377
  # get time left in cooldown
378
  # convert both to datetime objects
@@ -390,7 +417,6 @@ class Chatbot:
390
  minutes, seconds = divmod(remainder, 60)
391
  # Format the time as 00 hrs 00 mins 00 secs
392
  formatted_time = f"{hours:02} hrs {minutes:02} mins {seconds:02} secs"
393
- await update_user_info(user)
394
  await cl.Message(
395
  content=(
396
  "Ah, seems like you have run out of tokens...Click "
@@ -400,7 +426,20 @@ class Chatbot:
400
  ),
401
  author=SYSTEM,
402
  ).send()
 
 
403
  return
 
 
 
 
 
 
 
 
 
 
 
404
 
405
  llm_settings = cl.user_session.get("llm_settings", {})
406
  view_sources = llm_settings.get("view_sources", False)
@@ -479,10 +518,13 @@ class Chatbot:
479
  print("Time taken to generate questions: ", time.time() - start_time)
480
 
481
  # # update user info with token count
482
- if "admin" not in user.metadata["role"]:
483
- user.metadata["tokens_left"] = user.metadata["tokens_left"] - token_count
484
- user.metadata["last_message_time"] = get_time()
485
- await update_user_info(user)
 
 
 
486
 
487
  await cl.Message(
488
  content=answer_with_sources,
 
20
  update_user_info,
21
  get_time,
22
  check_user_cooldown,
23
+ reset_tokens_for_user,
24
+ get_user_details,
25
  )
26
  import copy
27
  from typing import Optional
 
56
  return data_layer
57
 
58
 
59
+ async def update_user_from_chainlit(user, token_count=0):
60
+ if "admin" not in user.metadata["role"]:
61
+ user.metadata["tokens_left"] = user.metadata["tokens_left"] - token_count
62
+ user.metadata["all_time_tokens_allocated"] = (
63
+ user.metadata["all_time_tokens_allocated"] - token_count
64
+ )
65
+ user.metadata["tokens_left_at_last_message"] = user.metadata[
66
+ "tokens_left"
67
+ ] # tokens_left will keep regenerating outside of chainlit
68
+ user.metadata["last_message_time"] = get_time()
69
+ await update_user_info(user)
70
+
71
+ tokens_left = user.metadata["tokens_left"]
72
+ if tokens_left < 0:
73
+ tokens_left = 0
74
+ return tokens_left
75
+
76
+
77
  class Chatbot:
78
  def __init__(self, config):
79
  """
 
241
  "view_sources": llm_settings.get("view_sources"),
242
  "follow_up_questions": llm_settings.get("follow_up_questions"),
243
  }
244
+ print("Settings Dict: ", settings_dict)
245
  await cl.Message(
246
  author=SYSTEM,
247
  content="LLM settings have been updated. You can continue with your Query!",
248
+ # elements=[
249
+ # cl.Text(
250
+ # name="settings",
251
+ # display="side",
252
+ # content=json.dumps(settings_dict, indent=4),
253
+ # language="json",
254
+ # ),
255
+ # ],
256
  ).send()
257
 
258
  async def set_starters(self):
 
386
 
387
  # update user info with last message time
388
  user = cl.user_session.get("user")
389
+ await reset_tokens_for_user(user)
390
+ updated_user = await get_user_details(user.identifier)
391
+ user.metadata = updated_user.metadata
392
+ cl.user_session.set("user", user)
393
 
394
  print("\n\n User Tokens Left: ", user.metadata["tokens_left"])
395
 
 
397
  # if not, return message saying they have run out of tokens
398
  if user.metadata["tokens_left"] <= 0 and "admin" not in user.metadata["role"]:
399
  current_datetime = get_time()
400
+ cooldown, cooldown_end_time = await check_user_cooldown(
401
+ user, current_datetime
402
+ )
403
  if cooldown:
404
  # get time left in cooldown
405
  # convert both to datetime objects
 
417
  minutes, seconds = divmod(remainder, 60)
418
  # Format the time as 00 hrs 00 mins 00 secs
419
  formatted_time = f"{hours:02} hrs {minutes:02} mins {seconds:02} secs"
 
420
  await cl.Message(
421
  content=(
422
  "Ah, seems like you have run out of tokens...Click "
 
426
  ),
427
  author=SYSTEM,
428
  ).send()
429
+ user.metadata["in_cooldown"] = True
430
+ await update_user_info(user)
431
  return
432
+ else:
433
+ await cl.Message(
434
+ content=(
435
+ "Ah, seems like you don't have any tokens left...Please wait while we regenerate your tokens. Click "
436
+ '<a href="/cooldown" style="color: #0000CD; text-decoration: none;" target="_self">here</a> to view your token credits.'
437
+ ),
438
+ author=SYSTEM,
439
+ ).send()
440
+ return
441
+
442
+ user.metadata["in_cooldown"] = False
443
 
444
  llm_settings = cl.user_session.get("llm_settings", {})
445
  view_sources = llm_settings.get("view_sources", False)
 
518
  print("Time taken to generate questions: ", time.time() - start_time)
519
 
520
  # # update user info with token count
521
+ tokens_left = await update_user_from_chainlit(user, token_count)
522
+
523
+ answer_with_sources += (
524
+ '\n\n<footer><span style="font-size: 0.8em; text-align: right; display: block;">Tokens Left: '
525
+ + str(tokens_left)
526
+ + "</span></footer>\n"
527
+ )
528
 
529
  await cl.Message(
530
  content=answer_with_sources,
code/modules/chat_processor/helpers.py CHANGED
@@ -1,7 +1,7 @@
1
  import os
2
  from literalai import AsyncLiteralClient
3
  from datetime import datetime, timedelta, timezone
4
- from modules.config.constants import COOLDOWN_TIME, TOKENS_LEFT
5
  from typing_extensions import TypedDict
6
  import tiktoken
7
  from typing import Any, Generic, List, Literal, Optional, TypeVar, Union
@@ -141,7 +141,7 @@ def get_time():
141
 
142
 
143
  async def get_user_details(user_email_id):
144
- user_info = await literal_client.api.get_user(identifier=user_email_id)
145
  return user_info
146
 
147
 
@@ -155,11 +155,11 @@ async def update_user_info(user_info):
155
  )
156
 
157
 
158
- def check_user_cooldown(user_info, current_time):
159
 
160
- # Check if no tokens left
161
  tokens_left = user_info.metadata.get("tokens_left", 0)
162
- if tokens_left > 0:
163
  return False, None
164
 
165
  user_info = convert_to_dict(user_info)
@@ -186,13 +186,54 @@ def check_user_cooldown(user_info, current_time):
186
  if elapsed_time_in_seconds < COOLDOWN_TIME:
187
  return True, cooldown_end_time_iso # Return in ISO 8601 format
188
 
 
 
 
 
189
  return False, None
190
 
191
 
192
  async def reset_tokens_for_user(user_info):
193
  user_info = convert_to_dict(user_info)
194
- user_info["metadata"]["tokens_left"] = TOKENS_LEFT
195
- await update_user_info(user_info)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
 
198
  async def get_thread_step_info(thread_id):
 
1
  import os
2
  from literalai import AsyncLiteralClient
3
  from datetime import datetime, timedelta, timezone
4
+ from modules.config.constants import COOLDOWN_TIME, TOKENS_LEFT, REGEN_TIME
5
  from typing_extensions import TypedDict
6
  import tiktoken
7
  from typing import Any, Generic, List, Literal, Optional, TypeVar, Union
 
141
 
142
 
143
  async def get_user_details(user_email_id):
144
+ user_info = await literal_client.api.get_or_create_user(identifier=user_email_id)
145
  return user_info
146
 
147
 
 
155
  )
156
 
157
 
158
+ async def check_user_cooldown(user_info, current_time):
159
 
160
+ # # Check if no tokens left
161
  tokens_left = user_info.metadata.get("tokens_left", 0)
162
+ if tokens_left > 0 and not user_info.metadata.get("in_cooldown", False):
163
  return False, None
164
 
165
  user_info = convert_to_dict(user_info)
 
186
  if elapsed_time_in_seconds < COOLDOWN_TIME:
187
  return True, cooldown_end_time_iso # Return in ISO 8601 format
188
 
189
+ user_info["metadata"]["in_cooldown"] = False
190
+ # If not in cooldown, regenerate tokens
191
+ await reset_tokens_for_user(user_info)
192
+
193
  return False, None
194
 
195
 
196
  async def reset_tokens_for_user(user_info):
197
  user_info = convert_to_dict(user_info)
198
+ last_message_time_str = user_info["metadata"].get("last_message_time")
199
+
200
+ last_message_time = datetime.fromisoformat(last_message_time_str).replace(
201
+ tzinfo=timezone.utc
202
+ )
203
+ current_time = datetime.fromisoformat(get_time()).replace(tzinfo=timezone.utc)
204
+
205
+ # Calculate the elapsed time since the last message
206
+ elapsed_time_in_seconds = (current_time - last_message_time).total_seconds()
207
+
208
+ # Current token count (can be negative)
209
+ current_tokens = user_info["metadata"].get("tokens_left_at_last_message", 0)
210
+ current_tokens = min(current_tokens, TOKENS_LEFT)
211
+
212
+ # Maximum tokens that can be regenerated
213
+ max_tokens = user_info["metadata"].get("max_tokens", TOKENS_LEFT)
214
+
215
+ # Calculate how many tokens should have been regenerated proportionally
216
+ if current_tokens < max_tokens:
217
+
218
+ # Calculate the regeneration rate per second based on REGEN_TIME for full regeneration
219
+ regeneration_rate_per_second = max_tokens / REGEN_TIME
220
+
221
+ # Calculate how many tokens should have been regenerated based on the elapsed time
222
+ tokens_to_regenerate = int(
223
+ elapsed_time_in_seconds * regeneration_rate_per_second
224
+ )
225
+
226
+ # Ensure the new token count does not exceed max_tokens
227
+ new_token_count = min(current_tokens + tokens_to_regenerate, max_tokens)
228
+
229
+ print(
230
+ f"\n\n Adding {tokens_to_regenerate} tokens to the user, Time elapsed: {elapsed_time_in_seconds} seconds, Tokens after regeneration: {new_token_count}, Tokens before: {current_tokens} \n\n"
231
+ )
232
+
233
+ # Update the user's token count
234
+ user_info["metadata"]["tokens_left"] = new_token_count
235
+
236
+ await update_user_info(user_info)
237
 
238
 
239
  async def get_thread_step_info(thread_id):
code/modules/config/constants.py CHANGED
@@ -5,7 +5,9 @@ load_dotenv()
5
 
6
  TIMEOUT = 60
7
  COOLDOWN_TIME = 60
8
- TOKENS_LEFT = 3000
 
 
9
 
10
  GITHUB_REPO = "https://github.com/DL4DS/dl4ds_tutor"
11
  DOCS_WEBSITE = "https://dl4ds.github.io/dl4ds_tutor/"
 
5
 
6
  TIMEOUT = 60
7
  COOLDOWN_TIME = 60
8
+ REGEN_TIME = 180
9
+ TOKENS_LEFT = 2000
10
+ ALL_TIME_TOKENS_ALLOCATED = 1000000
11
 
12
  GITHUB_REPO = "https://github.com/DL4DS/dl4ds_tutor"
13
  DOCS_WEBSITE = "https://dl4ds.github.io/dl4ds_tutor/"
code/templates/cooldown.html CHANGED
@@ -63,6 +63,13 @@
63
  margin-bottom: 30px;
64
  }
65
 
 
 
 
 
 
 
 
66
  .button {
67
  padding: 12px 0;
68
  margin: 12px 0;
@@ -70,33 +77,33 @@
70
  border-radius: 6px;
71
  cursor: pointer;
72
  width: 100%;
73
- border: 1px solid #4285F4; /* Button border color */
74
- background-color: #fff; /* Button background color */
75
- color: #4285F4; /* Button text color */
76
  transition: background-color 0.3s ease, border-color 0.3s ease;
77
- display: none; /* Initially hidden */
78
  }
79
 
80
  .button.start-tutor {
81
- display: none; /* Initially hidden */
82
  }
83
 
84
  .button:hover {
85
  background-color: #e0e0e0;
86
- border-color: #357ae8; /* Darker blue for hover */
87
  }
88
 
89
  .sign-out-button {
90
  border: 1px solid #FF4C4C;
91
  background-color: #fff;
92
  color: #FF4C4C;
93
- display: block; /* Ensure this button is always visible */
94
  }
95
 
96
  .sign-out-button:hover {
97
- background-color: #ffe6e6; /* Light red on hover */
98
- border-color: #e04343; /* Darker red for hover */
99
- color: #e04343; /* Red text on hover */
100
  }
101
 
102
  #countdown {
@@ -104,6 +111,12 @@
104
  color: #555;
105
  margin-bottom: 20px;
106
  }
 
 
 
 
 
 
107
  </style>
108
  </head>
109
  <body>
@@ -113,24 +126,26 @@
113
  <p>It seems like you need to wait a bit before starting a new session.</p>
114
  <p class="cooldown-message">Time remaining until the cooldown period ends:</p>
115
  <p id="countdown"></p>
 
116
  <button id="startTutorBtn" class="button start-tutor" onclick="startTutor()">Start AI Tutor</button>
117
  <form action="/logout" method="get">
118
  <button type="submit" class="button sign-out-button">Sign Out</button>
119
  </form>
 
120
  </div>
121
  <script>
122
  function startCountdown(endTime) {
123
  const countdownElement = document.getElementById('countdown');
124
  const startTutorBtn = document.getElementById('startTutorBtn');
125
- const endTimeDate = new Date(endTime); // Parse the cooldown end time
126
 
127
  function updateCountdown() {
128
- const now = new Date(); // Get the current time
129
- const timeLeft = endTimeDate.getTime() - now.getTime(); // Calculate the remaining time in milliseconds
130
 
131
  if (timeLeft <= 0) {
132
  countdownElement.textContent = "Cooldown period has ended.";
133
- startTutorBtn.style.display = "block"; // Show the start tutor button
134
  } else {
135
  const hours = Math.floor(timeLeft / 1000 / 60 / 60);
136
  const minutes = Math.floor((timeLeft / 1000 / 60) % 60);
@@ -139,17 +154,28 @@
139
  }
140
  }
141
 
142
- updateCountdown(); // Initial call to set the countdown
143
- setInterval(updateCountdown, 1000); // Update the countdown every second
144
  }
145
 
146
  function startTutor() {
147
- // Redirect to AI Tutor session start or any other logic to start the tutor
148
  window.location.href = "/start-tutor";
149
  }
150
 
151
- // Pass the cooldown_end_time to the script in ISO format with 'Z' to indicate UTC
 
 
 
 
 
 
 
 
 
152
  startCountdown("{{ cooldown_end_time }}");
 
 
 
153
  </script>
154
  </body>
155
  </html>
 
63
  margin-bottom: 30px;
64
  }
65
 
66
+ .tokens-left {
67
+ font-size: 14px;
68
+ color: #333;
69
+ margin-bottom: 30px;
70
+ font-weight: 600;
71
+ }
72
+
73
  .button {
74
  padding: 12px 0;
75
  margin: 12px 0;
 
77
  border-radius: 6px;
78
  cursor: pointer;
79
  width: 100%;
80
+ border: 1px solid #4285F4;
81
+ background-color: #fff;
82
+ color: #4285F4;
83
  transition: background-color 0.3s ease, border-color 0.3s ease;
84
+ display: none;
85
  }
86
 
87
  .button.start-tutor {
88
+ display: none;
89
  }
90
 
91
  .button:hover {
92
  background-color: #e0e0e0;
93
+ border-color: #357ae8;
94
  }
95
 
96
  .sign-out-button {
97
  border: 1px solid #FF4C4C;
98
  background-color: #fff;
99
  color: #FF4C4C;
100
+ display: block;
101
  }
102
 
103
  .sign-out-button:hover {
104
+ background-color: #ffe6e6;
105
+ border-color: #e04343;
106
+ color: #e04343;
107
  }
108
 
109
  #countdown {
 
111
  color: #555;
112
  margin-bottom: 20px;
113
  }
114
+
115
+ .footer {
116
+ font-size: 12px;
117
+ color: #777;
118
+ margin-top: 20px;
119
+ }
120
  </style>
121
  </head>
122
  <body>
 
126
  <p>It seems like you need to wait a bit before starting a new session.</p>
127
  <p class="cooldown-message">Time remaining until the cooldown period ends:</p>
128
  <p id="countdown"></p>
129
+ <p class="tokens-left">Tokens Left: <span id="tokensLeft">{{ tokens_left }}</span></p>
130
  <button id="startTutorBtn" class="button start-tutor" onclick="startTutor()">Start AI Tutor</button>
131
  <form action="/logout" method="get">
132
  <button type="submit" class="button sign-out-button">Sign Out</button>
133
  </form>
134
+ <div class="footer">Reload the page to update token stats</div>
135
  </div>
136
  <script>
137
  function startCountdown(endTime) {
138
  const countdownElement = document.getElementById('countdown');
139
  const startTutorBtn = document.getElementById('startTutorBtn');
140
+ const endTimeDate = new Date(endTime);
141
 
142
  function updateCountdown() {
143
+ const now = new Date();
144
+ const timeLeft = endTimeDate.getTime() - now.getTime();
145
 
146
  if (timeLeft <= 0) {
147
  countdownElement.textContent = "Cooldown period has ended.";
148
+ startTutorBtn.style.display = "block";
149
  } else {
150
  const hours = Math.floor(timeLeft / 1000 / 60 / 60);
151
  const minutes = Math.floor((timeLeft / 1000 / 60) % 60);
 
154
  }
155
  }
156
 
157
+ updateCountdown();
158
+ setInterval(updateCountdown, 1000);
159
  }
160
 
161
  function startTutor() {
 
162
  window.location.href = "/start-tutor";
163
  }
164
 
165
+ function updateTokensLeft() {
166
+ fetch('/get-tokens-left')
167
+ .then(response => response.json())
168
+ .then(data => {
169
+ document.getElementById('tokensLeft').textContent = data.tokens_left;
170
+ })
171
+ .catch(error => console.error('Error fetching tokens:', error));
172
+ }
173
+
174
+ // Start the countdown
175
  startCountdown("{{ cooldown_end_time }}");
176
+
177
+ // Update tokens left when the page loads
178
+ updateTokensLeft();
179
  </script>
180
  </body>
181
  </html>
code/templates/dashboard.html CHANGED
@@ -27,7 +27,7 @@
27
  border-radius: 8px;
28
  width: 100%;
29
  max-width: 400px;
30
- padding: 50px;
31
  box-sizing: border-box;
32
  text-align: center;
33
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
@@ -39,35 +39,43 @@
39
  width: 90px;
40
  height: 90px;
41
  border-radius: 50%;
42
- margin-bottom: 25px;
43
  border: 2px solid #ddd;
44
  }
45
 
46
  .container h1 {
47
- margin-bottom: 15px;
48
- font-size: 24px;
49
  font-weight: 600;
50
  color: #1a1a1a;
51
  }
52
 
53
  .container p {
54
- font-size: 16px;
55
  color: #4a4a4a;
56
- margin-bottom: 30px;
57
  line-height: 1.5;
58
  }
59
 
60
  .tokens-left {
61
- font-size: 16px;
62
  color: #333;
63
- margin-bottom: 30px;
64
  font-weight: 600;
65
  }
66
 
 
 
 
 
 
 
 
 
67
  .button {
68
  padding: 12px 0;
69
  margin: 12px 0;
70
- font-size: 14px;
71
  border-radius: 6px;
72
  cursor: pointer;
73
  width: 100%;
@@ -105,6 +113,12 @@
105
  border-color: #e04343; /* Darker red for hover */
106
  color: #e04343; /* Red text on hover */
107
  }
 
 
 
 
 
 
108
  </style>
109
  </head>
110
  <body>
@@ -113,12 +127,14 @@
113
  <h1>Welcome, {{ username }}</h1>
114
  <p>Ready to start your AI tutoring session?</p>
115
  <p class="tokens-left">Tokens Left: {{ tokens_left }}</p>
 
116
  <form action="/start-tutor" method="post">
117
  <button type="submit" class="button start-button">Start AI Tutor</button>
118
  </form>
119
  <form action="/logout" method="get">
120
  <button type="submit" class="button sign-out-button">Sign Out</button>
121
  </form>
 
122
  </div>
123
  <script>
124
  let token = "{{ jwt_token }}";
 
27
  border-radius: 8px;
28
  width: 100%;
29
  max-width: 400px;
30
+ padding: 40px;
31
  box-sizing: border-box;
32
  text-align: center;
33
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
 
39
  width: 90px;
40
  height: 90px;
41
  border-radius: 50%;
42
+ margin-bottom: 20px;
43
  border: 2px solid #ddd;
44
  }
45
 
46
  .container h1 {
47
+ margin-bottom: 20px;
48
+ font-size: 26px;
49
  font-weight: 600;
50
  color: #1a1a1a;
51
  }
52
 
53
  .container p {
54
+ font-size: 15px;
55
  color: #4a4a4a;
56
+ margin-bottom: 25px;
57
  line-height: 1.5;
58
  }
59
 
60
  .tokens-left {
61
+ font-size: 17px;
62
  color: #333;
63
+ margin-bottom: 10px;
64
  font-weight: 600;
65
  }
66
 
67
+ .all-time-tokens {
68
+ font-size: 14px; /* Reduced font size */
69
+ color: #555;
70
+ margin-bottom: 30px;
71
+ font-weight: 500;
72
+ white-space: nowrap; /* Prevents breaking to a new line */
73
+ }
74
+
75
  .button {
76
  padding: 12px 0;
77
  margin: 12px 0;
78
+ font-size: 15px;
79
  border-radius: 6px;
80
  cursor: pointer;
81
  width: 100%;
 
113
  border-color: #e04343; /* Darker red for hover */
114
  color: #e04343; /* Red text on hover */
115
  }
116
+
117
+ .footer {
118
+ font-size: 12px;
119
+ color: #777;
120
+ margin-top: 25px;
121
+ }
122
  </style>
123
  </head>
124
  <body>
 
127
  <h1>Welcome, {{ username }}</h1>
128
  <p>Ready to start your AI tutoring session?</p>
129
  <p class="tokens-left">Tokens Left: {{ tokens_left }}</p>
130
+ <p class="all-time-tokens">All-Time Tokens Allocated: {{ all_time_tokens_allocated }} / {{ total_tokens_allocated }}</p>
131
  <form action="/start-tutor" method="post">
132
  <button type="submit" class="button start-button">Start AI Tutor</button>
133
  </form>
134
  <form action="/logout" method="get">
135
  <button type="submit" class="button sign-out-button">Sign Out</button>
136
  </form>
137
+ <div class="footer">Reload the page to update token stats</div>
138
  </div>
139
  <script>
140
  let token = "{{ jwt_token }}";