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