eaglelandsonce commited on
Commit
025a7d5
1 Parent(s): fb373a3

Upload 22 files

Browse files
crewai/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from crewai.agent import Agent
2
+ from crewai.crew import Crew
3
+ from crewai.process import Process
4
+ from crewai.task import Task
crewai/agent.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import uuid
2
+ from typing import Any, List, Optional
3
+
4
+ from langchain.prompts.chat import (
5
+ ChatPromptTemplate,
6
+ HumanMessagePromptTemplate,
7
+ SystemMessagePromptTemplate,
8
+ )
9
+ from langchain.schema import HumanMessage, SystemMessage
10
+ from langchain_openai import ChatOpenAI
11
+
12
+ # from langchain_google_genai import ChatGoogleGenerativeAI
13
+
14
+
15
+
16
+ from langchain.agents.format_scratchpad import format_log_to_str
17
+ from langchain.memory import ConversationSummaryMemory
18
+
19
+
20
+ from langchain.tools.render import render_text_description
21
+ from langchain_core.runnables.config import RunnableConfig
22
+ from pydantic import (
23
+
24
+
25
+ UUID4,
26
+ BaseModel,
27
+ ConfigDict,
28
+ Field,
29
+ InstanceOf,
30
+ field_validator,
31
+ model_validator,
32
+ )
33
+ from pydantic_core import PydanticCustomError
34
+
35
+ from crewai.agents import (
36
+ CacheHandler,
37
+ CrewAgentExecutor,
38
+ CrewAgentOutputParser,
39
+ ToolsHandler,
40
+ )
41
+ from crewai.prompts import Prompts
42
+
43
+
44
+ class Agent(BaseModel):
45
+ """Represents an agent in a system.
46
+
47
+ Each agent has a role, a goal, a backstory, and an optional language model (llm).
48
+ The agent can also have memory, can operate in verbose mode, and can delegate tasks to other agents.
49
+
50
+ Attributes:
51
+ agent_executor: An instance of the CrewAgentExecutor class.
52
+ role: The role of the agent.
53
+ goal: The objective of the agent.
54
+ backstory: The backstory of the agent.
55
+ llm: The language model that will run the agent.
56
+ memory: Whether the agent should have memory or not.
57
+ verbose: Whether the agent execution should be in verbose mode.
58
+ allow_delegation: Whether the agent is allowed to delegate tasks to other agents.
59
+ """
60
+
61
+ __hash__ = object.__hash__
62
+ model_config = ConfigDict(arbitrary_types_allowed=True)
63
+ id: UUID4 = Field(
64
+ default_factory=uuid.uuid4,
65
+ frozen=True,
66
+ description="Unique identifier for the object, not set by user.",
67
+ )
68
+ role: str = Field(description="Role of the agent")
69
+ goal: str = Field(description="Objective of the agent")
70
+ backstory: str = Field(description="Backstory of the agent")
71
+ llm: Optional[Any] = Field(
72
+ default_factory=lambda: ChatOpenAI(
73
+ temperature=0.7,
74
+ model_name="gpt-4",
75
+ ),
76
+ description="Language model that will run the agent.",
77
+ )
78
+ memory: bool = Field(
79
+ default=True, description="Whether the agent should have memory or not"
80
+ )
81
+ verbose: bool = Field(
82
+ default=False, description="Verbose mode for the Agent Execution"
83
+ )
84
+ allow_delegation: bool = Field(
85
+ default=True, description="Allow delegation of tasks to agents"
86
+ )
87
+ tools: List[Any] = Field(
88
+ default_factory=list, description="Tools at agents disposal"
89
+ )
90
+ agent_executor: Optional[InstanceOf[CrewAgentExecutor]] = Field(
91
+ default=None, description="An instance of the CrewAgentExecutor class."
92
+ )
93
+ tools_handler: Optional[InstanceOf[ToolsHandler]] = Field(
94
+ default=None, description="An instance of the ToolsHandler class."
95
+ )
96
+ cache_handler: Optional[InstanceOf[CacheHandler]] = Field(
97
+ default=CacheHandler(), description="An instance of the CacheHandler class."
98
+ )
99
+
100
+ @field_validator("id", mode="before")
101
+ @classmethod
102
+ def _deny_user_set_id(cls, v: Optional[UUID4]) -> None:
103
+ if v:
104
+ raise PydanticCustomError(
105
+ "may_not_set_field", "This field is not to be set by the user.", {}
106
+ )
107
+
108
+ @model_validator(mode="after")
109
+ def check_agent_executor(self) -> "Agent":
110
+ if not self.agent_executor:
111
+ self.set_cache_handler(self.cache_handler)
112
+ return self
113
+
114
+ def execute_task(
115
+ self, task: str, context: str = None, tools: List[Any] = None
116
+ ) -> str:
117
+ """Execute a task with the agent.
118
+
119
+ Args:
120
+ task: Task to execute.
121
+ context: Context to execute the task in.
122
+ tools: Tools to use for the task.
123
+
124
+ Returns:
125
+ Output of the agent
126
+ """
127
+ if context:
128
+ task = "\n".join(
129
+ [task, "\nThis is the context you are working with:", context]
130
+ )
131
+
132
+ tools = tools or self.tools
133
+ self.agent_executor.tools = tools
134
+
135
+ return self.agent_executor.invoke(
136
+ {
137
+ "input": task,
138
+ "tool_names": self.__tools_names(tools),
139
+ "tools": render_text_description(tools),
140
+ },
141
+ RunnableConfig(callbacks=[self.tools_handler]),
142
+ )["output"]
143
+
144
+ def set_cache_handler(self, cache_handler) -> None:
145
+ self.cache_handler = cache_handler
146
+ self.tools_handler = ToolsHandler(cache=self.cache_handler)
147
+ self.__create_agent_executor()
148
+
149
+ def __create_agent_executor(self) -> CrewAgentExecutor:
150
+ """Create an agent executor for the agent.
151
+
152
+ Returns:
153
+ An instance of the CrewAgentExecutor class.
154
+ """
155
+ agent_args = {
156
+ "input": lambda x: x["input"],
157
+ "tools": lambda x: x["tools"],
158
+ "tool_names": lambda x: x["tool_names"],
159
+ "agent_scratchpad": lambda x: format_log_to_str(x["intermediate_steps"]),
160
+ }
161
+ executor_args = {
162
+ "tools": self.tools,
163
+ "verbose": self.verbose,
164
+ "handle_parsing_errors": True,
165
+ }
166
+
167
+ if self.memory:
168
+ summary_memory = ConversationSummaryMemory(
169
+ llm=self.llm, memory_key="chat_history", input_key="input"
170
+ )
171
+ executor_args["memory"] = summary_memory
172
+ agent_args["chat_history"] = lambda x: x["chat_history"]
173
+ prompt = Prompts.TASK_EXECUTION_WITH_MEMORY_PROMPT
174
+ else:
175
+ prompt = Prompts.TASK_EXECUTION_PROMPT
176
+
177
+ execution_prompt = prompt.partial(
178
+ goal=self.goal,
179
+ role=self.role,
180
+ backstory=self.backstory,
181
+ )
182
+
183
+ bind = self.llm.bind(stop=["\nObservation"])
184
+ inner_agent = (
185
+ agent_args
186
+ | execution_prompt
187
+ | bind
188
+ | CrewAgentOutputParser(
189
+ tools_handler=self.tools_handler, cache=self.cache_handler
190
+ )
191
+ )
192
+ self.agent_executor = CrewAgentExecutor(agent=inner_agent, **executor_args)
193
+
194
+ @staticmethod
195
+ def __tools_names(tools) -> str:
196
+ return ", ".join([t.name for t in tools])
crewai/agents/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from .cache.cache_handler import CacheHandler
2
+ from .executor import CrewAgentExecutor
3
+ from .output_parser import CrewAgentOutputParser
4
+ from .tools_handler import ToolsHandler
crewai/agents/cache/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ from .cache_handler import CacheHandler
2
+ from .cache_hit import CacheHit
crewai/agents/cache/cache_handler.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+
3
+ from pydantic import PrivateAttr
4
+
5
+
6
+ class CacheHandler:
7
+ """Callback handler for tool usage."""
8
+
9
+ _cache: PrivateAttr = {}
10
+
11
+ def __init__(self):
12
+ self._cache = {}
13
+
14
+ def add(self, tool, input, output):
15
+ input = input.strip()
16
+ self._cache[f"{tool}-{input}"] = output
17
+
18
+ def read(self, tool, input) -> Optional[str]:
19
+ input = input.strip()
20
+ return self._cache.get(f"{tool}-{input}")
crewai/agents/cache/cache_hit.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_core.agents import AgentAction
2
+ from pydantic.v1 import BaseModel, Field
3
+
4
+ from .cache_handler import CacheHandler
5
+
6
+
7
+ class CacheHit(BaseModel):
8
+ """Cache Hit Object."""
9
+
10
+ class Config:
11
+ arbitrary_types_allowed = True
12
+
13
+ action: AgentAction = Field(description="Action taken")
14
+ cache: CacheHandler = Field(description="Cache Handler for the tool")
crewai/agents/exceptions.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_core.exceptions import OutputParserException
2
+
3
+
4
+ class TaskRepeatedUsageException(OutputParserException):
5
+ """Exception raised when a task is used twice in a roll."""
6
+
7
+ error: str = "TaskRepeatedUsageException"
8
+ message: str = "\nI just used the {tool} tool with input {tool_input}. So I already know the result of that.\n"
9
+
10
+ def __init__(self, tool: str, tool_input: str):
11
+ self.tool = tool
12
+ self.tool_input = tool_input
13
+ self.message = self.message.format(tool=tool, tool_input=tool_input)
14
+
15
+ super().__init__(
16
+ error=self.error, observation=self.message, send_to_llm=True, llm_output=""
17
+ )
18
+
19
+ def __str__(self):
20
+ return self.message
crewai/agents/executor.py ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict, Iterator, List, Optional, Tuple, Union
2
+
3
+ from langchain.agents import AgentExecutor
4
+ from langchain.agents.agent import ExceptionTool
5
+ from langchain.agents.tools import InvalidTool
6
+ from langchain.callbacks.manager import CallbackManagerForChainRun
7
+ from langchain_core.agents import AgentAction, AgentFinish, AgentStep
8
+ from langchain_core.exceptions import OutputParserException
9
+ from langchain_core.tools import BaseTool
10
+
11
+ from ..tools.cache_tools import CacheTools
12
+ from .cache.cache_hit import CacheHit
13
+
14
+
15
+ class CrewAgentExecutor(AgentExecutor):
16
+ def _iter_next_step(
17
+ self,
18
+ name_to_tool_map: Dict[str, BaseTool],
19
+ color_mapping: Dict[str, str],
20
+ inputs: Dict[str, str],
21
+ intermediate_steps: List[Tuple[AgentAction, str]],
22
+ run_manager: Optional[CallbackManagerForChainRun] = None,
23
+ ) -> Iterator[Union[AgentFinish, AgentAction, AgentStep]]:
24
+ """Take a single step in the thought-action-observation loop.
25
+
26
+ Override this to take control of how the agent makes and acts on choices.
27
+ """
28
+ try:
29
+ intermediate_steps = self._prepare_intermediate_steps(intermediate_steps)
30
+
31
+ # Call the LLM to see what to do.
32
+ output = self.agent.plan(
33
+ intermediate_steps,
34
+ callbacks=run_manager.get_child() if run_manager else None,
35
+ **inputs,
36
+ )
37
+ except OutputParserException as e:
38
+ if isinstance(self.handle_parsing_errors, bool):
39
+ raise_error = not self.handle_parsing_errors
40
+ else:
41
+ raise_error = False
42
+ if raise_error:
43
+ raise ValueError(
44
+ "An output parsing error occurred. "
45
+ "In order to pass this error back to the agent and have it try "
46
+ "again, pass `handle_parsing_errors=True` to the AgentExecutor. "
47
+ f"This is the error: {str(e)}"
48
+ )
49
+ text = str(e)
50
+ if isinstance(self.handle_parsing_errors, bool):
51
+ if e.send_to_llm:
52
+ observation = str(e.observation)
53
+ text = str(e.llm_output)
54
+ else:
55
+ observation = "Invalid or incomplete response"
56
+ elif isinstance(self.handle_parsing_errors, str):
57
+ observation = self.handle_parsing_errors
58
+ elif callable(self.handle_parsing_errors):
59
+ observation = self.handle_parsing_errors(e)
60
+ else:
61
+ raise ValueError("Got unexpected type of `handle_parsing_errors`")
62
+ output = AgentAction("_Exception", observation, text)
63
+ if run_manager:
64
+ run_manager.on_agent_action(output, color="green")
65
+ tool_run_kwargs = self.agent.tool_run_logging_kwargs()
66
+ observation = ExceptionTool().run(
67
+ output.tool_input,
68
+ verbose=self.verbose,
69
+ color=None,
70
+ callbacks=run_manager.get_child() if run_manager else None,
71
+ **tool_run_kwargs,
72
+ )
73
+ yield AgentStep(action=output, observation=observation)
74
+ return
75
+
76
+ # If the tool chosen is the finishing tool, then we end and return.
77
+ if isinstance(output, AgentFinish):
78
+ yield output
79
+ return
80
+
81
+ # Override tool usage to use CacheTools
82
+ if isinstance(output, CacheHit):
83
+ cache = output.cache
84
+ action = output.action
85
+ tool = CacheTools(cache_handler=cache).tool()
86
+ output = action.copy()
87
+ output.tool_input = f"tool:{action.tool}|input:{action.tool_input}"
88
+ output.tool = tool.name
89
+ name_to_tool_map[tool.name] = tool
90
+ color_mapping[tool.name] = color_mapping[action.tool]
91
+
92
+ actions: List[AgentAction]
93
+ if isinstance(output, AgentAction):
94
+ actions = [output]
95
+ else:
96
+ actions = output
97
+ for agent_action in actions:
98
+ yield agent_action
99
+ for agent_action in actions:
100
+ if run_manager:
101
+ run_manager.on_agent_action(agent_action, color="green")
102
+ # Otherwise we lookup the tool
103
+ if agent_action.tool in name_to_tool_map:
104
+ tool = name_to_tool_map[agent_action.tool]
105
+ return_direct = tool.return_direct
106
+ color = color_mapping[agent_action.tool]
107
+ tool_run_kwargs = self.agent.tool_run_logging_kwargs()
108
+ if return_direct:
109
+ tool_run_kwargs["llm_prefix"] = ""
110
+ # We then call the tool on the tool input to get an observation
111
+ observation = tool.run(
112
+ agent_action.tool_input,
113
+ verbose=self.verbose,
114
+ color=color,
115
+ callbacks=run_manager.get_child() if run_manager else None,
116
+ **tool_run_kwargs,
117
+ )
118
+ else:
119
+ tool_run_kwargs = self.agent.tool_run_logging_kwargs()
120
+ observation = InvalidTool().run(
121
+ {
122
+ "requested_tool_name": agent_action.tool,
123
+ "available_tool_names": list(name_to_tool_map.keys()),
124
+ },
125
+ verbose=self.verbose,
126
+ color=None,
127
+ callbacks=run_manager.get_child() if run_manager else None,
128
+ **tool_run_kwargs,
129
+ )
130
+ yield AgentStep(action=agent_action, observation=observation)
crewai/agents/output_parser.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ from typing import Union
3
+
4
+ from langchain.agents.output_parsers import ReActSingleInputOutputParser
5
+ from langchain_core.agents import AgentAction, AgentFinish
6
+
7
+ from .cache import CacheHandler, CacheHit
8
+ from .exceptions import TaskRepeatedUsageException
9
+ from .tools_handler import ToolsHandler
10
+
11
+ FINAL_ANSWER_ACTION = "Final Answer:"
12
+ FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE = (
13
+ "Parsing LLM output produced both a final answer and a parse-able action:"
14
+ )
15
+
16
+
17
+ class CrewAgentOutputParser(ReActSingleInputOutputParser):
18
+ """Parses ReAct-style LLM calls that have a single tool input.
19
+
20
+ Expects output to be in one of two formats.
21
+
22
+ If the output signals that an action should be taken,
23
+ should be in the below format. This will result in an AgentAction
24
+ being returned.
25
+
26
+ ```
27
+ Thought: agent thought here
28
+ Action: search
29
+ Action Input: what is the temperature in SF?
30
+ ```
31
+
32
+ If the output signals that a final answer should be given,
33
+ should be in the below format. This will result in an AgentFinish
34
+ being returned.
35
+
36
+ ```
37
+ Thought: agent thought here
38
+ Final Answer: The temperature is 100 degrees
39
+ ```
40
+
41
+ It also prevents tools from being reused in a roll.
42
+ """
43
+
44
+ class Config:
45
+ arbitrary_types_allowed = True
46
+
47
+ tools_handler: ToolsHandler
48
+ cache: CacheHandler
49
+
50
+ def parse(self, text: str) -> Union[AgentAction, AgentFinish, CacheHit]:
51
+ FINAL_ANSWER_ACTION in text
52
+ regex = (
53
+ r"Action\s*\d*\s*:[\s]*(.*?)[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
54
+ )
55
+ action_match = re.search(regex, text, re.DOTALL)
56
+ if action_match:
57
+ action = action_match.group(1).strip()
58
+ action_input = action_match.group(2)
59
+ tool_input = action_input.strip(" ")
60
+ tool_input = tool_input.strip('"')
61
+
62
+ last_tool_usage = self.tools_handler.last_used_tool
63
+ if last_tool_usage:
64
+ usage = {
65
+ "tool": action,
66
+ "input": tool_input,
67
+ }
68
+ if usage == last_tool_usage:
69
+ raise TaskRepeatedUsageException(tool=action, tool_input=tool_input)
70
+
71
+ result = self.cache.read(action, tool_input)
72
+ if result:
73
+ action = AgentAction(action, tool_input, text)
74
+ return CacheHit(action=action, cache=self.cache)
75
+
76
+ return super().parse(text)
crewai/agents/tools_handler.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any, Dict
2
+
3
+ from langchain.callbacks.base import BaseCallbackHandler
4
+
5
+ from ..tools.cache_tools import CacheTools
6
+ from .cache.cache_handler import CacheHandler
7
+
8
+
9
+ class ToolsHandler(BaseCallbackHandler):
10
+ """Callback handler for tool usage."""
11
+
12
+ last_used_tool: Dict[str, Any] = {}
13
+ cache: CacheHandler = None
14
+
15
+ def __init__(self, cache: CacheHandler = None, **kwargs: Any):
16
+ """Initialize the callback handler."""
17
+ self.cache = cache
18
+ super().__init__(**kwargs)
19
+
20
+ def on_tool_start(
21
+ self, serialized: Dict[str, Any], input_str: str, **kwargs: Any
22
+ ) -> Any:
23
+ """Run when tool starts running."""
24
+ name = serialized.get("name")
25
+ if name not in ["invalid_tool", "_Exception"]:
26
+ tools_usage = {
27
+ "tool": name,
28
+ "input": input_str,
29
+ }
30
+ self.last_used_tool = tools_usage
31
+
32
+ def on_tool_end(self, output: str, **kwargs: Any) -> Any:
33
+ """Run when tool ends running."""
34
+ if (
35
+ "is not a valid tool" not in output
36
+ and "Invalid or incomplete response" not in output
37
+ and "Invalid Format" not in output
38
+ ):
39
+ if self.last_used_tool["tool"] != CacheTools().name:
40
+ self.cache.add(
41
+ tool=self.last_used_tool["tool"],
42
+ input=self.last_used_tool["input"],
43
+ output=output,
44
+ )
crewai/crew.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import uuid
3
+ from typing import Any, Dict, List, Optional, Union
4
+
5
+ from pydantic import (
6
+ UUID4,
7
+ BaseModel,
8
+ ConfigDict,
9
+ Field,
10
+ InstanceOf,
11
+ Json,
12
+ field_validator,
13
+ model_validator,
14
+ )
15
+ from pydantic_core import PydanticCustomError
16
+
17
+ from crewai.agent import Agent
18
+ from crewai.agents.cache import CacheHandler
19
+ from crewai.process import Process
20
+ from crewai.task import Task
21
+ from crewai.tools.agent_tools import AgentTools
22
+
23
+
24
+ class Crew(BaseModel):
25
+ """Class that represents a group of agents, how they should work together and their tasks."""
26
+
27
+ __hash__ = object.__hash__
28
+ model_config = ConfigDict(arbitrary_types_allowed=True)
29
+ tasks: List[Task] = Field(description="List of tasks", default_factory=list)
30
+ agents: List[Agent] = Field(
31
+ description="List of agents in this crew.", default_factory=list
32
+ )
33
+ process: Process = Field(
34
+ description="Process that the crew will follow.", default=Process.sequential
35
+ )
36
+ verbose: Union[int, bool] = Field(
37
+ description="Verbose mode for the Agent Execution", default=0
38
+ )
39
+ config: Optional[Union[Json, Dict[str, Any]]] = Field(
40
+ description="Configuration of the crew.", default=None
41
+ )
42
+ cache_handler: Optional[InstanceOf[CacheHandler]] = Field(
43
+ default=CacheHandler(), description="An instance of the CacheHandler class."
44
+ )
45
+ id: UUID4 = Field(
46
+ default_factory=uuid.uuid4,
47
+ frozen=True,
48
+ description="Unique identifier for the object, not set by user.",
49
+ )
50
+
51
+ @field_validator("id", mode="before")
52
+ @classmethod
53
+ def _deny_user_set_id(cls, v: Optional[UUID4]) -> None:
54
+ if v:
55
+ raise PydanticCustomError(
56
+ "may_not_set_field", "This field is not to be set by the user.", {}
57
+ )
58
+
59
+ @classmethod
60
+ @field_validator("config", mode="before")
61
+ def check_config_type(cls, v: Union[Json, Dict[str, Any]]):
62
+ if isinstance(v, Json):
63
+ return json.loads(v)
64
+ return v
65
+
66
+ @model_validator(mode="after")
67
+ def check_config(self):
68
+ if not self.config and not self.tasks and not self.agents:
69
+ raise PydanticCustomError(
70
+ "missing_keys", "Either agents and task need to be set or config.", {}
71
+ )
72
+
73
+ if self.config:
74
+ if not self.config.get("agents") or not self.config.get("tasks"):
75
+ raise PydanticCustomError(
76
+ "missing_keys_in_config", "Config should have agents and tasks", {}
77
+ )
78
+
79
+ self.agents = [Agent(**agent) for agent in self.config["agents"]]
80
+
81
+ tasks = []
82
+ for task in self.config["tasks"]:
83
+ task_agent = [agt for agt in self.agents if agt.role == task["agent"]][
84
+ 0
85
+ ]
86
+ del task["agent"]
87
+ tasks.append(Task(**task, agent=task_agent))
88
+
89
+ self.tasks = tasks
90
+
91
+ if self.agents:
92
+ for agent in self.agents:
93
+ agent.set_cache_handler(self.cache_handler)
94
+ return self
95
+
96
+ def kickoff(self) -> str:
97
+ """Kickoff the crew to work on its tasks.
98
+
99
+ Returns:
100
+ Output of the crew for each task.
101
+ """
102
+ for agent in self.agents:
103
+ agent.cache_handler = self.cache_handler
104
+
105
+ if self.process == Process.sequential:
106
+ return self.__sequential_loop()
107
+
108
+ def __sequential_loop(self) -> str:
109
+ """Loop that executes the sequential process.
110
+
111
+ Returns:
112
+ Output of the crew.
113
+ """
114
+ task_outcome = None
115
+ for task in self.tasks:
116
+ # Add delegation tools to the task if the agent allows it
117
+ if task.agent.allow_delegation:
118
+ tools = AgentTools(agents=self.agents).tools()
119
+ task.tools += tools
120
+
121
+ self.__log("debug", f"Working Agent: {task.agent.role}")
122
+ self.__log("info", f"Starting Task: {task.description} ...")
123
+
124
+ task_outcome = task.execute(task_outcome)
125
+
126
+ self.__log("debug", f"Task output: {task_outcome}")
127
+
128
+ return task_outcome
129
+
130
+ def __log(self, level, message):
131
+ """Log a message"""
132
+ level_map = {"debug": 1, "info": 2}
133
+ verbose_level = (
134
+ 2 if isinstance(self.verbose, bool) and self.verbose else self.verbose
135
+ )
136
+ if verbose_level and level_map[level] <= verbose_level:
137
+ print(message)
crewai/process.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from enum import Enum
2
+
3
+
4
+ class Process(str, Enum):
5
+ """
6
+ Class representing the different processes that can be used to tackle tasks
7
+ """
8
+
9
+ sequential = "sequential"
10
+ # TODO: consensual = 'consensual'
11
+ # TODO: hierarchical = 'hierarchical'
crewai/prompts.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Prompts for generic agent."""
2
+
3
+ from textwrap import dedent
4
+ from typing import ClassVar
5
+
6
+ from langchain.prompts import PromptTemplate
7
+ from pydantic import BaseModel
8
+
9
+
10
+ class Prompts(BaseModel):
11
+ """Prompts for generic agent."""
12
+
13
+ TASK_SLICE: ClassVar[str] = dedent(
14
+ """\
15
+ Begin! This is VERY important to you, your job depends on it!
16
+
17
+ Current Task: {input}"""
18
+ )
19
+
20
+ SCRATCHPAD_SLICE: ClassVar[str] = "\n{agent_scratchpad}"
21
+
22
+ MEMORY_SLICE: ClassVar[str] = dedent(
23
+ """\
24
+ This is the summary of your work so far:
25
+ {chat_history}"""
26
+ )
27
+
28
+ ROLE_PLAYING_SLICE: ClassVar[str] = dedent(
29
+ """\
30
+ You are {role}.
31
+ {backstory}
32
+
33
+ Your personal goal is: {goal}"""
34
+ )
35
+
36
+ TOOLS_SLICE: ClassVar[str] = dedent(
37
+ """\
38
+
39
+
40
+ TOOLS:
41
+ ------
42
+ You have access to the following tools:
43
+
44
+ {tools}
45
+
46
+ To use a tool, please use the exact following format:
47
+
48
+ ```
49
+ Thought: Do I need to use a tool? Yes
50
+ Action: the action to take, should be one of [{tool_names}], just the name.
51
+ Action Input: the input to the action
52
+ Observation: the result of the action
53
+ ```
54
+
55
+ When you have a response for your task, or if you do not need to use a tool, you MUST use the format:
56
+
57
+ ```
58
+ Thought: Do I need to use a tool? No
59
+ Final Answer: [your response here]
60
+ ```"""
61
+ )
62
+
63
+ VOTING_SLICE: ClassVar[str] = dedent(
64
+ """\
65
+ You are working on a crew with your co-workers and need to decide who will execute the task.
66
+
67
+ These are your format instructions:
68
+ {format_instructions}
69
+
70
+ These are your co-workers and their roles:
71
+ {coworkers}"""
72
+ )
73
+
74
+ TASK_EXECUTION_WITH_MEMORY_PROMPT: ClassVar[str] = PromptTemplate.from_template(
75
+ ROLE_PLAYING_SLICE + TOOLS_SLICE + MEMORY_SLICE + TASK_SLICE + SCRATCHPAD_SLICE
76
+ )
77
+
78
+ TASK_EXECUTION_PROMPT: ClassVar[str] = PromptTemplate.from_template(
79
+ ROLE_PLAYING_SLICE + TOOLS_SLICE + TASK_SLICE + SCRATCHPAD_SLICE
80
+ )
81
+
82
+ CONSENSUNS_VOTING_PROMPT: ClassVar[str] = PromptTemplate.from_template(
83
+ ROLE_PLAYING_SLICE + VOTING_SLICE + TASK_SLICE + SCRATCHPAD_SLICE
84
+ )
crewai/task.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import uuid
2
+ from typing import Any, List, Optional
3
+
4
+ from pydantic import UUID4, BaseModel, Field, field_validator, model_validator
5
+ from pydantic_core import PydanticCustomError
6
+
7
+ from crewai.agent import Agent
8
+
9
+
10
+ class Task(BaseModel):
11
+ """Class that represent a task to be executed."""
12
+
13
+ __hash__ = object.__hash__
14
+ description: str = Field(description="Description of the actual task.")
15
+ agent: Optional[Agent] = Field(
16
+ description="Agent responsible for the task.", default=None
17
+ )
18
+ tools: List[Any] = Field(
19
+ default_factory=list,
20
+ description="Tools the agent are limited to use for this task.",
21
+ )
22
+ id: UUID4 = Field(
23
+ default_factory=uuid.uuid4,
24
+ frozen=True,
25
+ description="Unique identifier for the object, not set by user.",
26
+ )
27
+
28
+ @field_validator("id", mode="before")
29
+ @classmethod
30
+ def _deny_user_set_id(cls, v: Optional[UUID4]) -> None:
31
+ if v:
32
+ raise PydanticCustomError(
33
+ "may_not_set_field", "This field is not to be set by the user.", {}
34
+ )
35
+
36
+ @model_validator(mode="after")
37
+ def check_tools(self):
38
+ if not self.tools and (self.agent and self.agent.tools):
39
+ self.tools.extend(self.agent.tools)
40
+ return self
41
+
42
+ def execute(self, context: str = None) -> str:
43
+ """Execute the task.
44
+
45
+ Returns:
46
+ Output of the task.
47
+ """
48
+ if self.agent:
49
+ return self.agent.execute_task(
50
+ task=self.description, context=context, tools=self.tools
51
+ )
52
+ else:
53
+ raise Exception(
54
+ f"The task '{self.description}' has no agent assigned, therefore it can't be executed directly and should be executed in a Crew using a specific process that support that, either consensual or hierarchical."
55
+ )
crewai/tools/__init__.py ADDED
File without changes
crewai/tools/agent_tools.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from textwrap import dedent
2
+ from typing import List
3
+
4
+ from langchain.tools import Tool
5
+ from pydantic import BaseModel, Field
6
+
7
+ from crewai.agent import Agent
8
+
9
+
10
+ class AgentTools(BaseModel):
11
+ """Tools for generic agent."""
12
+
13
+ agents: List[Agent] = Field(description="List of agents in this crew.")
14
+
15
+ def tools(self):
16
+ return [
17
+ Tool.from_function(
18
+ func=self.delegate_work,
19
+ name="Delegate work to co-worker",
20
+ description=dedent(
21
+ f"""Useful to delegate a specific task to one of the
22
+ following co-workers: [{', '.join([agent.role for agent in self.agents])}].
23
+ The input to this tool should be a pipe (|) separated text of length
24
+ three, representing the role you want to delegate it to, the task and
25
+ information necessary. For example, `coworker|task|information`.
26
+ """
27
+ ),
28
+ ),
29
+ Tool.from_function(
30
+ func=self.ask_question,
31
+ name="Ask question to co-worker",
32
+ description=dedent(
33
+ f"""Useful to ask a question, opinion or take from on
34
+ of the following co-workers: [{', '.join([agent.role for agent in self.agents])}].
35
+ The input to this tool should be a pipe (|) separated text of length
36
+ three, representing the role you want to ask it to, the question and
37
+ information necessary. For example, `coworker|question|information`.
38
+ """
39
+ ),
40
+ ),
41
+ ]
42
+
43
+ def delegate_work(self, command):
44
+ """Useful to delegate a specific task to a coworker."""
45
+ return self.__execute(command)
46
+
47
+ def ask_question(self, command):
48
+ """Useful to ask a question, opinion or take from a coworker."""
49
+ return self.__execute(command)
50
+
51
+ def __execute(self, command):
52
+ """Execute the command."""
53
+ try:
54
+ agent, task, information = command.split("|")
55
+ except ValueError:
56
+ return "\nError executing tool. Missing exact 3 pipe (|) separated values. For example, `coworker|task|information`.\n"
57
+
58
+ if not agent or not task or not information:
59
+ return "\nError executing tool. Missing exact 3 pipe (|) separated values. For example, `coworker|question|information`.\n"
60
+
61
+ agent = [
62
+ available_agent
63
+ for available_agent in self.agents
64
+ if available_agent.role == agent
65
+ ]
66
+
67
+ if len(agent) == 0:
68
+ return f"\nError executing tool. Co-worker mentioned on the Action Input not found, it must to be one of the following options: {', '.join([agent.role for agent in self.agents])}.\n"
69
+
70
+ agent = agent[0]
71
+ result = agent.execute_task(task, information)
72
+ return result
crewai/tools/browser_tools.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+
4
+ import requests
5
+ from crewai import Agent, Task
6
+ from langchain.tools import tool
7
+ from unstructured.partition.html import partition_html
8
+
9
+
10
+ class BrowserTools():
11
+
12
+ @tool("Scrape website content")
13
+ def scrape_and_summarize_website(website):
14
+ """Useful to scrape and summarize a website content"""
15
+ url = f"https://chrome.browserless.io/content?token={os.environ['BROWSERLESS_API_KEY']}"
16
+ payload = json.dumps({"url": website})
17
+ headers = {'cache-control': 'no-cache', 'content-type': 'application/json'}
18
+ response = requests.request("POST", url, headers=headers, data=payload)
19
+ elements = partition_html(text=response.text)
20
+ content = "\n\n".join([str(el) for el in elements])
21
+ content = [content[i:i + 8000] for i in range(0, len(content), 8000)]
22
+ summaries = []
23
+ for chunk in content:
24
+ agent = Agent(
25
+ role='Principal Researcher',
26
+ goal=
27
+ 'Do amazing research and summaries based on the content you are working with',
28
+ backstory=
29
+ "You're a Principal Researcher at a big company and you need to do research about a given topic.",
30
+ allow_delegation=False)
31
+ task = Task(
32
+ agent=agent,
33
+ description=
34
+ f'Analyze and summarize the content below, make sure to include the most relevant information in the summary, return only the summary nothing else.\n\nCONTENT\n----------\n{chunk}'
35
+ )
36
+ summary = task.execute()
37
+ summaries.append(summary)
38
+ return "\n\n".join(summaries)
crewai/tools/cache_tools.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain.tools import Tool
2
+ from pydantic import BaseModel, ConfigDict, Field
3
+
4
+ from crewai.agents.cache import CacheHandler
5
+
6
+
7
+ class CacheTools(BaseModel):
8
+ """Default tools to hit the cache."""
9
+
10
+ model_config = ConfigDict(arbitrary_types_allowed=True)
11
+ name: str = "Hit Cache"
12
+ cache_handler: CacheHandler = Field(
13
+ description="Cache Handler for the crew",
14
+ default=CacheHandler(),
15
+ )
16
+
17
+ def tool(self):
18
+ return Tool.from_function(
19
+ func=self.hit_cache,
20
+ name=self.name,
21
+ description="Reads directly from the cache",
22
+ )
23
+
24
+ def hit_cache(self, key):
25
+ split = key.split("tool:")
26
+ tool = split[1].split("|input:")[0].strip()
27
+ tool_input = split[1].split("|input:")[1].strip()
28
+ return self.cache_handler.read(tool, tool_input)
crewai/tools/calculator_tools.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain.tools import tool
2
+
3
+
4
+ class CalculatorTools():
5
+
6
+ @tool("Make a calcualtion")
7
+ def calculate(operation):
8
+ """Useful to perform any mathematica calculations,
9
+ like sum, minus, mutiplcation, division, etc.
10
+ The input to this tool should be a mathematical
11
+ expression, a couple examples are `200*7` or `5000/2*10`
12
+ """
13
+ return eval(operation)
crewai/tools/gemini_tools.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # tools created using gemini
2
+
3
+ import json
4
+ import os
5
+
6
+ import google.generativeai as genai
7
+ from google.api_core import exceptions
8
+
9
+ # Retrieve API Key from Environment Variable
10
+ GOOGLE_AI_STUDIO = os.environ.get('GOOGLE_API_KEY')
11
+
12
+ # Ensure the API key is available
13
+ if not GOOGLE_AI_STUDIO:
14
+ raise ValueError("API key not found. Please set the GOOGLE_AI_STUDIO2 environment variable.")
15
+
16
+ import requests
17
+ from langchain.tools import tool
18
+
19
+ # Rest of your code remains the same
20
+ genai.configure(api_key=GOOGLE_AI_STUDIO)
21
+ model = genai.GenerativeModel('gemini-pro')
22
+
23
+ class GeminiSearchTools():
24
+ @tool("Gemini search the internet")
25
+ def gemini_search(query):
26
+ """
27
+ Searches for content based on the provided query using the Gemini model.
28
+ Handles DeadlineExceeded exceptions from the Google API.
29
+
30
+ Args:
31
+ query (str): The search query.
32
+
33
+ Returns:
34
+ str: The response text from the Gemini model or an error message.
35
+ """
36
+ try:
37
+ response = model.generate_content(query)
38
+ return response.text
39
+ except exceptions.DeadlineExceeded as e:
40
+ # Handle the DeadlineExceeded exception here
41
+ print("Error: Deadline Exceeded -", str(e))
42
+ # You can return a custom message or take other appropriate actions
43
+ return "Error: The request timed out. Please try again later."
44
+
45
+
46
+
47
+ @tool("Gemini search news on the internet")
48
+ def gemini_search_news(query):
49
+ """
50
+ Searches for content based on the provided query using the Gemini model.
51
+ Handles DeadlineExceeded exceptions from the Google API.
52
+
53
+ Args:
54
+ query (str): The search query.
55
+
56
+ Returns:
57
+ str: The response text from the Gemini model or an error message.
58
+ """
59
+ try:
60
+ response = model.generate_content(query)
61
+ return response.text
62
+ except exceptions.DeadlineExceeded as e:
63
+ # Handle the DeadlineExceeded exception here
64
+ print("Error: Deadline Exceeded -", str(e))
65
+ # You can return a custom message or take other appropriate actions
66
+ return "Error: The request timed out. Please try again later."
crewai/tools/search_tools.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+
4
+ import requests
5
+ from langchain.tools import tool
6
+
7
+
8
+ class SearchTools():
9
+ @tool("Search the internet")
10
+ def search_internet(query):
11
+ """Useful to search the internet
12
+ about a a given topic and return relevant results"""
13
+ top_result_to_return = 4
14
+ url = "https://google.serper.dev/search"
15
+ payload = json.dumps({"q": query})
16
+ headers = {
17
+ 'X-API-KEY': os.environ['SERPER_API_KEY'],
18
+ 'content-type': 'application/json'
19
+ }
20
+ response = requests.request("POST", url, headers=headers, data=payload)
21
+ results = response.json()['organic']
22
+ string = []
23
+ for result in results[:top_result_to_return]:
24
+ try:
25
+ string.append('\n'.join([
26
+ f"Title: {result['title']}", f"Link: {result['link']}",
27
+ f"Snippet: {result['snippet']}", "\n-----------------"
28
+ ]))
29
+ except KeyError:
30
+ next
31
+
32
+ return '\n'.join(string)
33
+
34
+ @tool("Search news on the internet")
35
+ def search_news(query):
36
+ """Useful to search news about a company, stock or any other
37
+ topic and return relevant results"""""
38
+ top_result_to_return = 4
39
+ url = "https://google.serper.dev/news"
40
+ payload = json.dumps({"q": query})
41
+ headers = {
42
+ 'X-API-KEY': os.environ['SERPER_API_KEY'],
43
+ 'content-type': 'application/json'
44
+ }
45
+ response = requests.request("POST", url, headers=headers, data=payload)
46
+ results = response.json()['news']
47
+ string = []
48
+ for result in results[:top_result_to_return]:
49
+ try:
50
+ string.append('\n'.join([
51
+ f"Title: {result['title']}", f"Link: {result['link']}",
52
+ f"Snippet: {result['snippet']}", "\n-----------------"
53
+ ]))
54
+ except KeyError:
55
+ next
56
+
57
+ return '\n'.join(string)
crewai/tools/sec_tools.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ import requests
4
+
5
+ from langchain.tools import tool
6
+ from langchain.text_splitter import CharacterTextSplitter
7
+ from langchain_community.embeddings import OpenAIEmbeddings
8
+ from langchain_community.vectorstores import FAISS
9
+
10
+ from sec_api import QueryApi
11
+ from unstructured.partition.html import partition_html
12
+
13
+ class SECTools():
14
+ @tool("Search 10-Q form")
15
+ def search_10q(data):
16
+ """
17
+ Useful to search information from the latest 10-Q form for a
18
+ given stock.
19
+ The input to this tool should be a pipe (|) separated text of
20
+ length two, representing the stock ticker you are interested, what
21
+ question you have from it.
22
+ For example, `AAPL|what was last quarter's revenue`.
23
+ """
24
+ stock, ask = data.split("|")
25
+ queryApi = QueryApi(api_key=os.environ['SEC_API_API_KEY'])
26
+ query = {
27
+ "query": {
28
+ "query_string": {
29
+ "query": f"ticker:{stock} AND formType:\"10-Q\""
30
+ }
31
+ },
32
+ "from": "0",
33
+ "size": "1",
34
+ "sort": [{ "filedAt": { "order": "desc" }}]
35
+ }
36
+
37
+ filings = queryApi.get_filings(query)['filings']
38
+ link = filings[0]['linkToFilingDetails']
39
+ answer = SECTools.__embedding_search(link, ask)
40
+ return answer
41
+
42
+ @tool("Search 10-K form")
43
+ def search_10k(data):
44
+ """
45
+ Useful to search information from the latest 10-K form for a
46
+ given stock.
47
+ The input to this tool should be a pipe (|) separated text of
48
+ length two, representing the stock ticker you are interested, what
49
+ question you have from it.
50
+ For example, `AAPL|what was last year's revenue`.
51
+ """
52
+ stock, ask = data.split("|")
53
+ queryApi = QueryApi(api_key=os.environ['SEC_API_API_KEY'])
54
+ query = {
55
+ "query": {
56
+ "query_string": {
57
+ "query": f"ticker:{stock} AND formType:\"10-K\""
58
+ }
59
+ },
60
+ "from": "0",
61
+ "size": "1",
62
+ "sort": [{ "filedAt": { "order": "desc" }}]
63
+ }
64
+
65
+ filings = queryApi.get_filings(query)['filings']
66
+ link = filings[0]['linkToFilingDetails']
67
+ answer = SECTools.__embedding_search(link, ask)
68
+ return answer
69
+
70
+ def __embedding_search(url, ask):
71
+ text = SECTools.__download_form_html(url)
72
+ elements = partition_html(text=text)
73
+ content = "\n".join([str(el) for el in elements])
74
+ text_splitter = CharacterTextSplitter(
75
+ separator = "\n",
76
+ chunk_size = 1000,
77
+ chunk_overlap = 150,
78
+ length_function = len,
79
+ is_separator_regex = False,
80
+ )
81
+ docs = text_splitter.create_documents([content])
82
+ retriever = FAISS.from_documents(
83
+ docs, OpenAIEmbeddings()
84
+ ).as_retriever()
85
+ answers = retriever.get_relevant_documents(ask, top_k=4)
86
+ answers = "\n\n".join([a.page_content for a in answers])
87
+ return answers
88
+
89
+ def __download_form_html(url):
90
+ headers = {
91
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
92
+ 'Accept-Encoding': 'gzip, deflate, br',
93
+ 'Accept-Language': 'en-US,en;q=0.9,pt-BR;q=0.8,pt;q=0.7',
94
+ 'Cache-Control': 'max-age=0',
95
+ 'Dnt': '1',
96
+ 'Sec-Ch-Ua': '"Not_A Brand";v="8", "Chromium";v="120"',
97
+ 'Sec-Ch-Ua-Mobile': '?0',
98
+ 'Sec-Ch-Ua-Platform': '"macOS"',
99
+ 'Sec-Fetch-Dest': 'document',
100
+ 'Sec-Fetch-Mode': 'navigate',
101
+ 'Sec-Fetch-Site': 'none',
102
+ 'Sec-Fetch-User': '?1',
103
+ 'Upgrade-Insecure-Requests': '1',
104
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
105
+ }
106
+
107
+ response = requests.get(url, headers=headers)
108
+ return response.text