agentensemble 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.
@@ -0,0 +1,76 @@
1
+ """
2
+ AgentEnsemble - Orchestrate AI agents in perfect harmony
3
+
4
+ A comprehensive framework for building, testing, and orchestrating multi-agent AI systems.
5
+ """
6
+
7
+ __version__ = "0.1.0"
8
+ __author__ = "Irfan Ali"
9
+
10
+ # Core imports
11
+ from agentensemble.agents import (
12
+ ReActAgent,
13
+ StateGraphAgent,
14
+ RAGAgent,
15
+ HybridAgent,
16
+ )
17
+
18
+ # Structured output agent (optional, requires langchain>=1.1)
19
+ try:
20
+ from agentensemble.agents import StructuredAgent
21
+ STRUCTURED_AVAILABLE = True
22
+ except ImportError:
23
+ STRUCTURED_AVAILABLE = False
24
+ StructuredAgent = None
25
+
26
+ from agentensemble.orchestration import (
27
+ Ensemble,
28
+ SupervisorOrchestrator,
29
+ SwarmOrchestrator,
30
+ PipelineOrchestrator,
31
+ )
32
+
33
+ from agentensemble.tools import (
34
+ ToolRegistry,
35
+ SearchTool,
36
+ ScraperTool,
37
+ RAGTool,
38
+ ValidationTool,
39
+ )
40
+
41
+ from agentensemble.testing import (
42
+ AgentComparison,
43
+ Benchmark,
44
+ Metrics,
45
+ )
46
+
47
+ __all__ = [
48
+ # Version
49
+ "__version__",
50
+ "__author__",
51
+ # Agents
52
+ "ReActAgent",
53
+ "StateGraphAgent",
54
+ "RAGAgent",
55
+ "HybridAgent",
56
+ # Orchestration
57
+ "Ensemble",
58
+ "SupervisorOrchestrator",
59
+ "SwarmOrchestrator",
60
+ "PipelineOrchestrator",
61
+ # Tools
62
+ "ToolRegistry",
63
+ "SearchTool",
64
+ "ScraperTool",
65
+ "RAGTool",
66
+ "ValidationTool",
67
+ # Testing
68
+ "AgentComparison",
69
+ "Benchmark",
70
+ "Metrics",
71
+ ]
72
+
73
+ # Add StructuredAgent if available
74
+ if STRUCTURED_AVAILABLE:
75
+ __all__.append("StructuredAgent")
76
+
@@ -0,0 +1,32 @@
1
+ """
2
+ Reference Agent Implementations
3
+
4
+ Provides production-ready agent architectures:
5
+ - ReActAgent: Simple reasoning + acting pattern
6
+ - StateGraphAgent: Custom nodes with intelligent routing
7
+ - RAGAgent: RAG-enhanced with fallback strategies
8
+ - HybridAgent: Advanced iterative refinement
9
+ - StructuredAgent: Structured output support (langchain>=1.1 or with_structured_output)
10
+ """
11
+
12
+ from agentensemble.agents.react_agent import ReActAgent
13
+ from agentensemble.agents.stategraph_agent import StateGraphAgent
14
+ from agentensemble.agents.rag_agent import RAGAgent
15
+ from agentensemble.agents.hybrid_agent import HybridAgent
16
+
17
+ try:
18
+ from agentensemble.agents.structured_agent import StructuredAgent
19
+ STRUCTURED_AVAILABLE = True
20
+ except ImportError:
21
+ STRUCTURED_AVAILABLE = False
22
+ StructuredAgent = None
23
+
24
+ __all__ = [
25
+ "ReActAgent",
26
+ "StateGraphAgent",
27
+ "RAGAgent",
28
+ "HybridAgent",
29
+ ]
30
+
31
+ if STRUCTURED_AVAILABLE:
32
+ __all__.append("StructuredAgent")
@@ -0,0 +1,84 @@
1
+ """
2
+ Base Agent Class
3
+
4
+ Defines the interface and common functionality for all agents.
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+ from typing import Any, Dict, List, Optional
9
+ from pydantic import BaseModel, Field
10
+
11
+
12
+ class AgentState(BaseModel):
13
+ """Base state model for agents"""
14
+
15
+ query: str
16
+ context: Dict[str, Any] = Field(default_factory=dict)
17
+ iteration_count: int = 0
18
+ tool_calls: List[Dict[str, Any]] = Field(default_factory=list)
19
+ result: Optional[str] = None
20
+ metadata: Dict[str, Any] = Field(default_factory=dict)
21
+
22
+
23
+ class BaseAgent(ABC):
24
+ """
25
+ Base class for all agents in AgentEnsemble.
26
+
27
+ All agent implementations should inherit from this class.
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ name: str,
33
+ tools: Optional[List[Any]] = None,
34
+ max_iterations: int = 10,
35
+ **kwargs
36
+ ):
37
+ """
38
+ Initialize the agent.
39
+
40
+ Args:
41
+ name: Name of the agent
42
+ tools: List of tools available to the agent
43
+ max_iterations: Maximum number of iterations
44
+ **kwargs: Additional configuration
45
+ """
46
+ self.name = name
47
+ self.tools = tools or []
48
+ self.max_iterations = max_iterations
49
+ self.config = kwargs
50
+
51
+ @abstractmethod
52
+ def run(self, query: str, **kwargs) -> Dict[str, Any]:
53
+ """
54
+ Execute the agent with a given query.
55
+
56
+ Args:
57
+ query: The input query
58
+ **kwargs: Additional parameters
59
+
60
+ Returns:
61
+ Dictionary containing the result and metadata
62
+ """
63
+ pass
64
+
65
+ def _validate_state(self, state: AgentState) -> bool:
66
+ """Validate agent state"""
67
+ return state.iteration_count < self.max_iterations
68
+
69
+ def _update_state(
70
+ self,
71
+ state: AgentState,
72
+ result: Optional[str] = None,
73
+ tool_calls: Optional[List[Dict]] = None,
74
+ **metadata
75
+ ) -> AgentState:
76
+ """Update agent state"""
77
+ if result:
78
+ state.result = result
79
+ if tool_calls:
80
+ state.tool_calls.extend(tool_calls)
81
+ state.iteration_count += 1
82
+ state.metadata.update(metadata)
83
+ return state
84
+
@@ -0,0 +1,147 @@
1
+ """
2
+ Hybrid Agent Implementation
3
+
4
+ Advanced agent with iterative refinement and early stopping.
5
+ """
6
+
7
+ from typing import Any, Dict, Optional
8
+ from agentensemble.agents.base import BaseAgent, AgentState
9
+
10
+
11
+ class HybridAgent(BaseAgent):
12
+ """
13
+ Hybrid Agent with intelligent routing and iterative refinement.
14
+
15
+ Combines multiple strategies: analysis → search → RAG → validation
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ name: str = "hybrid_agent",
21
+ tools: Optional[list] = None,
22
+ max_iterations: int = 15,
23
+ early_stopping: bool = True,
24
+ **kwargs
25
+ ):
26
+ """
27
+ Initialize Hybrid agent.
28
+
29
+ Args:
30
+ name: Agent name
31
+ tools: Available tools
32
+ max_iterations: Maximum iterations
33
+ early_stopping: Enable early stopping when answer found
34
+ **kwargs: Additional configuration
35
+ """
36
+ super().__init__(name, tools, max_iterations, **kwargs)
37
+ self.early_stopping = early_stopping
38
+
39
+ def run(self, query: str, **kwargs) -> Dict[str, Any]:
40
+ """
41
+ Execute Hybrid agent with intelligent routing.
42
+
43
+ Args:
44
+ query: Input query
45
+ **kwargs: Additional parameters
46
+
47
+ Returns:
48
+ Result dictionary
49
+ """
50
+ state = AgentState(query=query, context=kwargs.get("context", {}))
51
+
52
+ # Hybrid workflow: Analyze → Search → RAG → Validate
53
+ for iteration in range(self.max_iterations):
54
+ if not self._validate_state(state):
55
+ break
56
+
57
+ # Determine next action
58
+ action = self._decide_action(state)
59
+
60
+ if action == "ANSWER":
61
+ if self.early_stopping:
62
+ break
63
+ elif action == "SEARCH":
64
+ result = self._search(state)
65
+ state.context["search_results"] = result
66
+ elif action == "RAG":
67
+ result = self._rag(state)
68
+ state.context["rag_results"] = result
69
+ elif action == "VALIDATE":
70
+ validation = self._validate(state)
71
+ state.context["validation"] = validation
72
+ else:
73
+ break
74
+
75
+ state = self._update_state(state, iteration=iteration)
76
+
77
+ # Final answer synthesis
78
+ if not state.result:
79
+ state.result = self._synthesize_answer(state)
80
+
81
+ return {
82
+ "result": state.result or "No result generated",
83
+ "metadata": {
84
+ "iterations": state.iteration_count,
85
+ "actions_taken": state.context.get("actions", []),
86
+ "agent": self.name,
87
+ }
88
+ }
89
+
90
+ def _decide_action(self, state: AgentState) -> str:
91
+ """
92
+ Intelligent action decision.
93
+
94
+ Returns:
95
+ Next action: "SEARCH", "RAG", "VALIDATE", "ANSWER"
96
+ """
97
+ # Simplified logic - in production, use LLM for routing
98
+ if state.result:
99
+ return "ANSWER"
100
+ if state.iteration_count == 0:
101
+ return "SEARCH"
102
+ if state.iteration_count == 1:
103
+ return "RAG"
104
+ if state.iteration_count == 2:
105
+ return "VALIDATE"
106
+ return "ANSWER"
107
+
108
+ def _search(self, state: AgentState) -> str:
109
+ """Execute search"""
110
+ search_tool = next((t for t in self.tools if hasattr(t, "name") and t.name == "search"), None)
111
+ if search_tool:
112
+ return search_tool.run(state.query)
113
+ return "Search tool not available"
114
+
115
+ def _rag(self, state: AgentState) -> str:
116
+ """Execute RAG"""
117
+ rag_tool = next((t for t in self.tools if hasattr(t, "name") and t.name == "rag"), None)
118
+ if rag_tool:
119
+ # Handle search_results as either string or dict
120
+ search_results = state.context.get("search_results", "")
121
+ if isinstance(search_results, dict):
122
+ urls = search_results.get("urls", [])
123
+ else:
124
+ urls = [] # No URLs from search
125
+ return rag_tool.run(state.query, urls=urls)
126
+ return "RAG tool not available"
127
+
128
+ def _validate(self, state: AgentState) -> Dict[str, Any]:
129
+ """Execute validation"""
130
+ validator = next((t for t in self.tools if hasattr(t, "name") and t.name == "validator"), None)
131
+ if validator:
132
+ result = state.context.get("rag_results", "")
133
+ return validator.run(result, context=state.context)
134
+ return {"valid": True, "confidence": 0.8}
135
+
136
+ def _synthesize_answer(self, state: AgentState) -> str:
137
+ """Synthesize final answer from all results"""
138
+ # Combine search and RAG results
139
+ search_result = state.context.get("search_results", "")
140
+ rag_result = state.context.get("rag_results", "")
141
+
142
+ if rag_result and rag_result != "RAG tool not available":
143
+ return rag_result
144
+ if search_result and search_result != "Search tool not available":
145
+ return search_result
146
+ return "Unable to generate answer"
147
+
@@ -0,0 +1,92 @@
1
+ """
2
+ RAG-Enhanced Agent
3
+
4
+ Agent with RAG capabilities and fallback strategies.
5
+ """
6
+
7
+ from typing import Any, Dict, List, Optional
8
+ from agentensemble.agents.base import BaseAgent, AgentState
9
+
10
+
11
+ class RAGAgent(BaseAgent):
12
+ """
13
+ RAG-Enhanced Agent with fallback strategies.
14
+
15
+ Implements scraping → embedding → retrieval with query variations.
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ name: str = "rag_agent",
21
+ tools: Optional[list] = None,
22
+ max_iterations: int = 10,
23
+ fallback_strategies: int = 3,
24
+ **kwargs
25
+ ):
26
+ """
27
+ Initialize RAG agent.
28
+
29
+ Args:
30
+ name: Agent name
31
+ tools: Available tools (should include RAG tool)
32
+ max_iterations: Maximum iterations
33
+ fallback_strategies: Number of fallback query variations
34
+ **kwargs: Additional configuration
35
+ """
36
+ super().__init__(name, tools, max_iterations, **kwargs)
37
+ self.fallback_strategies = fallback_strategies
38
+
39
+ def run(self, query: str, urls: Optional[List[str]] = None, **kwargs) -> Dict[str, Any]:
40
+ """
41
+ Execute RAG agent with fallback strategies.
42
+
43
+ Args:
44
+ query: Input query
45
+ urls: Optional URLs to use for RAG
46
+ **kwargs: Additional parameters
47
+
48
+ Returns:
49
+ Result dictionary
50
+ """
51
+ state = AgentState(query=query, context=kwargs.get("context", {}))
52
+
53
+ # Try primary query
54
+ result = self._query_rag(state.query, urls)
55
+
56
+ if result and result != "not found":
57
+ state.result = result
58
+ else:
59
+ # Try fallback strategies
60
+ for i in range(self.fallback_strategies):
61
+ fallback_query = self._generate_fallback_query(state.query, i)
62
+ result = self._query_rag(fallback_query, urls)
63
+ if result and result != "not found":
64
+ state.result = result
65
+ break
66
+
67
+ return {
68
+ "result": state.result or "No result found",
69
+ "metadata": {
70
+ "fallback_attempts": min(self.fallback_strategies, state.iteration_count),
71
+ "agent": self.name,
72
+ }
73
+ }
74
+
75
+ def _query_rag(self, query: str, urls: Optional[List[str]]) -> str:
76
+ """Query RAG system"""
77
+ # Find RAG tool
78
+ rag_tool = next((t for t in self.tools if hasattr(t, "name") and t.name == "rag"), None)
79
+ if rag_tool:
80
+ return rag_tool.run(query, urls=urls)
81
+ return "RAG tool not available"
82
+
83
+ def _generate_fallback_query(self, original_query: str, attempt: int) -> str:
84
+ """Generate fallback query variation"""
85
+ # Simplified - in production, use LLM to rephrase
86
+ variations = [
87
+ f"More details about: {original_query}",
88
+ f"Alternative phrasing: {original_query}",
89
+ f"Related information: {original_query}",
90
+ ]
91
+ return variations[attempt % len(variations)]
92
+
@@ -0,0 +1,118 @@
1
+ """
2
+ ReAct Agent Implementation
3
+
4
+ Simple reasoning + acting pattern with tool calling.
5
+ """
6
+
7
+ from typing import Any, Dict, List, Optional
8
+ from agentensemble.agents.base import BaseAgent, AgentState
9
+
10
+
11
+ class ReActAgent(BaseAgent):
12
+ """
13
+ ReAct (Reasoning + Acting) Agent
14
+
15
+ A simple agent that reasons about actions and uses tools to accomplish tasks.
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ name: str = "react_agent",
21
+ tools: Optional[List[Any]] = None,
22
+ max_iterations: int = 10,
23
+ **kwargs
24
+ ):
25
+ """
26
+ Initialize ReAct agent.
27
+
28
+ Args:
29
+ name: Agent name
30
+ tools: Available tools
31
+ max_iterations: Maximum reasoning/acting cycles
32
+ **kwargs: Additional configuration
33
+ """
34
+ super().__init__(name, tools, max_iterations, **kwargs)
35
+
36
+ def run(self, query: str, **kwargs) -> Dict[str, Any]:
37
+ """
38
+ Execute ReAct agent.
39
+
40
+ Args:
41
+ query: Input query
42
+ **kwargs: Additional parameters
43
+
44
+ Returns:
45
+ Result dictionary with answer and metadata
46
+ """
47
+ state = AgentState(query=query, context=kwargs.get("context", {}))
48
+
49
+ # Simple ReAct loop: Think -> Act -> Observe
50
+ for iteration in range(self.max_iterations):
51
+ if not self._validate_state(state):
52
+ break
53
+
54
+ # Think: Reason about what to do
55
+ action = self._think(state)
56
+
57
+ if action == "ANSWER":
58
+ break
59
+
60
+ # Act: Use a tool
61
+ if action == "TOOL":
62
+ tool_result = self._act(state)
63
+ state.context["last_tool_result"] = tool_result
64
+
65
+ # If we got a good result, use it as the answer
66
+ if tool_result and tool_result != "No tools available" and tool_result != "Tool execution failed":
67
+ state.result = tool_result
68
+
69
+ state = self._update_state(
70
+ state,
71
+ tool_calls=[{"iteration": iteration, "result": tool_result}]
72
+ )
73
+ else:
74
+ break
75
+
76
+ # If no result yet, try to synthesize from context
77
+ if not state.result and state.context.get("last_tool_result"):
78
+ state.result = state.context["last_tool_result"]
79
+
80
+ return {
81
+ "result": state.result or state.context.get("last_tool_result", "No result generated"),
82
+ "metadata": {
83
+ "iterations": state.iteration_count,
84
+ "tool_calls": len(state.tool_calls),
85
+ "agent": self.name,
86
+ }
87
+ }
88
+
89
+ def _think(self, state: AgentState) -> str:
90
+ """
91
+ Reasoning step: Decide what action to take.
92
+
93
+ Returns:
94
+ "TOOL" to use a tool, "ANSWER" to provide final answer
95
+ """
96
+ # Simplified logic - in production, this would use an LLM
97
+ if state.result:
98
+ return "ANSWER"
99
+ if state.iteration_count < 3 and self.tools:
100
+ return "TOOL"
101
+ return "ANSWER"
102
+
103
+ def _act(self, state: AgentState) -> str:
104
+ """
105
+ Acting step: Execute a tool.
106
+
107
+ Returns:
108
+ Tool result
109
+ """
110
+ if not self.tools:
111
+ return "No tools available"
112
+
113
+ # Use first available tool (simplified)
114
+ tool = self.tools[0]
115
+ if hasattr(tool, "run"):
116
+ return tool.run(state.query)
117
+ return "Tool execution failed"
118
+
@@ -0,0 +1,97 @@
1
+ """
2
+ StateGraph Agent Implementation
3
+
4
+ Custom StateGraph with intelligent routing between nodes.
5
+ """
6
+
7
+ from typing import Any, Dict, Optional
8
+ from agentensemble.agents.base import BaseAgent, AgentState
9
+
10
+
11
+ class StateGraphAgent(BaseAgent):
12
+ """
13
+ StateGraph Agent with custom nodes and routing.
14
+
15
+ Implements a graph-based workflow with intelligent routing.
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ name: str = "stategraph_agent",
21
+ nodes: Optional[Dict[str, Any]] = None,
22
+ tools: Optional[list] = None,
23
+ max_iterations: int = 15,
24
+ **kwargs
25
+ ):
26
+ """
27
+ Initialize StateGraph agent.
28
+
29
+ Args:
30
+ name: Agent name
31
+ nodes: Dictionary of node name -> node function
32
+ tools: Available tools
33
+ max_iterations: Maximum graph iterations
34
+ **kwargs: Additional configuration
35
+ """
36
+ super().__init__(name, tools, max_iterations, **kwargs)
37
+ self.nodes = nodes or {}
38
+
39
+ def run(self, query: str, **kwargs) -> Dict[str, Any]:
40
+ """
41
+ Execute StateGraph agent.
42
+
43
+ Args:
44
+ query: Input query
45
+ **kwargs: Additional parameters
46
+
47
+ Returns:
48
+ Result dictionary
49
+ """
50
+ state = AgentState(query=query, context=kwargs.get("context", {}))
51
+
52
+ # Simplified StateGraph execution
53
+ current_node = "start"
54
+
55
+ for iteration in range(self.max_iterations):
56
+ if not self._validate_state(state):
57
+ break
58
+
59
+ # Execute current node
60
+ if current_node in self.nodes:
61
+ node_result = self.nodes[current_node](state)
62
+ state = self._update_state(state, **node_result)
63
+
64
+ # Route to next node
65
+ current_node = self._route(state, current_node)
66
+ if current_node == "end":
67
+ break
68
+ else:
69
+ break
70
+
71
+ return {
72
+ "result": state.result or "No result generated",
73
+ "metadata": {
74
+ "iterations": state.iteration_count,
75
+ "nodes_visited": list(self.nodes.keys()),
76
+ "agent": self.name,
77
+ }
78
+ }
79
+
80
+ def _route(self, state: AgentState, current_node: str) -> str:
81
+ """
82
+ Intelligent routing logic.
83
+
84
+ Args:
85
+ state: Current state
86
+ current_node: Current node name
87
+
88
+ Returns:
89
+ Next node name or "end"
90
+ """
91
+ # Simplified routing - in production, this would use LLM or rules
92
+ if state.result:
93
+ return "end"
94
+ if current_node == "start":
95
+ return "analyze" if "analyze" in self.nodes else "end"
96
+ return "end"
97
+