JatsTheAIGen commited on
Commit
0d56066
·
1 Parent(s): 0b5851a

safety agent upgrades to enable creative freedom, loops fixed v2

Browse files
Files changed (2) hide show
  1. app.py +39 -14
  2. src/orchestrator_engine.py +29 -0
app.py CHANGED
@@ -514,28 +514,37 @@ async def process_message_async(message: str, history: Optional[List], session_i
514
 
515
  new_history = list(history) if isinstance(history, list) else []
516
 
517
- # Check if this is a safety choice response
518
  message_upper = message.strip().upper()
519
  is_safety_choice = message_upper in ['YES', 'NO', 'APPLY', 'KEEP', 'Y', 'N']
520
 
521
  # Check if we have a pending safety choice for this session
522
- if is_safety_choice and orchestrator is not None and hasattr(orchestrator, '_pending_choices'):
523
- pending_choice = orchestrator._pending_choices.get(session_id)
524
- if pending_choice:
525
- logger.info(f"Processing safety choice: {message_upper}")
 
 
 
526
 
527
  # Determine user decision
528
  user_decision = message_upper in ['YES', 'APPLY', 'Y']
529
 
530
- # Process the safety choice
531
- choice_result = await orchestrator.handle_user_safety_decision(
532
- pending_choice['choice_id'],
533
- user_decision,
534
- session_id
535
- )
536
-
537
- # Clean up pending choice
538
- del orchestrator._pending_choices[session_id]
 
 
 
 
 
 
539
 
540
  # Add user message
541
  new_history.append({"role": "user", "content": message.strip()})
@@ -559,6 +568,10 @@ async def process_message_async(message: str, history: Optional[List], session_i
559
  "session_id": session_id
560
  }
561
 
 
 
 
 
562
  return new_history, "", reasoning_data, performance_data, context_data, session_id, ""
563
 
564
  # Add user message (normal flow)
@@ -578,11 +591,18 @@ async def process_message_async(message: str, history: Optional[List], session_i
578
  try:
579
  logger.info("Attempting full orchestration...")
580
  # First, try normal processing to check for user choice
 
581
  result = await orchestrator.process_request(
582
  session_id=session_id,
583
  user_input=message.strip()
584
  )
585
 
 
 
 
 
 
 
586
  # Check if user choice is required
587
  if result.get('requires_user_choice', False):
588
  logger.info("User choice required for safety concerns")
@@ -610,6 +630,11 @@ async def process_message_async(message: str, history: Optional[List], session_i
610
  'safety_analysis': result.get('safety_analysis', {})
611
  }
612
 
 
 
 
 
 
613
  # Add assistant message with choice prompt
614
  new_history.append({
615
  "role": "assistant",
 
514
 
515
  new_history = list(history) if isinstance(history, list) else []
516
 
517
+ # Check if this is a safety choice response (BEFORE normal processing)
518
  message_upper = message.strip().upper()
519
  is_safety_choice = message_upper in ['YES', 'NO', 'APPLY', 'KEEP', 'Y', 'N']
520
 
521
  # Check if we have a pending safety choice for this session
522
+ if is_safety_choice and orchestrator is not None:
523
+ # Check both _pending_choices (from app.py) and awaiting_safety_response (from orchestrator)
524
+ pending_choice = getattr(orchestrator, '_pending_choices', {}).get(session_id)
525
+ awaiting_response = getattr(orchestrator, 'awaiting_safety_response', {}).get(session_id, False)
526
+
527
+ if pending_choice or awaiting_response:
528
+ logger.info(f"Processing safety choice response: {message_upper} (session: {session_id})")
529
 
530
  # Determine user decision
531
  user_decision = message_upper in ['YES', 'APPLY', 'Y']
532
 
533
+ # Process the safety choice directly (bypasses normal safety checks)
534
+ if pending_choice:
535
+ choice_result = await orchestrator.handle_user_safety_decision(
536
+ pending_choice['choice_id'],
537
+ user_decision,
538
+ session_id
539
+ )
540
+
541
+ # Clean up pending choice
542
+ if hasattr(orchestrator, '_pending_choices'):
543
+ orchestrator._pending_choices.pop(session_id, None)
544
+ else:
545
+ # Fallback: if no pending choice but flag is set, skip safety check
546
+ logger.warning(f"Safety response flag set but no pending choice found - bypassing safety check")
547
+ return new_history, "", {}, {}, {}, session_id, ""
548
 
549
  # Add user message
550
  new_history.append({"role": "user", "content": message.strip()})
 
568
  "session_id": session_id
569
  }
570
 
571
+ # Ensure flags are cleared
572
+ if hasattr(orchestrator, 'awaiting_safety_response'):
573
+ orchestrator.awaiting_safety_response.pop(session_id, None)
574
+
575
  return new_history, "", reasoning_data, performance_data, context_data, session_id, ""
576
 
577
  # Add user message (normal flow)
 
591
  try:
592
  logger.info("Attempting full orchestration...")
593
  # First, try normal processing to check for user choice
594
+ # NOTE: Binary safety responses are already handled above, so this won't process them
595
  result = await orchestrator.process_request(
596
  session_id=session_id,
597
  user_input=message.strip()
598
  )
599
 
600
+ # Check if result indicates this was a safety response (should have been handled above)
601
+ if result.get('is_safety_response', False):
602
+ logger.warning("Safety response detected in normal processing - should have been handled earlier")
603
+ # Skip further processing
604
+ return new_history, "", {}, {}, {}, session_id, ""
605
+
606
  # Check if user choice is required
607
  if result.get('requires_user_choice', False):
608
  logger.info("User choice required for safety concerns")
 
630
  'safety_analysis': result.get('safety_analysis', {})
631
  }
632
 
633
+ # Ensure awaiting_safety_response flag is also set in orchestrator
634
+ if not hasattr(orchestrator, 'awaiting_safety_response'):
635
+ orchestrator.awaiting_safety_response = {}
636
+ orchestrator.awaiting_safety_response[session_id] = True
637
+
638
  # Add assistant message with choice prompt
639
  new_history.append({
640
  "role": "assistant",
src/orchestrator_engine.py CHANGED
@@ -41,6 +41,11 @@ class MVPOrchestrator:
41
  }
42
  self.max_revision_attempts = 2
43
  self.revision_timeout = 30 # seconds
 
 
 
 
 
44
  logger.info("MVPOrchestrator initialized with safety revision thresholds")
45
 
46
  async def process_request(self, session_id: str, user_input: str) -> dict:
@@ -50,6 +55,23 @@ class MVPOrchestrator:
50
  logger.info(f"Processing request for session {session_id}")
51
  logger.info(f"User input: {user_input[:100]}")
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  # Clear previous trace for new request
54
  self.execution_trace = []
55
  start_time = time.time()
@@ -231,6 +253,9 @@ class MVPOrchestrator:
231
  logger.info(f"Safety concerns detected for intent '{intent_class}' - requiring user choice")
232
  processing_time = time.time() - start_time
233
 
 
 
 
234
  return {
235
  'requires_user_choice': True,
236
  'choice_prompt': choice_prompt,
@@ -401,6 +426,10 @@ class MVPOrchestrator:
401
  dict: Final response based on user choice
402
  """
403
  try:
 
 
 
 
404
  if not SAFETY_CHOICE_AVAILABLE:
405
  logger.warning("Safety choice modules not available")
406
  return {'error': 'Safety choice system not available'}
 
41
  }
42
  self.max_revision_attempts = 2
43
  self.revision_timeout = 30 # seconds
44
+
45
+ # Safety response tracking to prevent infinite loops
46
+ self.awaiting_safety_response = {} # session_id -> True/False
47
+ self._pending_choices = {} # session_id -> choice_data
48
+
49
  logger.info("MVPOrchestrator initialized with safety revision thresholds")
50
 
51
  async def process_request(self, session_id: str, user_input: str) -> dict:
 
55
  logger.info(f"Processing request for session {session_id}")
56
  logger.info(f"User input: {user_input[:100]}")
57
 
58
+ # Safety context bypass: Skip safety checks for binary responses to safety prompts
59
+ user_input_upper = user_input.strip().upper()
60
+ is_binary_response = user_input_upper in ['YES', 'NO', 'APPLY', 'KEEP', 'Y', 'N']
61
+
62
+ if is_binary_response and self.awaiting_safety_response.get(session_id, False):
63
+ logger.info(f"Binary safety response detected ({user_input_upper}) - bypassing safety check to prevent loop")
64
+ # Clear the flag immediately to prevent re-triggering
65
+ self.awaiting_safety_response[session_id] = False
66
+
67
+ # Return a signal that this should be handled by the choice handler, not normal processing
68
+ return {
69
+ 'is_safety_response': True,
70
+ 'response': user_input_upper,
71
+ 'requires_user_choice': False,
72
+ 'skip_safety_check': True
73
+ }
74
+
75
  # Clear previous trace for new request
76
  self.execution_trace = []
77
  start_time = time.time()
 
253
  logger.info(f"Safety concerns detected for intent '{intent_class}' - requiring user choice")
254
  processing_time = time.time() - start_time
255
 
256
+ # Set flag to indicate we're awaiting safety response for this session
257
+ self.awaiting_safety_response[session_id] = True
258
+
259
  return {
260
  'requires_user_choice': True,
261
  'choice_prompt': choice_prompt,
 
426
  dict: Final response based on user choice
427
  """
428
  try:
429
+ # Clear the awaiting safety response flag immediately to prevent loops
430
+ if session_id:
431
+ self.awaiting_safety_response[session_id] = False
432
+
433
  if not SAFETY_CHOICE_AVAILABLE:
434
  logger.warning("Safety choice modules not available")
435
  return {'error': 'Safety choice system not available'}