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,520 @@
1
+ """
2
+ MongoDB plugin for Daita Agents.
3
+
4
+ Simple MongoDB connection and querying - no over-engineering.
5
+ """
6
+ import logging
7
+ from typing import Any, Dict, List, Optional, Union, TYPE_CHECKING
8
+ from .base_db import BaseDatabasePlugin
9
+
10
+ if TYPE_CHECKING:
11
+ from ..core.tools import AgentTool
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ class MongoDBPlugin(BaseDatabasePlugin):
16
+ """
17
+ MongoDB plugin for agents with standardized connection management.
18
+
19
+ Inherits common database functionality from BaseDatabasePlugin.
20
+ """
21
+
22
+ def __init__(
23
+ self,
24
+ host: str = "localhost",
25
+ port: int = 27017,
26
+ database: str = "",
27
+ username: Optional[str] = None,
28
+ password: Optional[str] = None,
29
+ connection_string: Optional[str] = None,
30
+ **kwargs
31
+ ):
32
+ """
33
+ Initialize MongoDB connection.
34
+
35
+ Args:
36
+ host: MongoDB host
37
+ port: MongoDB port
38
+ database: Database name
39
+ username: Optional username
40
+ password: Optional password
41
+ connection_string: Full connection string (overrides individual params)
42
+ **kwargs: Additional motor configuration
43
+ """
44
+ if connection_string:
45
+ self.connection_string = connection_string
46
+ else:
47
+ if username and password:
48
+ self.connection_string = f"mongodb://{username}:{password}@{host}:{port}/{database}"
49
+ else:
50
+ self.connection_string = f"mongodb://{host}:{port}/{database}"
51
+
52
+ self.database_name = database
53
+
54
+ self.client_config = {
55
+ 'maxPoolSize': kwargs.get('max_pool_size', 10),
56
+ 'minPoolSize': kwargs.get('min_pool_size', 1),
57
+ 'serverSelectionTimeoutMS': kwargs.get('server_timeout', 30000),
58
+ }
59
+
60
+ # Initialize base class with all config
61
+ super().__init__(
62
+ host=host, port=port, database=database,
63
+ username=username, connection_string=connection_string,
64
+ **kwargs
65
+ )
66
+
67
+ logger.debug(f"MongoDB plugin configured for {host}:{port}/{database}")
68
+
69
+ async def connect(self):
70
+ """Connect to MongoDB."""
71
+ if self._client is not None:
72
+ return # Already connected
73
+
74
+ try:
75
+ import motor.motor_asyncio
76
+
77
+ self._client = motor.motor_asyncio.AsyncIOMotorClient(
78
+ self.connection_string,
79
+ **self.client_config
80
+ )
81
+
82
+ # Test connection
83
+ await self._client.admin.command('ping')
84
+
85
+ # Get database
86
+ self._db = self._client[self.database_name]
87
+
88
+ logger.info(f"Connected to MongoDB database '{self.database_name}'")
89
+ except ImportError:
90
+ self._handle_connection_error(
91
+ ImportError("motor not installed. Run: pip install motor"),
92
+ "connection"
93
+ )
94
+ except Exception as e:
95
+ self._handle_connection_error(e, "connection")
96
+
97
+ async def disconnect(self):
98
+ """Disconnect from MongoDB."""
99
+ if self._client:
100
+ self._client.close()
101
+ self._client = None
102
+ self._db = None
103
+ logger.info("Disconnected from MongoDB")
104
+
105
+ async def find(
106
+ self,
107
+ collection: str,
108
+ filter_doc: Optional[Dict[str, Any]] = None,
109
+ limit: Optional[int] = None,
110
+ skip: Optional[int] = None,
111
+ sort: Optional[List[tuple]] = None
112
+ ) -> List[Dict[str, Any]]:
113
+ """
114
+ Find documents in a collection.
115
+
116
+ Args:
117
+ collection: Collection name
118
+ filter_doc: Query filter (defaults to {} for all documents)
119
+ limit: Maximum number of documents to return
120
+ skip: Number of documents to skip
121
+ sort: Sort specification as list of (field, direction) tuples
122
+
123
+ Returns:
124
+ List of documents
125
+
126
+ Example:
127
+ # Find all users
128
+ users = await db.find("users")
129
+
130
+ # Find users in a specific city
131
+ users = await db.find("users", {"city": "New York"})
132
+
133
+ # Find with pagination and sorting
134
+ users = await db.find("users",
135
+ filter_doc={"age": {"$gte": 18}},
136
+ sort=[("name", 1)],
137
+ limit=10,
138
+ skip=20)
139
+ """
140
+ # Only auto-connect if client/db is None - allows manual mocking
141
+ if self._client is None or self._db is None:
142
+ await self.connect()
143
+
144
+ filter_doc = filter_doc or {}
145
+
146
+ cursor = self._db[collection].find(filter_doc)
147
+
148
+ # Apply cursor modifications - these return the cursor object
149
+ if sort:
150
+ cursor = cursor.sort(sort)
151
+ if skip:
152
+ cursor = cursor.skip(skip)
153
+ if limit:
154
+ cursor = cursor.limit(limit)
155
+
156
+ # Convert cursor to list and handle ObjectId
157
+ results = await cursor.to_list(length=None)
158
+
159
+ # Convert ObjectId to string for JSON serialization
160
+ for doc in results:
161
+ if '_id' in doc:
162
+ doc['_id'] = str(doc['_id'])
163
+
164
+ return results
165
+
166
+ async def insert(self, collection: str, document: Dict[str, Any]) -> str:
167
+ """
168
+ Insert a single document.
169
+
170
+ Args:
171
+ collection: Collection name
172
+ document: Document to insert
173
+
174
+ Returns:
175
+ Inserted document ID as string
176
+ """
177
+ # Only auto-connect if client/db is None - allows manual mocking
178
+ if self._client is None or self._db is None:
179
+ await self.connect()
180
+
181
+ result = await self._db[collection].insert_one(document)
182
+ return str(result.inserted_id)
183
+
184
+ async def insert_many(self, collection: str, documents: List[Dict[str, Any]]) -> List[str]:
185
+ """
186
+ Insert multiple documents.
187
+
188
+ Args:
189
+ collection: Collection name
190
+ documents: List of documents to insert
191
+
192
+ Returns:
193
+ List of inserted document IDs as strings
194
+ """
195
+ if not documents:
196
+ return []
197
+
198
+ # Only auto-connect if client/db is None - allows manual mocking
199
+ if self._client is None or self._db is None:
200
+ await self.connect()
201
+
202
+ result = await self._db[collection].insert_many(documents)
203
+ return [str(doc_id) for doc_id in result.inserted_ids]
204
+
205
+ async def update(
206
+ self,
207
+ collection: str,
208
+ filter_doc: Dict[str, Any],
209
+ update_doc: Dict[str, Any],
210
+ upsert: bool = False
211
+ ) -> Dict[str, Any]:
212
+ """
213
+ Update documents.
214
+
215
+ Args:
216
+ collection: Collection name
217
+ filter_doc: Filter to match documents
218
+ update_doc: Update operations (use $set, $inc, etc.)
219
+ upsert: Create document if not found
220
+
221
+ Returns:
222
+ Update result info
223
+
224
+ Example:
225
+ result = await db.update("users",
226
+ {"name": "John"},
227
+ {"$set": {"age": 30}})
228
+ """
229
+ # Only auto-connect if client/db is None - allows manual mocking
230
+ if self._client is None or self._db is None:
231
+ await self.connect()
232
+
233
+ result = await self._db[collection].update_many(
234
+ filter_doc,
235
+ update_doc,
236
+ upsert=upsert
237
+ )
238
+
239
+ return {
240
+ 'matched_count': result.matched_count,
241
+ 'modified_count': result.modified_count,
242
+ 'upserted_id': str(result.upserted_id) if result.upserted_id else None
243
+ }
244
+
245
+ async def delete(self, collection: str, filter_doc: Dict[str, Any]) -> int:
246
+ """
247
+ Delete documents.
248
+
249
+ Args:
250
+ collection: Collection name
251
+ filter_doc: Filter to match documents to delete
252
+
253
+ Returns:
254
+ Number of deleted documents
255
+ """
256
+ # Only auto-connect if client/db is None - allows manual mocking
257
+ if self._client is None or self._db is None:
258
+ await self.connect()
259
+
260
+ result = await self._db[collection].delete_many(filter_doc)
261
+ return result.deleted_count
262
+
263
+ async def count(self, collection: str, filter_doc: Optional[Dict[str, Any]] = None) -> int:
264
+ """
265
+ Count documents in collection.
266
+
267
+ Args:
268
+ collection: Collection name
269
+ filter_doc: Optional filter
270
+
271
+ Returns:
272
+ Document count
273
+ """
274
+ # Only auto-connect if client/db is None - allows manual mocking
275
+ if self._client is None or self._db is None:
276
+ await self.connect()
277
+
278
+ filter_doc = filter_doc or {}
279
+ return await self._db[collection].count_documents(filter_doc)
280
+
281
+ async def collections(self) -> List[str]:
282
+ """List all collections in the database."""
283
+ # Only auto-connect if client/db is None - allows manual mocking
284
+ if self._client is None or self._db is None:
285
+ await self.connect()
286
+
287
+ collections = await self._db.list_collection_names()
288
+ return sorted(collections)
289
+
290
+ async def aggregate(self, collection: str, pipeline: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
291
+ """
292
+ Run aggregation pipeline.
293
+
294
+ Args:
295
+ collection: Collection name
296
+ pipeline: Aggregation pipeline
297
+
298
+ Returns:
299
+ Aggregation results
300
+
301
+ Example:
302
+ pipeline = [
303
+ {"$match": {"status": "active"}},
304
+ {"$group": {"_id": "$department", "count": {"$sum": 1}}}
305
+ ]
306
+ results = await db.aggregate("employees", pipeline)
307
+ """
308
+ # Only auto-connect if client/db is None - allows manual mocking
309
+ if self._client is None or self._db is None:
310
+ await self.connect()
311
+
312
+ cursor = self._db[collection].aggregate(pipeline)
313
+ results = await cursor.to_list(length=None)
314
+
315
+ # Convert ObjectId to string
316
+ for doc in results:
317
+ if '_id' in doc and hasattr(doc['_id'], 'binary'):
318
+ doc['_id'] = str(doc['_id'])
319
+
320
+ return results
321
+
322
+ def get_tools(self) -> List['AgentTool']:
323
+ """
324
+ Expose MongoDB operations as agent tools.
325
+
326
+ Returns:
327
+ List of AgentTool instances for database operations
328
+ """
329
+ from ..core.tools import AgentTool
330
+
331
+ return [
332
+ AgentTool(
333
+ name="find_documents",
334
+ description="Find documents in a MongoDB collection. Returns matching documents as a list.",
335
+ parameters={
336
+ "type": "object",
337
+ "properties": {
338
+ "collection": {
339
+ "type": "string",
340
+ "description": "Collection name to search in"
341
+ },
342
+ "filter": {
343
+ "type": "object",
344
+ "description": "MongoDB query filter (e.g., {\"status\": \"active\"}). Empty object {} matches all documents."
345
+ },
346
+ "limit": {
347
+ "type": "integer",
348
+ "description": "Maximum number of documents to return"
349
+ }
350
+ },
351
+ "required": ["collection"]
352
+ },
353
+ handler=self._tool_find,
354
+ category="database",
355
+ source="plugin",
356
+ plugin_name="MongoDB",
357
+ timeout_seconds=60
358
+ ),
359
+ AgentTool(
360
+ name="insert_document",
361
+ description="Insert a single document into a MongoDB collection. Returns the inserted document ID.",
362
+ parameters={
363
+ "type": "object",
364
+ "properties": {
365
+ "collection": {
366
+ "type": "string",
367
+ "description": "Collection name to insert into"
368
+ },
369
+ "document": {
370
+ "type": "object",
371
+ "description": "Document data to insert as JSON object"
372
+ }
373
+ },
374
+ "required": ["collection", "document"]
375
+ },
376
+ handler=self._tool_insert,
377
+ category="database",
378
+ source="plugin",
379
+ plugin_name="MongoDB",
380
+ timeout_seconds=30
381
+ ),
382
+ AgentTool(
383
+ name="update_documents",
384
+ description="Update documents in a MongoDB collection. Returns the count of matched and modified documents.",
385
+ parameters={
386
+ "type": "object",
387
+ "properties": {
388
+ "collection": {
389
+ "type": "string",
390
+ "description": "Collection name"
391
+ },
392
+ "filter": {
393
+ "type": "object",
394
+ "description": "Query filter to match documents to update"
395
+ },
396
+ "update": {
397
+ "type": "object",
398
+ "description": "Update operations (e.g., {\"$set\": {\"status\": \"completed\"}})"
399
+ }
400
+ },
401
+ "required": ["collection", "filter", "update"]
402
+ },
403
+ handler=self._tool_update,
404
+ category="database",
405
+ source="plugin",
406
+ plugin_name="MongoDB",
407
+ timeout_seconds=60
408
+ ),
409
+ AgentTool(
410
+ name="delete_documents",
411
+ description="Delete documents from a MongoDB collection. Returns the count of deleted documents.",
412
+ parameters={
413
+ "type": "object",
414
+ "properties": {
415
+ "collection": {
416
+ "type": "string",
417
+ "description": "Collection name"
418
+ },
419
+ "filter": {
420
+ "type": "object",
421
+ "description": "Query filter to match documents to delete"
422
+ }
423
+ },
424
+ "required": ["collection", "filter"]
425
+ },
426
+ handler=self._tool_delete,
427
+ category="database",
428
+ source="plugin",
429
+ plugin_name="MongoDB",
430
+ timeout_seconds=60
431
+ ),
432
+ AgentTool(
433
+ name="list_collections",
434
+ description="List all collections in the MongoDB database",
435
+ parameters={
436
+ "type": "object",
437
+ "properties": {},
438
+ "required": []
439
+ },
440
+ handler=self._tool_list_collections,
441
+ category="database",
442
+ source="plugin",
443
+ plugin_name="MongoDB",
444
+ timeout_seconds=30
445
+ )
446
+ ]
447
+
448
+ async def _tool_find(self, args: Dict[str, Any]) -> Dict[str, Any]:
449
+ """Tool handler for find_documents"""
450
+ collection = args.get("collection")
451
+ filter_doc = args.get("filter", {})
452
+ limit = args.get("limit")
453
+
454
+ results = await self.find(
455
+ collection=collection,
456
+ filter_doc=filter_doc,
457
+ limit=limit
458
+ )
459
+
460
+ return {
461
+ "success": True,
462
+ "documents": results,
463
+ "count": len(results)
464
+ }
465
+
466
+ async def _tool_insert(self, args: Dict[str, Any]) -> Dict[str, Any]:
467
+ """Tool handler for insert_document"""
468
+ collection = args.get("collection")
469
+ document = args.get("document")
470
+
471
+ inserted_id = await self.insert(collection, document)
472
+
473
+ return {
474
+ "success": True,
475
+ "inserted_id": inserted_id,
476
+ "collection": collection
477
+ }
478
+
479
+ async def _tool_update(self, args: Dict[str, Any]) -> Dict[str, Any]:
480
+ """Tool handler for update_documents"""
481
+ collection = args.get("collection")
482
+ filter_doc = args.get("filter")
483
+ update_doc = args.get("update")
484
+
485
+ result = await self.update(collection, filter_doc, update_doc)
486
+
487
+ return {
488
+ "success": True,
489
+ "matched_count": result["matched_count"],
490
+ "modified_count": result["modified_count"],
491
+ "collection": collection
492
+ }
493
+
494
+ async def _tool_delete(self, args: Dict[str, Any]) -> Dict[str, Any]:
495
+ """Tool handler for delete_documents"""
496
+ collection = args.get("collection")
497
+ filter_doc = args.get("filter")
498
+
499
+ deleted_count = await self.delete(collection, filter_doc)
500
+
501
+ return {
502
+ "success": True,
503
+ "deleted_count": deleted_count,
504
+ "collection": collection
505
+ }
506
+
507
+ async def _tool_list_collections(self, args: Dict[str, Any]) -> Dict[str, Any]:
508
+ """Tool handler for list_collections"""
509
+ collections = await self.collections()
510
+
511
+ return {
512
+ "success": True,
513
+ "collections": collections,
514
+ "count": len(collections)
515
+ }
516
+
517
+
518
+ def mongodb(**kwargs) -> MongoDBPlugin:
519
+ """Create MongoDB plugin with simplified interface."""
520
+ return MongoDBPlugin(**kwargs)