        :meta private:
        """
        if self.return_intermediate_steps:
            return self.agent.return_values + ["intermediate_steps"]
        else:
            return self.agent.return_values

    def lookup_tool(self, name: str) -> BaseTool:
        """Lookup tool by name."""
        return {tool.name: tool for tool in self.tools}[name]

    def _should_continue(self, iterations: int, time_elapsed: float) -> bool:
        if self.max_iterations is not None and iterations >= self.max_iterations:
            return False
        if self.max_execution_time is not None and time_elapsed >= self.max_execution_time:
            return False

        return True

    def _return(
        self,
        output: AgentFinish,
        intermediate_steps: list,
        run_manager: Optional[CallbackManagerForChainRun] = None,
    ) -> Dict[str, Any]:
        if run_manager:
            run_manager.on_agent_finish(output, color="green", verbose=self.verbose)
        final_output = output.return_values
        if self.return_intermediate_steps:
            final_output["intermediate_steps"] = intermediate_steps
        return final_output

    def _take_next_step(
        self,
        name_to_tool_map: Dict[str, BaseTool],
        color_mapping: Dict[str, str],
        inputs: Dict[str, str],
        intermediate_steps: List[Tuple[AgentAction, str]],
        run_manager: Optional[CallbackManagerForChainRun] = None,
    ) -> Union[AgentFinish, List[Tuple[AgentAction, str]]]:
        """Take a single step in the thought-action-observation loop.

        Override this to take control of how the agent makes and acts on choices.
        """
        try:
            # Call the LLM to see what to do.
            output = self.agent.plan(
                intermediate_steps,
                callbacks=run_manager.get_child() if run_manager else None,
                **inputs,
            )


        if self.max_iterations is not None and iterations >= self.max_iterations:
            return False
        if self.max_execution_time is not None and time_elapsed >= self.max_execution_time:
            return False

        return True

    def _return(
        self,
        output: AgentFinish,
        intermediate_steps: list,
        run_manager: Optional[CallbackManagerForChainRun] = None,
    ) -> Dict[str, Any]:
        if run_manager:
            run_manager.on_agent_finish(output, color="green", verbose=self.verbose)
        final_output = output.return_values
        if self.return_intermediate_steps:
            final_output["intermediate_steps"] = intermediate_steps
        return final_output

    def _take_next_step(
        self,
        name_to_tool_map: Dict[str, BaseTool],
        color_mapping: Dict[str, str],
        inputs: Dict[str, str],
        intermediate_steps: List[Tuple[AgentAction, str]],
        run_manager: Optional[CallbackManagerForChainRun] = None,
    ) -> Union[AgentFinish, List[Tuple[AgentAction, str]]]:
        """Take a single step in the thought-action-observation loop.

        Override this to take control of how the agent makes and acts on choices.
        """
        try:
            # Call the LLM to see what to do.
            output = self.agent.plan(
                intermediate_steps,
                callbacks=run_manager.get_child() if run_manager else None,
                **inputs,
            )
        except OutputParserException as e:
            if isinstance(self.handle_parsing_errors, bool):
                raise_error = not self.handle_parsing_errors
            else:
                raise_error = False
            if raise_error:
                raise e
            text = str(e)
            if isinstance(self.handle_parsing_errors, bool):
                observation = "Invalid or incomplete response"
            elif isinstance(self.handle_parsing_errors, str):
                observation = self.handle_parsing_errors
            elif callable(self.handle_parsing_errors):
                observation = self.handle_parsing_errors(e)
            else:
                raise ValueError("Got unexpected type of `handle_parsing_errors`")
            output = AgentAction("_Exception", observation, text)
            tool_run_kwargs = self.agent.tool_run_logging_kwargs()
            observation = ExceptionTool().run(
                output.tool_input,
                verbose=self.verbose,
                color=None,
                callbacks=run_manager.get_child() if run_manager else None,
                **tool_run_kwargs,
            )
            return [(output, observation)]
        # If the tool chosen is the finishing tool, then we end and return.
        if isinstance(output, AgentFinish):
            return output
        actions: List[AgentAction]
        if isinstance(output, AgentAction):
            actions = [output]
        else:
            actions = output
        result = []
        for agent_action in actions:
            if run_manager:
                run_manager.on_agent_action(agent_action, color="green")
            # Otherwise we lookup the tool
            if agent_action.tool in name_to_tool_map:
                tool = name_to_tool_map[agent_action.tool]
                return_direct = tool.return_direct
                color = color_mapping[agent_action.tool]
                tool_run_kwargs = self.agent.tool_run_logging_kwargs()
                if return_direct:
                    tool_run_kwargs["llm_prefix"] = ""
                # We then call the tool on the tool input to get an observation
                observation = tool.run(
                    agent_action.tool_input,
                    verbose=self.verbose,
                    color=color,
                    callbacks=run_manager.get_child() if run_manager else None,
                    **tool_run_kwargs,
                )
            else:
                tool_run_kwargs = self.agent.tool_run_logging_kwargs()
                observation = InvalidTool().run(
                    agent_action.tool,
                    verbose=self.verbose,
                    color=None,
                    callbacks=run_manager.get_child() if run_manager else None,
                    **tool_run_kwargs,
                )
            result.append((agent_action, observation))
        return result

    def _call(
        self,
        inputs: Dict[str, str],
        run_manager: Optional[CallbackManagerForChainRun] = None,
    ) -> Dict[str, Any]:
        """Run text through and get agent response."""
        # Construct a mapping of tool name to tool for easy lookup
        name_to_tool_map = {tool.name: tool for tool in self.tools}
        # We construct a mapping from each tool to a color, used for logging.
        color_mapping = get_color_mapping([tool.name for tool in self.tools], excluded_colors=["green"])
        intermediate_steps: List[Tuple[AgentAction, str]] = []
        # Let's start tracking the number of iterations and time elapsed
        iterations = 0
        time_elapsed = 0.0
        start_time = time.time()
        # We now enter the agent loop (until it returns something).
        while self._should_continue(iterations, time_elapsed):
            next_step_output = self._take_next_step(
                name_to_tool_map,


                # We then call the tool on the tool input to get an observation
                observation = tool.run(
                    agent_action.tool_input,
                    verbose=self.verbose,
                    color=color,
                    callbacks=run_manager.get_child() if run_manager else None,
                    **tool_run_kwargs,
                )
            else:
                tool_run_kwargs = self.agent.tool_run_logging_kwargs()
                observation = InvalidTool().run(
                    agent_action.tool,
                    verbose=self.verbose,
                    color=None,
                    callbacks=run_manager.get_child() if run_manager else None,
                    **tool_run_kwargs,
                )
            result.append((agent_action, observation))
        return result

    def _call(
        self,
        inputs: Dict[str, str],
        run_manager: Optional[CallbackManagerForChainRun] = None,
    ) -> Dict[str, Any]:
        """Run text through and get agent response."""
        # Construct a mapping of tool name to tool for easy lookup
        name_to_tool_map = {tool.name: tool for tool in self.tools}
        # We construct a mapping from each tool to a color, used for logging.
        color_mapping = get_color_mapping([tool.name for tool in self.tools], excluded_colors=["green"])
        intermediate_steps: List[Tuple[AgentAction, str]] = []
        # Let's start tracking the number of iterations and time elapsed
        iterations = 0
        time_elapsed = 0.0
        start_time = time.time()
        # We now enter the agent loop (until it returns something).
        while self._should_continue(iterations, time_elapsed):
            next_step_output = self._take_next_step(
                name_to_tool_map,
                color_mapping,
                inputs,
                intermediate_steps,
                run_manager=run_manager,
            )
            if isinstance(next_step_output, AgentFinish):
                return self._return(next_step_output, intermediate_steps, run_manager=run_manager)

            intermediate_steps.extend(next_step_output)
            if len(next_step_output) == 1:
                next_step_action = next_step_output[0]
                # See if tool should return directly
                tool_return = self._get_tool_return(next_step_action)
                if tool_return is not None:
                    return self._return(tool_return, intermediate_steps, run_manager=run_manager)
            iterations += 1
            time_elapsed = time.time() - start_time
        output = self.agent.return_stopped_response(self.early_stopping_method, intermediate_steps, **inputs)
        return self._return(output, intermediate_steps, run_manager=run_manager)

    def _get_tool_return(self, next_step_output: Tuple[AgentAction, Union[str, DataModel]]) -> Optional[AgentFinish]:
        """Check if the tool is a returning tool."""
        agent_action, full_observation = next_step_output
        observation = full_observation
        if isinstance(full_observation, DataModel):
            llm_raw_observation = full_observation.get_llm_side_data()
            observation = MessageDataModel.extract_tool_response_for_llm(
                llm_raw_observation, tool_style=self.memory.style
            )
        name_to_tool_map = {tool.name: tool for tool in self.tools}
        # Invalid tools won't be in the map, so we return False.
        if agent_action.tool in name_to_tool_map:
            if name_to_tool_map[agent_action.tool].return_direct:
                return AgentFinish(
                    {self.agent.return_values[0]: observation},
                    "",
                )
        return None


                color_mapping,
                inputs,
                intermediate_steps,
                run_manager=run_manager,
            )
            if isinstance(next_step_output, AgentFinish):
                return self._return(next_step_output, intermediate_steps, run_manager=run_manager)

            intermediate_steps.extend(next_step_output)
            if len(next_step_output) == 1:
                next_step_action = next_step_output[0]
                # See if tool should return directly
                tool_return = self._get_tool_return(next_step_action)
                if tool_return is not None:
                    return self._return(tool_return, intermediate_steps, run_manager=run_manager)
            iterations += 1
            time_elapsed = time.time() - start_time
        output = self.agent.return_stopped_response(self.early_stopping_method, intermediate_steps, **inputs)
        return self._return(output, intermediate_steps, run_manager=run_manager)

    def _get_tool_return(self, next_step_output: Tuple[AgentAction, Union[str, DataModel]]) -> Optional[AgentFinish]:
        """Check if the tool is a returning tool."""
        agent_action, full_observation = next_step_output
        observation = full_observation
        if isinstance(full_observation, DataModel):
            llm_raw_observation = full_observation.get_llm_side_data()
            observation = MessageDataModel.extract_tool_response_for_llm(
                llm_raw_observation, tool_style=self.memory.style
            )
        name_to_tool_map = {tool.name: tool for tool in self.tools}
        # Invalid tools won't be in the map, so we return False.
        if agent_action.tool in name_to_tool_map:
            if name_to_tool_map[agent_action.tool].return_direct:
                return AgentFinish(
                    {self.agent.return_values[0]: observation},
                    "",
                )
        return None


from __future__ import annotations

from typing import Optional, Union
from pydantic import Extra

from langchain.schema import (
    AgentAction,
    AgentFinish,
)
from real_agents.adapters.agent_helpers.agent import AgentOutputParser
from real_agents.adapters.schema import AgentTransition

class ConversationOutputParser(AgentOutputParser):
    class Config:
        """Configuration for this pydantic object."""

        extra = Extra.allow
        arbitrary_types_allowed = True

    def get_format_instructions(self, app_name="copilot") -> str:
        from real_agents.data_agent.copilot_prompt import FORMAT_INSTRUCTIONS as COPILOT_FORMAT_INSTRUCTIONS
        from real_agents.plugins_agent.plugin_prompt import FORMAT_INSTRUCTIONS as PLUGINS_FORMAT_INSTRUCTIONS
        from real_agents.web_agent.webot_prompt import FORMAT_INSTRUCTIONS as WEBOT_FORMAT_INSTRUCTIONS

        if app_name == "copilot":
            return COPILOT_FORMAT_INSTRUCTIONS
        elif app_name == "webot":
            return WEBOT_FORMAT_INSTRUCTIONS
        elif app_name == "plugins":
            return PLUGINS_FORMAT_INSTRUCTIONS
        else:
            raise ValueError(f"Unknown app_name {app_name}")

    def parse(self, text: str) -> Union[AgentTransition, AgentAction, AgentFinish]:
        cleaned_output = text.strip()
        import re

        def _extract_explanation(json_string: str) -> Optional[str]:
            if "```" in json_string:
                return json_string.split("```")[0]
            else:
                return None

        def _extract_value(json_string: str, key: str) -> str:
            pattern = re.compile(rf'"?{key}"?\s*:\s*("((?:[^"\\]|\\.)*)"|(\b[^,\s]*\b))', re.MULTILINE)
            match = pattern.search(json_string)
            if match:
                return match.group(1).replace('\\"', '"').replace("\\\\", "\\").strip('"').strip("'")

            raise ValueError(f"Could not find {key} in {json_string}")

        try:
            _action = _extract_value(cleaned_output, "action")
            _action_input = _extract_value(cleaned_output, "action_input")
            if _action == "Final Answer":
                return AgentFinish({"output": _action_input}, cleaned_output)

            # Transition sentence should only be used not final answer.
            _explanation = _extract_explanation(cleaned_output)
            return AgentAction(_action, _action_input, cleaned_output)
        except Exception:
            if cleaned_output.startswith("Action:"):
                lines = cleaned_output.splitlines()
                action = lines[1].strip()
                import textwrap

                action_input = textwrap.dedent("\n".join(lines[2:])).strip()
                return AgentAction(action, action_input, cleaned_output)

            return AgentFinish({"output": cleaned_output}, cleaned_output)

    @property
    def _type(self) -> str:
        return "conversational_chat"


Config

from __future__ import annotations

from typing import Optional, Union
from pydantic import Extra

from langchain.schema import (
    AgentAction,
    AgentFinish,
)
from real_agents.adapters.agent_helpers.agent import AgentOutputParser
from real_agents.adapters.schema import AgentTransition

class ConversationOutputParser(AgentOutputParser):
    class Config:
        """Configuration for this pydantic object."""

        extra = Extra.allow
        arbitrary_types_allowed = True

    def get_format_instructions(self, app_name="copilot") -> str:
        from real_agents.data_agent.copilot_prompt import FORMAT_INSTRUCTIONS as COPILOT_FORMAT_INSTRUCTIONS
        from real_agents.plugins_agent.plugin_prompt import FORMAT_INSTRUCTIONS as PLUGINS_FORMAT_INSTRUCTIONS
        from real_agents.web_agent.webot_prompt import FORMAT_INSTRUCTIONS as WEBOT_FORMAT_INSTRUCTIONS

        if app_name == "copilot":
            return COPILOT_FORMAT_INSTRUCTIONS
        elif app_name == "webot":
            return WEBOT_FORMAT_INSTRUCTIONS
        elif app_name == "plugins":
            return PLUGINS_FORMAT_INSTRUCTIONS
        else:
            raise ValueError(f"Unknown app_name {app_name}")

    def parse(self, text: str) -> Union[AgentTransition, AgentAction, AgentFinish]:
        cleaned_output = text.strip()
        import re

        def _extract_explanation(json_string: str) -> Optional[str]:


from __future__ import annotations

from typing import Optional, Union
from pydantic import Extra

from langchain.schema import (
    AgentAction,
    AgentFinish,
)
from real_agents.adapters.agent_helpers.agent import AgentOutputParser
from real_agents.adapters.schema import AgentTransition

class ConversationOutputParser(AgentOutputParser):
    class Config:
        """Configuration for this pydantic object."""

        extra = Extra.allow
        arbitrary_types_allowed = True

    def get_format_instructions(self, app_name="copilot") -> str:
        from real_agents.data_agent.copilot_prompt import FORMAT_INSTRUCTIONS as COPILOT_FORMAT_INSTRUCTIONS
        from real_agents.plugins_agent.plugin_prompt import FORMAT_INSTRUCTIONS as PLUGINS_FORMAT_INSTRUCTIONS
        from real_agents.web_agent.webot_prompt import FORMAT_INSTRUCTIONS as WEBOT_FORMAT_INSTRUCTIONS

        if app_name == "copilot":
            return COPILOT_FORMAT_INSTRUCTIONS
        elif app_name == "webot":
            return WEBOT_FORMAT_INSTRUCTIONS
        elif app_name == "plugins":
            return PLUGINS_FORMAT_INSTRUCTIONS
        else:
            raise ValueError(f"Unknown app_name {app_name}")

    def parse(self, text: str) -> Union[AgentTransition, AgentAction, AgentFinish]:
        cleaned_output = text.strip()
        import re

        def _extract_explanation(json_string: str) -> Optional[str]:
            if "```" in json_string:
                return json_string.split("```")[0]
            else:
                return None

        def _extract_value(json_string: str, key: str) -> str:
            pattern = re.compile(rf'"?{key}"?\s*:\s*("((?:[^"\\]|\\.)*)"|(\b[^,\s]*\b))', re.MULTILINE)
            match = pattern.search(json_string)
            if match:
                return match.group(1).replace('\\"', '"').replace("\\\\", "\\").strip('"').strip("'")

            raise ValueError(f"Could not find {key} in {json_string}")

        try:


parse

    class Config:
        """Configuration for this pydantic object."""

        extra = Extra.allow
        arbitrary_types_allowed = True

    def get_format_instructions(self, app_name="copilot") -> str:
        from real_agents.data_agent.copilot_prompt import FORMAT_INSTRUCTIONS as COPILOT_FORMAT_INSTRUCTIONS
        from real_agents.plugins_agent.plugin_prompt import FORMAT_INSTRUCTIONS as PLUGINS_FORMAT_INSTRUCTIONS
        from real_agents.web_agent.webot_prompt import FORMAT_INSTRUCTIONS as WEBOT_FORMAT_INSTRUCTIONS

        if app_name == "copilot":
            return COPILOT_FORMAT_INSTRUCTIONS
        elif app_name == "webot":
            return WEBOT_FORMAT_INSTRUCTIONS
        elif app_name == "plugins":
            return PLUGINS_FORMAT_INSTRUCTIONS
        else:
            raise ValueError(f"Unknown app_name {app_name}")

    def parse(self, text: str) -> Union[AgentTransition, AgentAction, AgentFinish]:
        cleaned_output = text.strip()
        import re

        def _extract_explanation(json_string: str) -> Optional[str]:
            if "```" in json_string:
                return json_string.split("```")[0]
            else:
                return None

        def _extract_value(json_string: str, key: str) -> str:
            pattern = re.compile(rf'"?{key}"?\s*:\s*("((?:[^"\\]|\\.)*)"|(\b[^,\s]*\b))', re.MULTILINE)
            match = pattern.search(json_string)
            if match:
                return match.group(1).replace('\\"', '"').replace("\\\\", "\\").strip('"').strip("'")

            raise ValueError(f"Could not find {key} in {json_string}")

        try:
            _action = _extract_value(cleaned_output, "action")
            _action_input = _extract_value(cleaned_output, "action_input")
            if _action == "Final Answer":
                return AgentFinish({"output": _action_input}, cleaned_output)

            # Transition sentence should only be used not final answer.
            _explanation = _extract_explanation(cleaned_output)
            return AgentAction(_action, _action_input, cleaned_output)
        except Exception:
            if cleaned_output.startswith("Action:"):
                lines = cleaned_output.splitlines()
                action = lines[1].strip()
                import textwrap

                action_input = textwrap.dedent("\n".join(lines[2:])).strip()
                return AgentAction(action, action_input, cleaned_output)

            return AgentFinish({"output": cleaned_output}, cleaned_output)

    @property
    def _type(self) -> str:
        return "conversational_chat"


            _action = _extract_value(cleaned_output, "action")
            _action_input = _extract_value(cleaned_output, "action_input")
            if _action == "Final Answer":
                return AgentFinish({"output": _action_input}, cleaned_output)

            # Transition sentence should only be used not final answer.
            _explanation = _extract_explanation(cleaned_output)
            return AgentAction(_action, _action_input, cleaned_output)
        except Exception:
            if cleaned_output.startswith("Action:"):
                lines = cleaned_output.splitlines()
                action = lines[1].strip()
                import textwrap

                action_input = textwrap.dedent("\n".join(lines[2:])).strip()
                return AgentAction(action, action_input, cleaned_output)

            return AgentFinish({"output": cleaned_output}, cleaned_output)

    @property
    def _type(self) -> str:
        return "conversational_chat"


        arbitrary_types_allowed = True

    def get_format_instructions(self, app_name="copilot") -> str:
        from real_agents.data_agent.copilot_prompt import FORMAT_INSTRUCTIONS as COPILOT_FORMAT_INSTRUCTIONS
        from real_agents.plugins_agent.plugin_prompt import FORMAT_INSTRUCTIONS as PLUGINS_FORMAT_INSTRUCTIONS
        from real_agents.web_agent.webot_prompt import FORMAT_INSTRUCTIONS as WEBOT_FORMAT_INSTRUCTIONS

        if app_name == "copilot":
            return COPILOT_FORMAT_INSTRUCTIONS
        elif app_name == "webot":
            return WEBOT_FORMAT_INSTRUCTIONS
        elif app_name == "plugins":
            return PLUGINS_FORMAT_INSTRUCTIONS
        else:
            raise ValueError(f"Unknown app_name {app_name}")

    def parse(self, text: str) -> Union[AgentTransition, AgentAction, AgentFinish]:
        cleaned_output = text.strip()
        import re

        def _extract_explanation(json_string: str) -> Optional[str]:
            if "```" in json_string:
                return json_string.split("```")[0]
            else:
                return None

        def _extract_value(json_string: str, key: str) -> str:
            pattern = re.compile(rf'"?{key}"?\s*:\s*("((?:[^"\\]|\\.)*)"|(\b[^,\s]*\b))', re.MULTILINE)
            match = pattern.search(json_string)
            if match:
                return match.group(1).replace('\\"', '"').replace("\\\\", "\\").strip('"').strip("'")

            raise ValueError(f"Could not find {key} in {json_string}")

        try:
            _action = _extract_value(cleaned_output, "action")
            _action_input = _extract_value(cleaned_output, "action_input")
            if _action == "Final Answer":
                return AgentFinish({"output": _action_input}, cleaned_output)

            # Transition sentence should only be used not final answer.
            _explanation = _extract_explanation(cleaned_output)
            return AgentAction(_action, _action_input, cleaned_output)
        except Exception:
            if cleaned_output.startswith("Action:"):


        if app_name == "copilot":
            return COPILOT_FORMAT_INSTRUCTIONS
        elif app_name == "webot":
            return WEBOT_FORMAT_INSTRUCTIONS
        elif app_name == "plugins":
            return PLUGINS_FORMAT_INSTRUCTIONS
        else:
            raise ValueError(f"Unknown app_name {app_name}")

    def parse(self, text: str) -> Union[AgentTransition, AgentAction, AgentFinish]:
        cleaned_output = text.strip()
        import re

        def _extract_explanation(json_string: str) -> Optional[str]:
            if "```" in json_string:
                return json_string.split("```")[0]
            else:
                return None

        def _extract_value(json_string: str, key: str) -> str:
            pattern = re.compile(rf'"?{key}"?\s*:\s*("((?:[^"\\]|\\.)*)"|(\b[^,\s]*\b))', re.MULTILINE)
            match = pattern.search(json_string)
            if match:
                return match.group(1).replace('\\"', '"').replace("\\\\", "\\").strip('"').strip("'")

            raise ValueError(f"Could not find {key} in {json_string}")

        try:
            _action = _extract_value(cleaned_output, "action")
            _action_input = _extract_value(cleaned_output, "action_input")
            if _action == "Final Answer":
                return AgentFinish({"output": _action_input}, cleaned_output)

            # Transition sentence should only be used not final answer.
            _explanation = _extract_explanation(cleaned_output)
            return AgentAction(_action, _action_input, cleaned_output)
        except Exception:
            if cleaned_output.startswith("Action:"):
                lines = cleaned_output.splitlines()
                action = lines[1].strip()
                import textwrap

                action_input = textwrap.dedent("\n".join(lines[2:])).strip()
                return AgentAction(action, action_input, cleaned_output)

            return AgentFinish({"output": cleaned_output}, cleaned_output)


"""Interface for tools."""
from inspect import signature
from typing import Any, Awaitable, Callable, Dict, Optional, Type, Union
from pydantic import BaseModel, validate_arguments

from langchain.tools.base import BaseTool

from real_agents.adapters.data_model import DataModel
from real_agents.adapters.callbacks.manager import (
    CallbackManager,
    Callbacks,
)

class Tool(BaseTool):
    """Tool that takes in function or coroutine directly."""

    description: str = ""
    func: Callable[..., str]
    """The function to run when the tool is called."""
    coroutine: Optional[Callable[..., Awaitable[str]]] = None
    """The asynchronous version of the function."""

    @property
    def args(self) -> dict:
        if self.args_schema is not None:
            return self.args_schema.schema()["properties"]
        else:
            inferred_model = validate_arguments(self.func).model  # type: ignore
            schema = inferred_model.schema()["properties"]
            valid_keys = signature(self.func).parameters
            return {k: schema[k] for k in valid_keys}

    def _run(self, *args: Any, **kwargs: Any) -> str:
        """Use the tool."""
        return self.func(*args, **kwargs)

    async def _arun(self, *args: Any, **kwargs: Any) -> str:
        """Use the tool asynchronously."""
        if self.coroutine:
            return await self.coroutine(*args, **kwargs)
        raise NotImplementedError("Tool does not support async")

    # TODO: this is for backwards compatibility, remove in future
    def __init__(
        self, name: str, func: Callable[[str], Union[Dict[Any, Any], DataModel]], description: str, **kwargs: Any
    ) -> None:
        """Initialize tool."""
        super(Tool, self).__init__(name=name, func=func, description=description, **kwargs)

    def run(
        self,
        tool_input: Union[str, Dict],
        verbose: Optional[bool] = None,
        start_color: Optional[str] = "green",
        color: Optional[str] = "green",
        callbacks: Callbacks = None,
        **kwargs: Any,
    ) -> Any:
        """Run the tool."""
        parsed_input = self._parse_input(tool_input)
        if not self.verbose and verbose is not None:
            verbose_ = verbose
        else:
            verbose_ = self.verbose

        # todo: fix this place
        callback_manager = CallbackManager.configure(callbacks, self.callbacks, verbose=verbose_)
        # TODO: maybe also pass through run_manager is _run supports kwargs
        new_arg_supported = signature(self._run).parameters.get("run_manager")
        run_manager = callback_manager.on_tool_start(
            {"name": self.name, "description": self.description},
            tool_input if isinstance(tool_input, str) else str(tool_input),
            color=start_color,
            **kwargs,
        )
        try:
            tool_args, tool_kwargs = self._to_args_and_kwargs(parsed_input)
            observation = (
                self._run(*tool_args, run_manager=run_manager, **tool_kwargs)
                if new_arg_supported
                else self._run(*tool_args, **tool_kwargs)
            )
        except (Exception, KeyboardInterrupt) as e:
            run_manager.on_tool_error(e)
            raise e

        run_manager.on_tool_end(observation, color=color, name=self.name, **kwargs)

        return observation

class InvalidTool(BaseTool):
    """Tool that is run when invalid tool name is encountered by agent."""

    name = "invalid_tool"
    description = "Called when tool name is invalid."

    def _run(self, tool_name: str) -> str:
        """Use the tool."""
        return f"{tool_name} is not a valid tool, try another one."

    async def _arun(self, tool_name: str) -> str:
        """Use the tool asynchronously."""
        return f"{tool_name} is not a valid tool, try another one."

def tool(
    *args: Union[str, Callable],
    return_direct: bool = False,
    args_schema: Optional[Type[BaseModel]] = None,
    infer_schema: bool = True,
) -> Callable:
    """Make tools out of functions, can be used with or without arguments.

    Args:
        *args: The arguments to the tool.
        return_direct: Whether to return directly from the tool rather
            than continuing the agent loop.
        args_schema: optional argument schema for user to specify
        infer_schema: Whether to infer the schema of the arguments from
            the function's signature. This also makes the resultant tool
            accept a dictionary input to its `run()` function.

    Requires:
        - Function must be of type (str) -> str
        - Function must have a docstring

    Examples:
        .. code-block:: python

            @tool
            def search_api(query: str) -> str:
                # Searches the API for the query.
                return

            @tool("search", return_direct=True)
            def search_api(query: str) -> str:
                # Searches the API for the query.
                return
    """

    def _make_with_name(tool_name: str) -> Callable:
        def _make_tool(func: Callable) -> Tool:
            assert func.__doc__, "Function must have a docstring"
            # Description example:
            # search_api(query: str) - Searches the API for the query.
            description = f"{tool_name}{signature(func)} - {func.__doc__.strip()}"
            _args_schema = args_schema
            if _args_schema is None and infer_schema:
                _args_schema = validate_arguments(func).model  # type: ignore
            tool_ = Tool(
                name=tool_name,
                func=func,
                args_schema=_args_schema,
                description=description,
                return_direct=return_direct,
            )
            return tool_

        return _make_tool

    if len(args) == 1 and isinstance(args[0], str):
        # if the argument is a string, then we use the string as the tool name
        # Example usage: @tool("search", return_direct=True)
        return _make_with_name(args[0])
    elif len(args) == 1 and callable(args[0]):
        # if the argument is a function, then we use the function name as the tool name
        # Example usage: @tool
        return _make_with_name(args[0].__name__)(args[0])
    elif len(args) == 0:
        # if there are no arguments, then we use the function name as the tool name
        # Example usage: @tool(return_direct=True)
        def _partial(func: Callable[[str], str]) -> BaseTool:
            return _make_with_name(func.__name__)(func)

        return _partial
    else:
        raise ValueError("Too many arguments for tool decorator")


Tool

"""Interface for tools."""
from inspect import signature
from typing import Any, Awaitable, Callable, Dict, Optional, Type, Union
from pydantic import BaseModel, validate_arguments

from langchain.tools.base import BaseTool

from real_agents.adapters.data_model import DataModel
from real_agents.adapters.callbacks.manager import (
    CallbackManager,
    Callbacks,
)

class Tool(BaseTool):
    """Tool that takes in function or coroutine directly."""

    description: str = ""
    func: Callable[..., str]
    """The function to run when the tool is called."""
    coroutine: Optional[Callable[..., Awaitable[str]]] = None
    """The asynchronous version of the function."""

    @property
    def args(self) -> dict:
        if self.args_schema is not None:
            return self.args_schema.schema()["properties"]
        else:
            inferred_model = validate_arguments(self.func).model  # type: ignore
            schema = inferred_model.schema()["properties"]
            valid_keys = signature(self.func).parameters
            return {k: schema[k] for k in valid_keys}

    def _run(self, *args: Any, **kwargs: Any) -> str:
        """Use the tool."""
        return self.func(*args, **kwargs)

    async def _arun(self, *args: Any, **kwargs: Any) -> str:
        """Use the tool asynchronously."""
        if self.coroutine:
            return await self.coroutine(*args, **kwargs)
        raise NotImplementedError("Tool does not support async")

    # TODO: this is for backwards compatibility, remove in future
    def __init__(
        self, name: str, func: Callable[[str], Union[Dict[Any, Any], DataModel]], description: str, **kwargs: Any
    ) -> None:
        """Initialize tool."""
        super(Tool, self).__init__(name=name, func=func, description=description, **kwargs)

    def run(
        self,
        tool_input: Union[str, Dict],
        verbose: Optional[bool] = None,
        start_color: Optional[str] = "green",
        color: Optional[str] = "green",
        callbacks: Callbacks = None,
        **kwargs: Any,
    ) -> Any:
        """Run the tool."""
        parsed_input = self._parse_input(tool_input)
        if not self.verbose and verbose is not None:
            verbose_ = verbose
        else:
            verbose_ = self.verbose

        # todo: fix this place
        callback_manager = CallbackManager.configure(callbacks, self.callbacks, verbose=verbose_)
        # TODO: maybe also pass through run_manager is _run supports kwargs
        new_arg_supported = signature(self._run).parameters.get("run_manager")
        run_manager = callback_manager.on_tool_start(
            {"name": self.name, "description": self.description},
            tool_input if isinstance(tool_input, str) else str(tool_input),
            color=start_color,
            **kwargs,
        )
        try:
            tool_args, tool_kwargs = self._to_args_and_kwargs(parsed_input)
            observation = (
                self._run(*tool_args, run_manager=run_manager, **tool_kwargs)
                if new_arg_supported
                else self._run(*tool_args, **tool_kwargs)
            )
        except (Exception, KeyboardInterrupt) as e:
            run_manager.on_tool_error(e)
            raise e

        run_manager.on_tool_end(observation, color=color, name=self.name, **kwargs)

        return observation

class InvalidTool(BaseTool):
    """Tool that is run when invalid tool name is encountered by agent."""

    name = "invalid_tool"
    description = "Called when tool name is invalid."

    def _run(self, tool_name: str) -> str:
        """Use the tool."""
        return f"{tool_name} is not a valid tool, try another one."

    async def _arun(self, tool_name: str) -> str:
        """Use the tool asynchronously."""
        return f"{tool_name} is not a valid tool, try another one."

def tool(
    *args: Union[str, Callable],
    return_direct: bool = False,


InvalidTool

            tool_input if isinstance(tool_input, str) else str(tool_input),
            color=start_color,
            **kwargs,
        )
        try:
            tool_args, tool_kwargs = self._to_args_and_kwargs(parsed_input)
            observation = (
                self._run(*tool_args, run_manager=run_manager, **tool_kwargs)
                if new_arg_supported
                else self._run(*tool_args, **tool_kwargs)
            )
        except (Exception, KeyboardInterrupt) as e:
            run_manager.on_tool_error(e)
            raise e

        run_manager.on_tool_end(observation, color=color, name=self.name, **kwargs)

        return observation

class InvalidTool(BaseTool):
    """Tool that is run when invalid tool name is encountered by agent."""

    name = "invalid_tool"
    description = "Called when tool name is invalid."

    def _run(self, tool_name: str) -> str:
        """Use the tool."""
        return f"{tool_name} is not a valid tool, try another one."

    async def _arun(self, tool_name: str) -> str:
        """Use the tool asynchronously."""
        return f"{tool_name} is not a valid tool, try another one."

def tool(
    *args: Union[str, Callable],
    return_direct: bool = False,
    args_schema: Optional[Type[BaseModel]] = None,
    infer_schema: bool = True,
) -> Callable:
    """Make tools out of functions, can be used with or without arguments.

    Args:
        *args: The arguments to the tool.
        return_direct: Whether to return directly from the tool rather
            than continuing the agent loop.
        args_schema: optional argument schema for user to specify
        infer_schema: Whether to infer the schema of the arguments from
            the function's signature. This also makes the resultant tool
            accept a dictionary input to its `run()` function.

    Requires:


tool

        run_manager.on_tool_end(observation, color=color, name=self.name, **kwargs)

        return observation

class InvalidTool(BaseTool):
    """Tool that is run when invalid tool name is encountered by agent."""

    name = "invalid_tool"
    description = "Called when tool name is invalid."

    def _run(self, tool_name: str) -> str:
        """Use the tool."""
        return f"{tool_name} is not a valid tool, try another one."

    async def _arun(self, tool_name: str) -> str:
        """Use the tool asynchronously."""
        return f"{tool_name} is not a valid tool, try another one."

def tool(
    *args: Union[str, Callable],
    return_direct: bool = False,
    args_schema: Optional[Type[BaseModel]] = None,
    infer_schema: bool = True,
) -> Callable:
    """Make tools out of functions, can be used with or without arguments.

    Args:
        *args: The arguments to the tool.
        return_direct: Whether to return directly from the tool rather
            than continuing the agent loop.
        args_schema: optional argument schema for user to specify
        infer_schema: Whether to infer the schema of the arguments from
            the function's signature. This also makes the resultant tool
            accept a dictionary input to its `run()` function.

    Requires:
        - Function must be of type (str) -> str
        - Function must have a docstring

    Examples:
        .. code-block:: python

            @tool
            def search_api(query: str) -> str:
                # Searches the API for the query.
                return

            @tool("search", return_direct=True)
            def search_api(query: str) -> str:
                # Searches the API for the query.
                return
    """

    def _make_with_name(tool_name: str) -> Callable:
        def _make_tool(func: Callable) -> Tool:
            assert func.__doc__, "Function must have a docstring"
            # Description example:
            # search_api(query: str) - Searches the API for the query.
            description = f"{tool_name}{signature(func)} - {func.__doc__.strip()}"
            _args_schema = args_schema
            if _args_schema is None and infer_schema:
                _args_schema = validate_arguments(func).model  # type: ignore
            tool_ = Tool(
                name=tool_name,
                func=func,
                args_schema=_args_schema,
                description=description,
                return_direct=return_direct,
            )
            return tool_

        return _make_tool

    if len(args) == 1 and isinstance(args[0], str):
        # if the argument is a string, then we use the string as the tool name
        # Example usage: @tool("search", return_direct=True)
        return _make_with_name(args[0])
    elif len(args) == 1 and callable(args[0]):
        # if the argument is a function, then we use the function name as the tool name
        # Example usage: @tool
        return _make_with_name(args[0].__name__)(args[0])
    elif len(args) == 0:
        # if there are no arguments, then we use the function name as the tool name
        # Example usage: @tool(return_direct=True)
        def _partial(func: Callable[[str], str]) -> BaseTool:
            return _make_with_name(func.__name__)(func)

        return _partial
    else:
        raise ValueError("Too many arguments for tool decorator")


args

from langchain.tools.base import BaseTool

from real_agents.adapters.data_model import DataModel
from real_agents.adapters.callbacks.manager import (
    CallbackManager,
    Callbacks,
)

class Tool(BaseTool):
    """Tool that takes in function or coroutine directly."""

    description: str = ""
    func: Callable[..., str]
    """The function to run when the tool is called."""
    coroutine: Optional[Callable[..., Awaitable[str]]] = None
    """The asynchronous version of the function."""

    @property
    def args(self) -> dict:
        if self.args_schema is not None:
            return self.args_schema.schema()["properties"]
        else:
            inferred_model = validate_arguments(self.func).model  # type: ignore
            schema = inferred_model.schema()["properties"]
            valid_keys = signature(self.func).parameters
            return {k: schema[k] for k in valid_keys}

    def _run(self, *args: Any, **kwargs: Any) -> str:
        """Use the tool."""
        return self.func(*args, **kwargs)

    async def _arun(self, *args: Any, **kwargs: Any) -> str:
        """Use the tool asynchronously."""
        if self.coroutine:
            return await self.coroutine(*args, **kwargs)
        raise NotImplementedError("Tool does not support async")

    # TODO: this is for backwards compatibility, remove in future
    def __init__(
        self, name: str, func: Callable[[str], Union[Dict[Any, Any], DataModel]], description: str, **kwargs: Any
    ) -> None:
        """Initialize tool."""
        super(Tool, self).__init__(name=name, func=func, description=description, **kwargs)

    def run(
        self,


            observation = (
                self._run(*tool_args, run_manager=run_manager, **tool_kwargs)
                if new_arg_supported
                else self._run(*tool_args, **tool_kwargs)
            )
        except (Exception, KeyboardInterrupt) as e:
            run_manager.on_tool_error(e)
            raise e

        run_manager.on_tool_end(observation, color=color, name=self.name, **kwargs)

        return observation

class InvalidTool(BaseTool):
    """Tool that is run when invalid tool name is encountered by agent."""

    name = "invalid_tool"
    description = "Called when tool name is invalid."

    def _run(self, tool_name: str) -> str:
        """Use the tool."""
        return f"{tool_name} is not a valid tool, try another one."

    async def _arun(self, tool_name: str) -> str:
        """Use the tool asynchronously."""
        return f"{tool_name} is not a valid tool, try another one."

def tool(
    *args: Union[str, Callable],
    return_direct: bool = False,
    args_schema: Optional[Type[BaseModel]] = None,
    infer_schema: bool = True,
) -> Callable:
    """Make tools out of functions, can be used with or without arguments.

    Args:
        *args: The arguments to the tool.
        return_direct: Whether to return directly from the tool rather
            than continuing the agent loop.
        args_schema: optional argument schema for user to specify
        infer_schema: Whether to infer the schema of the arguments from


            )
        except (Exception, KeyboardInterrupt) as e:
            run_manager.on_tool_error(e)
            raise e

        run_manager.on_tool_end(observation, color=color, name=self.name, **kwargs)

        return observation

class InvalidTool(BaseTool):
    """Tool that is run when invalid tool name is encountered by agent."""

    name = "invalid_tool"
    description = "Called when tool name is invalid."

    def _run(self, tool_name: str) -> str:
        """Use the tool."""
        return f"{tool_name} is not a valid tool, try another one."

    async def _arun(self, tool_name: str) -> str:
        """Use the tool asynchronously."""
        return f"{tool_name} is not a valid tool, try another one."

def tool(
    *args: Union[str, Callable],
    return_direct: bool = False,
    args_schema: Optional[Type[BaseModel]] = None,
    infer_schema: bool = True,
) -> Callable:
    """Make tools out of functions, can be used with or without arguments.

    Args:
        *args: The arguments to the tool.
        return_direct: Whether to return directly from the tool rather
            than continuing the agent loop.
        args_schema: optional argument schema for user to specify
        infer_schema: Whether to infer the schema of the arguments from
            the function's signature. This also makes the resultant tool
            accept a dictionary input to its `run()` function.

    Requires:


    def args(self) -> dict:
        if self.args_schema is not None:
            return self.args_schema.schema()["properties"]
        else:
            inferred_model = validate_arguments(self.func).model  # type: ignore
            schema = inferred_model.schema()["properties"]
            valid_keys = signature(self.func).parameters
            return {k: schema[k] for k in valid_keys}

    def _run(self, *args: Any, **kwargs: Any) -> str:
        """Use the tool."""
        return self.func(*args, **kwargs)

    async def _arun(self, *args: Any, **kwargs: Any) -> str:
        """Use the tool asynchronously."""
        if self.coroutine:
            return await self.coroutine(*args, **kwargs)
        raise NotImplementedError("Tool does not support async")

    # TODO: this is for backwards compatibility, remove in future
    def __init__(
        self, name: str, func: Callable[[str], Union[Dict[Any, Any], DataModel]], description: str, **kwargs: Any
    ) -> None:
        """Initialize tool."""
        super(Tool, self).__init__(name=name, func=func, description=description, **kwargs)

    def run(
        self,
        tool_input: Union[str, Dict],
        verbose: Optional[bool] = None,
        start_color: Optional[str] = "green",
        color: Optional[str] = "green",
        callbacks: Callbacks = None,
        **kwargs: Any,
    ) -> Any:
        """Run the tool."""
        parsed_input = self._parse_input(tool_input)
        if not self.verbose and verbose is not None:
            verbose_ = verbose
        else:
            verbose_ = self.verbose

        # todo: fix this place
        callback_manager = CallbackManager.configure(callbacks, self.callbacks, verbose=verbose_)
        # TODO: maybe also pass through run_manager is _run supports kwargs


run

            valid_keys = signature(self.func).parameters
            return {k: schema[k] for k in valid_keys}

    def _run(self, *args: Any, **kwargs: Any) -> str:
        """Use the tool."""
        return self.func(*args, **kwargs)

    async def _arun(self, *args: Any, **kwargs: Any) -> str:
        """Use the tool asynchronously."""
        if self.coroutine:
            return await self.coroutine(*args, **kwargs)
        raise NotImplementedError("Tool does not support async")

    # TODO: this is for backwards compatibility, remove in future
    def __init__(
        self, name: str, func: Callable[[str], Union[Dict[Any, Any], DataModel]], description: str, **kwargs: Any
    ) -> None:
        """Initialize tool."""
        super(Tool, self).__init__(name=name, func=func, description=description, **kwargs)

    def run(
        self,
        tool_input: Union[str, Dict],
        verbose: Optional[bool] = None,
        start_color: Optional[str] = "green",
        color: Optional[str] = "green",
        callbacks: Callbacks = None,
        **kwargs: Any,
    ) -> Any:
        """Run the tool."""
        parsed_input = self._parse_input(tool_input)
        if not self.verbose and verbose is not None:
            verbose_ = verbose
        else:
            verbose_ = self.verbose

        # todo: fix this place
        callback_manager = CallbackManager.configure(callbacks, self.callbacks, verbose=verbose_)
        # TODO: maybe also pass through run_manager is _run supports kwargs
        new_arg_supported = signature(self._run).parameters.get("run_manager")
        run_manager = callback_manager.on_tool_start(
            {"name": self.name, "description": self.description},
            tool_input if isinstance(tool_input, str) else str(tool_input),
            color=start_color,
            **kwargs,
        )
        try:
            tool_args, tool_kwargs = self._to_args_and_kwargs(parsed_input)
            observation = (
                self._run(*tool_args, run_manager=run_manager, **tool_kwargs)
                if new_arg_supported
                else self._run(*tool_args, **tool_kwargs)
            )
        except (Exception, KeyboardInterrupt) as e:
            run_manager.on_tool_error(e)
            raise e

        run_manager.on_tool_end(observation, color=color, name=self.name, **kwargs)

        return observation

class InvalidTool(BaseTool):
    """Tool that is run when invalid tool name is encountered by agent."""

    name = "invalid_tool"
    description = "Called when tool name is invalid."

    def _run(self, tool_name: str) -> str:
        """Use the tool."""
        return f"{tool_name} is not a valid tool, try another one."

    async def _arun(self, tool_name: str) -> str:
        """Use the tool asynchronously."""
        return f"{tool_name} is not a valid tool, try another one."

def tool(
    *args: Union[str, Callable],
    return_direct: bool = False,


            accept a dictionary input to its `run()` function.

    Requires:
        - Function must be of type (str) -> str
        - Function must have a docstring

    Examples:
        .. code-block:: python

            @tool
            def search_api(query: str) -> str:
                # Searches the API for the query.
                return

            @tool("search", return_direct=True)
            def search_api(query: str) -> str:
                # Searches the API for the query.
                return
    """

    def _make_with_name(tool_name: str) -> Callable:
        def _make_tool(func: Callable) -> Tool:
            assert func.__doc__, "Function must have a docstring"
            # Description example:
            # search_api(query: str) - Searches the API for the query.
            description = f"{tool_name}{signature(func)} - {func.__doc__.strip()}"
            _args_schema = args_schema
            if _args_schema is None and infer_schema:
                _args_schema = validate_arguments(func).model  # type: ignore
            tool_ = Tool(
                name=tool_name,
                func=func,
                args_schema=_args_schema,
                description=description,
                return_direct=return_direct,
            )
            return tool_

        return _make_tool

    if len(args) == 1 and isinstance(args[0], str):
        # if the argument is a string, then we use the string as the tool name
        # Example usage: @tool("search", return_direct=True)
        return _make_with_name(args[0])
    elif len(args) == 1 and callable(args[0]):
        # if the argument is a function, then we use the function name as the tool name
        # Example usage: @tool
        return _make_with_name(args[0].__name__)(args[0])
    elif len(args) == 0:
        # if there are no arguments, then we use the function name as the tool name
        # Example usage: @tool(return_direct=True)
        def _partial(func: Callable[[str], str]) -> BaseTool:
            return _make_with_name(func.__name__)(func)

        return _partial
    else:
        raise ValueError("Too many arguments for tool decorator")


    Requires:
        - Function must be of type (str) -> str
        - Function must have a docstring

    Examples:
        .. code-block:: python

            @tool
            def search_api(query: str) -> str:
                # Searches the API for the query.
                return

            @tool("search", return_direct=True)
            def search_api(query: str) -> str:
                # Searches the API for the query.
                return
    """

    def _make_with_name(tool_name: str) -> Callable:
        def _make_tool(func: Callable) -> Tool:
            assert func.__doc__, "Function must have a docstring"
            # Description example:
            # search_api(query: str) - Searches the API for the query.
            description = f"{tool_name}{signature(func)} - {func.__doc__.strip()}"
            _args_schema = args_schema
            if _args_schema is None and infer_schema:
                _args_schema = validate_arguments(func).model  # type: ignore
            tool_ = Tool(
                name=tool_name,
                func=func,
                args_schema=_args_schema,
                description=description,
                return_direct=return_direct,
            )
            return tool_

        return _make_tool

    if len(args) == 1 and isinstance(args[0], str):
        # if the argument is a string, then we use the string as the tool name
        # Example usage: @tool("search", return_direct=True)
        return _make_with_name(args[0])
    elif len(args) == 1 and callable(args[0]):
        # if the argument is a function, then we use the function name as the tool name
        # Example usage: @tool
        return _make_with_name(args[0].__name__)(args[0])
    elif len(args) == 0:
        # if there are no arguments, then we use the function name as the tool name
        # Example usage: @tool(return_direct=True)
        def _partial(func: Callable[[str], str]) -> BaseTool:
            return _make_with_name(func.__name__)(func)

        return _partial
    else:
        raise ValueError("Too many arguments for tool decorator")


                func=func,
                args_schema=_args_schema,
                description=description,
                return_direct=return_direct,
            )
            return tool_

        return _make_tool

    if len(args) == 1 and isinstance(args[0], str):
        # if the argument is a string, then we use the string as the tool name
        # Example usage: @tool("search", return_direct=True)
        return _make_with_name(args[0])
    elif len(args) == 1 and callable(args[0]):
        # if the argument is a function, then we use the function name as the tool name
        # Example usage: @tool
        return _make_with_name(args[0].__name__)(args[0])
    elif len(args) == 0:
        # if there are no arguments, then we use the function name as the tool name
        # Example usage: @tool(return_direct=True)
        def _partial(func: Callable[[str], str]) -> BaseTool:
            return _make_with_name(func.__name__)(func)

        return _partial
    else:
        raise ValueError("Too many arguments for tool decorator")


# flake8: noqa
import datetime

PREFIX = (
    """You are XLang Plugins Agent, a friendly and intuitive assistant developed by the XLang Team to guide you through every aspects of your work and your daily life. XLang Agent is always at your fingertips through our interactive chat system.

You can aware of what plugins you have, and use the plugins properly in right order to finish what user wants.

Today is
""".strip() + " "
    + datetime.datetime.now().strftime("%Y-%m-%d")
    + """, and you should adapt the input to fit into the date, for example, seasonal information, or today's date as coordinate, etc.

To make your response informative, always speak includes the following information in MARKDOWN format when responding a message, that is:
1. Natural language explanation, that make explain the API output in a human readable way;
2. Organized information such as bullet points or MARKDOWN tables, followed by the links to the items (that in the API output), news etc. if API output contains the information;
3. The links should in MARKDOWN format and have value in it. If reference information is provided in the API output, like links to the items, news etc. Your explanation MUST provide the links on each items and links can be clicked on when API output contains the information. The links better attach on some natural language explanation through MARKDOWN syntax, for example, - [Renewable Energy - Center for Climate and Energy Solutions](https://www.c2es.org/content/renewable-energy/);
4. If there are image we would like to display, please use MARKDOWN syntax to display it, for example, ![image](https://www.c2es.org/content/renewable-energy/);
5. Try to speak more and show all the information you got in a organized way, that will make you a better assistant, especially when you are giving the final answer.

PLUGINS
------
The plugins you can use are:
""".strip() + "\n"
)

FORMAT_INSTRUCTIONS = """RESPONSE FORMAT INSTRUCTIONS
----------------------------

When you use tools or generate final answer, please output a response in one of two formats:
**Option 1: Explain and Use plugin**
If the response involves using a plugin, you can start with a natural language explanation[Optional], plus exactly one plugin calling[MUST], and ends with no more words. The plugin calling format should be a markdown code snippet with the following JSON schema:

```json
{{{{
    "action": string wrapped with \"\", // The action to take. Must be one in the list [{tool_names}]
    "action_input": string wrapped with \"\" // Query to be input to the action plugin. Pass as much information as possible to the plugin from the history of the conversation.
}}}}
```
NEVER EVER EVER make up a plugin not in [{tool_names}]
You MUST pass as much information as possible to the plugin from the history of the conversation. It could be natural language or structured language like jsonl, csv, etc. BUT MUST in a single line.
(Please note that ONLY ONE plugin should be used per response.)

**Option #2: **
If you want to respond directly to the human without using a plugin, provide a plain natural language response. However, if you initially generated a natural language response and then decide to use a plugin, make sure to include the plugin action and input after the initial response.

Begin.
"""

SUFFIX = "{input}"

TEMPLATE_TOOL_RESPONSE = """PLUGINS RESPONSE:
---------------------
{observation}

THOUGHT
--------------------

Okay, So what's next? Are the plugins' response enough to answer human's initial query? Please follow these instructions:

1. Evaluate plugin Response [Mandatory]: Carefully evaluate the plugin's response and determine if it sufficiently addresses the human's query. Consider the content and implications of the plugin's response.

2. Consider Additional plugin Use [Optional 2 or 3]: If the plugin response does not fully address the query or if an error occurred during execution, you may proceed with additional plugin usage. However, exercise caution and limit the number of iterations to a maximum of 5. You can start with a natural language explanation[Optional], plus exactly one plugin calling[MUST]. Follow this format for additional plugin usage:

```json
{{{{
    "action": string wrapped with \"\", // The action to take. Must be one of [{tool_names}]
    "action_input": string wrapped with \"\" // Query to be input to the action plugin. Pass as much information as possible to the plugin from the history of the conversation.
}}}}
```
(Please note that ONLY ONE plugin should be used per response.)

3. Deliver Comprehensive Answer [Optional 2 or 3]: If the plugin response sufficiently addresses the query, deliver a comprehensive answer to the human. Focus solely on the content and implications of the plugin's response. MUST NOT include explanations of the plugin's functions.

Note. you must do 1; For 2 and 3, You must choose one from them.

Begin.
"""

# models like anthropic claude-v1 or claude-2 can only return valid completion with human message as the last message, so we append the fake AI message at the end.
fake_continue_prompt = {
    "claude-2": "you can start to think and respond to me using the above formats. No Apology. Just respond with format in Option 2(use tool) or Option 3(direct text response), no other words.\n\nBegin.",
    "claude-v1": "you can start to think and respond to me using the above formats. No Apology. Just respond with format in Option 2(use tool) or Option 3(direct text response), no other words.\n\nBegin.",
}


"""An agent designed to hold a conversation in addition to using tools. (Specially designed for plugins model)"""
from __future__ import annotations

from typing import Any, List, Optional, Sequence, Tuple, Union
from pydantic import Extra, Field
from typing_extensions import override

from langchain.agents.agent import AgentOutputParser
from langchain.agents.utils import validate_tools_single_input
from langchain.base_language import BaseLanguageModel
from langchain.chains import LLMChain
from langchain.schema import (
    AgentAction,
    AgentFinish,
    AIMessage,
    BaseMessage,
    BaseOutputParser,
    HumanMessage
)
from langchain.callbacks.manager import (
    Callbacks
)
from langchain.tools.base import BaseTool
from langchain.prompts import (
    BasePromptTemplate,
    ChatPromptTemplate,
    HumanMessagePromptTemplate,
    MessagesPlaceholder,
    SystemMessagePromptTemplate,
)

from real_agents.adapters.agent_helpers.agent import Agent
from real_agents.adapters.agent_helpers.output_parser import ConversationOutputParser
from real_agents.plugins_agent.plugin_prompt import (
    PREFIX,
    SUFFIX,
    TEMPLATE_TOOL_RESPONSE,
    fake_continue_prompt
)
from real_agents.adapters.data_model import DataModel, MessageDataModel
from real_agents.data_agent.copilot import ConversationalChatAgent

class ConversationalPluginChatAgent(ConversationalChatAgent):
    """An agent designed to hold a conversation in addition to using plugin tool."""

    output_parser: ConversationOutputParser = Field(
        default_factory=ConversationOutputParser()
    )

    template_tool_response: str = TEMPLATE_TOOL_RESPONSE
    continue_model: Optional[str] = None

    class Config:
        """Configuration for this pydantic object."""

        extra = Extra.allow
        arbitrary_types_allowed = True

    @classmethod
    def _get_default_output_parser(
            cls, **kwargs: Any
    ) -> ConversationOutputParser:
        return ConversationOutputParser()

    @property
    def _agent_type(self) -> str:
        raise NotImplementedError

    @property
    def observation_prefix(self) -> str:
        """Prefix to append the observation with."""
        return "Observation: "

    @property
    def llm_prefix(self) -> str:
        """Prefix to append the llm call with."""
        return "Thought:"

    @classmethod
    def _validate_tools(cls, tools: Sequence[BaseTool]) -> None:
        super()._validate_tools(tools)
        validate_tools_single_input(cls.__name__, tools)

    @classmethod
    def create_prompt(
            cls,
            tools: Sequence[BaseTool],
            system_message: str = PREFIX,
            human_message: str = SUFFIX,
            input_variables: Optional[List[str]] = None,
            output_parser: Optional[BaseOutputParser] = None,
    ) -> BasePromptTemplate:
        tool_strings = "\n".join([f"Name: {tool.name}\nDescription: {tool.description}" for tool in tools])
        tool_strings = tool_strings.replace("{", "{{").replace("}", "}}")
        tool_names = ", ".join([tool.name for tool in tools])
        _output_parser = output_parser or cls._get_default_output_parser()

        format_instructions = _output_parser.get_format_instructions("plugins")
        format_instructions = format_instructions.format(tool_names=tool_names)

        # system message
        system_message = system_message + f"{tool_strings}\n\n{format_instructions}"

        # human input
        final_prompt = human_message

        if input_variables is None:
            input_variables = ["input", "chat_history", "agent_scratchpad"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_message),
            MessagesPlaceholder(variable_name="chat_history"),
            HumanMessagePromptTemplate.from_template(final_prompt),
            MessagesPlaceholder(variable_name="agent_scratchpad"),
        ]
        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    def _construct_scratchpad(self, intermediate_steps: List[Tuple[AgentAction, str]]) -> List[BaseMessage]:
        """Construct the scratchpad that lets the agent continue its thought process."""
        thoughts: List[BaseMessage] = []
        # Try to only use AI message for scratchpad
        content = []
        for idx, (action, full_observation) in enumerate(intermediate_steps):
            content.append(MessageDataModel.extract_action_for_llm(action.log))

            observation = full_observation
            if isinstance(full_observation, DataModel):
                llm_raw_observation = full_observation.get_llm_side_data()

                observation = MessageDataModel.extract_tool_response_for_llm(llm_raw_observation, tool_style="plugin")
                tool_response = self.template_tool_response.format(
                    observation=str(observation), tool_names=self.allowed_tools
                )
                if idx == len(intermediate_steps) - 1:
                    content.append(tool_response)
                else:
                    content.append(observation)

        content_str = "\n".join(content)
        thoughts.append(AIMessage(content=content_str))
        if self.continue_model is not None and len(intermediate_steps) != 0:
            thoughts.append(HumanMessage(content=fake_continue_prompt[self.continue_model]))
        return thoughts

    @override
    def plan(
            self,
            intermediate_steps: List[Tuple[AgentAction, str]],
            **kwargs: Any,
    ) -> Union[AgentAction, AgentFinish]:
        """Given input, decided what to do.

        Args:
            intermediate_steps: Steps the LLM has taken to date,
                along with observations
            **kwargs: User inputs.

        Returns:
            Action specifying what tool to use.
        """
        full_inputs = self.get_full_inputs(intermediate_steps, **kwargs)
        system_prompt = self.llm_chain.prompt.messages[0].format().content
        system_prompt_tokens = MessageDataModel._count_tokens(
            system_prompt
        )
        max_tokens = 8000
        max_gen_tokens = 1000
        # FIXME: need more accurate token limit calculation
        full_inputs = MessageDataModel.truncate_chat_history(full_inputs,
                                                             max_token=max_tokens - system_prompt_tokens - max_gen_tokens)
        full_output = self.llm_chain.predict(**full_inputs)

        return self.output_parser.parse(full_output)

    @classmethod
    def from_llm_and_tools(
            cls,
            llm: BaseLanguageModel,
            tools: Sequence[BaseTool],
            callbacks: Callbacks = None,
            output_parser: Optional[AgentOutputParser] = None,
            system_message: str = PREFIX,
            human_message: str = SUFFIX,
            input_variables: Optional[List[str]] = None,
            **kwargs: Any,
    ) -> Agent:
        """Construct an agent from an LLM and tools."""
        cls._validate_tools(tools)

        _output_parser = output_parser or cls._get_default_output_parser()
        prompt = cls.create_prompt(
            tools,
            system_message=system_message,
            human_message=human_message,
            input_variables=input_variables,
            output_parser=_output_parser,
        )
        llm_chain = LLMChain(
            llm=llm,
            prompt=prompt,
        )
        tool_names = [tool.name for tool in tools]
        return cls(
            llm_chain=llm_chain,
            allowed_tools=tool_names,
            output_parser=_output_parser,
            **kwargs,
        )


ConversationalPluginChatAgent

from langchain.prompts import (
    BasePromptTemplate,
    ChatPromptTemplate,
    HumanMessagePromptTemplate,
    MessagesPlaceholder,
    SystemMessagePromptTemplate,
)

from real_agents.adapters.agent_helpers.agent import Agent
from real_agents.adapters.agent_helpers.output_parser import ConversationOutputParser
from real_agents.plugins_agent.plugin_prompt import (
    PREFIX,
    SUFFIX,
    TEMPLATE_TOOL_RESPONSE,
    fake_continue_prompt
)
from real_agents.adapters.data_model import DataModel, MessageDataModel
from real_agents.data_agent.copilot import ConversationalChatAgent

class ConversationalPluginChatAgent(ConversationalChatAgent):
    """An agent designed to hold a conversation in addition to using plugin tool."""

    output_parser: ConversationOutputParser = Field(
        default_factory=ConversationOutputParser()
    )

    template_tool_response: str = TEMPLATE_TOOL_RESPONSE
    continue_model: Optional[str] = None

    class Config:
        """Configuration for this pydantic object."""

        extra = Extra.allow
        arbitrary_types_allowed = True

    @classmethod
    def _get_default_output_parser(
            cls, **kwargs: Any
    ) -> ConversationOutputParser:
        return ConversationOutputParser()

    @property
    def _agent_type(self) -> str:
        raise NotImplementedError

    @property
    def observation_prefix(self) -> str:
        """Prefix to append the observation with."""
        return "Observation: "

    @property
    def llm_prefix(self) -> str:
        """Prefix to append the llm call with."""
        return "Thought:"

    @classmethod
    def _validate_tools(cls, tools: Sequence[BaseTool]) -> None:
        super()._validate_tools(tools)
        validate_tools_single_input(cls.__name__, tools)

    @classmethod
    def create_prompt(
            cls,
            tools: Sequence[BaseTool],
            system_message: str = PREFIX,
            human_message: str = SUFFIX,
            input_variables: Optional[List[str]] = None,
            output_parser: Optional[BaseOutputParser] = None,
    ) -> BasePromptTemplate:
        tool_strings = "\n".join([f"Name: {tool.name}\nDescription: {tool.description}" for tool in tools])
        tool_strings = tool_strings.replace("{", "{{").replace("}", "}}")
        tool_names = ", ".join([tool.name for tool in tools])
        _output_parser = output_parser or cls._get_default_output_parser()

        format_instructions = _output_parser.get_format_instructions("plugins")
        format_instructions = format_instructions.format(tool_names=tool_names)

        # system message
        system_message = system_message + f"{tool_strings}\n\n{format_instructions}"

        # human input
        final_prompt = human_message

        if input_variables is None:
            input_variables = ["input", "chat_history", "agent_scratchpad"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_message),
            MessagesPlaceholder(variable_name="chat_history"),
            HumanMessagePromptTemplate.from_template(final_prompt),
            MessagesPlaceholder(variable_name="agent_scratchpad"),
        ]
        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    def _construct_scratchpad(self, intermediate_steps: List[Tuple[AgentAction, str]]) -> List[BaseMessage]:
        """Construct the scratchpad that lets the agent continue its thought process."""
        thoughts: List[BaseMessage] = []
        # Try to only use AI message for scratchpad
        content = []
        for idx, (action, full_observation) in enumerate(intermediate_steps):
            content.append(MessageDataModel.extract_action_for_llm(action.log))

            observation = full_observation
            if isinstance(full_observation, DataModel):
                llm_raw_observation = full_observation.get_llm_side_data()

                observation = MessageDataModel.extract_tool_response_for_llm(llm_raw_observation, tool_style="plugin")
                tool_response = self.template_tool_response.format(
                    observation=str(observation), tool_names=self.allowed_tools
                )
                if idx == len(intermediate_steps) - 1:
                    content.append(tool_response)
                else:
                    content.append(observation)

        content_str = "\n".join(content)
        thoughts.append(AIMessage(content=content_str))
        if self.continue_model is not None and len(intermediate_steps) != 0:
            thoughts.append(HumanMessage(content=fake_continue_prompt[self.continue_model]))
        return thoughts

    @override
    def plan(
            self,
            intermediate_steps: List[Tuple[AgentAction, str]],
            **kwargs: Any,
    ) -> Union[AgentAction, AgentFinish]:
        """Given input, decided what to do.

        Args:
            intermediate_steps: Steps the LLM has taken to date,
                along with observations
            **kwargs: User inputs.

        Returns:
            Action specifying what tool to use.
        """
        full_inputs = self.get_full_inputs(intermediate_steps, **kwargs)
        system_prompt = self.llm_chain.prompt.messages[0].format().content
        system_prompt_tokens = MessageDataModel._count_tokens(
            system_prompt
        )
        max_tokens = 8000
        max_gen_tokens = 1000
        # FIXME: need more accurate token limit calculation
        full_inputs = MessageDataModel.truncate_chat_history(full_inputs,
                                                             max_token=max_tokens - system_prompt_tokens - max_gen_tokens)
        full_output = self.llm_chain.predict(**full_inputs)

        return self.output_parser.parse(full_output)

    @classmethod
    def from_llm_and_tools(
            cls,
            llm: BaseLanguageModel,
            tools: Sequence[BaseTool],
            callbacks: Callbacks = None,
            output_parser: Optional[AgentOutputParser] = None,
            system_message: str = PREFIX,
            human_message: str = SUFFIX,
            input_variables: Optional[List[str]] = None,
            **kwargs: Any,
    ) -> Agent:
        """Construct an agent from an LLM and tools."""
        cls._validate_tools(tools)

        _output_parser = output_parser or cls._get_default_output_parser()
        prompt = cls.create_prompt(
            tools,
            system_message=system_message,
            human_message=human_message,
            input_variables=input_variables,
            output_parser=_output_parser,
        )
        llm_chain = LLMChain(
            llm=llm,
            prompt=prompt,
        )
        tool_names = [tool.name for tool in tools]
        return cls(
            llm_chain=llm_chain,
            allowed_tools=tool_names,
            output_parser=_output_parser,
            **kwargs,
        )


Config

from real_agents.plugins_agent.plugin_prompt import (
    PREFIX,
    SUFFIX,
    TEMPLATE_TOOL_RESPONSE,
    fake_continue_prompt
)
from real_agents.adapters.data_model import DataModel, MessageDataModel
from real_agents.data_agent.copilot import ConversationalChatAgent

class ConversationalPluginChatAgent(ConversationalChatAgent):
    """An agent designed to hold a conversation in addition to using plugin tool."""

    output_parser: ConversationOutputParser = Field(
        default_factory=ConversationOutputParser()
    )

    template_tool_response: str = TEMPLATE_TOOL_RESPONSE
    continue_model: Optional[str] = None

    class Config:
        """Configuration for this pydantic object."""

        extra = Extra.allow
        arbitrary_types_allowed = True

    @classmethod
    def _get_default_output_parser(
            cls, **kwargs: Any
    ) -> ConversationOutputParser:
        return ConversationOutputParser()

    @property
    def _agent_type(self) -> str:
        raise NotImplementedError

    @property
    def observation_prefix(self) -> str:
        """Prefix to append the observation with."""
        return "Observation: "

    @property
    def llm_prefix(self) -> str:
        """Prefix to append the llm call with."""
        return "Thought:"


from real_agents.data_agent.copilot import ConversationalChatAgent

class ConversationalPluginChatAgent(ConversationalChatAgent):
    """An agent designed to hold a conversation in addition to using plugin tool."""

    output_parser: ConversationOutputParser = Field(
        default_factory=ConversationOutputParser()
    )

    template_tool_response: str = TEMPLATE_TOOL_RESPONSE
    continue_model: Optional[str] = None

    class Config:
        """Configuration for this pydantic object."""

        extra = Extra.allow
        arbitrary_types_allowed = True

    @classmethod
    def _get_default_output_parser(
            cls, **kwargs: Any
    ) -> ConversationOutputParser:
        return ConversationOutputParser()

    @property
    def _agent_type(self) -> str:
        raise NotImplementedError

    @property
    def observation_prefix(self) -> str:
        """Prefix to append the observation with."""
        return "Observation: "

    @property
    def llm_prefix(self) -> str:
        """Prefix to append the llm call with."""
        return "Thought:"

    @classmethod
    def _validate_tools(cls, tools: Sequence[BaseTool]) -> None:
        super()._validate_tools(tools)
        validate_tools_single_input(cls.__name__, tools)


    output_parser: ConversationOutputParser = Field(
        default_factory=ConversationOutputParser()
    )

    template_tool_response: str = TEMPLATE_TOOL_RESPONSE
    continue_model: Optional[str] = None

    class Config:
        """Configuration for this pydantic object."""

        extra = Extra.allow
        arbitrary_types_allowed = True

    @classmethod
    def _get_default_output_parser(
            cls, **kwargs: Any
    ) -> ConversationOutputParser:
        return ConversationOutputParser()

    @property
    def _agent_type(self) -> str:
        raise NotImplementedError

    @property
    def observation_prefix(self) -> str:
        """Prefix to append the observation with."""
        return "Observation: "

    @property
    def llm_prefix(self) -> str:
        """Prefix to append the llm call with."""
        return "Thought:"

    @classmethod
    def _validate_tools(cls, tools: Sequence[BaseTool]) -> None:
        super()._validate_tools(tools)
        validate_tools_single_input(cls.__name__, tools)

    @classmethod
    def create_prompt(
            cls,
            tools: Sequence[BaseTool],


    template_tool_response: str = TEMPLATE_TOOL_RESPONSE
    continue_model: Optional[str] = None

    class Config:
        """Configuration for this pydantic object."""

        extra = Extra.allow
        arbitrary_types_allowed = True

    @classmethod
    def _get_default_output_parser(
            cls, **kwargs: Any
    ) -> ConversationOutputParser:
        return ConversationOutputParser()

    @property
    def _agent_type(self) -> str:
        raise NotImplementedError

    @property
    def observation_prefix(self) -> str:
        """Prefix to append the observation with."""
        return "Observation: "

    @property
    def llm_prefix(self) -> str:
        """Prefix to append the llm call with."""
        return "Thought:"

    @classmethod
    def _validate_tools(cls, tools: Sequence[BaseTool]) -> None:
        super()._validate_tools(tools)
        validate_tools_single_input(cls.__name__, tools)

    @classmethod
    def create_prompt(
            cls,
            tools: Sequence[BaseTool],
            system_message: str = PREFIX,
            human_message: str = SUFFIX,
            input_variables: Optional[List[str]] = None,
            output_parser: Optional[BaseOutputParser] = None,
    ) -> BasePromptTemplate:


        extra = Extra.allow
        arbitrary_types_allowed = True

    @classmethod
    def _get_default_output_parser(
            cls, **kwargs: Any
    ) -> ConversationOutputParser:
        return ConversationOutputParser()

    @property
    def _agent_type(self) -> str:
        raise NotImplementedError

    @property
    def observation_prefix(self) -> str:
        """Prefix to append the observation with."""
        return "Observation: "

    @property
    def llm_prefix(self) -> str:
        """Prefix to append the llm call with."""
        return "Thought:"

    @classmethod
    def _validate_tools(cls, tools: Sequence[BaseTool]) -> None:
        super()._validate_tools(tools)
        validate_tools_single_input(cls.__name__, tools)

    @classmethod
    def create_prompt(
            cls,
            tools: Sequence[BaseTool],
            system_message: str = PREFIX,
            human_message: str = SUFFIX,
            input_variables: Optional[List[str]] = None,
            output_parser: Optional[BaseOutputParser] = None,
    ) -> BasePromptTemplate:
        tool_strings = "\n".join([f"Name: {tool.name}\nDescription: {tool.description}" for tool in tools])
        tool_strings = tool_strings.replace("{", "{{").replace("}", "}}")
        tool_names = ", ".join([tool.name for tool in tools])
        _output_parser = output_parser or cls._get_default_output_parser()


    def _get_default_output_parser(
            cls, **kwargs: Any
    ) -> ConversationOutputParser:
        return ConversationOutputParser()

    @property
    def _agent_type(self) -> str:
        raise NotImplementedError

    @property
    def observation_prefix(self) -> str:
        """Prefix to append the observation with."""
        return "Observation: "

    @property
    def llm_prefix(self) -> str:
        """Prefix to append the llm call with."""
        return "Thought:"

    @classmethod
    def _validate_tools(cls, tools: Sequence[BaseTool]) -> None:
        super()._validate_tools(tools)
        validate_tools_single_input(cls.__name__, tools)

    @classmethod
    def create_prompt(
            cls,
            tools: Sequence[BaseTool],
            system_message: str = PREFIX,
            human_message: str = SUFFIX,
            input_variables: Optional[List[str]] = None,
            output_parser: Optional[BaseOutputParser] = None,
    ) -> BasePromptTemplate:
        tool_strings = "\n".join([f"Name: {tool.name}\nDescription: {tool.description}" for tool in tools])
        tool_strings = tool_strings.replace("{", "{{").replace("}", "}}")
        tool_names = ", ".join([tool.name for tool in tools])
        _output_parser = output_parser or cls._get_default_output_parser()

        format_instructions = _output_parser.get_format_instructions("plugins")
        format_instructions = format_instructions.format(tool_names=tool_names)

        # system message
        system_message = system_message + f"{tool_strings}\n\n{format_instructions}"


    @property
    def _agent_type(self) -> str:
        raise NotImplementedError

    @property
    def observation_prefix(self) -> str:
        """Prefix to append the observation with."""
        return "Observation: "

    @property
    def llm_prefix(self) -> str:
        """Prefix to append the llm call with."""
        return "Thought:"

    @classmethod
    def _validate_tools(cls, tools: Sequence[BaseTool]) -> None:
        super()._validate_tools(tools)
        validate_tools_single_input(cls.__name__, tools)

    @classmethod
    def create_prompt(
            cls,
            tools: Sequence[BaseTool],
            system_message: str = PREFIX,
            human_message: str = SUFFIX,
            input_variables: Optional[List[str]] = None,
            output_parser: Optional[BaseOutputParser] = None,
    ) -> BasePromptTemplate:
        tool_strings = "\n".join([f"Name: {tool.name}\nDescription: {tool.description}" for tool in tools])
        tool_strings = tool_strings.replace("{", "{{").replace("}", "}}")
        tool_names = ", ".join([tool.name for tool in tools])
        _output_parser = output_parser or cls._get_default_output_parser()

        format_instructions = _output_parser.get_format_instructions("plugins")
        format_instructions = format_instructions.format(tool_names=tool_names)

        # system message
        system_message = system_message + f"{tool_strings}\n\n{format_instructions}"

        # human input
        final_prompt = human_message

        if input_variables is None:
            input_variables = ["input", "chat_history", "agent_scratchpad"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_message),
            MessagesPlaceholder(variable_name="chat_history"),
            HumanMessagePromptTemplate.from_template(final_prompt),
            MessagesPlaceholder(variable_name="agent_scratchpad"),
        ]
        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    def _construct_scratchpad(self, intermediate_steps: List[Tuple[AgentAction, str]]) -> List[BaseMessage]:
        """Construct the scratchpad that lets the agent continue its thought process."""
        thoughts: List[BaseMessage] = []
        # Try to only use AI message for scratchpad
        content = []
        for idx, (action, full_observation) in enumerate(intermediate_steps):
            content.append(MessageDataModel.extract_action_for_llm(action.log))

            observation = full_observation
            if isinstance(full_observation, DataModel):
                llm_raw_observation = full_observation.get_llm_side_data()

                observation = MessageDataModel.extract_tool_response_for_llm(llm_raw_observation, tool_style="plugin")
                tool_response = self.template_tool_response.format(
                    observation=str(observation), tool_names=self.allowed_tools
                )
                if idx == len(intermediate_steps) - 1:
                    content.append(tool_response)
                else:


        format_instructions = _output_parser.get_format_instructions("plugins")
        format_instructions = format_instructions.format(tool_names=tool_names)

        # system message
        system_message = system_message + f"{tool_strings}\n\n{format_instructions}"

        # human input
        final_prompt = human_message

        if input_variables is None:
            input_variables = ["input", "chat_history", "agent_scratchpad"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_message),
            MessagesPlaceholder(variable_name="chat_history"),
            HumanMessagePromptTemplate.from_template(final_prompt),
            MessagesPlaceholder(variable_name="agent_scratchpad"),
        ]
        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    def _construct_scratchpad(self, intermediate_steps: List[Tuple[AgentAction, str]]) -> List[BaseMessage]:
        """Construct the scratchpad that lets the agent continue its thought process."""
        thoughts: List[BaseMessage] = []
        # Try to only use AI message for scratchpad
        content = []
        for idx, (action, full_observation) in enumerate(intermediate_steps):
            content.append(MessageDataModel.extract_action_for_llm(action.log))

            observation = full_observation
            if isinstance(full_observation, DataModel):
                llm_raw_observation = full_observation.get_llm_side_data()

                observation = MessageDataModel.extract_tool_response_for_llm(llm_raw_observation, tool_style="plugin")
                tool_response = self.template_tool_response.format(
                    observation=str(observation), tool_names=self.allowed_tools
                )
                if idx == len(intermediate_steps) - 1:
                    content.append(tool_response)
                else:
                    content.append(observation)

        content_str = "\n".join(content)
        thoughts.append(AIMessage(content=content_str))
        if self.continue_model is not None and len(intermediate_steps) != 0:
            thoughts.append(HumanMessage(content=fake_continue_prompt[self.continue_model]))
        return thoughts

    @override
    def plan(
            self,
            intermediate_steps: List[Tuple[AgentAction, str]],
            **kwargs: Any,
    ) -> Union[AgentAction, AgentFinish]:
        """Given input, decided what to do.

        Args:
            intermediate_steps: Steps the LLM has taken to date,
                along with observations
            **kwargs: User inputs.

        Returns:
            Action specifying what tool to use.
        """
        full_inputs = self.get_full_inputs(intermediate_steps, **kwargs)
        system_prompt = self.llm_chain.prompt.messages[0].format().content
        system_prompt_tokens = MessageDataModel._count_tokens(


plan

            observation = full_observation
            if isinstance(full_observation, DataModel):
                llm_raw_observation = full_observation.get_llm_side_data()

                observation = MessageDataModel.extract_tool_response_for_llm(llm_raw_observation, tool_style="plugin")
                tool_response = self.template_tool_response.format(
                    observation=str(observation), tool_names=self.allowed_tools
                )
                if idx == len(intermediate_steps) - 1:
                    content.append(tool_response)
                else:
                    content.append(observation)

        content_str = "\n".join(content)
        thoughts.append(AIMessage(content=content_str))
        if self.continue_model is not None and len(intermediate_steps) != 0:
            thoughts.append(HumanMessage(content=fake_continue_prompt[self.continue_model]))
        return thoughts

    @override
    def plan(
            self,
            intermediate_steps: List[Tuple[AgentAction, str]],
            **kwargs: Any,
    ) -> Union[AgentAction, AgentFinish]:
        """Given input, decided what to do.

        Args:
            intermediate_steps: Steps the LLM has taken to date,
                along with observations
            **kwargs: User inputs.

        Returns:
            Action specifying what tool to use.
        """
        full_inputs = self.get_full_inputs(intermediate_steps, **kwargs)
        system_prompt = self.llm_chain.prompt.messages[0].format().content
        system_prompt_tokens = MessageDataModel._count_tokens(
            system_prompt
        )
        max_tokens = 8000
        max_gen_tokens = 1000
        # FIXME: need more accurate token limit calculation
        full_inputs = MessageDataModel.truncate_chat_history(full_inputs,
                                                             max_token=max_tokens - system_prompt_tokens - max_gen_tokens)
        full_output = self.llm_chain.predict(**full_inputs)

        return self.output_parser.parse(full_output)

    @classmethod
    def from_llm_and_tools(
            cls,
            llm: BaseLanguageModel,
            tools: Sequence[BaseTool],
            callbacks: Callbacks = None,
            output_parser: Optional[AgentOutputParser] = None,
            system_message: str = PREFIX,
            human_message: str = SUFFIX,
            input_variables: Optional[List[str]] = None,
            **kwargs: Any,
    ) -> Agent:
        """Construct an agent from an LLM and tools."""
        cls._validate_tools(tools)

        _output_parser = output_parser or cls._get_default_output_parser()
        prompt = cls.create_prompt(
            tools,
            system_message=system_message,


            **kwargs: User inputs.

        Returns:
            Action specifying what tool to use.
        """
        full_inputs = self.get_full_inputs(intermediate_steps, **kwargs)
        system_prompt = self.llm_chain.prompt.messages[0].format().content
        system_prompt_tokens = MessageDataModel._count_tokens(
            system_prompt
        )
        max_tokens = 8000
        max_gen_tokens = 1000
        # FIXME: need more accurate token limit calculation
        full_inputs = MessageDataModel.truncate_chat_history(full_inputs,
                                                             max_token=max_tokens - system_prompt_tokens - max_gen_tokens)
        full_output = self.llm_chain.predict(**full_inputs)

        return self.output_parser.parse(full_output)

    @classmethod
    def from_llm_and_tools(
            cls,
            llm: BaseLanguageModel,
            tools: Sequence[BaseTool],
            callbacks: Callbacks = None,
            output_parser: Optional[AgentOutputParser] = None,
            system_message: str = PREFIX,
            human_message: str = SUFFIX,
            input_variables: Optional[List[str]] = None,
            **kwargs: Any,
    ) -> Agent:
        """Construct an agent from an LLM and tools."""
        cls._validate_tools(tools)

        _output_parser = output_parser or cls._get_default_output_parser()
        prompt = cls.create_prompt(
            tools,
            system_message=system_message,
            human_message=human_message,
            input_variables=input_variables,
            output_parser=_output_parser,
        )
        llm_chain = LLMChain(
            llm=llm,
            prompt=prompt,
        )
        tool_names = [tool.name for tool in tools]
        return cls(
            llm_chain=llm_chain,
            allowed_tools=tool_names,
            output_parser=_output_parser,
            **kwargs,
        )


"""The .py file to control the plugin we use in the chatbot"""
import os
from enum import Enum

CURRENT_PATH = os.path.dirname(os.path.abspath(__file__))

class PluginName(str, Enum):
    """
    Enum class for plugin names
    each name is a plugin name 🔌 , each value is the folder name 📁 of the plugin
    """
    KLARNA = "klarna"
    ZAPIER = "zapier"
    COURSERA = "Coursera"
    JOBSEARCH = "jobsearch"
    SHOW_ME = "show_me"
    SPEAK = "speak"
    CREATE_QR_CODE = "create_qr_code"
    MAPS = "maps"
    ASKYOURPDF = "askyourpdf"
    OUTSCHOOL = "Outschool"
    NBA_STATS = "nba_stats"
    WOLFRAM = "wolfram"
    WEB_SCRAPER = "web_scraper"
    DREAMINTERPRETER = "DreamInterpreter"
    BIZTOC = "biztoc"
    XWEATHER = "XWeather"