@@ -737,6 +737,80 @@ def record_failure(self):
737737
738738redis_circuit_breaker = RedisCircuitBreaker ()
739739
740+ # File request storage functions
741+ def get_file_requests_key (session_id : str ) -> str :
742+ """Get Redis key for session file requests."""
743+ return f"file_requests:{ session_id } "
744+
745+ def store_file_requests (session_id : str , file_requests : List [Dict [str , Any ]], ttl_hours : int = 24 ):
746+ """Store file requests for a session."""
747+ if REDIS_AVAILABLE and redis_circuit_breaker .can_execute ():
748+ try :
749+ session_redis .setex (
750+ get_file_requests_key (session_id ),
751+ timedelta (hours = ttl_hours ),
752+ json .dumps (file_requests )
753+ )
754+ redis_circuit_breaker .record_success ()
755+ except Exception as e :
756+ logger .error (f"Error storing file requests: { e } " )
757+ redis_circuit_breaker .record_failure ()
758+ else :
759+ # Fallback to in-memory storage
760+ if session_id not in globals ():
761+ globals ()[f"file_requests_{ session_id } " ] = file_requests
762+
763+ def get_file_requests (session_id : str ) -> List [Dict [str , Any ]]:
764+ """Get file requests for a session."""
765+ if REDIS_AVAILABLE and redis_circuit_breaker .can_execute ():
766+ try :
767+ data = session_redis .get (get_file_requests_key (session_id ))
768+ if data :
769+ redis_circuit_breaker .record_success ()
770+ return json .loads (data )
771+ return []
772+ except Exception as e :
773+ logger .error (f"Error getting file requests: { e } " )
774+ redis_circuit_breaker .record_failure ()
775+ return []
776+ else :
777+ # Fallback to in-memory storage
778+ return globals ().get (f"file_requests_{ session_id } " , [])
779+
780+ async def create_file_request_entities (session_id : str , requested_files : List [Dict [str , Any ]], repository_name : str ):
781+ """Create file request entities that can be approved/rejected."""
782+ try :
783+ # Get existing file requests for this session
784+ existing_requests = get_file_requests (session_id )
785+
786+ # Create new file request entities
787+ new_requests = []
788+ for file_req in requested_files :
789+ file_request = {
790+ "id" : str (uuid .uuid4 ()),
791+ "session_id" : session_id ,
792+ "file_path" : file_req .get ("path" , "" ),
793+ "reason" : file_req .get ("reason" , "AI requested this file" ),
794+ "repository_name" : repository_name ,
795+ "branch" : "main" ,
796+ "status" : "pending" , # pending, approved, rejected
797+ "created_at" : datetime .now ().isoformat (),
798+ "metadata" : file_req .get ("metadata" , {})
799+ }
800+ new_requests .append (file_request )
801+
802+ # Combine with existing requests
803+ all_requests = existing_requests + new_requests
804+
805+ # Store updated file requests
806+ store_file_requests (session_id , all_requests , ttl_hours = 24 )
807+
808+ logger .info (f"Created { len (new_requests )} file request entities for session { session_id } " )
809+
810+ except Exception as e :
811+ logger .error (f"Error creating file request entities: { e } " )
812+ raise
813+
740814# Session management helper functions
741815def get_session_key (session_id : str ) -> str :
742816 """Get Redis key for session data."""
@@ -1351,6 +1425,15 @@ async def send_message(self, session_id: str, user_id: str, message: str, contex
13511425 knowledge_used = len (sources ) if sources else 0
13521426 model_used = cosmos_response .model_used or model_name
13531427
1428+ # Debug: Log Cosmos response metadata
1429+ logger .info (f"Cosmos response metadata: { cosmos_response .metadata } " )
1430+ if cosmos_response .metadata :
1431+ logger .info (f"Cosmos metadata keys: { list (cosmos_response .metadata .keys ())} " )
1432+ if 'requested_files' in cosmos_response .metadata :
1433+ logger .info (f"Cosmos found { len (cosmos_response .metadata ['requested_files' ])} file requests" )
1434+ if 'interactive_elements' in cosmos_response .metadata :
1435+ logger .info (f"Cosmos found { len (cosmos_response .metadata ['interactive_elements' ])} interactive elements" )
1436+
13541437 # Apply tool command filtering for security
13551438 try :
13561439 from services .tool_command_filter import filter_tool_commands
@@ -1363,6 +1446,44 @@ async def send_message(self, session_id: str, user_id: str, message: str, contex
13631446 logger .error (f"Error applying tool command filter: { filter_error } " )
13641447 assistant_content = raw_assistant_content
13651448
1449+ # Use the processed response from Cosmos wrapper (already processed)
1450+ processed_response = None
1451+ cosmos_metadata = cosmos_response .metadata or {}
1452+
1453+ # Check if Cosmos wrapper already processed the response for file requests
1454+ if cosmos_metadata .get ('requested_files' ) or cosmos_metadata .get ('interactive_elements' ):
1455+ logger .info (f"Using Cosmos processed response with { len (cosmos_metadata .get ('requested_files' , []))} file requests" )
1456+ processed_response = type ('ProcessedResponse' , (), {
1457+ 'metadata' : cosmos_metadata ,
1458+ 'interactive_elements' : cosmos_metadata .get ('interactive_elements' , [])
1459+ })()
1460+ else :
1461+ # Fallback: Process response for file requests if Cosmos didn't
1462+ try :
1463+ from services .response_processor import ResponseProcessor
1464+ processor = ResponseProcessor ()
1465+ processed_response = processor .process_response (
1466+ content = assistant_content ,
1467+ metadata = {
1468+ "model_used" : model_used ,
1469+ "confidence" : confidence ,
1470+ "knowledge_used" : knowledge_used ,
1471+ "sources_count" : len (sources ) if sources else 0
1472+ }
1473+ )
1474+ logger .info (f"Fallback response processed with { len (processed_response .interactive_elements )} interactive elements and { len (processed_response .metadata .get ('requested_files' , []))} file requests" )
1475+
1476+ # Debug: Log the response content and extracted requests
1477+ if processed_response .metadata .get ('requested_files' ):
1478+ logger .info (f"File requests extracted: { [req ['path' ] for req in processed_response .metadata ['requested_files' ]]} " )
1479+
1480+ except ImportError :
1481+ logger .warning ("Response processor not available" )
1482+ except Exception as process_error :
1483+ logger .error (f"Error processing response: { process_error } " )
1484+ import traceback
1485+ logger .error (f"Traceback: { traceback .format_exc ()} " )
1486+
13661487 # Add metadata to response if available
13671488 if knowledge_used > 0 :
13681489 assistant_content += f"\n \n *Analyzed { knowledge_used } files from your codebase*"
@@ -1431,6 +1552,32 @@ async def send_message(self, session_id: str, user_id: str, message: str, contex
14311552 }
14321553 }
14331554
1555+ # Add processed response metadata if available
1556+ if processed_response :
1557+ requested_files = processed_response .metadata .get ("requested_files" , [])
1558+ assistant_message ["metadata" ].update ({
1559+ "requested_files" : requested_files ,
1560+ "file_requests_count" : processed_response .metadata .get ("file_requests_count" , 0 ),
1561+ "interactive_elements" : [
1562+ {
1563+ "element_type" : elem .element_type ,
1564+ "label" : elem .label ,
1565+ "value" : elem .value ,
1566+ "action" : elem .action ,
1567+ "metadata" : elem .metadata
1568+ }
1569+ for elem in processed_response .interactive_elements
1570+ ]
1571+ })
1572+
1573+ # Create file request entities for approval/rejection
1574+ if requested_files :
1575+ try :
1576+ await create_file_request_entities (session_id , requested_files , repository_name )
1577+ logger .info (f"Created { len (requested_files )} file request entities for session { session_id } " )
1578+ except Exception as e :
1579+ logger .error (f"Error creating file request entities: { e } " )
1580+
14341581 # Add assistant message to history
14351582 messages = get_messages (session_id )
14361583 messages .append (assistant_message )
0 commit comments