daita-agents 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 (69) hide show
  1. daita/__init__.py +216 -0
  2. daita/agents/__init__.py +33 -0
  3. daita/agents/base.py +743 -0
  4. daita/agents/substrate.py +1141 -0
  5. daita/cli/__init__.py +145 -0
  6. daita/cli/__main__.py +7 -0
  7. daita/cli/ascii_art.py +44 -0
  8. daita/cli/core/__init__.py +0 -0
  9. daita/cli/core/create.py +254 -0
  10. daita/cli/core/deploy.py +473 -0
  11. daita/cli/core/deployments.py +309 -0
  12. daita/cli/core/import_detector.py +219 -0
  13. daita/cli/core/init.py +481 -0
  14. daita/cli/core/logs.py +239 -0
  15. daita/cli/core/managed_deploy.py +709 -0
  16. daita/cli/core/run.py +648 -0
  17. daita/cli/core/status.py +421 -0
  18. daita/cli/core/test.py +239 -0
  19. daita/cli/core/webhooks.py +172 -0
  20. daita/cli/main.py +588 -0
  21. daita/cli/utils.py +541 -0
  22. daita/config/__init__.py +62 -0
  23. daita/config/base.py +159 -0
  24. daita/config/settings.py +184 -0
  25. daita/core/__init__.py +262 -0
  26. daita/core/decision_tracing.py +701 -0
  27. daita/core/exceptions.py +480 -0
  28. daita/core/focus.py +251 -0
  29. daita/core/interfaces.py +76 -0
  30. daita/core/plugin_tracing.py +550 -0
  31. daita/core/relay.py +779 -0
  32. daita/core/reliability.py +381 -0
  33. daita/core/scaling.py +459 -0
  34. daita/core/tools.py +554 -0
  35. daita/core/tracing.py +770 -0
  36. daita/core/workflow.py +1144 -0
  37. daita/display/__init__.py +1 -0
  38. daita/display/console.py +160 -0
  39. daita/execution/__init__.py +58 -0
  40. daita/execution/client.py +856 -0
  41. daita/execution/exceptions.py +92 -0
  42. daita/execution/models.py +317 -0
  43. daita/llm/__init__.py +60 -0
  44. daita/llm/anthropic.py +291 -0
  45. daita/llm/base.py +530 -0
  46. daita/llm/factory.py +101 -0
  47. daita/llm/gemini.py +355 -0
  48. daita/llm/grok.py +219 -0
  49. daita/llm/mock.py +172 -0
  50. daita/llm/openai.py +220 -0
  51. daita/plugins/__init__.py +141 -0
  52. daita/plugins/base.py +37 -0
  53. daita/plugins/base_db.py +167 -0
  54. daita/plugins/elasticsearch.py +849 -0
  55. daita/plugins/mcp.py +481 -0
  56. daita/plugins/mongodb.py +520 -0
  57. daita/plugins/mysql.py +362 -0
  58. daita/plugins/postgresql.py +342 -0
  59. daita/plugins/redis_messaging.py +500 -0
  60. daita/plugins/rest.py +537 -0
  61. daita/plugins/s3.py +770 -0
  62. daita/plugins/slack.py +729 -0
  63. daita/utils/__init__.py +18 -0
  64. daita_agents-0.2.0.dist-info/METADATA +409 -0
  65. daita_agents-0.2.0.dist-info/RECORD +69 -0
  66. daita_agents-0.2.0.dist-info/WHEEL +5 -0
  67. daita_agents-0.2.0.dist-info/entry_points.txt +2 -0
  68. daita_agents-0.2.0.dist-info/licenses/LICENSE +56 -0
  69. daita_agents-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,480 @@
1
+ """
2
+ Core exceptions for Daita Agents.
3
+
4
+ Provides a hierarchy of exceptions with built-in retry behavior hints
5
+ to help agents make intelligent retry decisions.
6
+ """
7
+
8
+ class DaitaError(Exception):
9
+ """Base exception for all Daita errors."""
10
+
11
+ def __init__(self, message: str, retry_hint: str = "unknown", context: dict = None):
12
+ """
13
+ Initialize Daita error.
14
+
15
+ Args:
16
+ message: Error message
17
+ retry_hint: Hint for retry behavior ("transient", "retryable", "permanent", "unknown")
18
+ context: Additional error context
19
+ """
20
+ super().__init__(message)
21
+ self.retry_hint = retry_hint
22
+ self.context = context or {}
23
+
24
+ def is_transient(self) -> bool:
25
+ """Check if this error is likely transient."""
26
+ return self.retry_hint == "transient"
27
+
28
+ def is_retryable(self) -> bool:
29
+ """Check if this error might be retryable."""
30
+ return self.retry_hint in ["transient", "retryable"]
31
+
32
+ def is_permanent(self) -> bool:
33
+ """Check if this error is permanent."""
34
+ return self.retry_hint == "permanent"
35
+
36
+ class AgentError(DaitaError):
37
+ """Exception raised by agents during operation."""
38
+
39
+ def __init__(self, message: str, agent_id: str = None, task: str = None, retry_hint: str = "retryable", context: dict = None):
40
+ """
41
+ Initialize agent error.
42
+
43
+ Args:
44
+ message: Error message
45
+ agent_id: ID of the agent that failed
46
+ task: Task that was being executed
47
+ retry_hint: Hint for retry behavior
48
+ context: Additional error context
49
+ """
50
+ super().__init__(message, retry_hint, context)
51
+ self.agent_id = agent_id
52
+ self.task = task
53
+
54
+ class ConfigError(DaitaError):
55
+ """Exception raised for configuration issues."""
56
+
57
+ def __init__(self, message: str, config_section: str = None, context: dict = None):
58
+ """
59
+ Initialize configuration error.
60
+
61
+ Args:
62
+ message: Error message
63
+ config_section: Section of config that caused the error
64
+ context: Additional error context
65
+ """
66
+ super().__init__(message, retry_hint="permanent", context=context)
67
+ self.config_section = config_section
68
+
69
+ class LLMError(DaitaError):
70
+ """Exception raised by LLM providers."""
71
+
72
+ def __init__(self, message: str, provider: str = None, model: str = None, retry_hint: str = "retryable", context: dict = None):
73
+ """
74
+ Initialize LLM error.
75
+
76
+ Args:
77
+ message: Error message
78
+ provider: LLM provider name
79
+ model: Model name
80
+ retry_hint: Hint for retry behavior
81
+ context: Additional error context
82
+ """
83
+ super().__init__(message, retry_hint, context)
84
+ self.provider = provider
85
+ self.model = model
86
+
87
+ class PluginError(DaitaError):
88
+ """Exception raised by plugins."""
89
+
90
+ def __init__(self, message: str, plugin_name: str = None, retry_hint: str = "retryable", context: dict = None):
91
+ """
92
+ Initialize plugin error.
93
+
94
+ Args:
95
+ message: Error message
96
+ plugin_name: Name of the plugin that failed
97
+ retry_hint: Hint for retry behavior
98
+ context: Additional error context
99
+ """
100
+ super().__init__(message, retry_hint, context)
101
+ self.plugin_name = plugin_name
102
+
103
+ class WorkflowError(DaitaError):
104
+ """Exception raised by workflow operations."""
105
+
106
+ def __init__(self, message: str, workflow_name: str = None, retry_hint: str = "retryable", context: dict = None):
107
+ """
108
+ Initialize workflow error.
109
+
110
+ Args:
111
+ message: Error message
112
+ workflow_name: Name of the workflow that failed
113
+ retry_hint: Hint for retry behavior
114
+ context: Additional error context
115
+ """
116
+ super().__init__(message, retry_hint, context)
117
+ self.workflow_name = workflow_name
118
+
119
+ # ======= Retry-Specific Exception Classes =======
120
+
121
+ class TransientError(DaitaError):
122
+ """
123
+ Exception for temporary issues that are likely to resolve quickly.
124
+
125
+ Examples: Network timeouts, rate limits, temporary service unavailability
126
+ These errors should be retried with minimal delay.
127
+ """
128
+
129
+ def __init__(self, message: str, context: dict = None):
130
+ super().__init__(message, retry_hint="transient", context=context)
131
+
132
+ class RetryableError(DaitaError):
133
+ """
134
+ Exception for issues that might be resolved with a different approach or after delay.
135
+
136
+ Examples: Resource temporarily unavailable, processing queue full,
137
+ temporary data inconsistency. These errors should be retried with backoff.
138
+ """
139
+
140
+ def __init__(self, message: str, context: dict = None):
141
+ super().__init__(message, retry_hint="retryable", context=context)
142
+
143
+ class PermanentError(DaitaError):
144
+ """
145
+ Exception for issues that will not be resolved by retrying.
146
+
147
+ Examples: Authentication failures, permission errors, invalid configuration,
148
+ malformed data. These errors should not be retried.
149
+ """
150
+
151
+ def __init__(self, message: str, context: dict = None):
152
+ super().__init__(message, retry_hint="permanent", context=context)
153
+
154
+ # ======= Specific Transient Errors =======
155
+
156
+ class RateLimitError(TransientError):
157
+ """Exception for API rate limiting."""
158
+
159
+ def __init__(self, message: str = "Rate limit exceeded", retry_after: int = None, context: dict = None):
160
+ context = context or {}
161
+ if retry_after:
162
+ context['retry_after'] = retry_after
163
+ message = f"{message} (retry after {retry_after}s)"
164
+ super().__init__(message, context)
165
+ self.retry_after = retry_after
166
+
167
+ class TimeoutError(TransientError):
168
+ """Exception for timeout issues."""
169
+
170
+ def __init__(self, message: str = "Operation timed out", timeout_duration: float = None, context: dict = None):
171
+ context = context or {}
172
+ if timeout_duration:
173
+ context['timeout_duration'] = timeout_duration
174
+ message = f"{message} (after {timeout_duration}s)"
175
+ super().__init__(message, context)
176
+ self.timeout_duration = timeout_duration
177
+
178
+ class ConnectionError(TransientError):
179
+ """Exception for connection issues."""
180
+
181
+ def __init__(self, message: str = "Connection failed", host: str = None, port: int = None, context: dict = None):
182
+ context = context or {}
183
+ if host:
184
+ context['host'] = host
185
+ if port:
186
+ context['port'] = port
187
+ message = f"{message} (to {host}:{port})" if host else f"{message} (port {port})"
188
+ super().__init__(message, context)
189
+ self.host = host
190
+ self.port = port
191
+
192
+ class ServiceUnavailableError(TransientError):
193
+ """Exception for service unavailability."""
194
+
195
+ def __init__(self, message: str = "Service temporarily unavailable", service_name: str = None, context: dict = None):
196
+ context = context or {}
197
+ if service_name:
198
+ context['service_name'] = service_name
199
+ message = f"{message}: {service_name}"
200
+ super().__init__(message, context)
201
+ self.service_name = service_name
202
+
203
+ class TemporaryError(TransientError):
204
+ """Generic temporary error that should retry quickly."""
205
+ pass
206
+
207
+ class TooManyRequestsError(TransientError):
208
+ """Exception for too many requests (429 HTTP status)."""
209
+
210
+ def __init__(self, message: str = "Too many requests", retry_after: int = None, context: dict = None):
211
+ context = context or {}
212
+ if retry_after:
213
+ context['retry_after'] = retry_after
214
+ super().__init__(message, context)
215
+ self.retry_after = retry_after
216
+
217
+ # ======= Specific Retryable Errors =======
218
+
219
+ class ResourceBusyError(RetryableError):
220
+ """Exception for busy resources that might become available."""
221
+
222
+ def __init__(self, message: str = "Resource is busy", resource_name: str = None, context: dict = None):
223
+ context = context or {}
224
+ if resource_name:
225
+ context['resource_name'] = resource_name
226
+ message = f"{message}: {resource_name}"
227
+ super().__init__(message, context)
228
+ self.resource_name = resource_name
229
+
230
+ class DataInconsistencyError(RetryableError):
231
+ """Exception for temporary data inconsistency."""
232
+
233
+ def __init__(self, message: str = "Data inconsistency detected", data_source: str = None, context: dict = None):
234
+ context = context or {}
235
+ if data_source:
236
+ context['data_source'] = data_source
237
+ super().__init__(message, context)
238
+ self.data_source = data_source
239
+
240
+ class ProcessingQueueFullError(RetryableError):
241
+ """Exception for full processing queues."""
242
+
243
+ def __init__(self, message: str = "Processing queue is full", queue_name: str = None, context: dict = None):
244
+ context = context or {}
245
+ if queue_name:
246
+ context['queue_name'] = queue_name
247
+ super().__init__(message, context)
248
+ self.queue_name = queue_name
249
+
250
+ # ======= Specific Permanent Errors =======
251
+
252
+ class AuthenticationError(PermanentError):
253
+ """Exception for authentication failures."""
254
+
255
+ def __init__(self, message: str = "Authentication failed", provider: str = None, context: dict = None):
256
+ context = context or {}
257
+ if provider:
258
+ context['provider'] = provider
259
+ super().__init__(message, context)
260
+ self.provider = provider
261
+
262
+ class PermissionError(PermanentError):
263
+ """Exception for permission/authorization failures."""
264
+
265
+ def __init__(self, message: str = "Permission denied", resource: str = None, action: str = None, context: dict = None):
266
+ context = context or {}
267
+ if resource:
268
+ context['resource'] = resource
269
+ if action:
270
+ context['action'] = action
271
+ super().__init__(message, context)
272
+ self.resource = resource
273
+ self.action = action
274
+
275
+ class ValidationError(PermanentError):
276
+ """Exception for data validation failures."""
277
+
278
+ def __init__(self, message: str = "Validation failed", field: str = None, value: str = None, context: dict = None):
279
+ context = context or {}
280
+ if field:
281
+ context['field'] = field
282
+ if value:
283
+ context['value'] = str(value)[:100] # Truncate long values
284
+ super().__init__(message, context)
285
+ self.field = field
286
+ self.value = value
287
+
288
+ class InvalidDataError(PermanentError):
289
+ """Exception for invalid or malformed data."""
290
+
291
+ def __init__(self, message: str = "Invalid data format", data_type: str = None, expected_format: str = None, context: dict = None):
292
+ context = context or {}
293
+ if data_type:
294
+ context['data_type'] = data_type
295
+ if expected_format:
296
+ context['expected_format'] = expected_format
297
+ super().__init__(message, context)
298
+ self.data_type = data_type
299
+ self.expected_format = expected_format
300
+
301
+ class NotFoundError(PermanentError):
302
+ """Exception for missing resources."""
303
+
304
+ def __init__(self, message: str = "Resource not found", resource_type: str = None, resource_id: str = None, context: dict = None):
305
+ context = context or {}
306
+ if resource_type:
307
+ context['resource_type'] = resource_type
308
+ if resource_id:
309
+ context['resource_id'] = resource_id
310
+ super().__init__(message, context)
311
+ self.resource_type = resource_type
312
+ self.resource_id = resource_id
313
+
314
+ class BadRequestError(PermanentError):
315
+ """Exception for malformed requests."""
316
+
317
+ def __init__(self, message: str = "Bad request", request_type: str = None, context: dict = None):
318
+ context = context or {}
319
+ if request_type:
320
+ context['request_type'] = request_type
321
+ super().__init__(message, context)
322
+ self.request_type = request_type
323
+
324
+ # ======= Circuit Breaker Specific Errors =======
325
+
326
+ class CircuitBreakerOpenError(PermanentError):
327
+ """Exception when circuit breaker is open."""
328
+
329
+ def __init__(self, message: str = "Circuit breaker is open", agent_name: str = None, failure_count: int = None, context: dict = None):
330
+ context = context or {}
331
+ if agent_name:
332
+ context['agent_name'] = agent_name
333
+ if failure_count:
334
+ context['failure_count'] = failure_count
335
+ super().__init__(message, context)
336
+ self.agent_name = agent_name
337
+ self.failure_count = failure_count
338
+
339
+ # ======= Reliability Infrastructure Errors =======
340
+
341
+ class BackpressureError(RetryableError):
342
+ """Exception when backpressure limits are exceeded."""
343
+
344
+ def __init__(self, message: str = "Backpressure limit exceeded", agent_id: str = None, queue_size: int = None, context: dict = None):
345
+ context = context or {}
346
+ if agent_id:
347
+ context['agent_id'] = agent_id
348
+ if queue_size is not None:
349
+ context['queue_size'] = queue_size
350
+ message = f"{message} (queue size: {queue_size})"
351
+ super().__init__(message, context)
352
+ self.agent_id = agent_id
353
+ self.queue_size = queue_size
354
+
355
+ class TaskTimeoutError(TransientError):
356
+ """Exception when a task times out."""
357
+
358
+ def __init__(self, message: str = "Task execution timed out", task_id: str = None, timeout_duration: float = None, context: dict = None):
359
+ context = context or {}
360
+ if task_id:
361
+ context['task_id'] = task_id
362
+ if timeout_duration:
363
+ context['timeout_duration'] = timeout_duration
364
+ message = f"{message} after {timeout_duration}s"
365
+ super().__init__(message, context)
366
+ self.task_id = task_id
367
+ self.timeout_duration = timeout_duration
368
+
369
+ class AcknowledgmentTimeoutError(TransientError):
370
+ """Exception when message acknowledgment times out."""
371
+
372
+ def __init__(self, message: str = "Message acknowledgment timed out", message_id: str = None, timeout_duration: float = None, context: dict = None):
373
+ context = context or {}
374
+ if message_id:
375
+ context['message_id'] = message_id
376
+ if timeout_duration:
377
+ context['timeout_duration'] = timeout_duration
378
+ super().__init__(message, context)
379
+ self.message_id = message_id
380
+ self.timeout_duration = timeout_duration
381
+
382
+ class TaskNotFoundError(PermanentError):
383
+ """Exception when a referenced task cannot be found."""
384
+
385
+ def __init__(self, message: str = "Task not found", task_id: str = None, context: dict = None):
386
+ context = context or {}
387
+ if task_id:
388
+ context['task_id'] = task_id
389
+ message = f"{message}: {task_id}"
390
+ super().__init__(message, context)
391
+ self.task_id = task_id
392
+
393
+ class ReliabilityConfigurationError(PermanentError):
394
+ """Exception for invalid reliability configuration."""
395
+
396
+ def __init__(self, message: str = "Invalid reliability configuration", config_key: str = None, context: dict = None):
397
+ context = context or {}
398
+ if config_key:
399
+ context['config_key'] = config_key
400
+ message = f"{message}: {config_key}"
401
+ super().__init__(message, context)
402
+ self.config_key = config_key
403
+
404
+ class DeadLetterQueueError(RetryableError):
405
+ """Exception related to dead letter queue operations."""
406
+
407
+ def __init__(self, message: str = "Dead letter queue operation failed", operation: str = None, context: dict = None):
408
+ context = context or {}
409
+ if operation:
410
+ context['operation'] = operation
411
+ message = f"{message}: {operation}"
412
+ super().__init__(message, context)
413
+ self.operation = operation
414
+
415
+ # ======= Utility Functions =======
416
+
417
+ def classify_exception(exception: Exception) -> str:
418
+ """
419
+ Classify any exception to determine retry behavior.
420
+
421
+ Args:
422
+ exception: The exception to classify
423
+
424
+ Returns:
425
+ Retry hint: "transient", "retryable", "permanent", or "unknown"
426
+ """
427
+ # If it's already a Daita exception, use its hint
428
+ if isinstance(exception, DaitaError):
429
+ return exception.retry_hint
430
+
431
+ # Classify standard Python exceptions
432
+ exception_name = exception.__class__.__name__
433
+
434
+ # Transient errors (standard library)
435
+ transient_exceptions = {
436
+ 'TimeoutError', 'ConnectionError', 'ConnectionResetError',
437
+ 'ConnectionAbortedError', 'ConnectionRefusedError',
438
+ 'OSError', 'IOError', 'socket.timeout'
439
+ }
440
+
441
+ # Permanent errors (standard library)
442
+ permanent_exceptions = {
443
+ 'ValueError', 'TypeError', 'AttributeError', 'KeyError',
444
+ 'IndexError', 'NameError', 'SyntaxError', 'ImportError',
445
+ 'FileNotFoundError', 'PermissionError'
446
+ }
447
+
448
+ if exception_name in transient_exceptions:
449
+ return "transient"
450
+ elif exception_name in permanent_exceptions:
451
+ return "permanent"
452
+ else:
453
+ return "retryable" # Default to retryable for unknown exceptions
454
+
455
+ def create_contextual_error(
456
+ base_exception: Exception,
457
+ context: dict = None,
458
+ retry_hint: str = None
459
+ ) -> DaitaError:
460
+ """
461
+ Wrap a standard exception in a Daita exception with context.
462
+
463
+ Args:
464
+ base_exception: The original exception
465
+ context: Additional context information
466
+ retry_hint: Override retry hint classification
467
+
468
+ Returns:
469
+ Wrapped DaitaError with context and retry hint
470
+ """
471
+ message = str(base_exception)
472
+ hint = retry_hint or classify_exception(base_exception)
473
+
474
+ # Choose appropriate Daita exception type
475
+ if hint == "transient":
476
+ return TransientError(message, context)
477
+ elif hint == "permanent":
478
+ return PermanentError(message, context)
479
+ else:
480
+ return RetryableError(message, context)