webagents 0.1.12__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.
- webagents/__init__.py +18 -0
- webagents/agents/__init__.py +13 -0
- webagents/agents/core/__init__.py +19 -0
- webagents/agents/core/base_agent.py +1834 -0
- webagents/agents/core/handoffs.py +293 -0
- webagents/agents/handoffs/__init__.py +0 -0
- webagents/agents/interfaces/__init__.py +0 -0
- webagents/agents/lifecycle/__init__.py +0 -0
- webagents/agents/skills/__init__.py +109 -0
- webagents/agents/skills/base.py +136 -0
- webagents/agents/skills/core/__init__.py +8 -0
- webagents/agents/skills/core/guardrails/__init__.py +0 -0
- webagents/agents/skills/core/llm/__init__.py +0 -0
- webagents/agents/skills/core/llm/anthropic/__init__.py +1 -0
- webagents/agents/skills/core/llm/litellm/__init__.py +10 -0
- webagents/agents/skills/core/llm/litellm/skill.py +538 -0
- webagents/agents/skills/core/llm/openai/__init__.py +1 -0
- webagents/agents/skills/core/llm/xai/__init__.py +1 -0
- webagents/agents/skills/core/mcp/README.md +375 -0
- webagents/agents/skills/core/mcp/__init__.py +15 -0
- webagents/agents/skills/core/mcp/skill.py +731 -0
- webagents/agents/skills/core/memory/__init__.py +11 -0
- webagents/agents/skills/core/memory/long_term_memory/__init__.py +10 -0
- webagents/agents/skills/core/memory/long_term_memory/memory_skill.py +639 -0
- webagents/agents/skills/core/memory/short_term_memory/__init__.py +9 -0
- webagents/agents/skills/core/memory/short_term_memory/skill.py +341 -0
- webagents/agents/skills/core/memory/vector_memory/skill.py +447 -0
- webagents/agents/skills/core/planning/__init__.py +9 -0
- webagents/agents/skills/core/planning/planner.py +343 -0
- webagents/agents/skills/ecosystem/__init__.py +0 -0
- webagents/agents/skills/ecosystem/crewai/__init__.py +1 -0
- webagents/agents/skills/ecosystem/database/__init__.py +1 -0
- webagents/agents/skills/ecosystem/filesystem/__init__.py +0 -0
- webagents/agents/skills/ecosystem/google/__init__.py +0 -0
- webagents/agents/skills/ecosystem/google/calendar/__init__.py +6 -0
- webagents/agents/skills/ecosystem/google/calendar/skill.py +306 -0
- webagents/agents/skills/ecosystem/n8n/__init__.py +0 -0
- webagents/agents/skills/ecosystem/openai_agents/__init__.py +0 -0
- webagents/agents/skills/ecosystem/web/__init__.py +0 -0
- webagents/agents/skills/ecosystem/zapier/__init__.py +0 -0
- webagents/agents/skills/robutler/__init__.py +11 -0
- webagents/agents/skills/robutler/auth/README.md +63 -0
- webagents/agents/skills/robutler/auth/__init__.py +17 -0
- webagents/agents/skills/robutler/auth/skill.py +354 -0
- webagents/agents/skills/robutler/crm/__init__.py +18 -0
- webagents/agents/skills/robutler/crm/skill.py +368 -0
- webagents/agents/skills/robutler/discovery/README.md +281 -0
- webagents/agents/skills/robutler/discovery/__init__.py +16 -0
- webagents/agents/skills/robutler/discovery/skill.py +230 -0
- webagents/agents/skills/robutler/kv/__init__.py +6 -0
- webagents/agents/skills/robutler/kv/skill.py +80 -0
- webagents/agents/skills/robutler/message_history/__init__.py +9 -0
- webagents/agents/skills/robutler/message_history/skill.py +270 -0
- webagents/agents/skills/robutler/messages/__init__.py +0 -0
- webagents/agents/skills/robutler/nli/__init__.py +13 -0
- webagents/agents/skills/robutler/nli/skill.py +687 -0
- webagents/agents/skills/robutler/notifications/__init__.py +5 -0
- webagents/agents/skills/robutler/notifications/skill.py +141 -0
- webagents/agents/skills/robutler/payments/__init__.py +41 -0
- webagents/agents/skills/robutler/payments/exceptions.py +255 -0
- webagents/agents/skills/robutler/payments/skill.py +610 -0
- webagents/agents/skills/robutler/storage/__init__.py +10 -0
- webagents/agents/skills/robutler/storage/files/__init__.py +9 -0
- webagents/agents/skills/robutler/storage/files/skill.py +445 -0
- webagents/agents/skills/robutler/storage/json/__init__.py +9 -0
- webagents/agents/skills/robutler/storage/json/skill.py +336 -0
- webagents/agents/skills/robutler/storage/kv/skill.py +88 -0
- webagents/agents/skills/robutler/storage.py +389 -0
- webagents/agents/tools/__init__.py +0 -0
- webagents/agents/tools/decorators.py +426 -0
- webagents/agents/tracing/__init__.py +0 -0
- webagents/agents/workflows/__init__.py +0 -0
- webagents/api/__init__.py +17 -0
- webagents/api/client.py +1207 -0
- webagents/api/types.py +253 -0
- webagents/scripts/__init__.py +0 -0
- webagents/server/__init__.py +28 -0
- webagents/server/context/__init__.py +0 -0
- webagents/server/context/context_vars.py +121 -0
- webagents/server/core/__init__.py +0 -0
- webagents/server/core/app.py +843 -0
- webagents/server/core/middleware.py +69 -0
- webagents/server/core/models.py +98 -0
- webagents/server/core/monitoring.py +59 -0
- webagents/server/endpoints/__init__.py +0 -0
- webagents/server/interfaces/__init__.py +0 -0
- webagents/server/middleware.py +330 -0
- webagents/server/models.py +92 -0
- webagents/server/monitoring.py +659 -0
- webagents/utils/__init__.py +0 -0
- webagents/utils/logging.py +359 -0
- webagents-0.1.12.dist-info/METADATA +99 -0
- webagents-0.1.12.dist-info/RECORD +96 -0
- webagents-0.1.12.dist-info/WHEEL +4 -0
- webagents-0.1.12.dist-info/entry_points.txt +2 -0
- webagents-0.1.12.dist-info/licenses/LICENSE +1 -0
@@ -0,0 +1,389 @@
|
|
1
|
+
"""
|
2
|
+
RobutlerStorageSkill - Portal Content Integration
|
3
|
+
|
4
|
+
Provides integration with the Robutler 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 RobutlerStorageSkill(Skill):
|
19
|
+
"""
|
20
|
+
Robutler 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": "robutler_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": "RobutlerStorageSkill",
|
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
|