daita-agents 0.1.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.

Potentially problematic release.


This version of daita-agents might be problematic. Click here for more details.

Files changed (69) hide show
  1. daita/__init__.py +208 -0
  2. daita/agents/__init__.py +33 -0
  3. daita/agents/base.py +722 -0
  4. daita/agents/substrate.py +895 -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 +382 -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 +695 -0
  32. daita/core/reliability.py +381 -0
  33. daita/core/scaling.py +444 -0
  34. daita/core/tools.py +402 -0
  35. daita/core/tracing.py +770 -0
  36. daita/core/workflow.py +1084 -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 +166 -0
  45. daita/llm/base.py +373 -0
  46. daita/llm/factory.py +101 -0
  47. daita/llm/gemini.py +152 -0
  48. daita/llm/grok.py +114 -0
  49. daita/llm/mock.py +135 -0
  50. daita/llm/openai.py +109 -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 +844 -0
  55. daita/plugins/mcp.py +481 -0
  56. daita/plugins/mongodb.py +510 -0
  57. daita/plugins/mysql.py +351 -0
  58. daita/plugins/postgresql.py +331 -0
  59. daita/plugins/redis_messaging.py +500 -0
  60. daita/plugins/rest.py +529 -0
  61. daita/plugins/s3.py +761 -0
  62. daita/plugins/slack.py +729 -0
  63. daita/utils/__init__.py +18 -0
  64. daita_agents-0.1.0.dist-info/METADATA +350 -0
  65. daita_agents-0.1.0.dist-info/RECORD +69 -0
  66. daita_agents-0.1.0.dist-info/WHEEL +5 -0
  67. daita_agents-0.1.0.dist-info/entry_points.txt +2 -0
  68. daita_agents-0.1.0.dist-info/licenses/LICENSE +56 -0
  69. daita_agents-0.1.0.dist-info/top_level.txt +1 -0
daita/plugins/rest.py ADDED
@@ -0,0 +1,529 @@
1
+ """
2
+ REST API plugin for Daita Agents.
3
+
4
+ Simple REST API client - no over-engineering.
5
+ """
6
+ import logging
7
+ import asyncio
8
+ from typing import Any, Dict, List, Optional, Union, TYPE_CHECKING
9
+
10
+ if TYPE_CHECKING:
11
+ from ..core.tools import AgentTool
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ class RESTPlugin:
16
+ """
17
+ Simple REST API plugin for agents.
18
+
19
+ Just makes HTTP requests and handles responses. Nothing fancy.
20
+ """
21
+
22
+ def __init__(
23
+ self,
24
+ base_url: str,
25
+ api_key: Optional[str] = None,
26
+ auth_header: str = "Authorization",
27
+ auth_prefix: str = "Bearer",
28
+ timeout: int = 30,
29
+ **kwargs
30
+ ):
31
+ """
32
+ Initialize REST API client.
33
+
34
+ Args:
35
+ base_url: Base URL for all requests
36
+ api_key: Optional API key for authentication
37
+ auth_header: Header name for authentication (default: "Authorization")
38
+ auth_prefix: Prefix for auth value (default: "Bearer")
39
+ timeout: Request timeout in seconds
40
+ **kwargs: Additional headers or configuration
41
+ """
42
+ # Validate base_url
43
+ if not base_url or not base_url.strip():
44
+ raise ValueError("base_url cannot be empty or whitespace-only")
45
+
46
+ self.base_url = base_url.rstrip('/')
47
+ self.api_key = api_key
48
+ self.auth_header = auth_header
49
+ self.auth_prefix = auth_prefix
50
+ self.timeout = timeout
51
+
52
+ # Default headers
53
+ self.default_headers = {
54
+ 'Content-Type': 'application/json',
55
+ 'Accept': 'application/json',
56
+ **kwargs.get('headers', {})
57
+ }
58
+
59
+ # Add auth header if API key provided - fix spacing issue
60
+ if self.api_key:
61
+ if self.auth_prefix:
62
+ self.default_headers[self.auth_header] = f"{self.auth_prefix} {self.api_key}"
63
+ else:
64
+ self.default_headers[self.auth_header] = self.api_key
65
+
66
+ self._session = None
67
+ logger.debug(f"REST plugin configured for {self.base_url}")
68
+
69
+ async def connect(self):
70
+ """Initialize HTTP session."""
71
+ if self._session is not None:
72
+ return # Already connected
73
+
74
+ try:
75
+ import aiohttp
76
+
77
+ timeout = aiohttp.ClientTimeout(total=self.timeout)
78
+ self._session = aiohttp.ClientSession(
79
+ headers=self.default_headers,
80
+ timeout=timeout
81
+ )
82
+
83
+ logger.info(f"Connected to REST API: {self.base_url}")
84
+ except ImportError:
85
+ raise RuntimeError("aiohttp not installed. Run: pip install aiohttp")
86
+
87
+ async def disconnect(self):
88
+ """Close HTTP session."""
89
+ if self._session:
90
+ await self._session.close()
91
+ self._session = None
92
+ logger.info("Disconnected from REST API")
93
+
94
+ async def get(
95
+ self,
96
+ endpoint: str,
97
+ params: Optional[Dict[str, Any]] = None,
98
+ headers: Optional[Dict[str, str]] = None
99
+ ) -> Dict[str, Any]:
100
+ """
101
+ Make a GET request.
102
+
103
+ Args:
104
+ endpoint: API endpoint (without base URL)
105
+ params: Query parameters
106
+ headers: Additional headers for this request
107
+
108
+ Returns:
109
+ Response data as dictionary
110
+
111
+ Example:
112
+ data = await api.get("/users", params={"page": 1})
113
+ """
114
+ return await self._request("GET", endpoint, params=params, headers=headers)
115
+
116
+ async def post(
117
+ self,
118
+ endpoint: str,
119
+ data: Optional[Dict[str, Any]] = None,
120
+ json_data: Optional[Dict[str, Any]] = None,
121
+ headers: Optional[Dict[str, str]] = None
122
+ ) -> Dict[str, Any]:
123
+ """
124
+ Make a POST request.
125
+
126
+ Args:
127
+ endpoint: API endpoint
128
+ data: Form data to send
129
+ json_data: JSON data to send (takes precedence over data)
130
+ headers: Additional headers
131
+
132
+ Returns:
133
+ Response data as dictionary
134
+
135
+ Example:
136
+ result = await api.post("/users", json_data={"name": "John", "email": "john@example.com"})
137
+ """
138
+ return await self._request("POST", endpoint, data=data, json_data=json_data, headers=headers)
139
+
140
+ async def put(
141
+ self,
142
+ endpoint: str,
143
+ data: Optional[Dict[str, Any]] = None,
144
+ json_data: Optional[Dict[str, Any]] = None,
145
+ headers: Optional[Dict[str, str]] = None
146
+ ) -> Dict[str, Any]:
147
+ """
148
+ Make a PUT request.
149
+
150
+ Args:
151
+ endpoint: API endpoint
152
+ data: Form data to send
153
+ json_data: JSON data to send
154
+ headers: Additional headers
155
+
156
+ Returns:
157
+ Response data as dictionary
158
+ """
159
+ return await self._request("PUT", endpoint, data=data, json_data=json_data, headers=headers)
160
+
161
+ async def patch(
162
+ self,
163
+ endpoint: str,
164
+ data: Optional[Dict[str, Any]] = None,
165
+ json_data: Optional[Dict[str, Any]] = None,
166
+ headers: Optional[Dict[str, str]] = None
167
+ ) -> Dict[str, Any]:
168
+ """
169
+ Make a PATCH request.
170
+
171
+ Args:
172
+ endpoint: API endpoint
173
+ data: Form data to send
174
+ json_data: JSON data to send
175
+ headers: Additional headers
176
+
177
+ Returns:
178
+ Response data as dictionary
179
+ """
180
+ return await self._request("PATCH", endpoint, data=data, json_data=json_data, headers=headers)
181
+
182
+ async def delete(
183
+ self,
184
+ endpoint: str,
185
+ params: Optional[Dict[str, Any]] = None,
186
+ headers: Optional[Dict[str, str]] = None
187
+ ) -> Dict[str, Any]:
188
+ """
189
+ Make a DELETE request.
190
+
191
+ Args:
192
+ endpoint: API endpoint
193
+ params: Query parameters
194
+ headers: Additional headers
195
+
196
+ Returns:
197
+ Response data as dictionary
198
+ """
199
+ return await self._request("DELETE", endpoint, params=params, headers=headers)
200
+
201
+ async def _request(
202
+ self,
203
+ method: str,
204
+ endpoint: str,
205
+ params: Optional[Dict[str, Any]] = None,
206
+ data: Optional[Dict[str, Any]] = None,
207
+ json_data: Optional[Dict[str, Any]] = None,
208
+ headers: Optional[Dict[str, str]] = None
209
+ ) -> Dict[str, Any]:
210
+ """
211
+ Make HTTP request with error handling.
212
+
213
+ Args:
214
+ method: HTTP method
215
+ endpoint: API endpoint
216
+ params: Query parameters
217
+ data: Form data
218
+ json_data: JSON data
219
+ headers: Additional headers
220
+
221
+ Returns:
222
+ Response data
223
+ """
224
+ await self.connect()
225
+
226
+ # Build full URL
227
+ url = f"{self.base_url}/{endpoint.lstrip('/')}"
228
+
229
+ # Prepare request kwargs
230
+ request_kwargs = {
231
+ 'params': params,
232
+ 'headers': headers,
233
+ }
234
+
235
+ # Handle request body
236
+ if json_data is not None:
237
+ request_kwargs['json'] = json_data
238
+ elif data is not None:
239
+ request_kwargs['data'] = data
240
+
241
+ try:
242
+ async with self._session.request(method, url, **request_kwargs) as response:
243
+
244
+ # Log request
245
+ logger.debug(f"{method} {url} -> {response.status}")
246
+
247
+ # Handle different response types
248
+ content_type = response.headers.get('content-type', '')
249
+
250
+ # Check for errors
251
+ if response.status >= 400:
252
+ error_text = await response.text()
253
+ raise RuntimeError(f"HTTP {response.status}: {error_text}")
254
+
255
+ # Parse response based on content type
256
+ if 'application/json' in content_type:
257
+ return await response.json()
258
+ elif 'text/' in content_type:
259
+ text_content = await response.text()
260
+ return {'content': text_content, 'content_type': content_type}
261
+ else:
262
+ # Binary content
263
+ binary_content = await response.read()
264
+ return {
265
+ 'content': binary_content,
266
+ 'content_type': content_type,
267
+ 'size': len(binary_content)
268
+ }
269
+
270
+ except Exception as e:
271
+ logger.error(f"REST request failed: {method} {url} - {str(e)}")
272
+ raise RuntimeError(f"REST request failed: {str(e)}")
273
+
274
+ async def upload_file(
275
+ self,
276
+ endpoint: str,
277
+ file_path: str,
278
+ field_name: str = "file",
279
+ additional_data: Optional[Dict[str, Any]] = None
280
+ ) -> Dict[str, Any]:
281
+ """
282
+ Upload a file.
283
+
284
+ Args:
285
+ endpoint: API endpoint
286
+ file_path: Path to file to upload
287
+ field_name: Form field name for the file
288
+ additional_data: Additional form data
289
+
290
+ Returns:
291
+ Response data
292
+ """
293
+ import os
294
+ import aiohttp
295
+
296
+ await self.connect()
297
+
298
+ if not os.path.exists(file_path):
299
+ raise FileNotFoundError(f"File not found: {file_path}")
300
+
301
+ url = f"{self.base_url}/{endpoint.lstrip('/')}"
302
+
303
+ # Create form data
304
+ data = aiohttp.FormData()
305
+
306
+ # Add file
307
+ with open(file_path, 'rb') as f:
308
+ data.add_field(
309
+ field_name,
310
+ f,
311
+ filename=os.path.basename(file_path)
312
+ )
313
+
314
+ # Add additional form fields
315
+ if additional_data:
316
+ for key, value in additional_data.items():
317
+ data.add_field(key, str(value))
318
+
319
+ # Make request
320
+ async with self._session.post(url, data=data) as response:
321
+ if response.status >= 400:
322
+ error_text = await response.text()
323
+ raise RuntimeError(f"File upload failed ({response.status}): {error_text}")
324
+
325
+ content_type = response.headers.get('content-type', '')
326
+ if 'application/json' in content_type:
327
+ return await response.json()
328
+ else:
329
+ return {'content': await response.text()}
330
+
331
+ async def download_file(
332
+ self,
333
+ endpoint: str,
334
+ save_path: str,
335
+ params: Optional[Dict[str, Any]] = None
336
+ ) -> str:
337
+ """
338
+ Download a file.
339
+
340
+ Args:
341
+ endpoint: API endpoint
342
+ save_path: Where to save the file
343
+ params: Query parameters
344
+
345
+ Returns:
346
+ Path to downloaded file
347
+ """
348
+ import os
349
+
350
+ await self.connect()
351
+
352
+ url = f"{self.base_url}/{endpoint.lstrip('/')}"
353
+
354
+ async with self._session.get(url, params=params) as response:
355
+ if response.status >= 400:
356
+ error_text = await response.text()
357
+ raise RuntimeError(f"File download failed ({response.status}): {error_text}")
358
+
359
+ # Ensure directory exists
360
+ os.makedirs(os.path.dirname(save_path), exist_ok=True)
361
+
362
+ # Write file
363
+ with open(save_path, 'wb') as f:
364
+ async for chunk in response.content.iter_chunked(8192):
365
+ f.write(chunk)
366
+
367
+ logger.info(f"Downloaded file to {save_path}")
368
+ return save_path
369
+
370
+ def get_tools(self) -> List['AgentTool']:
371
+ """
372
+ Expose REST API operations as agent tools.
373
+
374
+ Returns:
375
+ List of AgentTool instances for REST API operations
376
+ """
377
+ from ..core.tools import AgentTool
378
+
379
+ return [
380
+ AgentTool(
381
+ name="http_get",
382
+ description="Make an HTTP GET request to the REST API endpoint. Use for retrieving data.",
383
+ parameters={
384
+ "endpoint": {
385
+ "type": "string",
386
+ "description": "API endpoint path (without base URL, e.g., /users or /data/123)",
387
+ "required": True
388
+ },
389
+ "params": {
390
+ "type": "object",
391
+ "description": "Optional query parameters as key-value pairs",
392
+ "required": False
393
+ }
394
+ },
395
+ handler=self._tool_get,
396
+ category="api",
397
+ source="plugin",
398
+ plugin_name="REST",
399
+ timeout_seconds=60
400
+ ),
401
+ AgentTool(
402
+ name="http_post",
403
+ description="Make an HTTP POST request to the REST API endpoint. Use for creating new resources.",
404
+ parameters={
405
+ "endpoint": {
406
+ "type": "string",
407
+ "description": "API endpoint path (without base URL)",
408
+ "required": True
409
+ },
410
+ "data": {
411
+ "type": "object",
412
+ "description": "JSON data to send in the request body",
413
+ "required": True
414
+ }
415
+ },
416
+ handler=self._tool_post,
417
+ category="api",
418
+ source="plugin",
419
+ plugin_name="REST",
420
+ timeout_seconds=60
421
+ ),
422
+ AgentTool(
423
+ name="http_put",
424
+ description="Make an HTTP PUT request to the REST API endpoint. Use for updating existing resources.",
425
+ parameters={
426
+ "endpoint": {
427
+ "type": "string",
428
+ "description": "API endpoint path (without base URL)",
429
+ "required": True
430
+ },
431
+ "data": {
432
+ "type": "object",
433
+ "description": "JSON data to send in the request body",
434
+ "required": True
435
+ }
436
+ },
437
+ handler=self._tool_put,
438
+ category="api",
439
+ source="plugin",
440
+ plugin_name="REST",
441
+ timeout_seconds=60
442
+ ),
443
+ AgentTool(
444
+ name="http_delete",
445
+ description="Make an HTTP DELETE request to the REST API endpoint. Use for deleting resources.",
446
+ parameters={
447
+ "endpoint": {
448
+ "type": "string",
449
+ "description": "API endpoint path (without base URL)",
450
+ "required": True
451
+ },
452
+ "params": {
453
+ "type": "object",
454
+ "description": "Optional query parameters",
455
+ "required": False
456
+ }
457
+ },
458
+ handler=self._tool_delete,
459
+ category="api",
460
+ source="plugin",
461
+ plugin_name="REST",
462
+ timeout_seconds=60
463
+ )
464
+ ]
465
+
466
+ async def _tool_get(self, args: Dict[str, Any]) -> Dict[str, Any]:
467
+ """Tool handler for http_get"""
468
+ endpoint = args.get("endpoint")
469
+ params = args.get("params")
470
+
471
+ result = await self.get(endpoint, params=params)
472
+
473
+ return {
474
+ "success": True,
475
+ "data": result,
476
+ "endpoint": endpoint
477
+ }
478
+
479
+ async def _tool_post(self, args: Dict[str, Any]) -> Dict[str, Any]:
480
+ """Tool handler for http_post"""
481
+ endpoint = args.get("endpoint")
482
+ data = args.get("data")
483
+
484
+ result = await self.post(endpoint, json_data=data)
485
+
486
+ return {
487
+ "success": True,
488
+ "data": result,
489
+ "endpoint": endpoint
490
+ }
491
+
492
+ async def _tool_put(self, args: Dict[str, Any]) -> Dict[str, Any]:
493
+ """Tool handler for http_put"""
494
+ endpoint = args.get("endpoint")
495
+ data = args.get("data")
496
+
497
+ result = await self.put(endpoint, json_data=data)
498
+
499
+ return {
500
+ "success": True,
501
+ "data": result,
502
+ "endpoint": endpoint
503
+ }
504
+
505
+ async def _tool_delete(self, args: Dict[str, Any]) -> Dict[str, Any]:
506
+ """Tool handler for http_delete"""
507
+ endpoint = args.get("endpoint")
508
+ params = args.get("params")
509
+
510
+ result = await self.delete(endpoint, params=params)
511
+
512
+ return {
513
+ "success": True,
514
+ "data": result,
515
+ "endpoint": endpoint
516
+ }
517
+
518
+ # Context manager support
519
+ async def __aenter__(self):
520
+ await self.connect()
521
+ return self
522
+
523
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
524
+ await self.disconnect()
525
+
526
+
527
+ def rest(**kwargs) -> RESTPlugin:
528
+ """Create REST plugin with simplified interface."""
529
+ return RESTPlugin(**kwargs)