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,293 @@
|
|
1
|
+
"""
|
2
|
+
Agent Handoffs - Robutler V2.0
|
3
|
+
|
4
|
+
LocalAgentHandoff implementation for same-instance agent transfers.
|
5
|
+
Provides basic agent-to-agent handoff functionality within a single server instance.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import asyncio
|
9
|
+
import time
|
10
|
+
from typing import Dict, Any, List, Optional, Union
|
11
|
+
from dataclasses import dataclass
|
12
|
+
from datetime import datetime
|
13
|
+
|
14
|
+
from ..skills.base import HandoffResult
|
15
|
+
from ...server.context.context_vars import get_context, create_context, set_context
|
16
|
+
|
17
|
+
|
18
|
+
@dataclass
|
19
|
+
class HandoffExecution:
|
20
|
+
"""Record of handoff execution"""
|
21
|
+
timestamp: datetime
|
22
|
+
source_agent: str
|
23
|
+
target_agent: str
|
24
|
+
handoff_type: str
|
25
|
+
context_data: Dict[str, Any]
|
26
|
+
result: Any
|
27
|
+
duration_ms: float
|
28
|
+
success: bool
|
29
|
+
error: Optional[str] = None
|
30
|
+
|
31
|
+
|
32
|
+
class LocalAgentHandoff:
|
33
|
+
"""
|
34
|
+
LocalAgentHandoff - Same-instance agent handoff system
|
35
|
+
|
36
|
+
Features:
|
37
|
+
- Agent-to-agent transfers within the same server instance
|
38
|
+
- Context preservation and transfer
|
39
|
+
- Handoff execution tracking and history
|
40
|
+
- Automatic context cleanup and management
|
41
|
+
"""
|
42
|
+
|
43
|
+
def __init__(self, agents: Dict[str, 'BaseAgent']):
|
44
|
+
"""
|
45
|
+
Initialize LocalAgentHandoff with available agents
|
46
|
+
|
47
|
+
Args:
|
48
|
+
agents: Dictionary of agent_name -> BaseAgent instances
|
49
|
+
"""
|
50
|
+
self.agents = agents
|
51
|
+
self.handoff_history: List[HandoffExecution] = []
|
52
|
+
self.active_handoffs: Dict[str, str] = {} # handoff_id -> target_agent
|
53
|
+
|
54
|
+
def register_agent(self, agent_name: str, agent: 'BaseAgent') -> None:
|
55
|
+
"""Register an agent for handoffs"""
|
56
|
+
self.agents[agent_name] = agent
|
57
|
+
|
58
|
+
def unregister_agent(self, agent_name: str) -> None:
|
59
|
+
"""Unregister an agent"""
|
60
|
+
if agent_name in self.agents:
|
61
|
+
del self.agents[agent_name]
|
62
|
+
|
63
|
+
def get_available_agents(self) -> List[str]:
|
64
|
+
"""Get list of available agents for handoff"""
|
65
|
+
return list(self.agents.keys())
|
66
|
+
|
67
|
+
async def execute_handoff(
|
68
|
+
self,
|
69
|
+
source_agent: str,
|
70
|
+
target_agent: str,
|
71
|
+
handoff_data: Dict[str, Any] = None,
|
72
|
+
preserve_context: bool = True
|
73
|
+
) -> HandoffResult:
|
74
|
+
"""
|
75
|
+
Execute handoff from source agent to target agent
|
76
|
+
|
77
|
+
Args:
|
78
|
+
source_agent: Name of the source agent
|
79
|
+
target_agent: Name of the target agent
|
80
|
+
handoff_data: Additional data to pass to target agent
|
81
|
+
preserve_context: Whether to preserve request context
|
82
|
+
|
83
|
+
Returns:
|
84
|
+
HandoffResult with execution details
|
85
|
+
"""
|
86
|
+
start_time = datetime.utcnow()
|
87
|
+
handoff_id = f"handoff_{int(time.time())}_{source_agent}_to_{target_agent}"
|
88
|
+
|
89
|
+
try:
|
90
|
+
# Validate agents exist
|
91
|
+
if source_agent not in self.agents:
|
92
|
+
raise ValueError(f"Source agent '{source_agent}' not found")
|
93
|
+
if target_agent not in self.agents:
|
94
|
+
raise ValueError(f"Target agent '{target_agent}' not found")
|
95
|
+
|
96
|
+
source = self.agents[source_agent]
|
97
|
+
target = self.agents[target_agent]
|
98
|
+
|
99
|
+
# Get current context
|
100
|
+
current_context = get_context()
|
101
|
+
if not current_context:
|
102
|
+
raise ValueError("No active context for handoff")
|
103
|
+
|
104
|
+
# Prepare handoff context data
|
105
|
+
context_data = {
|
106
|
+
'source_agent': source_agent,
|
107
|
+
'target_agent': target_agent,
|
108
|
+
'handoff_id': handoff_id,
|
109
|
+
'handoff_timestamp': start_time.isoformat(),
|
110
|
+
'handoff_data': handoff_data or {}
|
111
|
+
}
|
112
|
+
|
113
|
+
# Create new context for target agent if preserving context
|
114
|
+
if preserve_context:
|
115
|
+
target_context = create_context(
|
116
|
+
request_id=current_context.request_id,
|
117
|
+
peer_user_id=current_context.peer_user_id,
|
118
|
+
payment_user_id=current_context.payment_user_id,
|
119
|
+
origin_user_id=current_context.origin_user_id,
|
120
|
+
agent_owner_user_id=current_context.agent_owner_user_id,
|
121
|
+
messages=current_context.messages.copy(),
|
122
|
+
stream=current_context.stream
|
123
|
+
)
|
124
|
+
|
125
|
+
# Add handoff context data
|
126
|
+
target_context.set("handoff_context", context_data)
|
127
|
+
target_context.update_agent_context(target, target_agent)
|
128
|
+
set_context(target_context)
|
129
|
+
|
130
|
+
# Track active handoff
|
131
|
+
self.active_handoffs[handoff_id] = target_agent
|
132
|
+
|
133
|
+
# Execute source agent's handoff hooks (if any)
|
134
|
+
if hasattr(source, '_execute_hooks'):
|
135
|
+
current_context.set("handoff_data", context_data)
|
136
|
+
await source._execute_hooks("before_handoff", current_context)
|
137
|
+
|
138
|
+
# Create handoff message for target agent
|
139
|
+
handoff_message = {
|
140
|
+
"role": "system",
|
141
|
+
"content": f"Handoff from {source_agent}: {handoff_data.get('reason', 'Agent transfer requested')}"
|
142
|
+
}
|
143
|
+
|
144
|
+
# Add handoff context to messages
|
145
|
+
messages_with_handoff = current_context.messages + [handoff_message]
|
146
|
+
if handoff_data and handoff_data.get('user_message'):
|
147
|
+
messages_with_handoff.append({
|
148
|
+
"role": "user",
|
149
|
+
"content": handoff_data['user_message']
|
150
|
+
})
|
151
|
+
|
152
|
+
# Execute target agent with handoff context
|
153
|
+
# Note: This is a basic implementation - could be enhanced with specific handoff methods
|
154
|
+
response = await target.run(
|
155
|
+
messages=messages_with_handoff,
|
156
|
+
tools=handoff_data.get('tools', []),
|
157
|
+
stream=False
|
158
|
+
)
|
159
|
+
|
160
|
+
# Execute target agent's handoff hooks (if any)
|
161
|
+
if hasattr(target, '_execute_hooks'):
|
162
|
+
if preserve_context:
|
163
|
+
target_context.set("handoff_result", response)
|
164
|
+
await target._execute_hooks("after_handoff", target_context)
|
165
|
+
|
166
|
+
# Calculate duration
|
167
|
+
duration_ms = (datetime.utcnow() - start_time).total_seconds() * 1000
|
168
|
+
|
169
|
+
# Record successful handoff
|
170
|
+
execution = HandoffExecution(
|
171
|
+
timestamp=start_time,
|
172
|
+
source_agent=source_agent,
|
173
|
+
target_agent=target_agent,
|
174
|
+
handoff_type="local_agent",
|
175
|
+
context_data=context_data,
|
176
|
+
result=response,
|
177
|
+
duration_ms=duration_ms,
|
178
|
+
success=True
|
179
|
+
)
|
180
|
+
self.handoff_history.append(execution)
|
181
|
+
|
182
|
+
# Remove from active handoffs
|
183
|
+
if handoff_id in self.active_handoffs:
|
184
|
+
del self.active_handoffs[handoff_id]
|
185
|
+
|
186
|
+
# Return handoff result
|
187
|
+
return HandoffResult(
|
188
|
+
result=response,
|
189
|
+
handoff_type="local_agent",
|
190
|
+
success=True,
|
191
|
+
metadata={
|
192
|
+
'handoff_id': handoff_id,
|
193
|
+
'source_agent': source_agent,
|
194
|
+
'target_agent': target_agent,
|
195
|
+
'duration_ms': duration_ms,
|
196
|
+
'context_preserved': preserve_context
|
197
|
+
}
|
198
|
+
)
|
199
|
+
|
200
|
+
except Exception as e:
|
201
|
+
# Calculate duration
|
202
|
+
duration_ms = (datetime.utcnow() - start_time).total_seconds() * 1000
|
203
|
+
|
204
|
+
# Record failed handoff
|
205
|
+
execution = HandoffExecution(
|
206
|
+
timestamp=start_time,
|
207
|
+
source_agent=source_agent,
|
208
|
+
target_agent=target_agent,
|
209
|
+
handoff_type="local_agent",
|
210
|
+
context_data=context_data if 'context_data' in locals() else {},
|
211
|
+
result=None,
|
212
|
+
duration_ms=duration_ms,
|
213
|
+
success=False,
|
214
|
+
error=str(e)
|
215
|
+
)
|
216
|
+
self.handoff_history.append(execution)
|
217
|
+
|
218
|
+
# Clean up active handoff
|
219
|
+
if handoff_id in self.active_handoffs:
|
220
|
+
del self.active_handoffs[handoff_id]
|
221
|
+
|
222
|
+
# Return error result
|
223
|
+
return HandoffResult(
|
224
|
+
result=None,
|
225
|
+
handoff_type="local_agent",
|
226
|
+
success=False,
|
227
|
+
metadata={
|
228
|
+
'handoff_id': handoff_id if 'handoff_id' in locals() else 'unknown',
|
229
|
+
'error': str(e),
|
230
|
+
'duration_ms': duration_ms
|
231
|
+
}
|
232
|
+
)
|
233
|
+
|
234
|
+
def get_handoff_history(self, limit: Optional[int] = None) -> List[HandoffExecution]:
|
235
|
+
"""Get handoff execution history"""
|
236
|
+
history = sorted(self.handoff_history, key=lambda x: x.timestamp, reverse=True)
|
237
|
+
if limit:
|
238
|
+
history = history[:limit]
|
239
|
+
return history
|
240
|
+
|
241
|
+
def get_active_handoffs(self) -> Dict[str, str]:
|
242
|
+
"""Get currently active handoffs"""
|
243
|
+
return self.active_handoffs.copy()
|
244
|
+
|
245
|
+
def get_handoff_stats(self) -> Dict[str, Any]:
|
246
|
+
"""Get handoff statistics"""
|
247
|
+
if not self.handoff_history:
|
248
|
+
return {
|
249
|
+
'total_handoffs': 0,
|
250
|
+
'success_rate': 0.0,
|
251
|
+
'average_duration_ms': 0.0,
|
252
|
+
'most_common_source': None,
|
253
|
+
'most_common_target': None
|
254
|
+
}
|
255
|
+
|
256
|
+
successful = [h for h in self.handoff_history if h.success]
|
257
|
+
failed = [h for h in self.handoff_history if not h.success]
|
258
|
+
|
259
|
+
# Calculate statistics
|
260
|
+
total = len(self.handoff_history)
|
261
|
+
success_rate = len(successful) / total if total > 0 else 0.0
|
262
|
+
avg_duration = sum(h.duration_ms for h in successful) / len(successful) if successful else 0.0
|
263
|
+
|
264
|
+
# Most common source and target
|
265
|
+
sources = [h.source_agent for h in self.handoff_history]
|
266
|
+
targets = [h.target_agent for h in self.handoff_history]
|
267
|
+
|
268
|
+
most_common_source = max(set(sources), key=sources.count) if sources else None
|
269
|
+
most_common_target = max(set(targets), key=targets.count) if targets else None
|
270
|
+
|
271
|
+
return {
|
272
|
+
'total_handoffs': total,
|
273
|
+
'successful_handoffs': len(successful),
|
274
|
+
'failed_handoffs': len(failed),
|
275
|
+
'success_rate': success_rate,
|
276
|
+
'average_duration_ms': avg_duration,
|
277
|
+
'most_common_source': most_common_source,
|
278
|
+
'most_common_target': most_common_target
|
279
|
+
}
|
280
|
+
|
281
|
+
|
282
|
+
# Factory function for easy creation
|
283
|
+
def create_local_handoff_system(agents: Dict[str, 'BaseAgent']) -> LocalAgentHandoff:
|
284
|
+
"""
|
285
|
+
Factory function to create LocalAgentHandoff system
|
286
|
+
|
287
|
+
Args:
|
288
|
+
agents: Dictionary of agent_name -> BaseAgent instances
|
289
|
+
|
290
|
+
Returns:
|
291
|
+
Configured LocalAgentHandoff instance
|
292
|
+
"""
|
293
|
+
return LocalAgentHandoff(agents)
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,109 @@
|
|
1
|
+
"""
|
2
|
+
Robutler Agents Skills
|
3
|
+
|
4
|
+
This module provides the skill system for Robutler agents, including core skills,
|
5
|
+
platform skills, and ecosystem skills. Skills are modular components that provide
|
6
|
+
specific functionality to agents.
|
7
|
+
"""
|
8
|
+
|
9
|
+
from .base import Skill, Handoff, HandoffResult
|
10
|
+
|
11
|
+
# Import all core skills that are always available
|
12
|
+
from .core.llm.litellm import LiteLLMSkill
|
13
|
+
|
14
|
+
# Core skills - these are fundamental and always available
|
15
|
+
CORE_SKILLS = {
|
16
|
+
"litellm": LiteLLMSkill,
|
17
|
+
}
|
18
|
+
|
19
|
+
# Import Robutler platform skills
|
20
|
+
from .robutler.crm import CRMAnalyticsSkill
|
21
|
+
|
22
|
+
# Robutler platform skills - these integrate with Robutler services
|
23
|
+
ROBUTLER_SKILLS = {
|
24
|
+
"crm": CRMAnalyticsSkill,
|
25
|
+
"analytics": CRMAnalyticsSkill, # Alias for convenience
|
26
|
+
}
|
27
|
+
|
28
|
+
# Ecosystem skills - these integrate with external services
|
29
|
+
ECOSYSTEM_SKILLS = {
|
30
|
+
# Will be populated as ecosystem integrations are implemented
|
31
|
+
}
|
32
|
+
|
33
|
+
# Combined skills registry
|
34
|
+
ALL_SKILLS = {
|
35
|
+
**CORE_SKILLS,
|
36
|
+
**ROBUTLER_SKILLS,
|
37
|
+
**ECOSYSTEM_SKILLS
|
38
|
+
}
|
39
|
+
|
40
|
+
# Export main classes and registries
|
41
|
+
__all__ = [
|
42
|
+
# Base classes
|
43
|
+
'Skill',
|
44
|
+
'Handoff',
|
45
|
+
'HandoffResult',
|
46
|
+
|
47
|
+
# Skill registries
|
48
|
+
'CORE_SKILLS',
|
49
|
+
'ROBUTLER_SKILLS',
|
50
|
+
'ECOSYSTEM_SKILLS',
|
51
|
+
'ALL_SKILLS',
|
52
|
+
|
53
|
+
# Core skills
|
54
|
+
'LiteLLMSkill',
|
55
|
+
]
|
56
|
+
|
57
|
+
# Lazy loading for ecosystem skills to avoid heavy imports
|
58
|
+
def get_skill(skill_name: str):
|
59
|
+
"""Get a skill class by name, with lazy loading for ecosystem skills
|
60
|
+
|
61
|
+
Args:
|
62
|
+
skill_name: Name of the skill to load
|
63
|
+
|
64
|
+
Returns:
|
65
|
+
Skill class if found, None otherwise
|
66
|
+
"""
|
67
|
+
# Check core skills first
|
68
|
+
if skill_name in CORE_SKILLS:
|
69
|
+
return CORE_SKILLS[skill_name]
|
70
|
+
|
71
|
+
# Check platform skills
|
72
|
+
if skill_name in ROBUTLER_SKILLS:
|
73
|
+
return ROBUTLER_SKILLS[skill_name]
|
74
|
+
|
75
|
+
# Lazy load ecosystem skills
|
76
|
+
ecosystem_imports = {
|
77
|
+
"google": ("robutler.agents.skills.ecosystem.google", "GoogleSkill"),
|
78
|
+
"database": ("robutler.agents.skills.ecosystem.database", "DatabaseSkill"),
|
79
|
+
"filesystem": ("robutler.agents.skills.ecosystem.filesystem", "FilesystemSkill"),
|
80
|
+
"web": ("robutler.agents.skills.ecosystem.web", "WebSkill"),
|
81
|
+
"crewai": ("robutler.agents.skills.ecosystem.crewai", "CrewAISkill"),
|
82
|
+
"n8n": ("robutler.agents.skills.ecosystem.n8n", "N8nSkill"),
|
83
|
+
"zapier": ("robutler.agents.skills.ecosystem.zapier", "ZapierSkill"),
|
84
|
+
}
|
85
|
+
|
86
|
+
if skill_name in ecosystem_imports:
|
87
|
+
try:
|
88
|
+
module_path, class_name = ecosystem_imports[skill_name]
|
89
|
+
module = __import__(module_path, fromlist=[class_name])
|
90
|
+
skill_class = getattr(module, class_name)
|
91
|
+
ECOSYSTEM_SKILLS[skill_name] = skill_class
|
92
|
+
return skill_class
|
93
|
+
except ImportError:
|
94
|
+
# Ecosystem skill not available
|
95
|
+
return None
|
96
|
+
|
97
|
+
return None
|
98
|
+
|
99
|
+
def list_available_skills() -> dict:
|
100
|
+
"""List all available skills by category
|
101
|
+
|
102
|
+
Returns:
|
103
|
+
Dict with skill categories and their available skills
|
104
|
+
"""
|
105
|
+
return {
|
106
|
+
"core": list(CORE_SKILLS.keys()),
|
107
|
+
"webagents": list(ROBUTLER_SKILLS.keys()),
|
108
|
+
"ecosystem": list(ECOSYSTEM_SKILLS.keys())
|
109
|
+
}
|
@@ -0,0 +1,136 @@
|
|
1
|
+
"""
|
2
|
+
Base Skill Interface - Robutler V2.0
|
3
|
+
|
4
|
+
Core skill interface with unified context access, automatic registration support,
|
5
|
+
and dependency resolution.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from abc import ABC, abstractmethod
|
9
|
+
from typing import Dict, Any, List, Optional, Callable, Union, TYPE_CHECKING
|
10
|
+
from dataclasses import dataclass
|
11
|
+
|
12
|
+
if TYPE_CHECKING:
|
13
|
+
from ..core.base_agent import BaseAgent
|
14
|
+
|
15
|
+
|
16
|
+
class Skill(ABC):
|
17
|
+
"""Base interface for agent skills with unified context access
|
18
|
+
|
19
|
+
Skills have access to everything through a single Context object:
|
20
|
+
- During initialization: Basic agent reference for registration
|
21
|
+
- During request processing: Full Context via get_context()
|
22
|
+
|
23
|
+
The Context contains BOTH request data AND agent capabilities:
|
24
|
+
- Request: messages, user, streaming, usage, etc.
|
25
|
+
- Agent: skills, tools, hooks, capabilities, etc.
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(self, config: Dict[str, Any] = None, scope: str = "all", dependencies: List[str] = None):
|
29
|
+
self.config = config or {}
|
30
|
+
self.scope = scope # "all", "owner", "admin" - controls skill availability
|
31
|
+
self.dependencies = dependencies or [] # List of skill names this skill depends on
|
32
|
+
self.skill_name = self.__class__.__name__ # For tracking registration source
|
33
|
+
self.agent = None # BaseAgent reference - set during initialization
|
34
|
+
|
35
|
+
async def initialize(self, agent: 'BaseAgent') -> None:
|
36
|
+
"""
|
37
|
+
Initialize skill with agent reference.
|
38
|
+
Skills should register their tools, hooks, and handoffs here.
|
39
|
+
|
40
|
+
Args:
|
41
|
+
agent: The BaseAgent instance (for registration only)
|
42
|
+
"""
|
43
|
+
self.agent = agent
|
44
|
+
# Subclasses implement their registration logic here
|
45
|
+
|
46
|
+
def get_tools(self) -> List[Callable]:
|
47
|
+
"""Return tools that this skill provides (from agent's central registry)"""
|
48
|
+
if not self.agent:
|
49
|
+
return []
|
50
|
+
return [tool['function'] for tool in self.agent.get_all_tools()
|
51
|
+
if tool.get('source') == self.skill_name]
|
52
|
+
|
53
|
+
def register_tool(self, tool_func: Callable, scope: str = None) -> None:
|
54
|
+
"""Register a tool with the agent (central registration)
|
55
|
+
|
56
|
+
Can be called during initialization or at runtime from hooks/tools.
|
57
|
+
"""
|
58
|
+
if not self.agent:
|
59
|
+
raise RuntimeError("Cannot register tool: skill not initialized")
|
60
|
+
|
61
|
+
# Use provided scope or fall back to skill's default scope
|
62
|
+
effective_scope = scope if scope is not None else self.scope
|
63
|
+
|
64
|
+
# Allow skill to override tool scope if provided
|
65
|
+
if scope and hasattr(tool_func, '_tool_scope'):
|
66
|
+
tool_func._tool_scope = scope
|
67
|
+
|
68
|
+
# Register with agent's central registry
|
69
|
+
self.agent.register_tool(tool_func, source=self.skill_name, scope=effective_scope)
|
70
|
+
|
71
|
+
def register_hook(self, event: str, handler: Callable, priority: int = 50) -> None:
|
72
|
+
"""Register a hook for lifecycle events (central registration)
|
73
|
+
|
74
|
+
Hooks receive and return the unified Context object containing everything.
|
75
|
+
"""
|
76
|
+
if not self.agent:
|
77
|
+
raise RuntimeError("Cannot register hook: skill not initialized")
|
78
|
+
|
79
|
+
# Register with agent's central registry
|
80
|
+
self.agent.register_hook(event, handler, priority, source=self.skill_name)
|
81
|
+
|
82
|
+
def register_handoff(self, handoff_config: 'Handoff') -> None:
|
83
|
+
"""Register a handoff configuration"""
|
84
|
+
if not self.agent:
|
85
|
+
raise RuntimeError("Cannot register handoff: skill not initialized")
|
86
|
+
|
87
|
+
# Register with agent's central registry
|
88
|
+
self.agent.register_handoff(handoff_config, source=self.skill_name)
|
89
|
+
|
90
|
+
# Everything accessible via unified Context during request processing
|
91
|
+
def get_context(self) -> Optional['Context']:
|
92
|
+
"""
|
93
|
+
Get unified Context containing EVERYTHING:
|
94
|
+
|
95
|
+
Request data:
|
96
|
+
- context.messages, context.user, context.stream
|
97
|
+
- context.track_usage(), context.get()/set()
|
98
|
+
|
99
|
+
Agent capabilities:
|
100
|
+
- context.agent_skills, context.agent_tools, context.agent_handoffs
|
101
|
+
- context.agent (BaseAgent instance)
|
102
|
+
"""
|
103
|
+
from ...server.context.context_vars import get_context
|
104
|
+
return get_context()
|
105
|
+
|
106
|
+
def get_dependencies(self) -> List[str]:
|
107
|
+
"""Return list of skill dependencies"""
|
108
|
+
return self.dependencies.copy()
|
109
|
+
|
110
|
+
|
111
|
+
# Base dataclasses for handoffs and other components
|
112
|
+
@dataclass
|
113
|
+
class Handoff:
|
114
|
+
"""Configuration for handoff operations"""
|
115
|
+
target: str
|
116
|
+
handoff_type: str
|
117
|
+
description: str = ""
|
118
|
+
scope: Union[str, List[str]] = "all"
|
119
|
+
metadata: Dict[str, Any] = None
|
120
|
+
|
121
|
+
def __post_init__(self):
|
122
|
+
if self.metadata is None:
|
123
|
+
self.metadata = {}
|
124
|
+
|
125
|
+
|
126
|
+
@dataclass
|
127
|
+
class HandoffResult:
|
128
|
+
"""Result from handoff execution"""
|
129
|
+
result: Any
|
130
|
+
handoff_type: str
|
131
|
+
metadata: Dict[str, Any] = None
|
132
|
+
success: bool = True
|
133
|
+
|
134
|
+
def __post_init__(self):
|
135
|
+
if self.metadata is None:
|
136
|
+
self.metadata = {}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
"""
|
2
|
+
Core Skills Package - Essential agent capabilities
|
3
|
+
"""
|
4
|
+
|
5
|
+
from .planning import PlannerSkill
|
6
|
+
from .memory import ShortTermMemorySkill, LongTermMemorySkill, MemoryItem
|
7
|
+
|
8
|
+
__all__ = ['PlannerSkill', 'ShortTermMemorySkill', 'LongTermMemorySkill', 'MemoryItem']
|
File without changes
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
# TODO: Implement dedicated Anthropic skill, currently using litellm
|
@@ -0,0 +1,10 @@
|
|
1
|
+
"""
|
2
|
+
LiteLLM Skill Package - Robutler V2.0
|
3
|
+
|
4
|
+
Cross-provider LLM routing with support for OpenAI, Anthropic, XAI/Grok, and more.
|
5
|
+
Provides unified interface for multiple LLM providers with automatic fallbacks.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from .skill import LiteLLMSkill
|
9
|
+
|
10
|
+
__all__ = ["LiteLLMSkill"]
|