webagents 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.
Files changed (94) hide show
  1. webagents/__init__.py +18 -0
  2. webagents/__main__.py +55 -0
  3. webagents/agents/__init__.py +13 -0
  4. webagents/agents/core/__init__.py +19 -0
  5. webagents/agents/core/base_agent.py +1834 -0
  6. webagents/agents/core/handoffs.py +293 -0
  7. webagents/agents/handoffs/__init__.py +0 -0
  8. webagents/agents/interfaces/__init__.py +0 -0
  9. webagents/agents/lifecycle/__init__.py +0 -0
  10. webagents/agents/skills/__init__.py +109 -0
  11. webagents/agents/skills/base.py +136 -0
  12. webagents/agents/skills/core/__init__.py +8 -0
  13. webagents/agents/skills/core/guardrails/__init__.py +0 -0
  14. webagents/agents/skills/core/llm/__init__.py +0 -0
  15. webagents/agents/skills/core/llm/anthropic/__init__.py +1 -0
  16. webagents/agents/skills/core/llm/litellm/__init__.py +10 -0
  17. webagents/agents/skills/core/llm/litellm/skill.py +538 -0
  18. webagents/agents/skills/core/llm/openai/__init__.py +1 -0
  19. webagents/agents/skills/core/llm/xai/__init__.py +1 -0
  20. webagents/agents/skills/core/mcp/README.md +375 -0
  21. webagents/agents/skills/core/mcp/__init__.py +15 -0
  22. webagents/agents/skills/core/mcp/skill.py +731 -0
  23. webagents/agents/skills/core/memory/__init__.py +11 -0
  24. webagents/agents/skills/core/memory/long_term_memory/__init__.py +10 -0
  25. webagents/agents/skills/core/memory/long_term_memory/memory_skill.py +639 -0
  26. webagents/agents/skills/core/memory/short_term_memory/__init__.py +9 -0
  27. webagents/agents/skills/core/memory/short_term_memory/skill.py +341 -0
  28. webagents/agents/skills/core/memory/vector_memory/skill.py +447 -0
  29. webagents/agents/skills/core/planning/__init__.py +9 -0
  30. webagents/agents/skills/core/planning/planner.py +343 -0
  31. webagents/agents/skills/ecosystem/__init__.py +0 -0
  32. webagents/agents/skills/ecosystem/crewai/__init__.py +1 -0
  33. webagents/agents/skills/ecosystem/database/__init__.py +1 -0
  34. webagents/agents/skills/ecosystem/filesystem/__init__.py +0 -0
  35. webagents/agents/skills/ecosystem/google/__init__.py +0 -0
  36. webagents/agents/skills/ecosystem/google/calendar/__init__.py +6 -0
  37. webagents/agents/skills/ecosystem/google/calendar/skill.py +306 -0
  38. webagents/agents/skills/ecosystem/n8n/__init__.py +0 -0
  39. webagents/agents/skills/ecosystem/openai_agents/__init__.py +0 -0
  40. webagents/agents/skills/ecosystem/web/__init__.py +0 -0
  41. webagents/agents/skills/ecosystem/zapier/__init__.py +0 -0
  42. webagents/agents/skills/robutler/__init__.py +11 -0
  43. webagents/agents/skills/robutler/auth/README.md +63 -0
  44. webagents/agents/skills/robutler/auth/__init__.py +17 -0
  45. webagents/agents/skills/robutler/auth/skill.py +354 -0
  46. webagents/agents/skills/robutler/crm/__init__.py +18 -0
  47. webagents/agents/skills/robutler/crm/skill.py +368 -0
  48. webagents/agents/skills/robutler/discovery/README.md +281 -0
  49. webagents/agents/skills/robutler/discovery/__init__.py +16 -0
  50. webagents/agents/skills/robutler/discovery/skill.py +230 -0
  51. webagents/agents/skills/robutler/kv/__init__.py +6 -0
  52. webagents/agents/skills/robutler/kv/skill.py +80 -0
  53. webagents/agents/skills/robutler/message_history/__init__.py +9 -0
  54. webagents/agents/skills/robutler/message_history/skill.py +270 -0
  55. webagents/agents/skills/robutler/messages/__init__.py +0 -0
  56. webagents/agents/skills/robutler/nli/__init__.py +13 -0
  57. webagents/agents/skills/robutler/nli/skill.py +687 -0
  58. webagents/agents/skills/robutler/notifications/__init__.py +5 -0
  59. webagents/agents/skills/robutler/notifications/skill.py +141 -0
  60. webagents/agents/skills/robutler/payments/__init__.py +41 -0
  61. webagents/agents/skills/robutler/payments/exceptions.py +255 -0
  62. webagents/agents/skills/robutler/payments/skill.py +610 -0
  63. webagents/agents/skills/robutler/storage/__init__.py +10 -0
  64. webagents/agents/skills/robutler/storage/files/__init__.py +9 -0
  65. webagents/agents/skills/robutler/storage/files/skill.py +445 -0
  66. webagents/agents/skills/robutler/storage/json/__init__.py +9 -0
  67. webagents/agents/skills/robutler/storage/json/skill.py +336 -0
  68. webagents/agents/skills/robutler/storage/kv/skill.py +88 -0
  69. webagents/agents/skills/robutler/storage.py +389 -0
  70. webagents/agents/tools/__init__.py +0 -0
  71. webagents/agents/tools/decorators.py +426 -0
  72. webagents/agents/tracing/__init__.py +0 -0
  73. webagents/agents/workflows/__init__.py +0 -0
  74. webagents/scripts/__init__.py +0 -0
  75. webagents/server/__init__.py +28 -0
  76. webagents/server/context/__init__.py +0 -0
  77. webagents/server/context/context_vars.py +121 -0
  78. webagents/server/core/__init__.py +0 -0
  79. webagents/server/core/app.py +843 -0
  80. webagents/server/core/middleware.py +69 -0
  81. webagents/server/core/models.py +98 -0
  82. webagents/server/core/monitoring.py +59 -0
  83. webagents/server/endpoints/__init__.py +0 -0
  84. webagents/server/interfaces/__init__.py +0 -0
  85. webagents/server/middleware.py +330 -0
  86. webagents/server/models.py +92 -0
  87. webagents/server/monitoring.py +659 -0
  88. webagents/utils/__init__.py +0 -0
  89. webagents/utils/logging.py +359 -0
  90. webagents-0.1.0.dist-info/METADATA +230 -0
  91. webagents-0.1.0.dist-info/RECORD +94 -0
  92. webagents-0.1.0.dist-info/WHEEL +4 -0
  93. webagents-0.1.0.dist-info/entry_points.txt +2 -0
  94. webagents-0.1.0.dist-info/licenses/LICENSE +20 -0
@@ -0,0 +1,389 @@
1
+ """
2
+ WebAgentsStorageSkill - Portal Content Integration
3
+
4
+ Provides integration with the WebAgents portal's content storage system
5
+ for persistent file storage tied to user accounts.
6
+ """
7
+
8
+ import httpx
9
+ import json
10
+ import os
11
+ from typing import Dict, List, Any, Optional, Union
12
+ from datetime import datetime
13
+
14
+ from ..base import Skill
15
+ from ...tools.decorators import tool
16
+
17
+
18
+ class WebAgentsStorageSkill(Skill):
19
+ """
20
+ WebAgents portal storage integration skill.
21
+
22
+ Features:
23
+ - Store/retrieve files in user's private content area
24
+ - Integration with portal authentication
25
+ - Support for JSON data storage
26
+ - User-scoped file management
27
+ """
28
+
29
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
30
+ super().__init__(config)
31
+ self.portal_url = config.get('portal_url', 'http://localhost:3000') if config else 'http://localhost:3000'
32
+ self.api_key = config.get('api_key') if config else None
33
+ self.agent_name = config.get('agent_name', 'default_agent') if config else 'default_agent'
34
+
35
+ async def initialize(self, agent):
36
+ """Initialize with agent reference"""
37
+ await super().initialize(agent)
38
+ self.agent = agent
39
+
40
+ # Use api_key as priority, fallback to agent's API key
41
+ self.api_key = self.api_key or getattr(agent, 'api_key', None)
42
+
43
+ # Extract agent name from agent reference if available
44
+ if hasattr(agent, 'name'):
45
+ self.agent_name = agent.name
46
+
47
+ @tool
48
+ async def store_json_data(
49
+ self,
50
+ filename: str,
51
+ data: Dict[str, Any],
52
+ description: Optional[str] = None
53
+ ) -> str:
54
+ """
55
+ Store JSON data in user's private content area.
56
+
57
+ Args:
58
+ filename: Name of the file (will add .json if not present)
59
+ data: JSON-serializable data to store
60
+ description: Optional description of the file
61
+
62
+ Returns:
63
+ JSON string with storage result
64
+ """
65
+ try:
66
+ # Ensure filename has .json extension
67
+ if not filename.endswith('.json'):
68
+ filename = f"{filename}.json"
69
+
70
+ # Convert data to JSON string
71
+ json_content = json.dumps(data, indent=2)
72
+ json_bytes = json_content.encode('utf-8')
73
+
74
+ # Prepare form data for upload
75
+ files = {
76
+ 'file': (filename, json_bytes, 'application/json')
77
+ }
78
+
79
+ form_data = {
80
+ 'visibility': 'private',
81
+ 'description': description or f"JSON data storage for {self.agent_name}",
82
+ 'tags': json.dumps(['agent_data', self.agent_name])
83
+ }
84
+
85
+ # Upload to portal
86
+ async with httpx.AsyncClient() as client:
87
+ response = await client.post(
88
+ f"{self.portal_url}/api/content",
89
+ files=files,
90
+ data=form_data,
91
+ headers={'Authorization': f'Bearer {self.api_key}'}
92
+ )
93
+
94
+ if response.status_code == 200:
95
+ result = response.json()
96
+ return json.dumps({
97
+ "success": True,
98
+ "file_id": result['file']['id'],
99
+ "filename": result['file']['fileName'],
100
+ "url": result['file']['url'],
101
+ "size": result['file']['size']
102
+ }, indent=2)
103
+ else:
104
+ return json.dumps({
105
+ "success": False,
106
+ "error": f"Upload failed: {response.status_code} - {response.text}"
107
+ })
108
+
109
+ except Exception as e:
110
+ return json.dumps({
111
+ "success": False,
112
+ "error": f"Failed to store JSON data: {str(e)}"
113
+ })
114
+
115
+ @tool
116
+ async def retrieve_json_data(self, filename: str) -> str:
117
+ """
118
+ Retrieve JSON data from user's private content area.
119
+
120
+ Args:
121
+ filename: Name of the file to retrieve
122
+
123
+ Returns:
124
+ JSON string with file content or error
125
+ """
126
+ try:
127
+ # Ensure filename has .json extension
128
+ if not filename.endswith('.json'):
129
+ filename = f"{filename}.json"
130
+
131
+ async with httpx.AsyncClient() as client:
132
+ # First, list agent's files to find the file
133
+ response = await client.get(
134
+ f"{self.portal_url}/api/content/agent",
135
+ headers={'Authorization': f'Bearer {self.api_key}'},
136
+ params={} # Use explicit access via content_access table
137
+ )
138
+
139
+ if response.status_code != 200:
140
+ return json.dumps({
141
+ "success": False,
142
+ "error": f"Failed to list content: {response.status_code}"
143
+ })
144
+
145
+ files_data = response.json()
146
+ target_file = None
147
+
148
+ # Find the target file
149
+ for file_info in files_data.get('content', []): # Agent API uses 'content' not 'files'
150
+ if file_info['fileName'] == filename or file_info['originalFileName'] == filename:
151
+ target_file = file_info
152
+ break
153
+
154
+ if not target_file:
155
+ return json.dumps({
156
+ "success": False,
157
+ "error": f"File '{filename}' not found",
158
+ "available_files": [f['fileName'] for f in files_data.get('content', [])]
159
+ })
160
+
161
+ # Retrieve file content
162
+ file_response = await client.get(
163
+ target_file['url'],
164
+ headers={'Authorization': f'Bearer {self.api_key}'}
165
+ )
166
+
167
+ if file_response.status_code == 200:
168
+ # Parse JSON content
169
+ content_data = file_response.json()
170
+ return json.dumps({
171
+ "success": True,
172
+ "filename": target_file['fileName'],
173
+ "data": content_data,
174
+ "metadata": {
175
+ "size": target_file['size'],
176
+ "uploaded_at": target_file['uploadedAt'],
177
+ "description": target_file.get('description')
178
+ }
179
+ }, indent=2)
180
+ else:
181
+ return json.dumps({
182
+ "success": False,
183
+ "error": f"Failed to retrieve file content: {file_response.status_code}"
184
+ })
185
+
186
+ except Exception as e:
187
+ return json.dumps({
188
+ "success": False,
189
+ "error": f"Failed to retrieve JSON data: {str(e)}"
190
+ })
191
+
192
+ @tool
193
+ async def update_json_data(
194
+ self,
195
+ filename: str,
196
+ data: Dict[str, Any],
197
+ description: Optional[str] = None
198
+ ) -> str:
199
+ """
200
+ Update existing JSON data in user's content area.
201
+
202
+ Args:
203
+ filename: Name of the file to update
204
+ data: New JSON data
205
+ description: Optional new description
206
+
207
+ Returns:
208
+ JSON string with update result
209
+ """
210
+ try:
211
+ # First delete the old file, then upload new one
212
+ delete_result = await self.delete_file(filename)
213
+ delete_data = json.loads(delete_result)
214
+
215
+ if not delete_data.get("success"):
216
+ # File might not exist, that's okay for update operation
217
+ pass
218
+
219
+ # Upload new version
220
+ return await self.store_json_data(filename, data, description)
221
+
222
+ except Exception as e:
223
+ return json.dumps({
224
+ "success": False,
225
+ "error": f"Failed to update JSON data: {str(e)}"
226
+ })
227
+
228
+ @tool
229
+ async def delete_file(self, filename: str) -> str:
230
+ """
231
+ Delete a file from user's content area.
232
+
233
+ Args:
234
+ filename: Name of the file to delete
235
+
236
+ Returns:
237
+ JSON string with deletion result
238
+ """
239
+ try:
240
+ # Ensure filename has .json extension
241
+ if not filename.endswith('.json'):
242
+ filename = f"{filename}.json"
243
+
244
+ async with httpx.AsyncClient() as client:
245
+ response = await client.delete(
246
+ f"{self.portal_url}/api/content",
247
+ headers={'Authorization': f'Bearer {self.api_key}'},
248
+ params={'fileName': filename}
249
+ )
250
+
251
+ if response.status_code == 200:
252
+ return json.dumps({
253
+ "success": True,
254
+ "message": f"File '{filename}' deleted successfully"
255
+ })
256
+ else:
257
+ result = response.json() if response.headers.get('content-type', '').startswith('application/json') else {"error": response.text}
258
+ return json.dumps({
259
+ "success": False,
260
+ "error": result.get('error', f"Delete failed: {response.status_code}")
261
+ })
262
+
263
+ except Exception as e:
264
+ return json.dumps({
265
+ "success": False,
266
+ "error": f"Failed to delete file: {str(e)}"
267
+ })
268
+
269
+ @tool
270
+ async def list_agent_files(self) -> str:
271
+ """
272
+ List all files associated with this agent.
273
+
274
+ Returns:
275
+ JSON string with file list
276
+ """
277
+ try:
278
+ async with httpx.AsyncClient() as client:
279
+ response = await client.get(
280
+ f"{self.portal_url}/api/content/agent",
281
+ headers={'Authorization': f'Bearer {self.api_key}'},
282
+ params={}
283
+ )
284
+
285
+ if response.status_code == 200:
286
+ files_data = response.json()
287
+
288
+ # Agent content API already filters to agent-accessible files
289
+ agent_files = []
290
+ for file_info in files_data.get('content', []): # Note: agent API uses 'content' not 'files'
291
+ agent_files.append({
292
+ "filename": file_info['fileName'],
293
+ "size": file_info['size'],
294
+ "uploaded_at": file_info['uploadedAt'],
295
+ "description": file_info.get('description'),
296
+ "tags": file_info.get('tags', [])
297
+ })
298
+
299
+ return json.dumps({
300
+ "success": True,
301
+ "agent_name": self.agent_name,
302
+ "total_files": len(agent_files),
303
+ "files": agent_files
304
+ }, indent=2)
305
+ else:
306
+ return json.dumps({
307
+ "success": False,
308
+ "error": f"Failed to list files: {response.status_code}"
309
+ })
310
+
311
+ except Exception as e:
312
+ return json.dumps({
313
+ "success": False,
314
+ "error": f"Failed to list agent files: {str(e)}"
315
+ })
316
+
317
+ @tool
318
+ async def get_storage_stats(self) -> str:
319
+ """
320
+ Get storage statistics for this agent.
321
+
322
+ Returns:
323
+ JSON string with storage statistics
324
+ """
325
+ try:
326
+ async with httpx.AsyncClient() as client:
327
+ response = await client.get(
328
+ f"{self.portal_url}/api/content/agent",
329
+ headers={'Authorization': f'Bearer {self.api_key}'},
330
+ params={}
331
+ )
332
+
333
+ if response.status_code == 200:
334
+ files_data = response.json()
335
+
336
+ # Calculate stats for this agent
337
+ agent_files = files_data.get('content', []) # Agent API already filters to agent-accessible files
338
+ total_size = 0
339
+
340
+ for file_info in agent_files:
341
+ total_size += file_info.get('size', 0)
342
+
343
+ return json.dumps({
344
+ "agent_name": self.agent_name,
345
+ "total_files": len(agent_files),
346
+ "total_size_bytes": total_size,
347
+ "total_size_mb": round(total_size / (1024 * 1024), 2),
348
+ "portal_url": self.portal_url,
349
+ "storage_location": "webagents_portal_content"
350
+ }, indent=2)
351
+ else:
352
+ return json.dumps({
353
+ "success": False,
354
+ "error": f"Failed to get storage stats: {response.status_code}"
355
+ })
356
+
357
+ except Exception as e:
358
+ return json.dumps({
359
+ "success": False,
360
+ "error": f"Failed to get storage stats: {str(e)}"
361
+ })
362
+
363
+ def get_skill_info(self) -> Dict[str, Any]:
364
+ """Get comprehensive skill information"""
365
+ return {
366
+ "name": "WebAgentsStorageSkill",
367
+ "description": "Portal content storage integration for persistent data",
368
+ "version": "1.0.0",
369
+ "capabilities": [
370
+ "Store JSON data in user's private content area",
371
+ "Retrieve data from portal storage",
372
+ "Update and delete stored files",
373
+ "Agent-scoped file management",
374
+ "Integration with portal authentication"
375
+ ],
376
+ "tools": [
377
+ "store_json_data",
378
+ "retrieve_json_data",
379
+ "update_json_data",
380
+ "delete_file",
381
+ "list_agent_files",
382
+ "get_storage_stats"
383
+ ],
384
+ "config": {
385
+ "portal_url": self.portal_url,
386
+ "agent_name": self.agent_name,
387
+ "api_key_configured": bool(self.api_key)
388
+ }
389
+ }
File without changes