File size: 2,443 Bytes
c631b4c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a3dac62
c631b4c
 
 
 
 
a3dac62
c631b4c
 
 
 
 
a3dac62
 
 
c631b4c
a3dac62
c631b4c
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import re
from typing import Union

from langchain.agents.output_parsers import ReActSingleInputOutputParser
from langchain_core.agents import AgentAction, AgentFinish

from .cache import CacheHandler, CacheHit
from .exceptions import TaskRepeatedUsageException
from .tools_handler import ToolsHandler

FINAL_ANSWER_ACTION = "Final Answer:"
FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE = (
    "Parsing LLM output produced both a final answer and a parse-able action:"
)


class CrewAgentOutputParser(ReActSingleInputOutputParser):
    """Parses ReAct-style LLM calls that have a single tool input.

    Expects output to be in one of two formats.

    If the output signals that an action should be taken,
    should be in the below format. This will result in an AgentAction
    being returned.

    ```
    Thought: agent thought here
    Action: search
    Action Input: what is the temperature in SF?
    ```

    If the output signals that a final answer should be given,
    should be in the below format. This will result in an AgentFinish
    being returned.

    ```
    Thought: agent thought here
    Final Answer: The temperature is 100 degrees
    ```

    It also prevents tools from being reused in a roll.
    """

    class Config:
        arbitrary_types_allowed = True

    tools_handler: ToolsHandler
    cache: CacheHandler

    def parse(self, text: str) -> Union[AgentAction, AgentFinish, CacheHit]:
        FINAL_ANSWER_ACTION in text
        regex = (
            r"Action\s*\d*\s*:[\s]*(.*?)[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
        )
        if action_match := re.search(regex, text, re.DOTALL):
            action = action_match.group(1).strip()
            action_input = action_match.group(2)
            tool_input = action_input.strip(" ")
            tool_input = tool_input.strip('"')

            if last_tool_usage := self.tools_handler.last_used_tool:
                usage = {
                    "tool": action,
                    "input": tool_input,
                }
                if usage == last_tool_usage:
                    raise TaskRepeatedUsageException(
                        tool=action, tool_input=tool_input, text=text
                    )

            if result := self.cache.read(action, tool_input):
                action = AgentAction(action, tool_input, text)
                return CacheHit(action=action, cache=self.cache)

        return super().parse(text)