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,550 @@
1
+ """
2
+ Plugin Tracing Integration for Daita Agents - Fixed Complete Version
3
+
4
+ Simplified automatic tracing for all plugin operations:
5
+ - Database queries (PostgreSQL, MySQL, MongoDB)
6
+ - API calls (REST)
7
+ - Cloud storage operations (S3)
8
+ - File operations
9
+ - Custom plugin executions
10
+
11
+ FIXED ISSUES:
12
+ - Completed all missing function implementations
13
+ - Fixed async/sync method detection
14
+ - Improved error handling and logging
15
+ - Added proper metadata extraction
16
+ - Fixed circular import issues
17
+ """
18
+
19
+ import asyncio
20
+ import logging
21
+ import time
22
+ import functools
23
+ from typing import Dict, Any, Optional, Callable, List
24
+ from contextlib import asynccontextmanager
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+ class TracedPlugin:
29
+ """
30
+ Complete traced plugin wrapper that automatically captures all operations.
31
+
32
+ Transparently adds tracing to any plugin without changing the interface.
33
+ """
34
+
35
+ def __init__(self, original_plugin: Any, plugin_name: str):
36
+ """
37
+ Initialize traced plugin wrapper.
38
+
39
+ Args:
40
+ original_plugin: The original plugin instance
41
+ plugin_name: Name of plugin (postgresql, rest, etc.)
42
+ """
43
+ self._original = original_plugin
44
+ self._plugin_name = plugin_name
45
+
46
+ # Import here to avoid circular imports
47
+ from .tracing import get_trace_manager
48
+ self._trace_manager = get_trace_manager()
49
+
50
+ # Copy over all attributes from original plugin
51
+ for attr_name in dir(original_plugin):
52
+ if not attr_name.startswith('_') and not hasattr(self, attr_name):
53
+ attr = getattr(original_plugin, attr_name)
54
+ if callable(attr):
55
+ # Wrap callable methods with tracing
56
+ setattr(self, attr_name, self._wrap_method(attr, attr_name))
57
+ else:
58
+ # Copy non-callable attributes directly
59
+ setattr(self, attr_name, attr)
60
+
61
+ logger.debug(f"Created traced {plugin_name} plugin")
62
+
63
+ def _wrap_method(self, method: Callable, method_name: str) -> Callable:
64
+ """Wrap a plugin method with automatic tracing."""
65
+
66
+ if asyncio.iscoroutinefunction(method):
67
+ @functools.wraps(method)
68
+ async def async_traced_method(*args, **kwargs):
69
+ return await self._trace_async_method(method, method_name, args, kwargs)
70
+ return async_traced_method
71
+ else:
72
+ @functools.wraps(method)
73
+ def sync_traced_method(*args, **kwargs):
74
+ return self._trace_sync_method(method, method_name, args, kwargs)
75
+ return sync_traced_method
76
+
77
+ async def _trace_async_method(self, method: Callable, method_name: str, args: tuple, kwargs: dict):
78
+ """Trace an async plugin method."""
79
+ from .tracing import TraceType, TraceStatus
80
+
81
+ async with self._trace_manager.span(
82
+ operation_name=f"{self._plugin_name}_{method_name}",
83
+ trace_type=TraceType.TOOL_EXECUTION,
84
+ tool_name=self._plugin_name,
85
+ tool_operation=method_name,
86
+ input_data=self._prepare_input_data(args, kwargs)
87
+ ) as span_id:
88
+
89
+ start_time = time.time()
90
+ try:
91
+ result = await method(*args, **kwargs)
92
+
93
+ # Extract metadata from result
94
+ metadata = self._extract_result_metadata(result)
95
+
96
+ # Update span with success metadata
97
+ self._trace_manager.end_span(
98
+ span_id=span_id,
99
+ status=TraceStatus.SUCCESS,
100
+ output_data=result,
101
+ duration_ms=(time.time() - start_time) * 1000,
102
+ **metadata
103
+ )
104
+
105
+ return result
106
+
107
+ except Exception as e:
108
+ # Update span with error metadata
109
+ self._trace_manager.end_span(
110
+ span_id=span_id,
111
+ status=TraceStatus.ERROR,
112
+ error_message=str(e),
113
+ duration_ms=(time.time() - start_time) * 1000
114
+ )
115
+ raise
116
+
117
+ def _trace_sync_method(self, method: Callable, method_name: str, args: tuple, kwargs: dict):
118
+ """Trace a sync plugin method."""
119
+ from .tracing import TraceType, TraceStatus
120
+
121
+ # For sync methods, we need to create a simple span without async context
122
+ span_id = self._trace_manager.start_span(
123
+ operation_name=f"{self._plugin_name}_{method_name}",
124
+ trace_type=TraceType.TOOL_EXECUTION,
125
+ tool_name=self._plugin_name,
126
+ tool_operation=method_name,
127
+ input_data=self._prepare_input_data(args, kwargs)
128
+ )
129
+
130
+ start_time = time.time()
131
+ try:
132
+ result = method(*args, **kwargs)
133
+
134
+ # Extract metadata from result
135
+ metadata = self._extract_result_metadata(result)
136
+
137
+ # Update span with success metadata
138
+ self._trace_manager.end_span(
139
+ span_id=span_id,
140
+ status=TraceStatus.SUCCESS,
141
+ output_data=result,
142
+ duration_ms=(time.time() - start_time) * 1000,
143
+ **metadata
144
+ )
145
+
146
+ return result
147
+
148
+ except Exception as e:
149
+ # Update span with error metadata
150
+ self._trace_manager.end_span(
151
+ span_id=span_id,
152
+ status=TraceStatus.ERROR,
153
+ error_message=str(e),
154
+ duration_ms=(time.time() - start_time) * 1000
155
+ )
156
+ raise
157
+
158
+ def _prepare_input_data(self, args: tuple, kwargs: dict) -> Dict[str, Any]:
159
+ """Prepare input data for tracing (sanitized)."""
160
+ try:
161
+ # Sanitize sensitive data
162
+ safe_kwargs = {}
163
+ for key, value in kwargs.items():
164
+ if key.lower() in ['password', 'token', 'secret', 'key', 'auth']:
165
+ safe_kwargs[key] = '[REDACTED]'
166
+ elif isinstance(value, str) and len(value) > 200:
167
+ safe_kwargs[key] = value[:200] + '...'
168
+ else:
169
+ safe_kwargs[key] = value
170
+
171
+ return {
172
+ "args_count": len(args),
173
+ "kwargs": safe_kwargs,
174
+ "method_type": "async" if asyncio.iscoroutinefunction(args[0] if args else None) else "sync"
175
+ }
176
+ except Exception as e:
177
+ return {"input_error": str(e)}
178
+
179
+ def _extract_result_metadata(self, result: Any) -> Dict[str, Any]:
180
+ """Extract metadata from operation result."""
181
+ metadata = {
182
+ "result_type": type(result).__name__,
183
+ "success": True
184
+ }
185
+
186
+ try:
187
+ # Count results for database/collections
188
+ if hasattr(result, '__len__') and not isinstance(result, str):
189
+ metadata["result_count"] = len(result)
190
+ elif isinstance(result, (list, tuple)):
191
+ metadata["result_count"] = len(result)
192
+
193
+ # API response codes
194
+ if hasattr(result, 'status_code'):
195
+ metadata["status_code"] = result.status_code
196
+ metadata["success"] = 200 <= result.status_code < 300
197
+
198
+ # Content size for string responses
199
+ if isinstance(result, str):
200
+ metadata["content_size"] = len(result)
201
+ elif hasattr(result, 'text'):
202
+ metadata["content_size"] = len(result.text)
203
+ elif hasattr(result, 'content'):
204
+ metadata["content_size"] = len(str(result.content))
205
+
206
+ # Database-specific metadata
207
+ if hasattr(result, 'rowcount'):
208
+ metadata["rows_affected"] = result.rowcount
209
+ elif hasattr(result, 'inserted_id'):
210
+ metadata["inserted_id"] = str(result.inserted_id)
211
+
212
+ except Exception as e:
213
+ metadata["metadata_error"] = str(e)
214
+
215
+ return metadata
216
+
217
+ # Delegate all other attributes to the original plugin
218
+ def __getattr__(self, name):
219
+ return getattr(self._original, name)
220
+
221
+ def __setattr__(self, name, value):
222
+ if name.startswith('_') or name in ['_original', '_plugin_name', '_trace_manager']:
223
+ super().__setattr__(name, value)
224
+ else:
225
+ setattr(self._original, name, value)
226
+
227
+
228
+ # Simple plugin wrapper function
229
+
230
+ def trace_plugin(plugin: Any, plugin_name: str = None) -> Any:
231
+ """
232
+ Automatically wrap any plugin with tracing capabilities.
233
+
234
+ Args:
235
+ plugin: Original plugin instance
236
+ plugin_name: Plugin name (auto-detected if not provided)
237
+
238
+ Returns:
239
+ Traced plugin with automatic operation tracking
240
+ """
241
+ if plugin is None:
242
+ return None
243
+
244
+ # Auto-detect plugin name if not provided
245
+ if not plugin_name:
246
+ plugin_name = plugin.__class__.__name__.lower().replace('plugin', '')
247
+ if not plugin_name:
248
+ plugin_name = 'unknown_plugin'
249
+
250
+ return TracedPlugin(plugin, plugin_name)
251
+
252
+
253
+ # Traced plugin factory functions
254
+
255
+ def traced_postgresql(**kwargs):
256
+ """Create PostgreSQL plugin with automatic tracing."""
257
+ try:
258
+ from ..plugins.postgresql import postgresql
259
+ plugin = postgresql(**kwargs)
260
+ return trace_plugin(plugin, "postgresql")
261
+ except ImportError as e:
262
+ logger.error(f"PostgreSQL plugin not available: {e}")
263
+ return None
264
+ except Exception as e:
265
+ logger.error(f"Failed to create PostgreSQL plugin: {e}")
266
+ return None
267
+
268
+
269
+ def traced_mysql(**kwargs):
270
+ """Create MySQL plugin with automatic tracing."""
271
+ try:
272
+ from ..plugins.mysql import mysql
273
+ plugin = mysql(**kwargs)
274
+ return trace_plugin(plugin, "mysql")
275
+ except ImportError as e:
276
+ logger.error(f"MySQL plugin not available: {e}")
277
+ return None
278
+ except Exception as e:
279
+ logger.error(f"Failed to create MySQL plugin: {e}")
280
+ return None
281
+
282
+
283
+ def traced_mongodb(**kwargs):
284
+ """Create MongoDB plugin with automatic tracing."""
285
+ try:
286
+ from ..plugins.mongodb import mongodb
287
+ plugin = mongodb(**kwargs)
288
+ return trace_plugin(plugin, "mongodb")
289
+ except ImportError as e:
290
+ logger.error(f"MongoDB plugin not available: {e}")
291
+ return None
292
+ except Exception as e:
293
+ logger.error(f"Failed to create MongoDB plugin: {e}")
294
+ return None
295
+
296
+
297
+ def traced_rest(**kwargs):
298
+ """Create REST API plugin with automatic tracing."""
299
+ try:
300
+ from ..plugins.rest import rest
301
+ plugin = rest(**kwargs)
302
+ return trace_plugin(plugin, "rest")
303
+ except ImportError as e:
304
+ logger.error(f"REST plugin not available: {e}")
305
+ return None
306
+ except Exception as e:
307
+ logger.error(f"Failed to create REST plugin: {e}")
308
+ return None
309
+
310
+
311
+ def traced_s3(**kwargs):
312
+ """Create S3 plugin with automatic tracing."""
313
+ try:
314
+ from ..plugins.s3 import s3
315
+ plugin = s3(**kwargs)
316
+ return trace_plugin(plugin, "s3")
317
+ except ImportError as e:
318
+ logger.error(f"S3 plugin not available: {e}")
319
+ return None
320
+ except Exception as e:
321
+ logger.error(f"Failed to create S3 plugin: {e}")
322
+ return None
323
+
324
+
325
+ def traced_slack(**kwargs):
326
+ """Create Slack plugin with automatic tracing."""
327
+ try:
328
+ from ..plugins.slack import slack
329
+ plugin = slack(**kwargs)
330
+ return trace_plugin(plugin, "slack")
331
+ except ImportError as e:
332
+ logger.error(f"Slack plugin not available: {e}")
333
+ return None
334
+ except Exception as e:
335
+ logger.error(f"Failed to create Slack plugin: {e}")
336
+ return None
337
+
338
+
339
+ def traced_elasticsearch(**kwargs):
340
+ """Create Elasticsearch plugin with automatic tracing."""
341
+ try:
342
+ from ..plugins.elasticsearch import elasticsearch
343
+ plugin = elasticsearch(**kwargs)
344
+ return trace_plugin(plugin, "elasticsearch")
345
+ except ImportError as e:
346
+ logger.error(f"Elasticsearch plugin not available: {e}")
347
+ return None
348
+ except Exception as e:
349
+ logger.error(f"Failed to create Elasticsearch plugin: {e}")
350
+ return None
351
+
352
+
353
+ # Context managers for batch operations
354
+
355
+ @asynccontextmanager
356
+ async def traced_transaction(db_plugin, operation_name: str = "transaction"):
357
+ """
358
+ Context manager for tracing database transactions.
359
+
360
+ Usage:
361
+ async with traced_transaction(db, "user_registration"):
362
+ await db.query("INSERT INTO users ...")
363
+ await db.query("INSERT INTO profiles ...")
364
+ """
365
+ from .tracing import get_trace_manager, TraceType
366
+ trace_manager = get_trace_manager()
367
+
368
+ async with trace_manager.span(
369
+ operation_name=f"transaction_{operation_name}",
370
+ trace_type=TraceType.TOOL_EXECUTION,
371
+ tool_name=getattr(db_plugin, '_plugin_name', 'database'),
372
+ tool_operation=operation_name,
373
+ transaction_type="database"
374
+ ):
375
+ try:
376
+ yield db_plugin
377
+ except Exception as e:
378
+ logger.error(f"Transaction {operation_name} failed: {e}")
379
+ raise
380
+
381
+
382
+ @asynccontextmanager
383
+ async def traced_api_batch(api_plugin, batch_name: str = "api_batch"):
384
+ """
385
+ Context manager for tracing API batch operations.
386
+
387
+ Usage:
388
+ async with traced_api_batch(api, "user_sync"):
389
+ user_data = await api.get("/users/123")
390
+ await api.post("/sync", json=user_data)
391
+ """
392
+ from .tracing import get_trace_manager, TraceType
393
+ trace_manager = get_trace_manager()
394
+
395
+ async with trace_manager.span(
396
+ operation_name=f"api_batch_{batch_name}",
397
+ trace_type=TraceType.TOOL_EXECUTION,
398
+ tool_name=getattr(api_plugin, '_plugin_name', 'api'),
399
+ tool_operation=batch_name,
400
+ batch_type="api"
401
+ ):
402
+ try:
403
+ yield api_plugin
404
+ except Exception as e:
405
+ logger.error(f"API batch {batch_name} failed: {e}")
406
+ raise
407
+
408
+
409
+ # Utility functions
410
+
411
+ def get_plugin_traces(plugin_name: Optional[str] = None, limit: int = 20) -> List[Dict[str, Any]]:
412
+ """Get recent plugin execution traces."""
413
+ try:
414
+ from .tracing import get_trace_manager
415
+ trace_manager = get_trace_manager()
416
+ operations = trace_manager.get_recent_operations(limit=limit * 2)
417
+
418
+ # Filter for tool executions
419
+ tool_ops = [
420
+ op for op in operations
421
+ if op.get('type') == 'tool_execution'
422
+ ]
423
+
424
+ # Filter by plugin name if specified
425
+ if plugin_name:
426
+ tool_ops = [
427
+ op for op in tool_ops
428
+ if op.get('metadata', {}).get('tool_name') == plugin_name
429
+ ]
430
+
431
+ return tool_ops[:limit]
432
+ except Exception as e:
433
+ logger.error(f"Error getting plugin traces: {e}")
434
+ return []
435
+
436
+
437
+ def get_plugin_stats(plugin_name: Optional[str] = None) -> Dict[str, Any]:
438
+ """Get comprehensive plugin usage statistics."""
439
+ try:
440
+ traces = get_plugin_traces(plugin_name, limit=50)
441
+
442
+ if not traces:
443
+ return {"total_operations": 0, "success_rate": 0}
444
+
445
+ total_ops = len(traces)
446
+ successful_ops = len([t for t in traces if t.get('status') == 'success'])
447
+
448
+ # Calculate average latency
449
+ latencies = [t.get('duration_ms', 0) for t in traces if t.get('duration_ms')]
450
+ avg_latency = sum(latencies) / len(latencies) if latencies else 0
451
+
452
+ # Operation distribution
453
+ operations = {}
454
+ for trace in traces:
455
+ op_name = trace.get('metadata', {}).get('tool_operation', 'unknown')
456
+ operations[op_name] = operations.get(op_name, 0) + 1
457
+
458
+ # Error distribution
459
+ errors = {}
460
+ for trace in traces:
461
+ if trace.get('status') == 'error':
462
+ error_msg = trace.get('error', 'Unknown error')
463
+ # Categorize errors
464
+ if 'timeout' in error_msg.lower():
465
+ error_type = 'timeout'
466
+ elif 'connection' in error_msg.lower():
467
+ error_type = 'connection'
468
+ elif 'permission' in error_msg.lower() or 'auth' in error_msg.lower():
469
+ error_type = 'permission'
470
+ else:
471
+ error_type = 'other'
472
+ errors[error_type] = errors.get(error_type, 0) + 1
473
+
474
+ return {
475
+ "plugin_name": plugin_name,
476
+ "total_operations": total_ops,
477
+ "successful_operations": successful_ops,
478
+ "failed_operations": total_ops - successful_ops,
479
+ "success_rate": successful_ops / total_ops if total_ops > 0 else 0,
480
+ "average_latency_ms": avg_latency,
481
+ "operation_distribution": operations,
482
+ "error_distribution": errors,
483
+ "total_latency_ms": sum(latencies)
484
+ }
485
+ except Exception as e:
486
+ logger.error(f"Error getting plugin stats: {e}")
487
+ return {"total_operations": 0, "success_rate": 0}
488
+
489
+
490
+ # Advanced tracing helpers
491
+
492
+ def create_custom_traced_plugin(plugin_instance: Any, plugin_name: str, custom_metadata: Dict[str, Any] = None) -> TracedPlugin:
493
+ """
494
+ Create a custom traced plugin with additional metadata.
495
+
496
+ Args:
497
+ plugin_instance: The plugin to wrap
498
+ plugin_name: Name for tracing
499
+ custom_metadata: Additional metadata to include in traces
500
+
501
+ Returns:
502
+ TracedPlugin instance with custom metadata
503
+ """
504
+ traced_plugin = TracedPlugin(plugin_instance, plugin_name)
505
+
506
+ if custom_metadata:
507
+ # Add custom metadata to the traced plugin
508
+ traced_plugin._custom_metadata = custom_metadata
509
+
510
+ # Override the metadata extraction to include custom data
511
+ original_extract = traced_plugin._extract_result_metadata
512
+
513
+ def enhanced_extract(result):
514
+ metadata = original_extract(result)
515
+ metadata.update(custom_metadata)
516
+ return metadata
517
+
518
+ traced_plugin._extract_result_metadata = enhanced_extract
519
+
520
+ return traced_plugin
521
+
522
+
523
+ # Export everything
524
+ __all__ = [
525
+ # Main tracing functions
526
+ "trace_plugin",
527
+
528
+ # Traced plugin factories
529
+ "traced_postgresql",
530
+ "traced_mysql",
531
+ "traced_mongodb",
532
+ "traced_rest",
533
+ "traced_s3",
534
+ "traced_slack",
535
+ "traced_elasticsearch",
536
+
537
+ # Context managers
538
+ "traced_transaction",
539
+ "traced_api_batch",
540
+
541
+ # Utility functions
542
+ "get_plugin_traces",
543
+ "get_plugin_stats",
544
+
545
+ # Advanced functions
546
+ "create_custom_traced_plugin",
547
+
548
+ # Plugin class (for advanced usage)
549
+ "TracedPlugin"
550
+ ]