kailash 0.1.5__py3-none-any.whl → 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. kailash/__init__.py +1 -1
  2. kailash/access_control.py +740 -0
  3. kailash/api/__main__.py +6 -0
  4. kailash/api/auth.py +668 -0
  5. kailash/api/custom_nodes.py +285 -0
  6. kailash/api/custom_nodes_secure.py +377 -0
  7. kailash/api/database.py +620 -0
  8. kailash/api/studio.py +915 -0
  9. kailash/api/studio_secure.py +893 -0
  10. kailash/mcp/__init__.py +53 -0
  11. kailash/mcp/__main__.py +13 -0
  12. kailash/mcp/ai_registry_server.py +712 -0
  13. kailash/mcp/client.py +447 -0
  14. kailash/mcp/client_new.py +334 -0
  15. kailash/mcp/server.py +293 -0
  16. kailash/mcp/server_new.py +336 -0
  17. kailash/mcp/servers/__init__.py +12 -0
  18. kailash/mcp/servers/ai_registry.py +289 -0
  19. kailash/nodes/__init__.py +4 -2
  20. kailash/nodes/ai/__init__.py +2 -0
  21. kailash/nodes/ai/a2a.py +714 -67
  22. kailash/nodes/ai/intelligent_agent_orchestrator.py +31 -37
  23. kailash/nodes/ai/iterative_llm_agent.py +1280 -0
  24. kailash/nodes/ai/llm_agent.py +324 -1
  25. kailash/nodes/ai/self_organizing.py +5 -6
  26. kailash/nodes/base.py +15 -2
  27. kailash/nodes/base_async.py +45 -0
  28. kailash/nodes/base_cycle_aware.py +374 -0
  29. kailash/nodes/base_with_acl.py +338 -0
  30. kailash/nodes/code/python.py +135 -27
  31. kailash/nodes/data/__init__.py +1 -2
  32. kailash/nodes/data/readers.py +16 -6
  33. kailash/nodes/data/sql.py +699 -256
  34. kailash/nodes/data/writers.py +16 -6
  35. kailash/nodes/logic/__init__.py +8 -0
  36. kailash/nodes/logic/convergence.py +642 -0
  37. kailash/nodes/logic/loop.py +153 -0
  38. kailash/nodes/logic/operations.py +187 -27
  39. kailash/nodes/mixins/__init__.py +11 -0
  40. kailash/nodes/mixins/mcp.py +228 -0
  41. kailash/nodes/mixins.py +387 -0
  42. kailash/runtime/__init__.py +2 -1
  43. kailash/runtime/access_controlled.py +458 -0
  44. kailash/runtime/local.py +106 -33
  45. kailash/runtime/parallel_cyclic.py +529 -0
  46. kailash/sdk_exceptions.py +90 -5
  47. kailash/security.py +845 -0
  48. kailash/tracking/manager.py +38 -15
  49. kailash/tracking/models.py +1 -1
  50. kailash/tracking/storage/filesystem.py +30 -2
  51. kailash/utils/__init__.py +8 -0
  52. kailash/workflow/__init__.py +18 -0
  53. kailash/workflow/convergence.py +270 -0
  54. kailash/workflow/cycle_analyzer.py +889 -0
  55. kailash/workflow/cycle_builder.py +579 -0
  56. kailash/workflow/cycle_config.py +725 -0
  57. kailash/workflow/cycle_debugger.py +860 -0
  58. kailash/workflow/cycle_exceptions.py +615 -0
  59. kailash/workflow/cycle_profiler.py +741 -0
  60. kailash/workflow/cycle_state.py +338 -0
  61. kailash/workflow/cyclic_runner.py +985 -0
  62. kailash/workflow/graph.py +500 -39
  63. kailash/workflow/migration.py +809 -0
  64. kailash/workflow/safety.py +365 -0
  65. kailash/workflow/templates.py +763 -0
  66. kailash/workflow/validation.py +751 -0
  67. {kailash-0.1.5.dist-info → kailash-0.2.1.dist-info}/METADATA +259 -12
  68. kailash-0.2.1.dist-info/RECORD +125 -0
  69. kailash/nodes/mcp/__init__.py +0 -11
  70. kailash/nodes/mcp/client.py +0 -554
  71. kailash/nodes/mcp/resource.py +0 -682
  72. kailash/nodes/mcp/server.py +0 -577
  73. kailash-0.1.5.dist-info/RECORD +0 -88
  74. {kailash-0.1.5.dist-info → kailash-0.2.1.dist-info}/WHEEL +0 -0
  75. {kailash-0.1.5.dist-info → kailash-0.2.1.dist-info}/entry_points.txt +0 -0
  76. {kailash-0.1.5.dist-info → kailash-0.2.1.dist-info}/licenses/LICENSE +0 -0
  77. {kailash-0.1.5.dist-info → kailash-0.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,741 @@
1
+ """
2
+ Advanced Performance Profiling and Analysis for Cyclic Workflows.
3
+
4
+ This module provides comprehensive performance profiling capabilities for cyclic
5
+ workflows, including detailed statistical analysis, resource usage monitoring,
6
+ bottleneck identification, and automated optimization recommendations based on
7
+ execution patterns and performance characteristics.
8
+
9
+ Examples:
10
+ Basic profiling setup:
11
+
12
+ >>> profiler = CycleProfiler(enable_advanced_metrics=True)
13
+ >>> # Add execution traces
14
+ >>> profiler.add_trace(execution_trace)
15
+ >>> # Analyze performance
16
+ >>> metrics = profiler.analyze_performance()
17
+ >>> print(f"Overall efficiency: {metrics.efficiency_score}")
18
+
19
+ Comparative analysis:
20
+
21
+ >>> # Compare multiple cycles
22
+ >>> comparison = profiler.compare_cycles(["cycle_1", "cycle_2", "cycle_3"])
23
+ >>> print(f"Best cycle: {comparison['best_cycle']}")
24
+ >>> print(f"Performance gaps: {comparison['significant_differences']}")
25
+
26
+ Optimization recommendations:
27
+
28
+ >>> # Get actionable recommendations
29
+ >>> recommendations = profiler.get_optimization_recommendations()
30
+ >>> for rec in recommendations:
31
+ ... print(f"{rec['priority']}: {rec['description']}")
32
+ ... print(f" Suggestion: {rec['suggestion']}")
33
+ ... print(f" Current: {rec['current_value']}")
34
+ ... print(f" Target: {rec['target_improvement']}")
35
+
36
+ Comprehensive reporting:
37
+
38
+ >>> # Generate detailed report
39
+ >>> report = profiler.generate_performance_report()
40
+ >>> # Export for external analysis
41
+ >>> profiler.export_profile_data("performance_analysis.json")
42
+ """
43
+
44
+ import logging
45
+ import statistics
46
+ from dataclasses import dataclass, field
47
+ from datetime import datetime
48
+ from typing import Any, Dict, List, Optional, Tuple
49
+
50
+ from kailash.workflow.cycle_debugger import CycleExecutionTrace
51
+
52
+ logger = logging.getLogger(__name__)
53
+
54
+
55
+ @dataclass
56
+ class PerformanceMetrics:
57
+ """
58
+ Comprehensive performance metrics for cycle analysis.
59
+
60
+ This class aggregates and analyzes performance data from cycle executions,
61
+ providing detailed insights into timing, resource usage, and efficiency
62
+ characteristics of cyclic workflows.
63
+
64
+ Attributes:
65
+ total_cycles: Number of cycles analyzed.
66
+ total_iterations: Total iterations across all cycles.
67
+ avg_cycle_time: Average cycle execution time.
68
+ avg_iteration_time: Average iteration execution time.
69
+ min_iteration_time: Fastest iteration time.
70
+ max_iteration_time: Slowest iteration time.
71
+ iteration_time_stddev: Standard deviation of iteration times.
72
+ memory_stats: Memory usage statistics.
73
+ cpu_stats: CPU usage statistics.
74
+ convergence_stats: Convergence analysis.
75
+ bottlenecks: Identified performance bottlenecks.
76
+ optimization_opportunities: Suggested optimizations.
77
+ """
78
+
79
+ total_cycles: int = 0
80
+ total_iterations: int = 0
81
+ avg_cycle_time: float = 0.0
82
+ avg_iteration_time: float = 0.0
83
+ min_iteration_time: float = float("inf")
84
+ max_iteration_time: float = 0.0
85
+ iteration_time_stddev: float = 0.0
86
+ memory_stats: Dict[str, float] = field(default_factory=dict)
87
+ cpu_stats: Dict[str, float] = field(default_factory=dict)
88
+ convergence_stats: Dict[str, Any] = field(default_factory=dict)
89
+ bottlenecks: List[str] = field(default_factory=list)
90
+ optimization_opportunities: List[str] = field(default_factory=list)
91
+
92
+ def to_dict(self) -> Dict[str, Any]:
93
+ """Convert metrics to dictionary for serialization.
94
+
95
+ Returns:
96
+ Dictionary representation of performance metrics.
97
+ """
98
+ return {
99
+ "total_cycles": self.total_cycles,
100
+ "total_iterations": self.total_iterations,
101
+ "timing": {
102
+ "avg_cycle_time": self.avg_cycle_time,
103
+ "avg_iteration_time": self.avg_iteration_time,
104
+ "min_iteration_time": (
105
+ self.min_iteration_time
106
+ if self.min_iteration_time != float("inf")
107
+ else 0.0
108
+ ),
109
+ "max_iteration_time": self.max_iteration_time,
110
+ "iteration_time_stddev": self.iteration_time_stddev,
111
+ },
112
+ "memory_stats": self.memory_stats,
113
+ "cpu_stats": self.cpu_stats,
114
+ "convergence_stats": self.convergence_stats,
115
+ "bottlenecks": self.bottlenecks,
116
+ "optimization_opportunities": self.optimization_opportunities,
117
+ }
118
+
119
+
120
+ class CycleProfiler:
121
+ """
122
+ Advanced profiling and performance analysis for cyclic workflows.
123
+
124
+ This class provides comprehensive performance analysis capabilities for
125
+ cycles, including statistical analysis, bottleneck identification,
126
+ comparative analysis across multiple cycles, and detailed optimization
127
+ recommendations based on execution patterns.
128
+
129
+ Examples:
130
+ >>> profiler = CycleProfiler()
131
+ >>> profiler.add_trace(execution_trace)
132
+ >>> metrics = profiler.analyze_performance()
133
+ >>> recommendations = profiler.get_optimization_recommendations()
134
+ """
135
+
136
+ def __init__(self, enable_advanced_metrics: bool = True):
137
+ """
138
+ Initialize cycle profiler.
139
+
140
+ Args:
141
+ enable_advanced_metrics: Whether to enable advanced statistical analysis.
142
+ """
143
+ self.enable_advanced_metrics = enable_advanced_metrics
144
+ self.traces: List[CycleExecutionTrace] = []
145
+ self.performance_history: List[PerformanceMetrics] = []
146
+
147
+ def add_trace(self, trace: CycleExecutionTrace):
148
+ """
149
+ Add a cycle execution trace for analysis.
150
+
151
+ Args:
152
+ trace: Completed execution trace to analyze.
153
+
154
+ Examples:
155
+ >>> profiler.add_trace(execution_trace)
156
+ """
157
+ self.traces.append(trace)
158
+ logger.debug(
159
+ f"Added trace for cycle '{trace.cycle_id}' with {len(trace.iterations)} iterations"
160
+ )
161
+
162
+ def analyze_performance(self) -> PerformanceMetrics:
163
+ """
164
+ Perform comprehensive performance analysis on all traces.
165
+
166
+ Analyzes all collected traces to generate comprehensive performance
167
+ metrics, identify bottlenecks, and provide optimization recommendations
168
+ based on statistical analysis of execution patterns.
169
+
170
+ Returns:
171
+ Comprehensive performance analysis results.
172
+
173
+ Examples:
174
+ >>> metrics = profiler.analyze_performance()
175
+ >>> print(f"Average cycle time: {metrics.avg_cycle_time:.3f}s")
176
+ """
177
+ if not self.traces:
178
+ logger.warning("No traces available for performance analysis")
179
+ return PerformanceMetrics()
180
+
181
+ # Collect all timing data
182
+ cycle_times = []
183
+ iteration_times = []
184
+ memory_values = []
185
+ cpu_values = []
186
+ convergence_data = []
187
+
188
+ for trace in self.traces:
189
+ if trace.total_execution_time:
190
+ cycle_times.append(trace.total_execution_time)
191
+
192
+ for iteration in trace.iterations:
193
+ if iteration.execution_time:
194
+ iteration_times.append(iteration.execution_time)
195
+ if iteration.memory_usage_mb:
196
+ memory_values.append(iteration.memory_usage_mb)
197
+ if iteration.cpu_usage_percent:
198
+ cpu_values.append(iteration.cpu_usage_percent)
199
+ if iteration.convergence_value:
200
+ convergence_data.append(iteration.convergence_value)
201
+
202
+ # Calculate basic metrics
203
+ metrics = PerformanceMetrics(
204
+ total_cycles=len(self.traces),
205
+ total_iterations=sum(len(trace.iterations) for trace in self.traces),
206
+ )
207
+
208
+ # Timing analysis
209
+ if cycle_times:
210
+ metrics.avg_cycle_time = statistics.mean(cycle_times)
211
+
212
+ if iteration_times:
213
+ metrics.avg_iteration_time = statistics.mean(iteration_times)
214
+ metrics.min_iteration_time = min(iteration_times)
215
+ metrics.max_iteration_time = max(iteration_times)
216
+ if len(iteration_times) > 1:
217
+ metrics.iteration_time_stddev = statistics.stdev(iteration_times)
218
+
219
+ # Memory analysis
220
+ if memory_values:
221
+ metrics.memory_stats = {
222
+ "avg": statistics.mean(memory_values),
223
+ "min": min(memory_values),
224
+ "max": max(memory_values),
225
+ "stddev": (
226
+ statistics.stdev(memory_values) if len(memory_values) > 1 else 0.0
227
+ ),
228
+ "median": statistics.median(memory_values),
229
+ }
230
+
231
+ # CPU analysis
232
+ if cpu_values:
233
+ metrics.cpu_stats = {
234
+ "avg": statistics.mean(cpu_values),
235
+ "min": min(cpu_values),
236
+ "max": max(cpu_values),
237
+ "stddev": statistics.stdev(cpu_values) if len(cpu_values) > 1 else 0.0,
238
+ "median": statistics.median(cpu_values),
239
+ }
240
+
241
+ # Convergence analysis
242
+ if convergence_data:
243
+ metrics.convergence_stats = self._analyze_convergence_performance(
244
+ convergence_data
245
+ )
246
+
247
+ # Advanced analysis if enabled
248
+ if self.enable_advanced_metrics:
249
+ metrics.bottlenecks = self._identify_bottlenecks(metrics)
250
+ metrics.optimization_opportunities = self._identify_optimizations(metrics)
251
+
252
+ # Store in history
253
+ self.performance_history.append(metrics)
254
+
255
+ logger.info(
256
+ f"Analyzed performance for {metrics.total_cycles} cycles, "
257
+ f"{metrics.total_iterations} iterations, "
258
+ f"avg cycle time: {metrics.avg_cycle_time:.3f}s"
259
+ )
260
+
261
+ return metrics
262
+
263
+ def compare_cycles(self, cycle_ids: List[str]) -> Dict[str, Any]:
264
+ """
265
+ Compare performance across multiple specific cycles.
266
+
267
+ Provides detailed comparative analysis between specific cycles,
268
+ highlighting performance differences, convergence patterns, and
269
+ relative efficiency metrics.
270
+
271
+ Args:
272
+ cycle_ids (List[str]): List of cycle IDs to compare
273
+
274
+ Returns:
275
+ Dict[str, Any]: Comparative analysis results
276
+
277
+ Side Effects:
278
+ None - this is a pure analysis method
279
+
280
+ Example:
281
+ >>> comparison = profiler.compare_cycles(["cycle_1", "cycle_2"])
282
+ >>> print(f"Best performing cycle: {comparison['best_cycle']}")
283
+ """
284
+ relevant_traces = [
285
+ trace for trace in self.traces if trace.cycle_id in cycle_ids
286
+ ]
287
+
288
+ if len(relevant_traces) < 2:
289
+ return {"error": "Need at least 2 cycles for comparison"}
290
+
291
+ comparison = {
292
+ "cycles_compared": len(relevant_traces),
293
+ "cycle_details": {},
294
+ "performance_ranking": [],
295
+ "significant_differences": [],
296
+ }
297
+
298
+ # Analyze each cycle
299
+ for trace in relevant_traces:
300
+ stats = trace.get_statistics()
301
+
302
+ comparison["cycle_details"][trace.cycle_id] = {
303
+ "execution_time": trace.total_execution_time,
304
+ "iterations": len(trace.iterations),
305
+ "converged": trace.converged,
306
+ "efficiency_score": stats["efficiency_score"],
307
+ "avg_iteration_time": stats["avg_iteration_time"],
308
+ "convergence_rate": stats["convergence_rate"],
309
+ }
310
+
311
+ # Rank by efficiency score
312
+ ranking = sorted(
313
+ comparison["cycle_details"].items(),
314
+ key=lambda x: x[1]["efficiency_score"],
315
+ reverse=True,
316
+ )
317
+ comparison["performance_ranking"] = [cycle_id for cycle_id, _ in ranking]
318
+ comparison["best_cycle"] = ranking[0][0] if ranking else None
319
+ comparison["worst_cycle"] = ranking[-1][0] if ranking else None
320
+
321
+ # Identify significant differences
322
+ if len(ranking) >= 2:
323
+ best_score = ranking[0][1]["efficiency_score"]
324
+ worst_score = ranking[-1][1]["efficiency_score"]
325
+
326
+ if best_score - worst_score > 0.2:
327
+ comparison["significant_differences"].append(
328
+ f"Large efficiency gap: {best_score:.2f} vs {worst_score:.2f}"
329
+ )
330
+
331
+ # Compare convergence rates
332
+ convergence_rates = [details["convergence_rate"] for _, details in ranking]
333
+ if max(convergence_rates) - min(convergence_rates) > 0.3:
334
+ comparison["significant_differences"].append(
335
+ "Significant variation in convergence rates"
336
+ )
337
+
338
+ return comparison
339
+
340
+ def get_optimization_recommendations(
341
+ self, trace: Optional[CycleExecutionTrace] = None
342
+ ) -> List[Dict[str, Any]]:
343
+ """
344
+ Generate detailed optimization recommendations.
345
+
346
+ Provides specific, actionable optimization recommendations based on
347
+ performance analysis, including parameter tuning suggestions,
348
+ algorithmic improvements, and resource optimization strategies.
349
+
350
+ Args:
351
+ trace (Optional[CycleExecutionTrace]): Specific trace to analyze, or None for overall recommendations
352
+
353
+ Returns:
354
+ List[Dict[str, Any]]: List of optimization recommendations with details
355
+
356
+ Side Effects:
357
+ None - this is a pure analysis method
358
+
359
+ Example:
360
+ >>> recommendations = profiler.get_optimization_recommendations()
361
+ >>> for rec in recommendations:
362
+ ... print(f"{rec['priority']}: {rec['description']}")
363
+ """
364
+ recommendations = []
365
+
366
+ if trace:
367
+ traces_to_analyze = [trace]
368
+ else:
369
+ traces_to_analyze = self.traces
370
+
371
+ if not traces_to_analyze:
372
+ return recommendations
373
+
374
+ # Analyze all traces for patterns
375
+ self.analyze_performance() if not trace else None
376
+
377
+ for target_trace in traces_to_analyze:
378
+ stats = target_trace.get_statistics()
379
+
380
+ # Efficiency recommendations
381
+ if stats["efficiency_score"] < 0.3:
382
+ recommendations.append(
383
+ {
384
+ "priority": "HIGH",
385
+ "category": "efficiency",
386
+ "description": "Very low efficiency detected",
387
+ "suggestion": "Consider reducing max_iterations or improving convergence condition",
388
+ "cycle_id": target_trace.cycle_id,
389
+ "current_value": stats["efficiency_score"],
390
+ "target_improvement": "Increase to > 0.5",
391
+ }
392
+ )
393
+
394
+ # Convergence recommendations
395
+ if not target_trace.converged:
396
+ if stats["total_iterations"] >= (
397
+ target_trace.max_iterations_configured or 0
398
+ ):
399
+ recommendations.append(
400
+ {
401
+ "priority": "HIGH",
402
+ "category": "convergence",
403
+ "description": "Cycle reached max_iterations without converging",
404
+ "suggestion": "Increase max_iterations or improve algorithm",
405
+ "cycle_id": target_trace.cycle_id,
406
+ "current_value": target_trace.max_iterations_configured,
407
+ "target_improvement": f"Increase to {int((target_trace.max_iterations_configured or 10) * 1.5)}",
408
+ }
409
+ )
410
+
411
+ # Performance recommendations
412
+ if stats["avg_iteration_time"] > 0.5:
413
+ recommendations.append(
414
+ {
415
+ "priority": "MEDIUM",
416
+ "category": "performance",
417
+ "description": "High average iteration time",
418
+ "suggestion": "Optimize node execution or reduce data processing",
419
+ "cycle_id": target_trace.cycle_id,
420
+ "current_value": stats["avg_iteration_time"],
421
+ "target_improvement": "Reduce to < 0.5s per iteration",
422
+ }
423
+ )
424
+
425
+ # Memory recommendations
426
+ if target_trace.memory_peak_mb and target_trace.memory_peak_mb > 1000:
427
+ recommendations.append(
428
+ {
429
+ "priority": "MEDIUM",
430
+ "category": "memory",
431
+ "description": "High memory usage detected",
432
+ "suggestion": "Consider data streaming, chunking, or garbage collection",
433
+ "cycle_id": target_trace.cycle_id,
434
+ "current_value": target_trace.memory_peak_mb,
435
+ "target_improvement": "Reduce to < 1000 MB",
436
+ }
437
+ )
438
+
439
+ # Convergence pattern recommendations
440
+ convergence_trend = target_trace.get_convergence_trend()
441
+ if convergence_trend:
442
+ pattern_analysis = self._analyze_convergence_pattern(convergence_trend)
443
+ if pattern_analysis["unstable"]:
444
+ recommendations.append(
445
+ {
446
+ "priority": "HIGH",
447
+ "category": "stability",
448
+ "description": "Unstable convergence pattern detected",
449
+ "suggestion": "Reduce learning rate or add regularization",
450
+ "cycle_id": target_trace.cycle_id,
451
+ "current_value": "Unstable",
452
+ "target_improvement": "Stable convergence",
453
+ }
454
+ )
455
+
456
+ # Sort by priority
457
+ priority_order = {"HIGH": 0, "MEDIUM": 1, "LOW": 2}
458
+ recommendations.sort(key=lambda x: priority_order.get(x["priority"], 3))
459
+
460
+ return recommendations
461
+
462
+ def generate_performance_report(self) -> Dict[str, Any]:
463
+ """
464
+ Generate comprehensive performance report.
465
+
466
+ Creates a detailed performance report including metrics analysis,
467
+ recommendations, trends, and comparative insights across all
468
+ analyzed cycles.
469
+
470
+ Returns:
471
+ Dict[str, Any]: Comprehensive performance report
472
+
473
+ Side Effects:
474
+ None - this is a pure analysis method
475
+
476
+ Example:
477
+ >>> report = profiler.generate_performance_report()
478
+ >>> print(f"Overall score: {report['overall_score']}")
479
+ """
480
+ metrics = self.analyze_performance()
481
+
482
+ # Calculate overall performance score
483
+ overall_score = self._calculate_overall_score(metrics)
484
+
485
+ # Generate trend analysis if we have history
486
+ trend_analysis = (
487
+ self._analyze_performance_trends()
488
+ if len(self.performance_history) > 1
489
+ else None
490
+ )
491
+
492
+ # Get top recommendations
493
+ recommendations = self.get_optimization_recommendations()
494
+
495
+ report = {
496
+ "summary": {
497
+ "overall_score": overall_score,
498
+ "total_cycles_analyzed": metrics.total_cycles,
499
+ "total_iterations": metrics.total_iterations,
500
+ "avg_cycle_time": metrics.avg_cycle_time,
501
+ "primary_bottlenecks": metrics.bottlenecks[:3],
502
+ },
503
+ "detailed_metrics": metrics.to_dict(),
504
+ "trend_analysis": trend_analysis,
505
+ "recommendations": recommendations[:10], # Top 10 recommendations
506
+ "cycle_comparisons": self._get_cycle_comparisons(),
507
+ "generated_at": datetime.now().isoformat(),
508
+ }
509
+
510
+ return report
511
+
512
+ def export_profile_data(self, filepath: str, format: str = "json"):
513
+ """
514
+ Export profiling data for external analysis.
515
+
516
+ Args:
517
+ filepath (str): Output file path
518
+ format (str): Export format ("json", "csv")
519
+
520
+ Side Effects:
521
+ Creates file with profiling data
522
+
523
+ Example:
524
+ >>> profiler.export_profile_data("profile_analysis.json")
525
+ """
526
+ report = self.generate_performance_report()
527
+
528
+ if format == "json":
529
+ import json
530
+
531
+ with open(filepath, "w") as f:
532
+ json.dump(report, f, indent=2)
533
+ elif format == "csv":
534
+ import csv
535
+
536
+ with open(filepath, "w", newline="") as f:
537
+ writer = csv.writer(f)
538
+
539
+ # Write summary data
540
+ writer.writerow(["Metric", "Value"])
541
+ writer.writerow(["Overall Score", report["summary"]["overall_score"]])
542
+ writer.writerow(
543
+ ["Total Cycles", report["summary"]["total_cycles_analyzed"]]
544
+ )
545
+ writer.writerow(["Avg Cycle Time", report["summary"]["avg_cycle_time"]])
546
+ writer.writerow([])
547
+
548
+ # Write detailed metrics
549
+ writer.writerow(["Detailed Metrics"])
550
+ metrics = report["detailed_metrics"]
551
+ for key, value in metrics.items():
552
+ if isinstance(value, dict):
553
+ for sub_key, sub_value in value.items():
554
+ writer.writerow([f"{key}_{sub_key}", sub_value])
555
+ else:
556
+ writer.writerow([key, value])
557
+ else:
558
+ raise ValueError(f"Unsupported export format: {format}")
559
+
560
+ logger.info(f"Exported profiling data to {filepath} in {format} format")
561
+
562
+ def _analyze_convergence_performance(
563
+ self, convergence_data: List[float]
564
+ ) -> Dict[str, Any]:
565
+ """Analyze convergence performance characteristics."""
566
+ if not convergence_data:
567
+ return {}
568
+
569
+ return {
570
+ "avg_convergence": statistics.mean(convergence_data),
571
+ "min_convergence": min(convergence_data),
572
+ "max_convergence": max(convergence_data),
573
+ "convergence_stddev": (
574
+ statistics.stdev(convergence_data) if len(convergence_data) > 1 else 0.0
575
+ ),
576
+ "convergence_trend": (
577
+ "improving"
578
+ if convergence_data[0] > convergence_data[-1]
579
+ else "degrading"
580
+ ),
581
+ "data_points": len(convergence_data),
582
+ }
583
+
584
+ def _analyze_convergence_pattern(
585
+ self, convergence_trend: List[Tuple[int, Optional[float]]]
586
+ ) -> Dict[str, Any]:
587
+ """Analyze convergence pattern for stability."""
588
+ valid_points = [value for _, value in convergence_trend if value is not None]
589
+
590
+ if len(valid_points) < 3:
591
+ return {"unstable": False, "reason": "insufficient_data"}
592
+
593
+ # Calculate volatility
594
+ differences = [
595
+ abs(valid_points[i] - valid_points[i - 1])
596
+ for i in range(1, len(valid_points))
597
+ ]
598
+ avg_difference = statistics.mean(differences)
599
+ max_difference = max(differences)
600
+
601
+ # Consider unstable if large swings or high volatility
602
+ unstable = max_difference > (2 * avg_difference) and avg_difference > 0.1
603
+
604
+ return {
605
+ "unstable": unstable,
606
+ "avg_volatility": avg_difference,
607
+ "max_volatility": max_difference,
608
+ "reason": "high_volatility" if unstable else "stable",
609
+ }
610
+
611
+ def _identify_bottlenecks(self, metrics: PerformanceMetrics) -> List[str]:
612
+ """Identify performance bottlenecks from metrics."""
613
+ bottlenecks = []
614
+
615
+ # High iteration time variance suggests inconsistent performance
616
+ if metrics.iteration_time_stddev > metrics.avg_iteration_time * 0.5:
617
+ bottlenecks.append(
618
+ "High iteration time variance - inconsistent node performance"
619
+ )
620
+
621
+ # Very slow iterations
622
+ if metrics.max_iteration_time > metrics.avg_iteration_time * 3:
623
+ bottlenecks.append(
624
+ "Outlier slow iterations detected - potential resource contention"
625
+ )
626
+
627
+ # Memory bottlenecks
628
+ if metrics.memory_stats and metrics.memory_stats.get("max", 0) > 2000:
629
+ bottlenecks.append(
630
+ "High memory usage - potential memory leaks or inefficient data handling"
631
+ )
632
+
633
+ # CPU bottlenecks
634
+ if metrics.cpu_stats and metrics.cpu_stats.get("avg", 0) > 80:
635
+ bottlenecks.append("High CPU usage - computationally intensive operations")
636
+
637
+ return bottlenecks
638
+
639
+ def _identify_optimizations(self, metrics: PerformanceMetrics) -> List[str]:
640
+ """Identify optimization opportunities."""
641
+ optimizations = []
642
+
643
+ # Low convergence rate suggests early termination opportunities
644
+ convergence_rate = metrics.convergence_stats.get("avg_convergence")
645
+ if convergence_rate and convergence_rate < 0.5:
646
+ optimizations.append(
647
+ "Add early termination conditions for faster convergence"
648
+ )
649
+
650
+ # High memory variance suggests optimization potential
651
+ if metrics.memory_stats and metrics.memory_stats.get("stddev", 0) > 100:
652
+ optimizations.append("Optimize memory usage patterns for consistency")
653
+
654
+ # Slow average iteration time
655
+ if metrics.avg_iteration_time > 0.1:
656
+ optimizations.append("Optimize node execution performance")
657
+
658
+ return optimizations
659
+
660
+ def _calculate_overall_score(self, metrics: PerformanceMetrics) -> float:
661
+ """Calculate overall performance score (0-1, higher is better)."""
662
+ score_components = []
663
+
664
+ # Efficiency component (convergence rate)
665
+ if metrics.convergence_stats:
666
+ avg_convergence = metrics.convergence_stats.get("avg_convergence", 0.5)
667
+ score_components.append(min(1.0, avg_convergence))
668
+
669
+ # Speed component (based on iteration time)
670
+ if metrics.avg_iteration_time > 0:
671
+ speed_score = max(0.0, 1.0 - min(1.0, metrics.avg_iteration_time / 2.0))
672
+ score_components.append(speed_score)
673
+
674
+ # Consistency component (low variance is good)
675
+ if metrics.iteration_time_stddev >= 0:
676
+ consistency_score = max(
677
+ 0.0,
678
+ 1.0
679
+ - min(
680
+ 1.0,
681
+ (
682
+ metrics.iteration_time_stddev / metrics.avg_iteration_time
683
+ if metrics.avg_iteration_time > 0
684
+ else 0
685
+ ),
686
+ ),
687
+ )
688
+ score_components.append(consistency_score)
689
+
690
+ # Memory efficiency component
691
+ if metrics.memory_stats:
692
+ max_memory = metrics.memory_stats.get("max", 500)
693
+ memory_score = max(
694
+ 0.0, 1.0 - min(1.0, max_memory / 2000)
695
+ ) # Penalty after 2GB
696
+ score_components.append(memory_score)
697
+
698
+ return statistics.mean(score_components) if score_components else 0.5
699
+
700
+ def _analyze_performance_trends(self) -> Dict[str, Any]:
701
+ """Analyze performance trends over time."""
702
+ if len(self.performance_history) < 2:
703
+ return {"trend": "insufficient_data"}
704
+
705
+ recent_scores = [
706
+ self._calculate_overall_score(m) for m in self.performance_history[-5:]
707
+ ]
708
+
709
+ if len(recent_scores) >= 2:
710
+ trend = "improving" if recent_scores[-1] > recent_scores[0] else "degrading"
711
+ trend_strength = abs(recent_scores[-1] - recent_scores[0])
712
+ else:
713
+ trend = "stable"
714
+ trend_strength = 0.0
715
+
716
+ return {
717
+ "trend": trend,
718
+ "trend_strength": trend_strength,
719
+ "recent_scores": recent_scores,
720
+ "performance_history_length": len(self.performance_history),
721
+ }
722
+
723
+ def _get_cycle_comparisons(self) -> Dict[str, Any]:
724
+ """Get comparative analysis across all cycles."""
725
+ if len(self.traces) < 2:
726
+ return {"comparison": "insufficient_data"}
727
+
728
+ cycle_scores = {}
729
+ for trace in self.traces:
730
+ stats = trace.get_statistics()
731
+ cycle_scores[trace.cycle_id] = stats["efficiency_score"]
732
+
733
+ best_cycle = max(cycle_scores.items(), key=lambda x: x[1])
734
+ worst_cycle = min(cycle_scores.items(), key=lambda x: x[1])
735
+
736
+ return {
737
+ "best_cycle": {"id": best_cycle[0], "score": best_cycle[1]},
738
+ "worst_cycle": {"id": worst_cycle[0], "score": worst_cycle[1]},
739
+ "score_range": best_cycle[1] - worst_cycle[1],
740
+ "total_cycles": len(cycle_scores),
741
+ }