2222from utils .context_builder import ContextBuilder
2323from utils .notifier import Notifier
2424
25+
2526class AIController :
2627 """Controls AI operations and decision making."""
2728
2829 def __init__ (self , session_dir : str , config_path : str ):
2930 """Initialize the AI controller.
30-
31+
3132 Args:
3233 session_dir: Directory to store session data
3334 config_path: Path to configuration file
@@ -39,7 +40,7 @@ def __init__(self, session_dir: str, config_path: str):
3940 self .notifier = Notifier (session_dir , config_path )
4041 self .report_generator = ReportGenerator (self .session_dir )
4142 self .output_format = "all" # Default output format
42-
43+
4344 # Initialize session data
4445 self .session_data = {
4546 "start_time" : datetime .now ().isoformat (),
@@ -48,43 +49,48 @@ def __init__(self, session_dir: str, config_path: str):
4849 "findings" : [],
4950 "execution_history" : [],
5051 "ai_decisions" : [],
51- "errors" : []
52+ "errors" : [],
5253 }
5354
5455 @RateLimiter (max_calls = 5 , time_window = 60 )
55- async def process_query (self , query : str , context : Optional [Dict [str , Any ]] = None ,
56- session_id : Optional [str ] = None , identifier : str = 'default' ) -> Dict [str , Any ]:
56+ async def process_query (
57+ self ,
58+ query : str ,
59+ context : Optional [Dict [str , Any ]] = None ,
60+ session_id : Optional [str ] = None ,
61+ identifier : str = "default" ,
62+ ) -> Dict [str , Any ]:
5763 """Process an AI query with authentication and rate limiting.
58-
64+
5965 Args:
6066 query: The query to process
6167 context: Optional context data
6268 session_id: Optional session ID for authentication
6369 identifier: Rate limit identifier
64-
70+
6571 Returns:
6672 Dictionary containing the response and metadata
67-
73+
6874 Raises:
6975 PermissionError: If authentication fails
7076 """
7177 # SECURITY: Validate input
7278 if not isinstance (query , str ) or not query .strip ():
7379 raise ValueError ("Query must be a non-empty string" )
74-
80+
7581 # SECURITY: Limit query length to prevent abuse
7682 if len (query ) > 5000 :
7783 raise ValueError ("Query too long (max 5000 characters)" )
78-
84+
7985 # SECURITY: Sanitize query for logging
8086 safe_query = SecurityValidator .sanitize_log_input (query [:100 ])
8187 self .logger .info ("Processing AI query: %s..." , safe_query )
82-
88+
8389 # SECURITY: Check authentication if session_id provided
8490 if session_id :
8591 auth_manager = get_auth_manager ()
8692 auth_manager .require_permission (session_id , Permission .AI_QUERY )
87-
93+
8894 try :
8995 # Prepare context
9096 context_str = ""
@@ -93,7 +99,7 @@ async def process_query(self, query: str, context: Optional[Dict[str, Any]] = No
9399 if not isinstance (context , dict ):
94100 raise ValueError ("Context must be a dictionary" )
95101 context_str = json .dumps (context , indent = 2 )
96-
102+
97103 # Create prompt
98104 prompt = f"""You are a security expert. Please provide guidance on the following query:
99105
@@ -110,20 +116,20 @@ async def process_query(self, query: str, context: Optional[Dict[str, Any]] = No
1101165. Safety considerations
111117
112118Provide a detailed response:"""
113-
119+
114120 # Get AI response
115121 response = await self ._get_ai_response (prompt )
116-
122+
117123 # Log response
118124 self .logger .info ("AI response received" )
119-
125+
120126 return {
121127 "answer" : response ,
122128 # Return an empty log list as the logger doesn't expose in-memory logs
123129 "logs" : [],
124- "prompt" : prompt
130+ "prompt" : prompt ,
125131 }
126-
132+
127133 except ValueError as e :
128134 # SECURITY: Don't expose internal errors
129135 self .logger .error ("Validation error: %s" , str (e ))
@@ -135,52 +141,71 @@ async def process_query(self, query: str, context: Optional[Dict[str, Any]] = No
135141
136142 async def execute_intent (self , intent : Dict [str , Any ]) -> Dict [str , Any ]:
137143 """Execute a structured action intent from the Agentic AI.
138-
144+
139145 Args:
140146 intent: The structured intent JSON.
141-
147+
142148 Returns:
143149 Result of the execution.
144150 """
145- self .logger .info ("Executing intent: %s on %s" , intent .get ("type" ), intent .get ("target" ))
146-
151+ self .logger .info (
152+ "Executing intent: %s on %s" , intent .get ("type" ), intent .get ("target" )
153+ )
154+
147155 action_type = intent .get ("type" )
148156 target = intent .get ("target" )
149157 value = intent .get ("value" )
150-
158+
151159 try :
152160 if action_type == "module_call" :
153161 if target == "recon_scan" :
154162 # Placeholder for recon trigger logic
155- return {"status" : "success" , "message" : f"Triggered recon scan on { value } " }
163+ return {
164+ "status" : "success" ,
165+ "message" : f"Triggered recon scan on { value } " ,
166+ }
156167 elif target == "robin_search" :
157- return {"status" : "success" , "message" : f"Triggered Robin search for { value } " }
158-
168+ return {
169+ "status" : "success" ,
170+ "message" : f"Triggered Robin search for { value } " ,
171+ }
172+
159173 elif action_type == "tool_call" :
160174 if target == "install_tool" :
161- return {"status" : "info" , "message" : f"Suggested installation of tool: { value } " }
162-
175+ return {
176+ "status" : "info" ,
177+ "message" : f"Suggested installation of tool: { value } " ,
178+ }
179+
163180 elif action_type == "ui_click" :
164181 return {"status" : "ui_intent" , "action" : "navigate" , "tab" : target }
165-
182+
166183 elif action_type == "ui_input" :
167- return {"status" : "ui_intent" , "action" : "fill" , "element" : target , "value" : value }
168-
169- return {"status" : "error" , "message" : f"Unknown intent type or target: { action_type } /{ target } " }
170-
184+ return {
185+ "status" : "ui_intent" ,
186+ "action" : "fill" ,
187+ "element" : target ,
188+ "value" : value ,
189+ }
190+
191+ return {
192+ "status" : "error" ,
193+ "message" : f"Unknown intent type or target: { action_type } /{ target } " ,
194+ }
195+
171196 except Exception as e :
172197 self .logger .error (f"Intent execution failed: { e } " )
173198 return {"status" : "error" , "message" : str (e )}
174199
175200 async def _get_ai_response (self , prompt : str ) -> str :
176201 """Get response from AI model with timeout.
177-
202+
178203 Args:
179204 prompt: The prompt to send to the AI
180-
205+
181206 Returns:
182207 The AI's response
183-
208+
184209 Raises:
185210 TimeoutError: If request times out
186211 """
@@ -190,7 +215,7 @@ async def _get_ai_response(self, prompt: str) -> str:
190215 # TODO: Implement actual AI model call
191216 # For now, return a placeholder response
192217 return "This is a placeholder response. AI integration pending."
193-
218+
194219 except asyncio .TimeoutError :
195220 self .logger .error ("AI request timed out" )
196221 raise TimeoutError ("AI request timed out after 300 seconds" )
@@ -200,75 +225,82 @@ async def _get_ai_response(self, prompt: str) -> str:
200225
201226 def _generate_report (self ) -> Dict [str , str ]:
202227 """Generate reports for the current session.
203-
228+
204229 Returns:
205230 Dictionary mapping report types to their file paths
206231 """
207232 self .logger .info ("Generating reports in %s format..." , self .output_format )
208-
233+
209234 try :
210235 # Prepare context data
211236 context = {
212237 "target" : {
213238 "domain" : self .session_data .get ("target_domain" ),
214239 "ip" : self .session_data .get ("target_ip" ),
215240 "scan_time" : self .session_data .get ("start_time" ),
216- "duration" : self ._calculate_duration ()
241+ "duration" : self ._calculate_duration (),
217242 },
218243 "modules" : self .session_data ["modules" ],
219244 "tools" : self .session_data ["tools" ],
220245 "vulnerabilities" : self .session_data .get ("findings" , []),
221246 "exploits" : self .session_data .get ("execution_history" , []),
222247 "defensive_measures" : self .session_data .get ("defensive_measures" , []),
223248 "recommendations" : self .session_data .get ("recommendations" , []),
224- "errors" : self .session_data ["errors" ]
249+ "errors" : self .session_data ["errors" ],
225250 }
226-
251+
227252 # Generate reports
228- report_paths = self .report_generator .generate_reports (context , self .output_format )
229-
253+ report_paths = self .report_generator .generate_reports (
254+ context , self .output_format
255+ )
256+
230257 self .logger .info ("Reports generated successfully" )
231258 return report_paths
232-
259+
233260 except Exception as e :
234261 self .logger .error ("Error generating reports: %s" , str (e ))
235262 raise
236263
237264 def _calculate_duration (self ) -> str :
238265 """Calculate session duration.
239-
266+
240267 Returns:
241268 Formatted duration string
242269 """
243270 start_time = datetime .fromisoformat (self .session_data ["start_time" ])
244271 end_time = datetime .now ()
245272 duration = end_time - start_time
246-
273+
247274 hours = duration .seconds // 3600
248275 minutes = (duration .seconds % 3600 ) // 60
249276 seconds = duration .seconds % 60
250-
277+
251278 return f"{ hours :02d} :{ minutes :02d} :{ seconds :02d} "
252279
253280 def setup_ai (self ) -> bool :
254281 """Setup AI environment: check Ollama service and model availability."""
255282 try :
256- if hasattr (self , ' ollama' ):
283+ if hasattr (self , " ollama" ):
257284 client = self .ollama
258285 else :
259286 from ai_integration import OllamaClient
287+
260288 client = OllamaClient ()
261289 if not client .is_available ():
262- self .logger .error ("Ollama service not available. Start with: ollama serve" )
290+ self .logger .error (
291+ "Ollama service not available. Start with: ollama serve"
292+ )
263293 return False
264294 models = client .list_models ()
265295 if not models :
266296 self .logger .info ("No models found. Pulling default model..." )
267297 if not client .pull_model (client .main_model ):
268298 self .logger .error ("Failed to pull default model" )
269299 return False
270- self .logger .info ("AI setup complete. Available models: %s" , [m ['name' ] for m in models ])
300+ self .logger .info (
301+ "AI setup complete. Available models: %s" , [m ["name" ] for m in models ]
302+ )
271303 return True
272304 except Exception as e :
273305 self .logger .error ("Error in setup_ai: %s" , e )
274- return False
306+ return False
0 commit comments