macayaven commited on
Commit
758422c
·
verified ·
1 Parent(s): 2b8b84d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +117 -97
app.py CHANGED
@@ -13,6 +13,7 @@ from datetime import datetime
13
  from typing import Optional
14
 
15
  import gradio as gr
 
16
 
17
  # Import our CBT knowledge base
18
  from cbt_knowledge import (
@@ -29,7 +30,8 @@ try:
29
  from agents import CBTAgent
30
 
31
  AGENT_AVAILABLE = True
32
- except Exception:
 
33
  CBTAgent = None # type: ignore
34
  AGENT_AVAILABLE = False
35
 
@@ -44,7 +46,7 @@ def load_translations():
44
  translations[lang] = json.load(f)
45
  except FileNotFoundError:
46
  # Fallback to embedded translations if files don't exist
47
- pass
48
 
49
  # Fallback translations
50
  if 'en' not in translations:
@@ -185,7 +187,7 @@ class CBTChatbot:
185
  message: str,
186
  history: list[list[str]],
187
  use_agent: bool = False,
188
- agent: Optional["CBTAgent"] = None,
189
  ) -> tuple[list[list[str]], str, str, str]:
190
  """
191
  Process user message and generate response with CBT analysis
@@ -332,6 +334,55 @@ def create_app(language='en'):
332
  distortions_output = gr.Markdown(label="Patterns Detected")
333
  reframe_output = gr.Markdown(label="Reframe Suggestion")
334
  situations_output = gr.Markdown(label="Similar Situations")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
 
336
  # Internal state for agent instance, selected model, and agentic enable flag
337
  agent_state = gr.State(value=None)
@@ -510,7 +561,8 @@ def create_app(language='en'):
510
  username = prof[key]
511
  if username:
512
  break
513
- except Exception:
 
514
  pass
515
  raw = f"oauth:{username or 'unknown'}"
516
  # req is expected to be provided by Gradio
@@ -526,17 +578,12 @@ def create_app(language='en'):
526
  sess = getattr(req, "session_hash", None) or "?"
527
  raw = f"ipua:{ip}|{ua}|{sess}"
528
  return hashlib.sha256(f"{salt}|{raw}".encode()).hexdigest()
529
- except Exception:
 
530
  return "anon"
531
 
532
  user_id = _user_id(request, profile)
533
- interactions_before = 0
534
- try:
535
- interactions_before = _interactions_today(user_id)
536
- except Exception:
537
- interactions_before = 0
538
-
539
- # Per-user interaction quota (counts 1 per message)
540
  def _interactions_today(uid: str) -> int:
541
  data = _load_call_log()
542
  day = _today_key()
@@ -547,15 +594,13 @@ def create_app(language='en'):
547
  else {}
548
  )
549
  val = inter.get(uid, 0)
550
- # Validar tipo antes de convertir
551
  if isinstance(val, (str, int, float)):
552
  try:
553
  return int(val)
554
- except Exception:
 
555
  return 0
556
- else:
557
- return 0
558
-
559
 
560
  def _inc_interactions_today(uid: str):
561
  data = _load_call_log()
@@ -571,6 +616,14 @@ def create_app(language='en'):
571
  data[day] = day_blob
572
  _save_call_log(data)
573
 
 
 
 
 
 
 
 
 
574
  max_interactions_env = os.getenv("HF_AGENT_MAX_INTERACTIONS_PER_USER")
575
  try:
576
  # Default to a generous 12 if not configured
@@ -826,10 +879,10 @@ def create_app(language='en'):
826
  billing_notice,
827
  agentic_enabled_state,
828
  ],
829
- ).then(clear_input, outputs=[msg_input])
830
 
831
  send_btn.click(
832
- respond_stream,
833
  inputs=[
834
  msg_input,
835
  chatbot_ui,
@@ -854,7 +907,7 @@ def create_app(language='en'):
854
  return h, d, r, s, ""
855
 
856
  clear_btn.click(
857
- _clear_session_and_notice,
858
  outputs=[
859
  chatbot_ui,
860
  distortions_output,
@@ -868,50 +921,10 @@ def create_app(language='en'):
868
  with gr.Tab(t['learn']['title']):
869
  create_learn_tab(t['learn'], COGNITIVE_DISTORTIONS)
870
 
871
- # Owner Tab (hidden unless Space owner is logged in)
872
- with gr.Tab("Owner", visible=False) as owner_tab:
873
- # Locked panel shown to non-admins
874
- locked_panel = gr.Column(visible=True)
875
- with locked_panel:
876
- gr.Markdown("### Owner only\nPlease log in with your Hugging Face account.")
877
-
878
- # Admin panel
879
- admin_panel = gr.Column(visible=False)
880
- with admin_panel:
881
- gr.Markdown("## Admin Dashboard")
882
- admin_summary = gr.Markdown("")
883
- admin_limit_info = gr.Markdown("")
884
- # Owner-only model selection
885
- model_dropdown = gr.Dropdown(
886
- label="Model (HF)",
887
- choices=[
888
- "meta-llama/Llama-3.1-8B-Instruct",
889
- "meta-llama/Llama-3.1-70B-Instruct",
890
- "Qwen/Qwen2.5-7B-Instruct",
891
- "mistralai/Mixtral-8x7B-Instruct-v0.1",
892
- "google/gemma-2-9b-it",
893
- ],
894
- value=os.getenv("MODEL_NAME", "meta-llama/Llama-3.1-8B-Instruct"),
895
- allow_custom_value=True,
896
- info="Only visible to owner. Requires HF Inference API token.",
897
- )
898
- with gr.Row():
899
- override_tb = gr.Textbox(
900
- label="Per-user interaction limit override (blank to clear)"
901
- )
902
- set_override_btn = gr.Button("Set Limit Override", variant="secondary")
903
- refresh_btn = gr.Button("Refresh Metrics", variant="secondary")
904
-
905
- gr.Markdown("### Debug")
906
- owner_identity_md = gr.Markdown("")
907
- with gr.Row():
908
- identity_btn = gr.Button("Refresh Identity", variant="secondary")
909
- storage_btn = gr.Button("Check /data", variant="secondary")
910
- storage_info_md = gr.Markdown("")
911
-
912
- def _owner_is(profile: "gr.OAuthProfile | None") -> bool:
913
  try:
914
- # Prefer explicit OWNER_USER, fallback to the Space author (useful if OWNER_USER not set)
 
915
  owner = (
916
  os.getenv("OWNER_USER")
917
  or os.getenv("SPACE_AUTHOR_NAME")
@@ -919,7 +932,7 @@ def create_app(language='en'):
919
  ).strip().lower()
920
  if not owner:
921
  return False
922
- # Try common profile fields
923
  username = None
924
  for key in ("preferred_username", "username", "login", "name", "sub", "id"):
925
  try:
@@ -929,12 +942,21 @@ def create_app(language='en'):
929
  username = profile[key]
930
  if username:
931
  break
932
- except Exception:
 
933
  pass
 
 
 
 
 
 
 
934
  if not username:
935
  return False
936
  return str(username).lower() == owner
937
- except Exception:
 
938
  return False
939
 
940
  def _metrics_paths():
@@ -1007,18 +1029,6 @@ def create_app(language='en'):
1007
  f"{override if override else 'None'})"
1008
  )
1009
 
1010
- def show_admin(profile: "gr.OAuthProfile | None"):
1011
- visible = _owner_is(profile)
1012
- return (
1013
- gr.update(visible=visible), # owner_tab
1014
- gr.update(visible=visible), # admin_panel
1015
- gr.update(visible=not visible), # locked_panel
1016
- _summarize_metrics_md() if visible else "",
1017
- _limit_info_md(admin_state.value if hasattr(admin_state, "value") else None)
1018
- if visible
1019
- else "",
1020
- )
1021
-
1022
  def admin_set_limit(override_text: str, settings: dict | None):
1023
  # Only update runtime state; does not change env var
1024
  try:
@@ -1030,14 +1040,15 @@ def create_app(language='en'):
1030
  if override <= 0:
1031
  override = None
1032
  settings["per_user_limit_override"] = override
1033
- except Exception:
 
1034
  settings = {"per_user_limit_override": None}
1035
  return settings, _limit_info_md(settings)
1036
 
1037
  def admin_refresh():
1038
  return _summarize_metrics_md()
1039
 
1040
- def _profile_username(profile: gr.OAuthProfile | None) -> str:
1041
  try:
1042
  for key in ("preferred_username", "username", "login", "name", "sub", "id"):
1043
  if hasattr(profile, key):
@@ -1046,22 +1057,29 @@ def create_app(language='en'):
1046
  return str(v)
1047
  elif isinstance(profile, dict) and key in profile and profile[key]:
1048
  return str(profile[key])
1049
- except Exception:
 
 
 
 
 
 
 
1050
  pass
1051
  return "unknown"
1052
 
1053
- def identity_refresh(profile: gr.OAuthProfile | None):
1054
- viewer = _profile_username(profile)
1055
- visible = _owner_is(profile)
1056
  token_info = ""
1057
- try:
1058
- from huggingface_hub import whoami as _hf_whoami # local import
1059
  info = _hf_whoami()
1060
  uname = info.get("name") or info.get("fullname") or "?"
1061
  ttype = info.get("type", "?")
1062
  orgs = ", ".join([o.get("name", "?") for o in info.get("orgs", [])])
1063
  token_info = f"Token user: `{uname}` (type: {ttype}); orgs: [{orgs}]"
1064
  except Exception as e:
 
1065
  token_info = f"Token whoami failed: {e}"
1066
  return (
1067
  f"Logged in as (OAuth): `{viewer}`\n\n"
@@ -1095,22 +1113,22 @@ def create_app(language='en'):
1095
  return f"/data check failed: {e}"
1096
 
1097
  # Wire admin interactions
1098
- model_dropdown.change(lambda v: v, inputs=[model_dropdown], outputs=[model_state])
1099
  set_override_btn.click(
1100
- admin_set_limit,
1101
  inputs=[override_tb, admin_state],
1102
  outputs=[admin_state, admin_limit_info],
1103
  )
1104
- refresh_btn.click(admin_refresh, outputs=[admin_summary])
1105
- identity_btn.click(identity_refresh, outputs=[owner_identity_md])
1106
- storage_btn.click(storage_check, outputs=[storage_info_md])
1107
 
1108
- # Gate Owner tab & admin panel visibility on load (OAuth)
1109
  try:
1110
  # Also populate identity + storage placeholders
1111
- def _load(profile: "gr.OAuthProfile | None"):
1112
- visible = _owner_is(profile)
1113
- ident = identity_refresh(profile) if visible else ""
1114
  return (
1115
  gr.update(visible=visible),
1116
  gr.update(visible=visible),
@@ -1125,9 +1143,11 @@ def create_app(language='en'):
1125
 
1126
  app.load(
1127
  _load,
1128
- outputs=[owner_tab, admin_panel, locked_panel, admin_summary, admin_limit_info, owner_identity_md, storage_info_md],
 
1129
  )
1130
- except Exception:
 
1131
  # If OAuth not available, keep admin hidden
1132
  pass
1133
 
 
13
  from typing import Optional
14
 
15
  import gradio as gr
16
+ from huggingface_hub import whoami as _hf_whoami
17
 
18
  # Import our CBT knowledge base
19
  from cbt_knowledge import (
 
30
  from agents import CBTAgent
31
 
32
  AGENT_AVAILABLE = True
33
+ except Exception as e:
34
+ print(f"Error importing CBTAgent: {e}")
35
  CBTAgent = None # type: ignore
36
  AGENT_AVAILABLE = False
37
 
 
46
  translations[lang] = json.load(f)
47
  except FileNotFoundError:
48
  # Fallback to embedded translations if files don't exist
49
+ print(f"Error loading translations for {lang}: FileNotFoundError")
50
 
51
  # Fallback translations
52
  if 'en' not in translations:
 
187
  message: str,
188
  history: list[list[str]],
189
  use_agent: bool = False,
190
+ agent: Optional[CBTAgent] = None,
191
  ) -> tuple[list[list[str]], str, str, str]:
192
  """
193
  Process user message and generate response with CBT analysis
 
334
  distortions_output = gr.Markdown(label="Patterns Detected")
335
  reframe_output = gr.Markdown(label="Reframe Suggestion")
336
  situations_output = gr.Markdown(label="Similar Situations")
337
+ # Owner-only Admin controls (gated at load)
338
+ admin_accordion = gr.Accordion(
339
+ "Owner Controls", open=False, visible=False
340
+ )
341
+ with admin_accordion:
342
+ # Locked message for non-owners (kept hidden unless needed)
343
+ chat_locked_panel = gr.Markdown(
344
+ "### Owner only\nPlease log in with your Hugging Face account.",
345
+ visible=False,
346
+ )
347
+ chat_admin_panel = gr.Column(visible=False)
348
+ with chat_admin_panel:
349
+ gr.Markdown("## Admin Dashboard")
350
+ admin_summary = gr.Markdown("")
351
+ admin_limit_info = gr.Markdown("")
352
+ # Owner-only model selection
353
+ model_dropdown = gr.Dropdown(
354
+ label="Model (HF)",
355
+ choices=[
356
+ "meta-llama/Llama-3.1-8B-Instruct",
357
+ "meta-llama/Llama-3.1-70B-Instruct",
358
+ "Qwen/Qwen2.5-7B-Instruct",
359
+ "mistralai/Mixtral-8x7B-Instruct-v0.1",
360
+ "google/gemma-2-9b-it",
361
+ ],
362
+ value=os.getenv("MODEL_NAME", "meta-llama/Llama-3.1-8B-Instruct"),
363
+ allow_custom_value=True,
364
+ info="Only visible to owner. Requires HF Inference API token.",
365
+ )
366
+ with gr.Row():
367
+ override_tb = gr.Textbox(
368
+ label="Per-user interaction limit override (blank to clear)"
369
+ )
370
+ set_override_btn = gr.Button(
371
+ "Set Limit Override", variant="secondary"
372
+ )
373
+ refresh_btn = gr.Button(
374
+ "Refresh Metrics", variant="secondary"
375
+ )
376
+ gr.Markdown("### Debug")
377
+ owner_identity_md = gr.Markdown("")
378
+ with gr.Row():
379
+ identity_btn = gr.Button(
380
+ "Refresh Identity", variant="secondary"
381
+ )
382
+ storage_btn = gr.Button(
383
+ "Check /data", variant="secondary"
384
+ )
385
+ storage_info_md = gr.Markdown("")
386
 
387
  # Internal state for agent instance, selected model, and agentic enable flag
388
  agent_state = gr.State(value=None)
 
561
  username = prof[key]
562
  if username:
563
  break
564
+ except Exception as e:
565
+ print(f"Error getting username from profile: {e}")
566
  pass
567
  raw = f"oauth:{username or 'unknown'}"
568
  # req is expected to be provided by Gradio
 
578
  sess = getattr(req, "session_hash", None) or "?"
579
  raw = f"ipua:{ip}|{ua}|{sess}"
580
  return hashlib.sha256(f"{salt}|{raw}".encode()).hexdigest()
581
+ except Exception as e:
582
+ print(f"Error getting user id: {e}")
583
  return "anon"
584
 
585
  user_id = _user_id(request, profile)
586
+ # Helper functions for tracking per-user interactions
 
 
 
 
 
 
587
  def _interactions_today(uid: str) -> int:
588
  data = _load_call_log()
589
  day = _today_key()
 
594
  else {}
595
  )
596
  val = inter.get(uid, 0)
 
597
  if isinstance(val, (str, int, float)):
598
  try:
599
  return int(val)
600
+ except Exception as e:
601
+ print(f"Error getting interactions today: {e}")
602
  return 0
603
+ return 0
 
 
604
 
605
  def _inc_interactions_today(uid: str):
606
  data = _load_call_log()
 
616
  data[day] = day_blob
617
  _save_call_log(data)
618
 
619
+ # Determine how many interactions the user has already had today
620
+ try:
621
+ interactions_before = _interactions_today(user_id)
622
+ except Exception as e:
623
+ print(f"Error getting interactions today: {e}")
624
+ interactions_before = 0
625
+
626
+ # Per-user interaction quota (counts 1 per message)
627
  max_interactions_env = os.getenv("HF_AGENT_MAX_INTERACTIONS_PER_USER")
628
  try:
629
  # Default to a generous 12 if not configured
 
879
  billing_notice,
880
  agentic_enabled_state,
881
  ],
882
+ ).then(fn=clear_input, outputs=[msg_input])
883
 
884
  send_btn.click(
885
+ fn=respond_stream,
886
  inputs=[
887
  msg_input,
888
  chatbot_ui,
 
907
  return h, d, r, s, ""
908
 
909
  clear_btn.click(
910
+ fn=_clear_session_and_notice,
911
  outputs=[
912
  chatbot_ui,
913
  distortions_output,
 
921
  with gr.Tab(t['learn']['title']):
922
  create_learn_tab(t['learn'], COGNITIVE_DISTORTIONS)
923
 
924
+ def _owner_is(profile: gr.OAuthProfile | None, request: gr.Request | None = None) -> bool:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
925
  try:
926
+ # Prefer explicit OWNER_USER, fallback to the Space author
927
+ # (useful if OWNER_USER not set)
928
  owner = (
929
  os.getenv("OWNER_USER")
930
  or os.getenv("SPACE_AUTHOR_NAME")
 
932
  ).strip().lower()
933
  if not owner:
934
  return False
935
+ # Try common OAuth profile fields
936
  username = None
937
  for key in ("preferred_username", "username", "login", "name", "sub", "id"):
938
  try:
 
942
  username = profile[key]
943
  if username:
944
  break
945
+ except Exception as e:
946
+ print(f"Error getting username from profile: {e}")
947
  pass
948
+ # Fallback to request.username provided by Gradio when OAuth is enabled
949
+ if not username and request is not None:
950
+ try:
951
+ username = getattr(request, "username", None)
952
+ except Exception as e:
953
+ print(f"Error getting username from request: {e}")
954
+ username = None
955
  if not username:
956
  return False
957
  return str(username).lower() == owner
958
+ except Exception as e:
959
+ print(f"Error checking owner: {e}")
960
  return False
961
 
962
  def _metrics_paths():
 
1029
  f"{override if override else 'None'})"
1030
  )
1031
 
 
 
 
 
 
 
 
 
 
 
 
 
1032
  def admin_set_limit(override_text: str, settings: dict | None):
1033
  # Only update runtime state; does not change env var
1034
  try:
 
1040
  if override <= 0:
1041
  override = None
1042
  settings["per_user_limit_override"] = override
1043
+ except Exception as e:
1044
+ print(f"Error setting limit override: {e}")
1045
  settings = {"per_user_limit_override": None}
1046
  return settings, _limit_info_md(settings)
1047
 
1048
  def admin_refresh():
1049
  return _summarize_metrics_md()
1050
 
1051
+ def _profile_username(profile: gr.OAuthProfile | None, request: gr.Request | None = None) -> str:
1052
  try:
1053
  for key in ("preferred_username", "username", "login", "name", "sub", "id"):
1054
  if hasattr(profile, key):
 
1057
  return str(v)
1058
  elif isinstance(profile, dict) and key in profile and profile[key]:
1059
  return str(profile[key])
1060
+ except Exception as e:
1061
+ print(f"Error getting username from profile: {e}")
1062
+ pass
1063
+ try:
1064
+ if request is not None and getattr(request, "username", None):
1065
+ return str(request.username)
1066
+ except Exception as e:
1067
+ print(f"Error getting username from request: {e}")
1068
  pass
1069
  return "unknown"
1070
 
1071
+ def identity_refresh(profile: gr.OAuthProfile | None, request: gr.Request | None = None):
1072
+ viewer = _profile_username(profile, request)
1073
+ visible = _owner_is(profile, request)
1074
  token_info = ""
1075
+ try: # local import
 
1076
  info = _hf_whoami()
1077
  uname = info.get("name") or info.get("fullname") or "?"
1078
  ttype = info.get("type", "?")
1079
  orgs = ", ".join([o.get("name", "?") for o in info.get("orgs", [])])
1080
  token_info = f"Token user: `{uname}` (type: {ttype}); orgs: [{orgs}]"
1081
  except Exception as e:
1082
+ print(f"Error getting token info whoami: {e}")
1083
  token_info = f"Token whoami failed: {e}"
1084
  return (
1085
  f"Logged in as (OAuth): `{viewer}`\n\n"
 
1113
  return f"/data check failed: {e}"
1114
 
1115
  # Wire admin interactions
1116
+ model_dropdown.change(fn=lambda v: v, inputs=[model_dropdown], outputs=[model_state])
1117
  set_override_btn.click(
1118
+ fn=admin_set_limit,
1119
  inputs=[override_tb, admin_state],
1120
  outputs=[admin_state, admin_limit_info],
1121
  )
1122
+ refresh_btn.click(fn=admin_refresh, outputs=[admin_summary])
1123
+ identity_btn.click(fn=identity_refresh, outputs=[owner_identity_md])
1124
+ storage_btn.click(fn=storage_check, outputs=[storage_info_md])
1125
 
1126
+ # Gate admin panel visibility on load (OAuth)
1127
  try:
1128
  # Also populate identity + storage placeholders
1129
+ def _load(profile: gr.OAuthProfile | None, request: gr.Request | None = None):
1130
+ visible = _owner_is(profile, request)
1131
+ ident = identity_refresh(profile, request) if visible else ""
1132
  return (
1133
  gr.update(visible=visible),
1134
  gr.update(visible=visible),
 
1143
 
1144
  app.load(
1145
  _load,
1146
+ outputs=[admin_accordion, chat_admin_panel, chat_locked_panel, admin_summary,
1147
+ admin_limit_info, owner_identity_md, storage_info_md],
1148
  )
1149
+ except Exception as e:
1150
+ print(f"Error loading app: {e}")
1151
  # If OAuth not available, keep admin hidden
1152
  pass
1153