JatsTheAIGen commited on
Commit
38165ef
·
1 Parent(s): 45f26e8

api endpoints for UI migration v4

Browse files
Files changed (1) hide show
  1. app.py +129 -36
app.py CHANGED
@@ -225,8 +225,8 @@ def create_mobile_optimized_interface():
225
  with gr.Row():
226
  # User Selection Dropdown
227
  user_dropdown = gr.Dropdown(
228
- choices=["Admin_J", "Dev_K", "Dev_H", "Dev_A", "Test_Any"],
229
- value="Test_Any",
230
  label="User",
231
  show_label=False,
232
  container=False,
@@ -237,7 +237,7 @@ def create_mobile_optimized_interface():
237
 
238
  session_info = gr.Textbox(
239
  label="Session Info",
240
- value=f"Session: {str(uuid.uuid4())[:8]} | User: Test_Any | Interactions: 0",
241
  max_lines=1,
242
  show_label=False,
243
  container=False,
@@ -434,6 +434,36 @@ def create_mobile_optimized_interface():
434
  save_prefs_btn = gr.Button("Save Preferences", variant="primary")
435
  interface_components['save_prefs_btn'] = save_prefs_btn
436
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
437
  # Wire up the submit handler INSIDE the gr.Blocks context
438
  if 'send_btn' in interface_components and 'message_input' in interface_components and 'chatbot' in interface_components:
439
  # Store interface components globally for dynamic return values
@@ -451,7 +481,7 @@ def create_mobile_optimized_interface():
451
  inputs.append(interface_components['session_info'])
452
 
453
  interface_components['send_btn'].click(
454
- fn=chat_handler_fn,
455
  inputs=inputs,
456
  outputs=outputs
457
  )
@@ -461,11 +491,10 @@ def create_mobile_optimized_interface():
461
  def new_session(user_id):
462
  """
463
  Create new session with validation and database initialization.
464
- Enhanced API endpoint: /new_session
465
- Complete implementation with no placeholders.
466
  """
467
- # Validate user_id
468
- validated_user_id = _validate_user_id(user_id)
469
 
470
  # Generate new session ID
471
  new_session_id = str(uuid.uuid4())[:8]
@@ -498,12 +527,11 @@ def create_mobile_optimized_interface():
498
  def update_session_info(user_id, session_text):
499
  """
500
  Update session info with robust parsing and validation.
501
- Enhanced API endpoint: /update_session_info
502
- Complete implementation with no placeholders.
503
  Preserves session continuity - never generates new session ID.
504
  """
505
- # Validate user_id
506
- validated_user_id = _validate_user_id(user_id)
507
 
508
  # Extract session_id (never generates new one - preserves session continuity)
509
  try:
@@ -700,7 +728,7 @@ def create_mobile_optimized_interface():
700
 
701
  # Try to get current session_id and user_id
702
  session_id = None
703
- user_id = "Test_Any"
704
 
705
  try:
706
  # Try to get from global interface components if available
@@ -712,7 +740,7 @@ def create_mobile_optimized_interface():
712
  import re
713
  user_match = re.search(r'User:\s*([^|]+)', session_info_text)
714
  if user_match:
715
- user_id = _validate_user_id(user_match.group(1).strip())
716
  except Exception as e:
717
  logger.debug(f"Could not extract session info for preferences: {e}")
718
 
@@ -820,21 +848,84 @@ _settings_panel_visible = False
820
  # Global preferences cache (fallback when database unavailable)
821
  _user_preferences_cache = {}
822
 
823
- def _validate_user_id(user_id: str) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
824
  """
825
- Validate and return valid user_id.
 
 
826
 
827
  Args:
828
  user_id: User identifier to validate
 
829
 
830
  Returns:
831
- Validated user_id (defaults to 'Test_Any' if invalid)
832
  """
833
- allowed_users = ['Admin_J', 'Dev_K', 'Dev_H', 'Dev_A', 'Test_Any']
834
- if user_id not in allowed_users:
835
- logger.warning(f"Invalid user_id: {user_id}, defaulting to Test_Any")
836
- return "Test_Any"
837
- return user_id
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
838
 
839
  def _extract_session_id(session_text: str, allow_generate: bool = True) -> str:
840
  """
@@ -969,7 +1060,8 @@ def _create_error_response(error_msg: str, history: list = None) -> tuple:
969
  "" # skills_html
970
  )
971
 
972
- def _validate_chat_inputs(message: str, history, user_id: str, session_text: str) -> tuple:
 
973
  """
974
  Validate all chat handler inputs comprehensively.
975
 
@@ -978,6 +1070,7 @@ def _validate_chat_inputs(message: str, history, user_id: str, session_text: str
978
  history: Chat history list
979
  user_id: User identifier
980
  session_text: Session information text
 
981
 
982
  Returns:
983
  Tuple of (is_valid: bool, session_id: str, validated_user_id: str, error_msg: str)
@@ -1005,8 +1098,8 @@ def _validate_chat_inputs(message: str, history, user_id: str, session_text: str
1005
  logger.warning(f"Invalid history type: {type(history)}, converting to list")
1006
  history = []
1007
 
1008
- # Validate user_id
1009
- validated_user_id = _validate_user_id(user_id)
1010
 
1011
  # Extract session_id
1012
  try:
@@ -1185,7 +1278,7 @@ def process_with_metrics(message, session_id, user_id):
1185
  logger.error(f"Error in process_with_metrics: {e}", exc_info=True)
1186
  return None
1187
 
1188
- async def process_message_async(message: str, history: Optional[List], session_id: str, user_id: str = "Test_Any") -> Tuple[List, str, dict, dict, dict, str, str]:
1189
  """
1190
  Process message with full orchestration system
1191
  Returns (updated_history, empty_string, reasoning_data, performance_data, context_data, session_id, skills_html)
@@ -1596,7 +1689,7 @@ def _build_dynamic_return_values(result: tuple, skills_content: str, interface_c
1596
 
1597
  return tuple(return_values)
1598
 
1599
- def process_message(message: str, history: Optional[List], session_id: Optional[str] = None, user_id: str = "Test_Any") -> tuple:
1600
  """
1601
  Synchronous wrapper for async processing
1602
  Returns dynamic tuple based on available interface components
@@ -1658,14 +1751,14 @@ def process_message(message: str, history: Optional[List], session_id: Optional[
1658
  # Decorate the chat handler with GPU if available
1659
  if SPACES_GPU_AVAILABLE and GPU is not None:
1660
  @GPU # This decorator is detected by HF Spaces for ZeroGPU allocation
1661
- def gpu_chat_handler(message, history, user_id="Test_Any", session_text=""):
1662
  """
1663
  Handle chat messages with GPU support - Enhanced with validation.
1664
  Complete implementation with no placeholders.
1665
  """
1666
- # Validate inputs before processing
1667
  is_valid, session_id, validated_user_id, error_msg = _validate_chat_inputs(
1668
- message, history, user_id, session_text
1669
  )
1670
 
1671
  if not is_valid:
@@ -1682,15 +1775,15 @@ if SPACES_GPU_AVAILABLE and GPU is not None:
1682
  error_result = _create_error_response(f"Processing error: {str(e)}", history)
1683
  return _build_dynamic_return_values(error_result, "", _interface_components)
1684
 
1685
- def safe_gpu_chat_handler(message, history, user_id="Test_Any", session_text=""):
1686
  """
1687
  Wrapper to catch any exceptions from GPU decorator cleanup phase.
1688
  Enhanced with comprehensive validation and error handling.
1689
  Complete implementation with no placeholders.
1690
  """
1691
- # Input validation first
1692
  is_valid, session_id, validated_user_id, error_msg = _validate_chat_inputs(
1693
- message, history, user_id, session_text
1694
  )
1695
 
1696
  if not is_valid:
@@ -1730,15 +1823,15 @@ if SPACES_GPU_AVAILABLE and GPU is not None:
1730
 
1731
  chat_handler_fn = safe_gpu_chat_handler
1732
  else:
1733
- def chat_handler_wrapper(message, history, user_id="Test_Any", session_text=""):
1734
  """
1735
  Wrapper to handle session ID - Enhanced with validation.
1736
  Process Flow functionality moved to logs as per design.
1737
  Complete implementation with no placeholders.
1738
  """
1739
- # Validate inputs
1740
  is_valid, session_id, validated_user_id, error_msg = _validate_chat_inputs(
1741
- message, history, user_id, session_text
1742
  )
1743
 
1744
  if not is_valid:
 
225
  with gr.Row():
226
  # User Selection Dropdown
227
  user_dropdown = gr.Dropdown(
228
+ choices=["ADMINONLY"],
229
+ value="ADMINONLY",
230
  label="User",
231
  show_label=False,
232
  container=False,
 
237
 
238
  session_info = gr.Textbox(
239
  label="Session Info",
240
+ value=f"Session: {str(uuid.uuid4())[:8]} | User: ADMINONLY | Interactions: 0",
241
  max_lines=1,
242
  show_label=False,
243
  container=False,
 
434
  save_prefs_btn = gr.Button("Save Preferences", variant="primary")
435
  interface_components['save_prefs_btn'] = save_prefs_btn
436
 
437
+ # UI-specific chat handler - enforces ADMINONLY only
438
+ def ui_chat_handler(message, history, user_id="ADMINONLY", session_text=""):
439
+ """
440
+ UI-specific chat handler - enforces ADMINONLY only.
441
+ This wrapper ensures UI cannot bypass validation.
442
+ """
443
+ # Force ADMINONLY - UI dropdown only has this option, but add safety check
444
+ if user_id != "ADMINONLY":
445
+ logger.warning(f"UI attempted to use user_id {user_id}, enforcing ADMINONLY")
446
+ user_id = "ADMINONLY"
447
+
448
+ # Validate with UI source
449
+ is_valid, session_id, validated_user_id, error_msg = _validate_chat_inputs(
450
+ message, history, user_id, session_text, source="ui"
451
+ )
452
+
453
+ if not is_valid:
454
+ logger.error(f"Invalid UI chat input: {error_msg}")
455
+ error_result = _create_error_response(error_msg, history)
456
+ return _build_dynamic_return_values(error_result, "", _interface_components)
457
+
458
+ # Process with validated ADMINONLY user
459
+ try:
460
+ result = process_message(message, history, session_id, validated_user_id)
461
+ return result
462
+ except Exception as e:
463
+ logger.error(f"Error in ui_chat_handler: {e}", exc_info=True)
464
+ error_result = _create_error_response(f"Processing error: {str(e)}", history)
465
+ return _build_dynamic_return_values(error_result, "", _interface_components)
466
+
467
  # Wire up the submit handler INSIDE the gr.Blocks context
468
  if 'send_btn' in interface_components and 'message_input' in interface_components and 'chatbot' in interface_components:
469
  # Store interface components globally for dynamic return values
 
481
  inputs.append(interface_components['session_info'])
482
 
483
  interface_components['send_btn'].click(
484
+ fn=ui_chat_handler, # Use UI-specific handler
485
  inputs=inputs,
486
  outputs=outputs
487
  )
 
491
  def new_session(user_id):
492
  """
493
  Create new session with validation and database initialization.
494
+ UI version - enforces ADMINONLY.
 
495
  """
496
+ # Validate user_id for UI
497
+ validated_user_id = _validate_user_id(user_id, source="ui")
498
 
499
  # Generate new session ID
500
  new_session_id = str(uuid.uuid4())[:8]
 
527
  def update_session_info(user_id, session_text):
528
  """
529
  Update session info with robust parsing and validation.
530
+ UI version - enforces ADMINONLY.
 
531
  Preserves session continuity - never generates new session ID.
532
  """
533
+ # Validate user_id for UI
534
+ validated_user_id = _validate_user_id(user_id, source="ui")
535
 
536
  # Extract session_id (never generates new one - preserves session continuity)
537
  try:
 
728
 
729
  # Try to get current session_id and user_id
730
  session_id = None
731
+ user_id = "ADMINONLY" # UI default
732
 
733
  try:
734
  # Try to get from global interface components if available
 
740
  import re
741
  user_match = re.search(r'User:\s*([^|]+)', session_info_text)
742
  if user_match:
743
+ user_id = _validate_user_id(user_match.group(1).strip(), source="api")
744
  except Exception as e:
745
  logger.debug(f"Could not extract session info for preferences: {e}")
746
 
 
848
  # Global preferences cache (fallback when database unavailable)
849
  _user_preferences_cache = {}
850
 
851
+ def _ensure_user_exists(user_id: str):
852
+ """
853
+ Ensure user exists in database (auto-create if new).
854
+ Backend maintains user information - no hardcoded lists.
855
+ """
856
+ try:
857
+ global orchestrator
858
+ if orchestrator and hasattr(orchestrator, 'context_manager'):
859
+ context_manager = orchestrator.context_manager
860
+
861
+ # Check if user exists in user_contexts table
862
+ import sqlite3
863
+ db_path = getattr(context_manager, 'db_path', 'sessions.db')
864
+ conn = sqlite3.connect(db_path)
865
+ cursor = conn.cursor()
866
+
867
+ # Check if user exists
868
+ cursor.execute("SELECT user_id FROM user_contexts WHERE user_id = ?", (user_id,))
869
+ exists = cursor.fetchone()
870
+
871
+ if not exists:
872
+ # Auto-create user in database
873
+ cursor.execute("""
874
+ INSERT OR IGNORE INTO user_contexts (user_id, persona_summary, updated_at)
875
+ VALUES (?, ?, datetime('now'))
876
+ """, (user_id, ""))
877
+ conn.commit()
878
+ logger.info(f"Auto-created new API user: {user_id}")
879
+ else:
880
+ logger.debug(f"User {user_id} already exists in database")
881
+
882
+ conn.close()
883
+ except Exception as e:
884
+ logger.error(f"Error ensuring user exists: {e}", exc_info=True)
885
+ # Non-fatal - continue with user_id as-is
886
+
887
+ def _validate_user_id(user_id: str, source: str = "api") -> str:
888
  """
889
+ Validate user_id based on source (UI or API).
890
+ UI: Only ADMINONLY allowed
891
+ API: Admin_J base user, but accepts any user_id (auto-creates in database)
892
 
893
  Args:
894
  user_id: User identifier to validate
895
+ source: "ui" or "api" to determine validation rules
896
 
897
  Returns:
898
+ Validated user_id (for UI: ADMINONLY, for API: provided user_id or Admin_J)
899
  """
900
+ if source == "ui":
901
+ # UI can ONLY use ADMINONLY
902
+ if user_id != "ADMINONLY":
903
+ logger.warning(f"UI attempted invalid user_id: {user_id}, enforcing ADMINONLY")
904
+ return "ADMINONLY"
905
+ return "ADMINONLY"
906
+
907
+ else: # API source
908
+ # API: Base allowed user is Admin_J
909
+ api_base_user = "Admin_J"
910
+
911
+ # If provided user_id is Admin_J, allow it
912
+ if user_id == api_base_user:
913
+ return user_id
914
+
915
+ # For any other user_id from API, validate format and auto-create in database
916
+ # Allow any non-empty, valid string (alphanumeric + underscore, max 50 chars)
917
+ if user_id and isinstance(user_id, str):
918
+ # Basic validation: alphanumeric + underscore, no spaces, reasonable length
919
+ import re
920
+ if re.match(r'^[a-zA-Z0-9_]{1,50}$', user_id):
921
+ # Valid format - auto-create user in database
922
+ _ensure_user_exists(user_id)
923
+ logger.info(f"API user validated/created: {user_id}")
924
+ return user_id
925
+
926
+ # Invalid format - default to Admin_J
927
+ logger.warning(f"Invalid API user_id format: {user_id}, defaulting to Admin_J")
928
+ return api_base_user
929
 
930
  def _extract_session_id(session_text: str, allow_generate: bool = True) -> str:
931
  """
 
1060
  "" # skills_html
1061
  )
1062
 
1063
+ def _validate_chat_inputs(message: str, history, user_id: str,
1064
+ session_text: str, source: str = "api") -> tuple:
1065
  """
1066
  Validate all chat handler inputs comprehensively.
1067
 
 
1070
  history: Chat history list
1071
  user_id: User identifier
1072
  session_text: Session information text
1073
+ source: "ui" or "api" - determines validation rules
1074
 
1075
  Returns:
1076
  Tuple of (is_valid: bool, session_id: str, validated_user_id: str, error_msg: str)
 
1098
  logger.warning(f"Invalid history type: {type(history)}, converting to list")
1099
  history = []
1100
 
1101
+ # Validate user_id with source-aware validation
1102
+ validated_user_id = _validate_user_id(user_id, source=source)
1103
 
1104
  # Extract session_id
1105
  try:
 
1278
  logger.error(f"Error in process_with_metrics: {e}", exc_info=True)
1279
  return None
1280
 
1281
+ async def process_message_async(message: str, history: Optional[List], session_id: str, user_id: str = "Admin_J") -> Tuple[List, str, dict, dict, dict, str, str]:
1282
  """
1283
  Process message with full orchestration system
1284
  Returns (updated_history, empty_string, reasoning_data, performance_data, context_data, session_id, skills_html)
 
1689
 
1690
  return tuple(return_values)
1691
 
1692
+ def process_message(message: str, history: Optional[List], session_id: Optional[str] = None, user_id: str = "Admin_J") -> tuple:
1693
  """
1694
  Synchronous wrapper for async processing
1695
  Returns dynamic tuple based on available interface components
 
1751
  # Decorate the chat handler with GPU if available
1752
  if SPACES_GPU_AVAILABLE and GPU is not None:
1753
  @GPU # This decorator is detected by HF Spaces for ZeroGPU allocation
1754
+ def gpu_chat_handler(message, history, user_id="Admin_J", session_text=""):
1755
  """
1756
  Handle chat messages with GPU support - Enhanced with validation.
1757
  Complete implementation with no placeholders.
1758
  """
1759
+ # Validate inputs before processing (API source)
1760
  is_valid, session_id, validated_user_id, error_msg = _validate_chat_inputs(
1761
+ message, history, user_id, session_text, source="api"
1762
  )
1763
 
1764
  if not is_valid:
 
1775
  error_result = _create_error_response(f"Processing error: {str(e)}", history)
1776
  return _build_dynamic_return_values(error_result, "", _interface_components)
1777
 
1778
+ def safe_gpu_chat_handler(message, history, user_id="Admin_J", session_text=""):
1779
  """
1780
  Wrapper to catch any exceptions from GPU decorator cleanup phase.
1781
  Enhanced with comprehensive validation and error handling.
1782
  Complete implementation with no placeholders.
1783
  """
1784
+ # Input validation first (API source)
1785
  is_valid, session_id, validated_user_id, error_msg = _validate_chat_inputs(
1786
+ message, history, user_id, session_text, source="api"
1787
  )
1788
 
1789
  if not is_valid:
 
1823
 
1824
  chat_handler_fn = safe_gpu_chat_handler
1825
  else:
1826
+ def chat_handler_wrapper(message, history, user_id="Admin_J", session_text=""):
1827
  """
1828
  Wrapper to handle session ID - Enhanced with validation.
1829
  Process Flow functionality moved to logs as per design.
1830
  Complete implementation with no placeholders.
1831
  """
1832
+ # Validate inputs (API source)
1833
  is_valid, session_id, validated_user_id, error_msg = _validate_chat_inputs(
1834
+ message, history, user_id, session_text, source="api"
1835
  )
1836
 
1837
  if not is_valid: