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.
- kailash/__init__.py +1 -1
- kailash/access_control.py +740 -0
- kailash/api/__main__.py +6 -0
- kailash/api/auth.py +668 -0
- kailash/api/custom_nodes.py +285 -0
- kailash/api/custom_nodes_secure.py +377 -0
- kailash/api/database.py +620 -0
- kailash/api/studio.py +915 -0
- kailash/api/studio_secure.py +893 -0
- kailash/mcp/__init__.py +53 -0
- kailash/mcp/__main__.py +13 -0
- kailash/mcp/ai_registry_server.py +712 -0
- kailash/mcp/client.py +447 -0
- kailash/mcp/client_new.py +334 -0
- kailash/mcp/server.py +293 -0
- kailash/mcp/server_new.py +336 -0
- kailash/mcp/servers/__init__.py +12 -0
- kailash/mcp/servers/ai_registry.py +289 -0
- kailash/nodes/__init__.py +4 -2
- kailash/nodes/ai/__init__.py +2 -0
- kailash/nodes/ai/a2a.py +714 -67
- kailash/nodes/ai/intelligent_agent_orchestrator.py +31 -37
- kailash/nodes/ai/iterative_llm_agent.py +1280 -0
- kailash/nodes/ai/llm_agent.py +324 -1
- kailash/nodes/ai/self_organizing.py +5 -6
- kailash/nodes/base.py +15 -2
- kailash/nodes/base_async.py +45 -0
- kailash/nodes/base_cycle_aware.py +374 -0
- kailash/nodes/base_with_acl.py +338 -0
- kailash/nodes/code/python.py +135 -27
- kailash/nodes/data/readers.py +16 -6
- kailash/nodes/data/writers.py +16 -6
- kailash/nodes/logic/__init__.py +8 -0
- kailash/nodes/logic/convergence.py +642 -0
- kailash/nodes/logic/loop.py +153 -0
- kailash/nodes/logic/operations.py +187 -27
- kailash/nodes/mixins/__init__.py +11 -0
- kailash/nodes/mixins/mcp.py +228 -0
- kailash/nodes/mixins.py +387 -0
- kailash/runtime/__init__.py +2 -1
- kailash/runtime/access_controlled.py +458 -0
- kailash/runtime/local.py +106 -33
- kailash/runtime/parallel_cyclic.py +529 -0
- kailash/sdk_exceptions.py +90 -5
- kailash/security.py +845 -0
- kailash/tracking/manager.py +38 -15
- kailash/tracking/models.py +1 -1
- kailash/tracking/storage/filesystem.py +30 -2
- kailash/utils/__init__.py +8 -0
- kailash/workflow/__init__.py +18 -0
- kailash/workflow/convergence.py +270 -0
- kailash/workflow/cycle_analyzer.py +768 -0
- kailash/workflow/cycle_builder.py +573 -0
- kailash/workflow/cycle_config.py +709 -0
- kailash/workflow/cycle_debugger.py +760 -0
- kailash/workflow/cycle_exceptions.py +601 -0
- kailash/workflow/cycle_profiler.py +671 -0
- kailash/workflow/cycle_state.py +338 -0
- kailash/workflow/cyclic_runner.py +985 -0
- kailash/workflow/graph.py +500 -39
- kailash/workflow/migration.py +768 -0
- kailash/workflow/safety.py +365 -0
- kailash/workflow/templates.py +744 -0
- kailash/workflow/validation.py +693 -0
- {kailash-0.1.5.dist-info → kailash-0.2.0.dist-info}/METADATA +256 -12
- kailash-0.2.0.dist-info/RECORD +125 -0
- kailash/nodes/mcp/__init__.py +0 -11
- kailash/nodes/mcp/client.py +0 -554
- kailash/nodes/mcp/resource.py +0 -682
- kailash/nodes/mcp/server.py +0 -577
- kailash-0.1.5.dist-info/RECORD +0 -88
- {kailash-0.1.5.dist-info → kailash-0.2.0.dist-info}/WHEEL +0 -0
- {kailash-0.1.5.dist-info → kailash-0.2.0.dist-info}/entry_points.txt +0 -0
- {kailash-0.1.5.dist-info → kailash-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.1.5.dist-info → kailash-0.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,601 @@
|
|
1
|
+
"""
|
2
|
+
Enhanced exception classes for cyclic workflow operations.
|
3
|
+
|
4
|
+
This module provides specialized exception classes with actionable error messages,
|
5
|
+
debugging context, and suggested solutions for cycle-related errors. These exceptions
|
6
|
+
replace generic errors with detailed diagnostics to improve developer experience.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import logging
|
10
|
+
from typing import Any, Dict, List, Optional
|
11
|
+
|
12
|
+
from kailash.sdk_exceptions import WorkflowException
|
13
|
+
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
|
17
|
+
class CycleException(WorkflowException):
|
18
|
+
"""
|
19
|
+
Base exception for all cycle-related errors.
|
20
|
+
|
21
|
+
This base class provides common functionality for cycle exceptions
|
22
|
+
including error codes, context information, and suggested solutions.
|
23
|
+
All cycle-specific exceptions inherit from this class.
|
24
|
+
|
25
|
+
Design Philosophy:
|
26
|
+
Provides actionable error messages with specific suggestions for
|
27
|
+
resolution. Each exception includes context about what went wrong,
|
28
|
+
why it happened, and how to fix it.
|
29
|
+
|
30
|
+
Upstream Dependencies:
|
31
|
+
- Inherits from WorkflowException for consistency
|
32
|
+
- Used throughout cycle builder and execution systems
|
33
|
+
|
34
|
+
Downstream Consumers:
|
35
|
+
- Developer error handling and debugging
|
36
|
+
- IDE error highlighting and suggestions
|
37
|
+
- Automated error reporting and analysis
|
38
|
+
|
39
|
+
Attributes:
|
40
|
+
error_code (str): Unique code for programmatic error handling
|
41
|
+
context (Dict[str, Any]): Additional context about the error
|
42
|
+
suggestions (List[str]): Actionable suggestions for resolution
|
43
|
+
|
44
|
+
Example:
|
45
|
+
>>> try:
|
46
|
+
... # Some cycle operation
|
47
|
+
... pass
|
48
|
+
... except CycleException as e:
|
49
|
+
... print(f"Error: {e}")
|
50
|
+
... print(f"Code: {e.error_code}")
|
51
|
+
... print(f"Suggestions: {e.suggestions}")
|
52
|
+
"""
|
53
|
+
|
54
|
+
def __init__(
|
55
|
+
self,
|
56
|
+
message: str,
|
57
|
+
error_code: str = "CYCLE_ERROR",
|
58
|
+
context: Optional[Dict[str, Any]] = None,
|
59
|
+
suggestions: Optional[List[str]] = None,
|
60
|
+
documentation_url: Optional[str] = None
|
61
|
+
):
|
62
|
+
"""
|
63
|
+
Initialize cycle exception with enhanced error information.
|
64
|
+
|
65
|
+
Args:
|
66
|
+
message (str): Human-readable error description
|
67
|
+
error_code (str): Unique error code for programmatic handling
|
68
|
+
context (Dict[str, Any], optional): Additional error context
|
69
|
+
suggestions (List[str], optional): Actionable resolution suggestions
|
70
|
+
documentation_url (str, optional): URL to relevant documentation
|
71
|
+
|
72
|
+
Side Effects:
|
73
|
+
Logs error details for debugging and analysis
|
74
|
+
"""
|
75
|
+
super().__init__(message)
|
76
|
+
self.error_code = error_code
|
77
|
+
self.context = context or {}
|
78
|
+
self.suggestions = suggestions or []
|
79
|
+
self.documentation_url = documentation_url
|
80
|
+
|
81
|
+
# Log error for debugging
|
82
|
+
logger.debug(f"CycleException raised: {error_code} - {message}")
|
83
|
+
|
84
|
+
def get_detailed_message(self) -> str:
|
85
|
+
"""
|
86
|
+
Get detailed error message with context and suggestions.
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
str: Comprehensive error message with all available information
|
90
|
+
|
91
|
+
Example:
|
92
|
+
>>> exception.get_detailed_message()
|
93
|
+
'Error: Invalid cycle configuration
|
94
|
+
Code: CYCLE_CONFIG_001
|
95
|
+
Context: {"cycle_id": "test", "max_iterations": -5}
|
96
|
+
Suggestions:
|
97
|
+
• Set max_iterations to a positive value (recommended: 10-100)
|
98
|
+
• Add convergence_check for early termination
|
99
|
+
Documentation: https://docs.kailash.ai/cycles/configuration'
|
100
|
+
"""
|
101
|
+
message_parts = [f"Error: {self.args[0]}"]
|
102
|
+
|
103
|
+
if self.error_code != "CYCLE_ERROR":
|
104
|
+
message_parts.append(f"Code: {self.error_code}")
|
105
|
+
|
106
|
+
if self.context:
|
107
|
+
context_str = ", ".join(f"{k}={v}" for k, v in self.context.items())
|
108
|
+
message_parts.append(f"Context: {context_str}")
|
109
|
+
|
110
|
+
if self.suggestions:
|
111
|
+
message_parts.append("Suggestions:")
|
112
|
+
for suggestion in self.suggestions:
|
113
|
+
message_parts.append(f"• {suggestion}")
|
114
|
+
|
115
|
+
if self.documentation_url:
|
116
|
+
message_parts.append(f"Documentation: {self.documentation_url}")
|
117
|
+
|
118
|
+
return "\n".join(message_parts)
|
119
|
+
|
120
|
+
def __str__(self) -> str:
|
121
|
+
"""Return string representation with enhanced details."""
|
122
|
+
return self.get_detailed_message()
|
123
|
+
|
124
|
+
|
125
|
+
class CycleConfigurationError(CycleException):
|
126
|
+
"""
|
127
|
+
Raised when cycle configuration is invalid or incomplete.
|
128
|
+
|
129
|
+
This exception provides specific guidance for cycle configuration issues,
|
130
|
+
including missing parameters, invalid values, and conflicting settings.
|
131
|
+
It helps developers quickly identify and fix configuration problems.
|
132
|
+
|
133
|
+
Common scenarios:
|
134
|
+
- Missing required parameters (max_iterations or convergence_check)
|
135
|
+
- Invalid parameter values (negative iterations, empty conditions)
|
136
|
+
- Unsafe expressions in convergence conditions
|
137
|
+
- Conflicting cycle settings
|
138
|
+
|
139
|
+
Example:
|
140
|
+
>>> raise CycleConfigurationError(
|
141
|
+
... "Missing termination condition",
|
142
|
+
... error_code="CYCLE_CONFIG_001",
|
143
|
+
... context={"cycle_id": "test"},
|
144
|
+
... suggestions=["Add max_iterations parameter", "Add convergence_check condition"]
|
145
|
+
... )
|
146
|
+
"""
|
147
|
+
|
148
|
+
def __init__(
|
149
|
+
self,
|
150
|
+
message: str,
|
151
|
+
cycle_id: Optional[str] = None,
|
152
|
+
invalid_params: Optional[Dict[str, Any]] = None,
|
153
|
+
**kwargs
|
154
|
+
):
|
155
|
+
"""
|
156
|
+
Initialize cycle configuration error.
|
157
|
+
|
158
|
+
Args:
|
159
|
+
message (str): Error description
|
160
|
+
cycle_id (str, optional): ID of the problematic cycle
|
161
|
+
invalid_params (Dict[str, Any], optional): Invalid parameter values
|
162
|
+
**kwargs: Additional arguments for base exception
|
163
|
+
|
164
|
+
Side Effects:
|
165
|
+
Automatically generates context and suggestions based on parameters
|
166
|
+
"""
|
167
|
+
context = kwargs.get('context', {})
|
168
|
+
suggestions = kwargs.get('suggestions', [])
|
169
|
+
|
170
|
+
# Add cycle-specific context
|
171
|
+
if cycle_id:
|
172
|
+
context['cycle_id'] = cycle_id
|
173
|
+
if invalid_params:
|
174
|
+
context.update(invalid_params)
|
175
|
+
|
176
|
+
# Add common suggestions if none provided
|
177
|
+
if not suggestions:
|
178
|
+
suggestions = [
|
179
|
+
"Ensure at least one termination condition (max_iterations, convergence_check, or timeout)",
|
180
|
+
"Use positive values for numeric parameters",
|
181
|
+
"Avoid unsafe operations in convergence expressions",
|
182
|
+
"Check the CycleConfig documentation for valid parameter ranges"
|
183
|
+
]
|
184
|
+
|
185
|
+
super().__init__(
|
186
|
+
message,
|
187
|
+
error_code=kwargs.get('error_code', 'CYCLE_CONFIG_001'),
|
188
|
+
context=context,
|
189
|
+
suggestions=suggestions,
|
190
|
+
documentation_url="https://docs.kailash.ai/cycles/configuration"
|
191
|
+
)
|
192
|
+
|
193
|
+
|
194
|
+
class CycleConnectionError(CycleException):
|
195
|
+
"""
|
196
|
+
Raised when cycle connection creation fails.
|
197
|
+
|
198
|
+
This exception handles errors during cycle connection establishment,
|
199
|
+
including missing nodes, invalid mappings, and connection conflicts.
|
200
|
+
It provides specific guidance for fixing connection issues.
|
201
|
+
|
202
|
+
Common scenarios:
|
203
|
+
- Source or target nodes don't exist in workflow
|
204
|
+
- Invalid parameter mappings
|
205
|
+
- Conflicting cycle connections
|
206
|
+
- Missing required connection parameters
|
207
|
+
|
208
|
+
Example:
|
209
|
+
>>> raise CycleConnectionError(
|
210
|
+
... "Source node 'processor' not found",
|
211
|
+
... source_node="processor",
|
212
|
+
... available_nodes=["reader", "writer"]
|
213
|
+
... )
|
214
|
+
"""
|
215
|
+
|
216
|
+
def __init__(
|
217
|
+
self,
|
218
|
+
message: str,
|
219
|
+
source_node: Optional[str] = None,
|
220
|
+
target_node: Optional[str] = None,
|
221
|
+
available_nodes: Optional[List[str]] = None,
|
222
|
+
mapping_errors: Optional[Dict[str, str]] = None,
|
223
|
+
**kwargs
|
224
|
+
):
|
225
|
+
"""
|
226
|
+
Initialize cycle connection error.
|
227
|
+
|
228
|
+
Args:
|
229
|
+
message (str): Error description
|
230
|
+
source_node (str, optional): Source node ID
|
231
|
+
target_node (str, optional): Target node ID
|
232
|
+
available_nodes (List[str], optional): Available node IDs
|
233
|
+
mapping_errors (Dict[str, str], optional): Parameter mapping errors
|
234
|
+
**kwargs: Additional arguments for base exception
|
235
|
+
"""
|
236
|
+
context = kwargs.get('context', {})
|
237
|
+
suggestions = kwargs.get('suggestions', [])
|
238
|
+
|
239
|
+
# Add connection-specific context
|
240
|
+
if source_node:
|
241
|
+
context['source_node'] = source_node
|
242
|
+
if target_node:
|
243
|
+
context['target_node'] = target_node
|
244
|
+
if available_nodes:
|
245
|
+
context['available_nodes'] = available_nodes
|
246
|
+
if mapping_errors:
|
247
|
+
context['mapping_errors'] = mapping_errors
|
248
|
+
|
249
|
+
# Generate specific suggestions
|
250
|
+
if not suggestions:
|
251
|
+
suggestions = []
|
252
|
+
if available_nodes:
|
253
|
+
suggestions.append(f"Available nodes: {', '.join(available_nodes)}")
|
254
|
+
if source_node and available_nodes and source_node not in available_nodes:
|
255
|
+
suggestions.append(f"Add node '{source_node}' to workflow before connecting")
|
256
|
+
if mapping_errors:
|
257
|
+
suggestions.append("Check parameter mappings for typos and type compatibility")
|
258
|
+
suggestions.append("Verify node IDs match exactly (case-sensitive)")
|
259
|
+
|
260
|
+
super().__init__(
|
261
|
+
message,
|
262
|
+
error_code=kwargs.get('error_code', 'CYCLE_CONN_001'),
|
263
|
+
context=context,
|
264
|
+
suggestions=suggestions,
|
265
|
+
documentation_url="https://docs.kailash.ai/cycles/connections"
|
266
|
+
)
|
267
|
+
|
268
|
+
|
269
|
+
class CycleValidationError(CycleException):
|
270
|
+
"""
|
271
|
+
Raised when cycle validation fails during workflow validation.
|
272
|
+
|
273
|
+
This exception handles validation errors for complete cycle configurations,
|
274
|
+
including cycle graph analysis, parameter compatibility, and safety checks.
|
275
|
+
|
276
|
+
Common scenarios:
|
277
|
+
- Circular dependencies without proper cycle marking
|
278
|
+
- Conflicting cycle parameters within groups
|
279
|
+
- Invalid nested cycle relationships
|
280
|
+
- Unsafe cycle configurations
|
281
|
+
|
282
|
+
Example:
|
283
|
+
>>> raise CycleValidationError(
|
284
|
+
... "Conflicting max_iterations in cycle group",
|
285
|
+
... cycle_group="optimization",
|
286
|
+
... conflicting_values=[50, 100, 75]
|
287
|
+
... )
|
288
|
+
"""
|
289
|
+
|
290
|
+
def __init__(
|
291
|
+
self,
|
292
|
+
message: str,
|
293
|
+
cycle_group: Optional[str] = None,
|
294
|
+
validation_failures: Optional[List[str]] = None,
|
295
|
+
conflicting_values: Optional[List[Any]] = None,
|
296
|
+
**kwargs
|
297
|
+
):
|
298
|
+
"""
|
299
|
+
Initialize cycle validation error.
|
300
|
+
|
301
|
+
Args:
|
302
|
+
message (str): Error description
|
303
|
+
cycle_group (str, optional): Affected cycle group ID
|
304
|
+
validation_failures (List[str], optional): List of validation failures
|
305
|
+
conflicting_values (List[Any], optional): Conflicting parameter values
|
306
|
+
**kwargs: Additional arguments for base exception
|
307
|
+
"""
|
308
|
+
context = kwargs.get('context', {})
|
309
|
+
suggestions = kwargs.get('suggestions', [])
|
310
|
+
|
311
|
+
# Add validation-specific context
|
312
|
+
if cycle_group:
|
313
|
+
context['cycle_group'] = cycle_group
|
314
|
+
if validation_failures:
|
315
|
+
context['validation_failures'] = validation_failures
|
316
|
+
if conflicting_values:
|
317
|
+
context['conflicting_values'] = conflicting_values
|
318
|
+
|
319
|
+
# Generate validation-specific suggestions
|
320
|
+
if not suggestions:
|
321
|
+
suggestions = [
|
322
|
+
"Review cycle configuration for consistency",
|
323
|
+
"Ensure all cycle edges use the same parameters",
|
324
|
+
"Check for proper cycle marking (cycle=True)",
|
325
|
+
"Validate nested cycle relationships"
|
326
|
+
]
|
327
|
+
if conflicting_values:
|
328
|
+
suggestions.append("Use consistent parameter values across cycle group")
|
329
|
+
|
330
|
+
super().__init__(
|
331
|
+
message,
|
332
|
+
error_code=kwargs.get('error_code', 'CYCLE_VALID_001'),
|
333
|
+
context=context,
|
334
|
+
suggestions=suggestions,
|
335
|
+
documentation_url="https://docs.kailash.ai/cycles/validation"
|
336
|
+
)
|
337
|
+
|
338
|
+
|
339
|
+
class CycleExecutionError(CycleException):
|
340
|
+
"""
|
341
|
+
Raised when cycle execution fails during runtime.
|
342
|
+
|
343
|
+
This exception handles runtime errors during cycle execution,
|
344
|
+
including convergence failures, timeout issues, and iteration problems.
|
345
|
+
|
346
|
+
Common scenarios:
|
347
|
+
- Cycle fails to converge within max_iterations
|
348
|
+
- Timeout exceeded during cycle execution
|
349
|
+
- Memory limit exceeded
|
350
|
+
- Node execution failures within cycles
|
351
|
+
|
352
|
+
Example:
|
353
|
+
>>> raise CycleExecutionError(
|
354
|
+
... "Cycle timeout exceeded",
|
355
|
+
... cycle_id="optimization",
|
356
|
+
... current_iteration=50,
|
357
|
+
... timeout_seconds=300
|
358
|
+
... )
|
359
|
+
"""
|
360
|
+
|
361
|
+
def __init__(
|
362
|
+
self,
|
363
|
+
message: str,
|
364
|
+
cycle_id: Optional[str] = None,
|
365
|
+
current_iteration: Optional[int] = None,
|
366
|
+
max_iterations: Optional[int] = None,
|
367
|
+
timeout_seconds: Optional[float] = None,
|
368
|
+
memory_usage_mb: Optional[int] = None,
|
369
|
+
**kwargs
|
370
|
+
):
|
371
|
+
"""
|
372
|
+
Initialize cycle execution error.
|
373
|
+
|
374
|
+
Args:
|
375
|
+
message (str): Error description
|
376
|
+
cycle_id (str, optional): ID of the failing cycle
|
377
|
+
current_iteration (int, optional): Current iteration count
|
378
|
+
max_iterations (int, optional): Maximum allowed iterations
|
379
|
+
timeout_seconds (float, optional): Timeout limit in seconds
|
380
|
+
memory_usage_mb (int, optional): Current memory usage
|
381
|
+
**kwargs: Additional arguments for base exception
|
382
|
+
"""
|
383
|
+
context = kwargs.get('context', {})
|
384
|
+
suggestions = kwargs.get('suggestions', [])
|
385
|
+
|
386
|
+
# Add execution-specific context
|
387
|
+
if cycle_id:
|
388
|
+
context['cycle_id'] = cycle_id
|
389
|
+
if current_iteration is not None:
|
390
|
+
context['current_iteration'] = current_iteration
|
391
|
+
if max_iterations is not None:
|
392
|
+
context['max_iterations'] = max_iterations
|
393
|
+
if timeout_seconds is not None:
|
394
|
+
context['timeout_seconds'] = timeout_seconds
|
395
|
+
if memory_usage_mb is not None:
|
396
|
+
context['memory_usage_mb'] = memory_usage_mb
|
397
|
+
|
398
|
+
# Generate execution-specific suggestions
|
399
|
+
if not suggestions:
|
400
|
+
suggestions = []
|
401
|
+
if current_iteration and max_iterations and current_iteration >= max_iterations:
|
402
|
+
suggestions.extend([
|
403
|
+
"Increase max_iterations if more iterations are needed",
|
404
|
+
"Add or improve convergence_check for early termination",
|
405
|
+
"Review cycle logic for efficiency improvements"
|
406
|
+
])
|
407
|
+
if timeout_seconds:
|
408
|
+
suggestions.extend([
|
409
|
+
"Increase timeout limit if legitimate long execution is expected",
|
410
|
+
"Optimize node processing for faster execution",
|
411
|
+
"Consider breaking into smaller cycles"
|
412
|
+
])
|
413
|
+
if memory_usage_mb:
|
414
|
+
suggestions.extend([
|
415
|
+
"Increase memory_limit if more memory is needed",
|
416
|
+
"Optimize data handling to reduce memory usage",
|
417
|
+
"Consider streaming or chunked processing"
|
418
|
+
])
|
419
|
+
|
420
|
+
super().__init__(
|
421
|
+
message,
|
422
|
+
error_code=kwargs.get('error_code', 'CYCLE_EXEC_001'),
|
423
|
+
context=context,
|
424
|
+
suggestions=suggestions,
|
425
|
+
documentation_url="https://docs.kailash.ai/cycles/execution"
|
426
|
+
)
|
427
|
+
|
428
|
+
|
429
|
+
class CycleConvergenceError(CycleException):
|
430
|
+
"""
|
431
|
+
Raised when cycle convergence detection fails or behaves unexpectedly.
|
432
|
+
|
433
|
+
This exception handles convergence-related issues, including expression
|
434
|
+
evaluation errors, impossible convergence conditions, and convergence
|
435
|
+
detection failures.
|
436
|
+
|
437
|
+
Common scenarios:
|
438
|
+
- Convergence expression evaluation fails
|
439
|
+
- Convergence condition references undefined variables
|
440
|
+
- Convergence logic contradicts cycle behavior
|
441
|
+
- Premature or delayed convergence detection
|
442
|
+
|
443
|
+
Example:
|
444
|
+
>>> raise CycleConvergenceError(
|
445
|
+
... "Convergence expression evaluation failed",
|
446
|
+
... convergence_expression="undefined_var > 0.5",
|
447
|
+
... available_variables=["value", "quality", "iteration"]
|
448
|
+
... )
|
449
|
+
"""
|
450
|
+
|
451
|
+
def __init__(
|
452
|
+
self,
|
453
|
+
message: str,
|
454
|
+
convergence_expression: Optional[str] = None,
|
455
|
+
evaluation_error: Optional[str] = None,
|
456
|
+
available_variables: Optional[List[str]] = None,
|
457
|
+
cycle_data: Optional[Dict[str, Any]] = None,
|
458
|
+
**kwargs
|
459
|
+
):
|
460
|
+
"""
|
461
|
+
Initialize cycle convergence error.
|
462
|
+
|
463
|
+
Args:
|
464
|
+
message (str): Error description
|
465
|
+
convergence_expression (str, optional): The problematic expression
|
466
|
+
evaluation_error (str, optional): Expression evaluation error details
|
467
|
+
available_variables (List[str], optional): Available variable names
|
468
|
+
cycle_data (Dict[str, Any], optional): Current cycle data
|
469
|
+
**kwargs: Additional arguments for base exception
|
470
|
+
"""
|
471
|
+
context = kwargs.get('context', {})
|
472
|
+
suggestions = kwargs.get('suggestions', [])
|
473
|
+
|
474
|
+
# Add convergence-specific context
|
475
|
+
if convergence_expression:
|
476
|
+
context['convergence_expression'] = convergence_expression
|
477
|
+
if evaluation_error:
|
478
|
+
context['evaluation_error'] = evaluation_error
|
479
|
+
if available_variables:
|
480
|
+
context['available_variables'] = available_variables
|
481
|
+
if cycle_data:
|
482
|
+
context['cycle_data'] = {k: str(v)[:100] for k, v in cycle_data.items()} # Truncate for readability
|
483
|
+
|
484
|
+
# Generate convergence-specific suggestions
|
485
|
+
if not suggestions:
|
486
|
+
suggestions = [
|
487
|
+
"Check convergence expression syntax for typos",
|
488
|
+
"Ensure all referenced variables exist in cycle output",
|
489
|
+
"Use simple comparison operators (>, <, ==, >=, <=)",
|
490
|
+
"Avoid complex logic or function calls in expressions"
|
491
|
+
]
|
492
|
+
if available_variables:
|
493
|
+
suggestions.append(f"Available variables: {', '.join(available_variables)}")
|
494
|
+
if convergence_expression and evaluation_error:
|
495
|
+
suggestions.append("Test convergence expression with sample data")
|
496
|
+
|
497
|
+
super().__init__(
|
498
|
+
message,
|
499
|
+
error_code=kwargs.get('error_code', 'CYCLE_CONV_001'),
|
500
|
+
context=context,
|
501
|
+
suggestions=suggestions,
|
502
|
+
documentation_url="https://docs.kailash.ai/cycles/convergence"
|
503
|
+
)
|
504
|
+
|
505
|
+
|
506
|
+
# Utility functions for enhanced error reporting
|
507
|
+
def create_configuration_error(
|
508
|
+
issue: str,
|
509
|
+
cycle_id: Optional[str] = None,
|
510
|
+
**invalid_params
|
511
|
+
) -> CycleConfigurationError:
|
512
|
+
"""
|
513
|
+
Create a standardized configuration error with common suggestions.
|
514
|
+
|
515
|
+
Args:
|
516
|
+
issue (str): Description of the configuration issue
|
517
|
+
cycle_id (str, optional): Cycle identifier
|
518
|
+
**invalid_params: Invalid parameter values
|
519
|
+
|
520
|
+
Returns:
|
521
|
+
CycleConfigurationError: Configured exception with context
|
522
|
+
|
523
|
+
Example:
|
524
|
+
>>> error = create_configuration_error(
|
525
|
+
... "Invalid max_iterations value",
|
526
|
+
... cycle_id="test",
|
527
|
+
... max_iterations=-5
|
528
|
+
... )
|
529
|
+
"""
|
530
|
+
return CycleConfigurationError(
|
531
|
+
message=issue,
|
532
|
+
cycle_id=cycle_id,
|
533
|
+
invalid_params=invalid_params
|
534
|
+
)
|
535
|
+
|
536
|
+
|
537
|
+
def create_connection_error(
|
538
|
+
issue: str,
|
539
|
+
source_node: Optional[str] = None,
|
540
|
+
target_node: Optional[str] = None,
|
541
|
+
available_nodes: Optional[List[str]] = None
|
542
|
+
) -> CycleConnectionError:
|
543
|
+
"""
|
544
|
+
Create a standardized connection error with node context.
|
545
|
+
|
546
|
+
Args:
|
547
|
+
issue (str): Description of the connection issue
|
548
|
+
source_node (str, optional): Source node ID
|
549
|
+
target_node (str, optional): Target node ID
|
550
|
+
available_nodes (List[str], optional): Available node IDs
|
551
|
+
|
552
|
+
Returns:
|
553
|
+
CycleConnectionError: Configured exception with context
|
554
|
+
|
555
|
+
Example:
|
556
|
+
>>> error = create_connection_error(
|
557
|
+
... "Node not found",
|
558
|
+
... source_node="missing",
|
559
|
+
... available_nodes=["node1", "node2"]
|
560
|
+
... )
|
561
|
+
"""
|
562
|
+
return CycleConnectionError(
|
563
|
+
message=issue,
|
564
|
+
source_node=source_node,
|
565
|
+
target_node=target_node,
|
566
|
+
available_nodes=available_nodes
|
567
|
+
)
|
568
|
+
|
569
|
+
|
570
|
+
def create_execution_error(
|
571
|
+
issue: str,
|
572
|
+
cycle_id: Optional[str] = None,
|
573
|
+
current_iteration: Optional[int] = None,
|
574
|
+
max_iterations: Optional[int] = None
|
575
|
+
) -> CycleExecutionError:
|
576
|
+
"""
|
577
|
+
Create a standardized execution error with runtime context.
|
578
|
+
|
579
|
+
Args:
|
580
|
+
issue (str): Description of the execution issue
|
581
|
+
cycle_id (str, optional): Cycle identifier
|
582
|
+
current_iteration (int, optional): Current iteration
|
583
|
+
max_iterations (int, optional): Maximum iterations
|
584
|
+
|
585
|
+
Returns:
|
586
|
+
CycleExecutionError: Configured exception with context
|
587
|
+
|
588
|
+
Example:
|
589
|
+
>>> error = create_execution_error(
|
590
|
+
... "Max iterations exceeded",
|
591
|
+
... cycle_id="optimization",
|
592
|
+
... current_iteration=100,
|
593
|
+
... max_iterations=100
|
594
|
+
... )
|
595
|
+
"""
|
596
|
+
return CycleExecutionError(
|
597
|
+
message=issue,
|
598
|
+
cycle_id=cycle_id,
|
599
|
+
current_iteration=current_iteration,
|
600
|
+
max_iterations=max_iterations
|
601
|
+
)
|