ambivo-agents 1.0.1__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.
@@ -0,0 +1,370 @@
1
+ # ambivo_agents/services/factory.py
2
+ """
3
+ Agent Factory for creating different types of agents - UPDATED WITH YOUTUBE SUPPORT
4
+
5
+ Author: Hemant Gosain 'Sunny'
6
+ Company: Ambivo
7
+ Email: sgosain@ambivo.com
8
+ License: MIT
9
+ """
10
+
11
+ import logging
12
+ from typing import Dict, Any, Optional
13
+
14
+ from ..core.base import AgentRole, BaseAgent, AgentMessage, MessageType
15
+ from ..core.memory import MemoryManagerInterface
16
+ from ..core.llm import LLMServiceInterface
17
+ from ..config.loader import (
18
+ load_config,
19
+ get_config_section,
20
+ validate_agent_capabilities,
21
+ get_available_agent_types,
22
+ get_enabled_capabilities
23
+ )
24
+
25
+ from ..agents.assistant import AssistantAgent
26
+ from ..agents.code_executor import CodeExecutorAgent
27
+
28
+
29
+ class ProxyAgent(BaseAgent):
30
+ """Agent that routes messages to appropriate specialized agents - UPDATED WITH YOUTUBE SUPPORT"""
31
+
32
+ def __init__(self, agent_id: str, memory_manager, llm_service=None, **kwargs):
33
+ super().__init__(
34
+ agent_id=agent_id,
35
+ role=AgentRole.PROXY,
36
+ memory_manager=memory_manager,
37
+ llm_service=llm_service,
38
+ name="Proxy Agent",
39
+ description="Agent that routes messages to appropriate specialized agents",
40
+ **kwargs
41
+ )
42
+ self.agent_registry: Dict[str, BaseAgent] = {}
43
+
44
+ def register_agent(self, agent: BaseAgent) -> bool:
45
+ """Register an agent for routing"""
46
+ if agent and hasattr(agent, 'agent_id'):
47
+ self.agent_registry[agent.agent_id] = agent
48
+ logging.info(f"Registered agent {agent.agent_id} ({agent.__class__.__name__}) with proxy")
49
+ return True
50
+ else:
51
+ logging.error(f"Failed to register agent: invalid agent object")
52
+ return False
53
+
54
+ def get_registered_agents(self) -> Dict[str, BaseAgent]:
55
+ """Get all registered agents"""
56
+ return self.agent_registry.copy()
57
+
58
+ def unregister_agent(self, agent_id: str) -> bool:
59
+ """Unregister an agent"""
60
+ if agent_id in self.agent_registry:
61
+ del self.agent_registry[agent_id]
62
+ logging.info(f"Unregistered agent {agent_id} from proxy")
63
+ return True
64
+ return False
65
+
66
+ async def process_message(self, message, context=None):
67
+ """Route messages to appropriate agents based on content analysis - UPDATED WITH YOUTUBE"""
68
+ self.memory.store_message(message)
69
+
70
+ try:
71
+ content = message.content.lower()
72
+
73
+ # Improved routing logic with YouTube support
74
+ target_agent = None
75
+
76
+ # YouTube download routing (HIGHEST PRIORITY for YouTube URLs)
77
+ if any(keyword in content for keyword in [
78
+ 'youtube.com', 'youtu.be', 'download youtube', 'youtube download',
79
+ 'download video', 'download audio', 'youtube mp3', 'youtube mp4',
80
+ 'download from youtube'
81
+ ]) or 'youtube' in content:
82
+ # Look for YouTubeDownloadAgent by class name or agent key
83
+ for agent_key, agent in self.agent_registry.items():
84
+ if ('YouTubeDownloadAgent' in agent.__class__.__name__ or
85
+ 'youtube_download' in agent_key or
86
+ 'youtube' in agent_key):
87
+ target_agent = agent
88
+ break
89
+
90
+ # Web search routing (HIGH PRIORITY for web search requests)
91
+ elif any(keyword in content for keyword in [
92
+ 'search web', 'web search', 'find online', 'search_web',
93
+ 'brave search', 'aves search', 'search the web', 'search for'
94
+ ]):
95
+ # Look for WebSearchAgent by class name or agent key
96
+ for agent_key, agent in self.agent_registry.items():
97
+ if ('WebSearchAgent' in agent.__class__.__name__ or
98
+ 'web_search' in agent_key or
99
+ 'websearch' in agent_key):
100
+ target_agent = agent
101
+ break
102
+
103
+ # Knowledge Base routing (for KB operations)
104
+ elif any(keyword in content for keyword in [
105
+ 'ingest_document', 'ingest_text', 'ingest', 'knowledge base', 'kb',
106
+ 'query_knowledge_base', 'query', 'search documents', 'vector database',
107
+ 'qdrant', 'semantic search', 'document', 'pdf', 'docx'
108
+ ]):
109
+ # Look for KnowledgeBaseAgent
110
+ for agent_key, agent in self.agent_registry.items():
111
+ if ('KnowledgeBaseAgent' in agent.__class__.__name__ or
112
+ 'knowledge_base' in agent_key or
113
+ 'knowledge' in agent_key):
114
+ target_agent = agent
115
+ break
116
+
117
+ # Media processing routing (for FFmpeg operations)
118
+ elif any(keyword in content for keyword in [
119
+ 'extract_audio', 'convert_video', 'media', 'ffmpeg', 'audio', 'video',
120
+ 'mp4', 'mp3', 'wav', 'extract audio', 'resize video', 'trim video',
121
+ 'video format', 'audio format'
122
+ ]):
123
+ # Look for MediaEditorAgent
124
+ for agent_key, agent in self.agent_registry.items():
125
+ if ('MediaEditorAgent' in agent.__class__.__name__ or
126
+ 'media_editor' in agent_key or
127
+ 'mediaeditor' in agent_key):
128
+ target_agent = agent
129
+ break
130
+
131
+ # Code execution routing
132
+ elif any(keyword in content for keyword in [
133
+ 'execute', 'run code', 'python', 'bash', '```python', '```bash',
134
+ 'script', 'code execution'
135
+ ]):
136
+ # Look for CodeExecutorAgent
137
+ for agent in self.agent_registry.values():
138
+ if agent.role == AgentRole.CODE_EXECUTOR:
139
+ target_agent = agent
140
+ break
141
+
142
+ # Web scraping routing (only for explicit scraping requests)
143
+ elif any(keyword in content for keyword in [
144
+ 'scrape', 'web scraping', 'crawl', 'extract from url', 'scrape_url',
145
+ 'apartments.com', 'scrape website'
146
+ ]):
147
+ # Look for WebScraperAgent
148
+ for agent_key, agent in self.agent_registry.items():
149
+ if ('WebScraperAgent' in agent.__class__.__name__ or
150
+ 'web_scraper' in agent_key or
151
+ 'webscraper' in agent_key):
152
+ target_agent = agent
153
+ break
154
+
155
+ # Default to Assistant Agent if no specific routing found
156
+ if not target_agent:
157
+ for agent in self.agent_registry.values():
158
+ if agent.role == AgentRole.ASSISTANT:
159
+ target_agent = agent
160
+ break
161
+
162
+ if target_agent:
163
+ logging.info(f"Routing message to {target_agent.__class__.__name__} ({target_agent.agent_id})")
164
+ logging.info(f"Routing reason: Content analysis for '{content[:50]}...'")
165
+
166
+ response = await target_agent.process_message(message, context)
167
+
168
+ response.metadata.update({
169
+ 'routed_by': self.agent_id,
170
+ 'routed_to': target_agent.agent_id,
171
+ 'routed_to_class': target_agent.__class__.__name__,
172
+ 'routing_reason': f"Content matched {target_agent.__class__.__name__} patterns"
173
+ })
174
+
175
+ return response
176
+ else:
177
+ available_agents = [f"{agent.__class__.__name__} ({agent_id})" for agent_id, agent in
178
+ self.agent_registry.items()]
179
+ error_response = self.create_response(
180
+ content=f"I couldn't find an appropriate agent to handle your request. Available agents: {available_agents}",
181
+ recipient_id=message.sender_id,
182
+ message_type=MessageType.ERROR,
183
+ session_id=message.session_id,
184
+ conversation_id=message.conversation_id
185
+ )
186
+ return error_response
187
+
188
+ except Exception as e:
189
+ logging.error(f"Proxy routing error: {e}")
190
+ error_response = self.create_response(
191
+ content=f"Routing error: {str(e)}",
192
+ recipient_id=message.sender_id,
193
+ message_type=MessageType.ERROR,
194
+ session_id=message.session_id,
195
+ conversation_id=message.conversation_id
196
+ )
197
+ return error_response
198
+
199
+
200
+ class AgentFactory:
201
+ """Factory for creating different types of agents - UPDATED WITH YOUTUBE SUPPORT"""
202
+
203
+ @staticmethod
204
+ def create_agent(role: AgentRole,
205
+ agent_id: str,
206
+ memory_manager: MemoryManagerInterface,
207
+ llm_service: LLMServiceInterface = None,
208
+ config: Dict[str, Any] = None,
209
+ **kwargs) -> BaseAgent:
210
+ """Create an agent of the specified role"""
211
+
212
+ if role == AgentRole.ASSISTANT:
213
+ return AssistantAgent(
214
+ agent_id=agent_id,
215
+ memory_manager=memory_manager,
216
+ llm_service=llm_service,
217
+ **kwargs
218
+ )
219
+ elif role == AgentRole.CODE_EXECUTOR:
220
+ return CodeExecutorAgent(
221
+ agent_id=agent_id,
222
+ memory_manager=memory_manager,
223
+ llm_service=llm_service,
224
+ **kwargs
225
+ )
226
+ elif role == AgentRole.RESEARCHER:
227
+ # Import specialized agents dynamically based on configuration
228
+ capabilities = validate_agent_capabilities(config)
229
+
230
+ # PRIORITY ORDER: Knowledge Base > Web Search > YouTube > Web Scraper > Media Editor
231
+ if capabilities.get('knowledge_base', False):
232
+ try:
233
+ from ..agents.knowledge_base import KnowledgeBaseAgent
234
+ logging.info("Creating KnowledgeBaseAgent for RESEARCHER role")
235
+ return KnowledgeBaseAgent(
236
+ agent_id=agent_id,
237
+ memory_manager=memory_manager,
238
+ llm_service=llm_service,
239
+ **kwargs
240
+ )
241
+ except Exception as e:
242
+ logging.error(f"Failed to create KnowledgeBaseAgent: {e}")
243
+
244
+ elif capabilities.get('web_search', False):
245
+ try:
246
+ from ..agents.web_search import WebSearchAgent
247
+ logging.info("Creating WebSearchAgent for RESEARCHER role")
248
+ return WebSearchAgent(
249
+ agent_id=agent_id,
250
+ memory_manager=memory_manager,
251
+ llm_service=llm_service,
252
+ **kwargs
253
+ )
254
+ except Exception as e:
255
+ logging.error(f"Failed to create WebSearchAgent: {e}")
256
+
257
+ elif capabilities.get('youtube_download', False):
258
+ try:
259
+ from ..agents.youtube_download import YouTubeDownloadAgent
260
+ logging.info("Creating YouTubeDownloadAgent for RESEARCHER role")
261
+ return YouTubeDownloadAgent(
262
+ agent_id=agent_id,
263
+ memory_manager=memory_manager,
264
+ llm_service=llm_service,
265
+ **kwargs
266
+ )
267
+ except Exception as e:
268
+ logging.error(f"Failed to create YouTubeDownloadAgent: {e}")
269
+
270
+ elif capabilities.get('web_scraping', False):
271
+ try:
272
+ from ..agents.web_scraper import WebScraperAgent
273
+ logging.info("Creating WebScraperAgent for RESEARCHER role")
274
+ return WebScraperAgent(
275
+ agent_id=agent_id,
276
+ memory_manager=memory_manager,
277
+ llm_service=llm_service,
278
+ **kwargs
279
+ )
280
+ except Exception as e:
281
+ logging.error(f"Failed to create WebScraperAgent: {e}")
282
+
283
+ elif capabilities.get('media_editor', False):
284
+ try:
285
+ from ..agents.media_editor import MediaEditorAgent
286
+ logging.info("Creating MediaEditorAgent for RESEARCHER role")
287
+ return MediaEditorAgent(
288
+ agent_id=agent_id,
289
+ memory_manager=memory_manager,
290
+ llm_service=llm_service,
291
+ **kwargs
292
+ )
293
+ except Exception as e:
294
+ logging.error(f"Failed to create MediaEditorAgent: {e}")
295
+
296
+ # Fallback to assistant if no specialized researcher is available
297
+ logging.warning("No specialized researcher agents available, falling back to AssistantAgent")
298
+ return AssistantAgent(
299
+ agent_id=agent_id,
300
+ memory_manager=memory_manager,
301
+ llm_service=llm_service,
302
+ **kwargs
303
+ )
304
+
305
+ elif role == AgentRole.PROXY:
306
+ return ProxyAgent(
307
+ agent_id=agent_id,
308
+ memory_manager=memory_manager,
309
+ llm_service=llm_service,
310
+ **kwargs
311
+ )
312
+ else:
313
+ raise ValueError(f"Unsupported agent role: {role}")
314
+
315
+ @staticmethod
316
+ def create_specialized_agent(agent_type: str,
317
+ agent_id: str,
318
+ memory_manager: MemoryManagerInterface,
319
+ llm_service: LLMServiceInterface = None,
320
+ config: Dict[str, Any] = None,
321
+ **kwargs) -> BaseAgent:
322
+ """Create specialized agents by type name - UPDATED WITH YOUTUBE SUPPORT"""
323
+
324
+ capabilities = validate_agent_capabilities(config)
325
+
326
+ if agent_type == "knowledge_base":
327
+ if not capabilities.get('knowledge_base', False):
328
+ raise ValueError("Knowledge base not enabled in agent_config.yaml")
329
+ from ..agents.knowledge_base import KnowledgeBaseAgent
330
+ return KnowledgeBaseAgent(agent_id, memory_manager, llm_service, **kwargs)
331
+
332
+ elif agent_type == "web_scraper":
333
+ if not capabilities.get('web_scraping', False):
334
+ raise ValueError("Web scraping not enabled in agent_config.yaml")
335
+ from ..agents.web_scraper import WebScraperAgent
336
+ return WebScraperAgent(agent_id, memory_manager, llm_service, **kwargs)
337
+
338
+ elif agent_type == "web_search":
339
+ if not capabilities.get('web_search', False):
340
+ raise ValueError("Web search not enabled in agent_config.yaml")
341
+ from ..agents.web_search import WebSearchAgent
342
+ return WebSearchAgent(agent_id, memory_manager, llm_service, **kwargs)
343
+
344
+ elif agent_type == "media_editor":
345
+ if not capabilities.get('media_editor', False):
346
+ raise ValueError("Media editor not enabled in agent_config.yaml")
347
+ from ..agents.media_editor import MediaEditorAgent
348
+ return MediaEditorAgent(agent_id, memory_manager, llm_service, **kwargs)
349
+
350
+ elif agent_type == "youtube_download":
351
+ if not capabilities.get('youtube_download', False):
352
+ raise ValueError("YouTube download not enabled in agent_config.yaml")
353
+ from ..agents.youtube_download import YouTubeDownloadAgent
354
+ return YouTubeDownloadAgent(agent_id, memory_manager, llm_service, **kwargs)
355
+ elif agent_type == "moderator":
356
+
357
+ from ..agents.moderator import ModeratorAgent
358
+ return ModeratorAgent(agent_id, memory_manager, llm_service, **kwargs)
359
+
360
+ else:
361
+ raise ValueError(f"Unknown or unavailable agent type: {agent_type}")
362
+
363
+ @staticmethod
364
+ def get_available_agent_types(config: Dict[str, Any] = None) -> Dict[str, bool]:
365
+ """
366
+ Get available agent types based on configuration.
367
+
368
+ This now uses the centralized capability checking from loader.py
369
+ """
370
+ return get_available_agent_types(config)