Vignesh-1918/Kaggle_Agent
2
1"""
2Safe kernel client for code execution in HF Spaces Docker environment
3"""
4
5import time
6import random
7import re
8import os
9import tempfile
10from typing import Dict, Any, List, Optional
11import json
12
13class KernelClient:
14 """Safe kernel client optimized for Hugging Face Spaces Docker deployment."""
15
16 def __init__(self):
17 self.execution_count = 0
18 self.variables = {
19 'df': 'DataFrame(sample_data)',
20 'X': 'Feature matrix',
21 'y': 'Target variable'
22 }
23 self.imports = set()
24 self.execution_history = []
25 self.temp_dir = '/tmp'
26
27 # Ensure temp directory exists
28 os.makedirs(self.temp_dir, exist_ok=True)
29
30 def execute(self, code: str, namespace: Dict[str, Any] = None) -> Dict[str, Any]:
31 """
32 Main execution method compatible with HF Spaces.
33 Simulates safe code execution with realistic outputs.
34 """
35 return self.execute_code(code, namespace=namespace)
36
37 def execute_code(self, code: str, timeout: int = 30, namespace: Dict[str, Any] = None) -> Dict[str, Any]:
38 """
39 Simulate code execution safely for HF Spaces environment.
40 """
41 self.execution_count += 1
42
43 # Merge provided namespace with existing variables
44 if namespace:
45 self.variables.update(namespace)
46
47 # Simulate execution time based on code complexity
48 execution_time = self._estimate_execution_time(code)
49
50 # Store execution history
51 self.execution_history.append({
52 'code': code,
53 'execution_count': self.execution_count,
54 'timestamp': time.time()
55 })
56
57 # Analyze code to provide realistic simulation
58 result = self._simulate_execution(code)
59 result.update({
60 'success': result.get('status') == 'ok',
61 'execution_count': self.execution_count,
62 'execution_time_s': execution_time,
63 'variables': self.get_variables(),
64 'plots': result.get('plots', [])
65 })
66
67 return result
68
69 def _estimate_execution_time(self, code: str) -> float:
70 """Estimate execution time based on code complexity."""
71 base_time = 0.05
72
73 # Add time based on operations
74 complexity_factors = {
75 ('read_csv', 'load_data', 'pd.read'): 0.3,
76 ('plot', 'show', 'heatmap', 'hist', 'scatter'): 0.2,
77 ('fit', 'train', 'cross_val', 'GridSearch'): 0.8,
78 ('for ', 'while ', 'apply'): 0.15,
79 ('merge', 'join', 'concat'): 0.1
80 }
81
82 for patterns, time_add in complexity_factors.items():
83 if any(pattern in code.lower() for pattern in patterns):
84 base_time += time_add
85
86 return min(base_time, 2.0) # Cap at 2 seconds for HF Spaces
87
88 def _simulate_execution(self, code: str) -> Dict[str, Any]:
89 """Simulate code execution results optimized for HF Spaces."""
90
91 lines = code.strip().split('\n')
92 outputs = []
93 plots = []
94
95 # Track imports
96 self._update_imports(code)
97
98 # Check for syntax errors first
99 if self._has_syntax_errors(code):
100 return {
101 'status': 'error',
102 'success': False,
103 'error': 'Syntax error in code',
104 'ename': 'SyntaxError',
105 'evalue': 'invalid syntax',
106 'traceback': ['SyntaxError: invalid syntax'],
107 'output': 'Error: Invalid syntax detected',
108 'outputs': [],
109 'plots': []
110 }
111
112 # Process code line by line
113 stdout_output = []
114
115 for line in lines:
116 line = line.strip()
117 if not line or line.startswith('#'):
118 continue
119
120 # Handle different types of operations
121 line_output = self._process_code_line(line)
122 if line_output:
123 if line_output.get('type') == 'stdout':
124 stdout_output.append(line_output['content'])
125 elif line_output.get('type') == 'plot':
126 plots.append(line_output['content'])
127 else:
128 outputs.append(line_output)
129
130 # Combine stdout outputs
131 combined_output = '\n'.join(stdout_output) if stdout_output else 'Code executed successfully.'
132
133 return {
134 'status': 'ok',
135 'success': True,
136 'output': combined_output,
137 'outputs': outputs,
138 'plots': plots,
139 'error': None
140 }
141
142 def _process_code_line(self, line: str) -> Optional[Dict[str, Any]]:
143 """Process individual code line and return appropriate output."""
144
145 # Print statements
146 if 'print(' in line:
147 content = self._simulate_print_output(line)
148 return {'type': 'stdout', 'content': content}
149
150 # DataFrame operations
151 elif any(op in line for op in ['.head()', '.info()', '.describe()', '.shape']):
152 content = self._simulate_dataframe_text_output(line)
153 return {'type': 'stdout', 'content': content}
154
155 # Plotting operations
156 elif any(plot in line for plot in ['plt.show()', 'plt.savefig(', 'sns.', 'plot(', 'hist(', 'scatter(']):
157 plot_path = self._generate_plot_file()
158 return {'type': 'plot', 'content': plot_path}
159
160 # Model training and ML operations
161 elif any(ml in line for ml in ['fit(', 'train', 'cross_val_score', 'accuracy_score']):
162 content = self._simulate_ml_output(line)
163 return {'type': 'stdout', 'content': content}
164
165 # Variable assignments
166 elif '=' in line and not any(op in line for op in ['==', '!=', '>=', '<=']):
167 self._update_variables(line)
168 return None
169
170 return None
171
172 def _generate_plot_file(self) -> str:
173 """Generate a placeholder plot file path."""
174 plot_filename = f'plot_{self.execution_count}_{int(time.time())}.png'
175 plot_path = os.path.join(self.temp_dir, plot_filename)
176
177 # Create a minimal placeholder file (1x1 pixel PNG)
178 png_data = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\nIDATx\x9cc```\x00\x00\x00\x04\x00\x01\xdd\x8d\xb4\x1c\x00\x00\x00\x00IEND\xaeB`\x82'
179
180 try:
181 with open(plot_path, 'wb') as f:
182 f.write(png_data)
183 except Exception:
184 # Fallback if file creation fails
185 pass
186
187 return plot_path
188
189 def _update_imports(self, code: str):
190 """Track imported libraries."""
191 import_patterns = [
192 r'import (\w+)',
193 r'from (\w+) import',
194 r'import (\w+) as'
195 ]
196
197 for pattern in import_patterns:
198 matches = re.findall(pattern, code)
199 self.imports.update(matches)
200
201 def _simulate_print_output(self, print_statement: str) -> str:
202 """Simulate realistic output from print statements."""
203
204 # Extract content between parentheses
205 try:
206 start = print_statement.find('(') + 1
207 end = print_statement.rfind(')')
208 content = print_statement[start:end].strip()
209 except:
210 return 'Print output'
211
212 # Common patterns and their outputs
213 patterns = {
214 'df.shape': '(1000, 15)',
215 'shape': '(1000, 15)',
216 'df.columns': "Index(['feature1', 'feature2', 'feature3', 'target'], dtype='object')",
217 'columns': "['feature1', 'feature2', 'feature3', 'target']",
218 'len(df)': '1000',
219 'len(': '1000',
220 'dataset': 'Dataset loaded successfully!',
221 'loaded': 'Data loaded successfully!',
222 'shape': '(1000, 15)',
223 'accuracy': 'Accuracy: 0.8563',
224 'score': 'Model Score: 0.8234',
225 'training': 'Training completed successfully',
226 'model': 'Model training completed',
227 'cross': 'Cross-validation scores: [0.85, 0.83, 0.87, 0.84, 0.86]'
228 }
229
230 # Match patterns
231 content_lower = content.lower()
232 for pattern, output in patterns.items():
233 if pattern in content_lower:
234 return output
235
236 # Handle f-strings and formatted strings
237 if 'f"' in content or "f'" in content:
238 return f'Formatted output: {random.choice(["value = 42", "result = 0.85", "count = 1000"])}'
239 elif content.startswith('"') or content.startswith("'"):
240 # String literal - remove quotes
241 return content.strip('"\'')
242 else:
243 return f'Output: {content}'
244
245 def _simulate_dataframe_text_output(self, line: str) -> str:
246 """Simulate DataFrame operations as text output for HF Spaces."""
247
248 if '.head()' in line:
249 return """ feature1 feature2 feature3 target
2500 1.23 4.56 7.89 0
2511 2.34 5.67 8.90 1
2522 3.45 6.78 9.01 0
2533 4.56 7.89 0.12 1
2544 5.67 8.90 1.23 0"""
255
256 elif '.info()' in line:
257 return """<class 'pandas.core.frame.DataFrame'>
258RangeIndex: 1000 entries, 0 to 999
259Data columns (total 4 columns):
260 # Column Non-Null Count Dtype
261--- ------ -------------- -----
262 0 feature1 1000 non-null float64
263 1 feature2 1000 non-null float64
264 2 feature3 1000 non-null float64
265 3 target 1000 non-null int64
266dtypes: float64(3), int64(1)
267memory usage: 31.4 KB"""
268
269 elif '.describe()' in line:
270 return """ feature1 feature2 feature3 target
271count 1000.000000 1000.000000 1000.000000 1000.000000
272mean 2.456789 5.123456 7.890123 0.489000
273std 1.234567 2.345678 3.456789 0.500122
274min 0.100000 1.200000 2.300000 0.000000
27525% 1.800000 3.900000 5.600000 0.000000
27650% 2.400000 5.100000 7.800000 0.000000
27775% 3.100000 6.300000 9.100000 1.000000
278max 5.900000 8.700000 12.500000 1.000000"""
279
280 elif '.shape' in line:
281 return '(1000, 4)'
282
283 else:
284 return 'DataFrame operation completed'
285
286 def _simulate_ml_output(self, line: str) -> str:
287 """Simulate machine learning operation outputs."""
288
289 if 'fit(' in line:
290 return 'Model training completed successfully'
291 elif 'cross_val_score' in line:
292 scores = [round(0.8 + random.uniform(-0.1, 0.1), 4) for _ in range(5)]
293 return f'Cross-validation scores: {scores}\nMean CV score: {sum(scores)/len(scores):.4f}'
294 elif 'accuracy_score' in line or '.score(' in line:
295 score = round(0.8 + random.uniform(-0.15, 0.15), 4)
296 return f'Model Accuracy: {score}'
297 elif 'predict(' in line:
298 return 'Predictions generated successfully'
299 elif 'classification_report' in line:
300 return """ precision recall f1-score support
301
302 0 0.85 0.82 0.83 120
303 1 0.83 0.86 0.85 130
304
305 accuracy 0.84 250
306 macro avg 0.84 0.84 0.84 250
307weighted avg 0.84 0.84 0.84 250"""
308 else:
309 return 'Machine learning operation completed'
310
311 def _update_variables(self, assignment_line: str):
312 """Update tracked variables based on assignments."""
313 try:
314 var_name = assignment_line.split('=')[0].strip()
315 right_side = assignment_line.split('=')[1].strip()
316
317 if var_name.replace('_', '').replace('[', '').replace(']', '').isalnum():
318 var_type = self._determine_variable_type(right_side)
319 self.variables[var_name] = var_type
320 except:
321 pass
322
323 def _determine_variable_type(self, assignment_right: str) -> str:
324 """Determine variable type from assignment."""
325
326 type_patterns = {
327 'pd.read_csv': 'DataFrame',
328 'pd.read_excel': 'DataFrame',
329 'pd.DataFrame': 'DataFrame',
330 'train_test_split': 'Split Data',
331 'RandomForest': 'RandomForest Model',
332 'LogisticRegression': 'LogisticRegression Model',
333 'LinearRegression': 'LinearRegression Model',
334 'np.array': 'NumPy Array',
335 '.fit(': 'Trained Model',
336 '.predict(': 'Predictions',
337 '.transform(': 'Transformed Data'
338 }
339
340 for pattern, var_type in type_patterns.items():
341 if pattern in assignment_right:
342 return var_type
343
344 return 'Variable'
345
346 def _has_syntax_errors(self, code: str) -> bool:
347 """Check for basic syntax errors."""
348 try:
349 # Basic syntax validation
350 compile(code, '<string>', 'exec')
351 return False
352 except SyntaxError:
353 return True
354 except:
355 # Other errors (like undefined variables) are OK for simulation
356 return False
357
358 def get_variables(self) -> Dict[str, str]:
359 """Get currently tracked variables."""
360 return self.variables.copy()
361
362 def get_imports(self) -> List[str]:
363 """Get list of imported libraries."""
364 return list(self.imports)
365
366 def get_execution_history(self) -> List[Dict[str, Any]]:
367 """Get execution history."""
368 return self.execution_history.copy()
369
370 def reset_kernel(self):
371 """Reset the kernel state."""
372 self.execution_count = 0
373 self.variables = {'df': 'DataFrame(sample_data)'}
374 self.imports = set()
375 self.execution_history = []
376
377 # Clean up temp files
378 try:
379 for file in os.listdir(self.temp_dir):
380 if file.startswith('plot_'):
381 os.remove(os.path.join(self.temp_dir, file))
382 except:
383 pass
384
385 def shutdown(self):
386 """Shutdown the kernel client and cleanup."""
387 try:
388 # Clean up temporary files
389 for file in os.listdir(self.temp_dir):
390 if file.startswith('plot_'):
391 os.remove(os.path.join(self.temp_dir, file))
392 except:
393 pass
394
395 print(f"Kernel client shutdown after {self.execution_count} executions")
396
397 def get_status(self) -> Dict[str, Any]:
398 """Get kernel status information."""
399 return {
400 'status': 'ready',
401 'execution_count': self.execution_count,
402 'variables_count': len(self.variables),
403 'imports_count': len(self.imports),
404 'temp_dir': self.temp_dir,
405 'history_length': len(self.execution_history)
406 }