APICallingChain

from langchain.chains.base import Chain
from langchain.prompts.base import BasePromptTemplate
from langchain.prompts.chat import (
    ChatPromptTemplate,
    HumanMessagePromptTemplate,
    SystemMessagePromptTemplate,
)

from real_agents.plugins_agent.api_calling.custom_exceptions import ParsingError, \
    APICallingError
from real_agents.adapters.data_model import SpecModel
from real_agents.adapters.memory import ReadOnlySharedStringMemory
from real_agents.plugins_agent.api_calling.prompt import (
    RETRY_PROMPT,
    STOP_PROMPT,
    SYSTEM_PROMPT,
    USER_PROMPT,
)

class APICallingChain(Chain, BaseModel):
    """Chain for Calling API"""

    llm_basic_chain: LLMChain
    llm_retry_chain: LLMChain
    llm_stop_chain: LLMChain

    meta_info: Dict[str, Any]
    spec_model: SpecModel
    endpoint2caller: Dict[str, Callable]
    endpoint2output_model: Dict[str, Callable]
    api_key: Optional[str] = None

    memory: Optional[ReadOnlySharedStringMemory] = None  # fixme:
    retry_times = 1
    stop: str = "\n\n"
    verbose = True

    chat_id: Optional[str] = None
    user_id: Optional[str] = None

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

        extra = Extra.forbid
        arbitrary_types_allowed = True

    @property
    def input_keys(self) -> List[str]:
        """Return the singular input key.

        :meta private:
        """
        return ["input_str"]

    @property
    def output_keys(self) -> List[str]:
        """Return the output keys.

        :meta private:
        """
        return ["endpoint", "input_json", "api_output"]

    @property
    def specs_str(self):
        """The str representation of spec."""
        return "\n".join(
            [
                f"{i}.\n{self.spec_model.prepare_spec_for_one_path(p, include_api_info=False)}"
                for i, p in enumerate(self.spec_model.paths.keys())
            ]
        )

    @property
    def need_auth(self):
        """Whether the API call needs authentication."""
        return not self.meta_info["manifest"]["auth"]["type"] in [
            None,
            "None",
            "none",
            "Null",
            "null",
        ]  # the value of type is not null in ai-plugin.json

    @backoff.on_exception(backoff.expo, Exception, max_tries=10, max_time=20)
    def call_api(self, endpoint, input_json):
        """Call the API and return the output. Wrap the data in the output model."""
        # Find the endpoint by fuzzy match, in case sometimes LLM generated a wrong endpoint
        if endpoint not in self.endpoint2caller:
            return "Endpoint not found. Please try again."

        endpoint = process.extractOne(endpoint, list(self.endpoint2caller.keys()))[0]

        # Add fuzzy match for endpoint
        try:
            api_output = (
                self.endpoint2caller[endpoint](input_json, self.api_key)
                if self.need_auth
                else self.endpoint2caller[endpoint](input_json)
            )
        except Exception as e:
            raise APICallingError(f"{e}")

        compressed_output = self.endpoint2output_model[endpoint]({"out": api_output})[
            "out"]
        return compressed_output

    def parse_response(self, response: str):
        """Parse the endpoint and input_json"""
        endpoint = None
        input_json = None

        try:
            json_content = json5.loads(response)
            endpoint = json_content["endpoint"]
            input_json = json_content["input_json"]
        except:
            pattern = r"```json\n(.+?)\n```" if "```json" in response else r"```\n(.+?)\n```"
            match = re.search(pattern, response, re.DOTALL)

            if match:
                try:
                    json_content = json5.loads(match.group(1))
                    endpoint = json_content["endpoint"]
                    input_json = json_content["input_json"]
                except Exception as e:
                    raise ParsingError(f"{e}")

        # When the endpoint is null, we use the default endpoint
        if endpoint is None or endpoint == "null" or endpoint == "Null" or endpoint == "NULL":
            endpoint = "\\"

        return {"endpoint": endpoint, "input_json": input_json}

    @classmethod
    def create_basic_prompt(cls, system_prompt, user_prompt) -> BasePromptTemplate:
        # Call the LLM to get the predicted end_point and input_json
        input_variables = ["specs_str", "input_str"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(user_prompt),
        ]

        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    @classmethod
    def create_retry_prompt(cls, system_prompt, retry_prompt) -> BasePromptTemplate:
        # Call the LLM to get the predicted end_point and input_json in retry
        input_variables = ["specs_str", "input_str", "trial_history"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(retry_prompt),
        ]

        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    @classmethod
    def create_stop_prompt(cls, system_prompt, stop_prompt) -> BasePromptTemplate:
        # Decide the stop when the LLM are getting the predicted end_point and input_json
        input_variables = ["specs_str", "input_str", "api_output"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(stop_prompt),
        ]

        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    def retry(self, input_str: str, trial_history: List[Dict],
              _run_manager: CallbackManagerForChainRun,
              vars_to_pass: Dict) -> None:
        response_content = (
            self.llm_basic_chain.run(
                **{"specs_str": self.specs_str, "input_str": input_str})
            if len(trial_history) == 0
            else self.llm_retry_chain.run(
                **{"specs_str": self.specs_str, "input_str": input_str,
                   "trial_history": trial_history}
            )
        )

        parsed_return = self.parse_response(response_content)

        _run_manager.on_text(parsed_return, indent=4, color="green",
                             verbose=self.verbose)

        endpoint, input_json = (
            parsed_return["endpoint"],
            parsed_return["input_json"],
        )

        vars_to_pass["endpoint"] = endpoint
        vars_to_pass["input_json"] = input_json

        api_output = self.call_api(endpoint, input_json)
        vars_to_pass["api_output"] = api_output

        _run_manager.on_text(api_output, color="yellow", verbose=self.verbose)

        should_stop = (
                self.llm_stop_chain.run(
                    **{"specs_str": self.specs_str, "input_str": input_str,
                       "api_output": api_output})
                .lower()
                .strip()
                == "yes"
        )
        _run_manager.on_text(should_stop, color="yellow", verbose=self.verbose)
        vars_to_pass["should_stop"] = should_stop

    def _call(
            self,
            inputs: Dict[str, str],
            run_manager: Optional[CallbackManagerForChainRun] = None,
    ) -> Dict[str, Any]:
        _run_manager = run_manager or CallbackManagerForChainRun.get_noop_manager()

        input_str = inputs["input_str"]

        trial_history = []
        count = 0
        vars_to_pass = {"endpoint": None, "input_json": None, "api_output": None,
                        "should_stop": False}

        while count < self.retry_times:
            try:
                self.retry(input_str, trial_history, _run_manager, vars_to_pass)
                trial_history.append({"input_json": vars_to_pass["input_json"],
                                      "api_output": vars_to_pass["api_output"]})
                if vars_to_pass["should_stop"]:
                    break
                else:
                    count += 1
            except ParsingError as e:
                _run_manager.on_text(str(e) + "\n", color="red", verbose=self.verbose)
                trial_history.append({"errors": str(e)})
                count += 1
                continue
            except APICallingError as e:
                _run_manager.on_text(str(e) + "\n", color="red", verbose=self.verbose)
                trial_history.append({"errors": str(e)})
                count += 1
                continue
            except Exception as e:
                # fixme: Handle the exception, make error message shorter
                _run_manager.on_text(str(e) + "\n", color="red", verbose=self.verbose)
                _run_manager.on_text(traceback.format_exc(), color="red",
                                     verbose=self.verbose)
                trial_history.append({"errors": str(e)})
                count += 1
                continue

        if count == self.retry_times:
            if "errors" in trial_history[-1]:
                return {"endpoint": vars_to_pass["endpoint"],
                        "input_json": vars_to_pass["input_json"],
                        "api_output": trial_history[-1]["errors"]}
            else:
                return {"endpoint": vars_to_pass["endpoint"]} | (
                    trial_history[-1])  # return the last trial history
        else:
            return {"endpoint": vars_to_pass["endpoint"],
                    "input_json": vars_to_pass["input_json"],
                    "api_output": vars_to_pass["api_output"]}

    @classmethod
    def from_llm_and_plugin(
            cls,
            llm: BaseLanguageModel,
            meta_info: Dict[str, Any],
            spec_model: SpecModel,
            endpoint2caller: Dict[str, Callable],
            endpoint2output_model: Dict[str, Callable],
            api_key: str,
    ) -> APICallingChain:
        llm_basic_chain = LLMChain(
            llm=llm,
            prompt=cls.create_basic_prompt(
                system_prompt=SYSTEM_PROMPT,
                user_prompt=USER_PROMPT,
            ),
        )

        llm_retry_chain = LLMChain(
            llm=llm,
            prompt=cls.create_retry_prompt(
                system_prompt=SYSTEM_PROMPT,
                retry_prompt=RETRY_PROMPT,
            ),
        )
        llm_stop_chain = LLMChain(
            llm=llm,
            prompt=cls.create_stop_prompt(
                system_prompt=SYSTEM_PROMPT,
                stop_prompt=STOP_PROMPT,
            ),
        )

        return cls(
            llm_basic_chain=llm_basic_chain,
            llm_retry_chain=llm_retry_chain,
            llm_stop_chain=llm_stop_chain,
            meta_info=meta_info,
            spec_model=spec_model,
            endpoint2caller=endpoint2caller,
            endpoint2output_model=endpoint2output_model,
            api_key=api_key,
        )


Config

    """Chain for Calling API"""

    llm_basic_chain: LLMChain
    llm_retry_chain: LLMChain
    llm_stop_chain: LLMChain

    meta_info: Dict[str, Any]
    spec_model: SpecModel
    endpoint2caller: Dict[str, Callable]
    endpoint2output_model: Dict[str, Callable]
    api_key: Optional[str] = None

    memory: Optional[ReadOnlySharedStringMemory] = None  # fixme:
    retry_times = 1
    stop: str = "\n\n"
    verbose = True

    chat_id: Optional[str] = None
    user_id: Optional[str] = None

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

        extra = Extra.forbid
        arbitrary_types_allowed = True

    @property
    def input_keys(self) -> List[str]:
        """Return the singular input key.

        :meta private:
        """
        return ["input_str"]

    @property
    def output_keys(self) -> List[str]:
        """Return the output keys.

        :meta private:
        """
        return ["endpoint", "input_json", "api_output"]

    @property
    def specs_str(self):
        """The str representation of spec."""


    spec_model: SpecModel
    endpoint2caller: Dict[str, Callable]
    endpoint2output_model: Dict[str, Callable]
    api_key: Optional[str] = None

    memory: Optional[ReadOnlySharedStringMemory] = None  # fixme:
    retry_times = 1
    stop: str = "\n\n"
    verbose = True

    chat_id: Optional[str] = None
    user_id: Optional[str] = None

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

        extra = Extra.forbid
        arbitrary_types_allowed = True

    @property
    def input_keys(self) -> List[str]:
        """Return the singular input key.

        :meta private:
        """
        return ["input_str"]

    @property
    def output_keys(self) -> List[str]:
        """Return the output keys.

        :meta private:
        """
        return ["endpoint", "input_json", "api_output"]

    @property
    def specs_str(self):
        """The str representation of spec."""
        return "\n".join(
            [
                f"{i}.\n{self.spec_model.prepare_spec_for_one_path(p, include_api_info=False)}"
                for i, p in enumerate(self.spec_model.paths.keys())
            ]
        )

    @property


    verbose = True

    chat_id: Optional[str] = None
    user_id: Optional[str] = None

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

        extra = Extra.forbid
        arbitrary_types_allowed = True

    @property
    def input_keys(self) -> List[str]:
        """Return the singular input key.

        :meta private:
        """
        return ["input_str"]

    @property
    def output_keys(self) -> List[str]:
        """Return the output keys.

        :meta private:
        """
        return ["endpoint", "input_json", "api_output"]

    @property
    def specs_str(self):
        """The str representation of spec."""
        return "\n".join(
            [
                f"{i}.\n{self.spec_model.prepare_spec_for_one_path(p, include_api_info=False)}"
                for i, p in enumerate(self.spec_model.paths.keys())
            ]
        )

    @property
    def need_auth(self):
        """Whether the API call needs authentication."""
        return not self.meta_info["manifest"]["auth"]["type"] in [
            None,
            "None",
            "none",
            "Null",
            "null",


        extra = Extra.forbid
        arbitrary_types_allowed = True

    @property
    def input_keys(self) -> List[str]:
        """Return the singular input key.

        :meta private:
        """
        return ["input_str"]

    @property
    def output_keys(self) -> List[str]:
        """Return the output keys.

        :meta private:
        """
        return ["endpoint", "input_json", "api_output"]

    @property
    def specs_str(self):
        """The str representation of spec."""
        return "\n".join(
            [
                f"{i}.\n{self.spec_model.prepare_spec_for_one_path(p, include_api_info=False)}"
                for i, p in enumerate(self.spec_model.paths.keys())
            ]
        )

    @property
    def need_auth(self):
        """Whether the API call needs authentication."""
        return not self.meta_info["manifest"]["auth"]["type"] in [
            None,
            "None",
            "none",
            "Null",
            "null",
        ]  # the value of type is not null in ai-plugin.json

    @backoff.on_exception(backoff.expo, Exception, max_tries=10, max_time=20)
    def call_api(self, endpoint, input_json):
        """Call the API and return the output. Wrap the data in the output model."""
        # Find the endpoint by fuzzy match, in case sometimes LLM generated a wrong endpoint
        if endpoint not in self.endpoint2caller:
            return "Endpoint not found. Please try again."

        endpoint = process.extractOne(endpoint, list(self.endpoint2caller.keys()))[0]


    @property
    def output_keys(self) -> List[str]:
        """Return the output keys.

        :meta private:
        """
        return ["endpoint", "input_json", "api_output"]

    @property
    def specs_str(self):
        """The str representation of spec."""
        return "\n".join(
            [
                f"{i}.\n{self.spec_model.prepare_spec_for_one_path(p, include_api_info=False)}"
                for i, p in enumerate(self.spec_model.paths.keys())
            ]
        )

    @property
    def need_auth(self):
        """Whether the API call needs authentication."""
        return not self.meta_info["manifest"]["auth"]["type"] in [
            None,
            "None",
            "none",
            "Null",
            "null",
        ]  # the value of type is not null in ai-plugin.json

    @backoff.on_exception(backoff.expo, Exception, max_tries=10, max_time=20)
    def call_api(self, endpoint, input_json):
        """Call the API and return the output. Wrap the data in the output model."""
        # Find the endpoint by fuzzy match, in case sometimes LLM generated a wrong endpoint
        if endpoint not in self.endpoint2caller:
            return "Endpoint not found. Please try again."

        endpoint = process.extractOne(endpoint, list(self.endpoint2caller.keys()))[0]

        # Add fuzzy match for endpoint
        try:
            api_output = (
                self.endpoint2caller[endpoint](input_json, self.api_key)
                if self.need_auth
                else self.endpoint2caller[endpoint](input_json)
            )
        except Exception as e:
            raise APICallingError(f"{e}")


        """The str representation of spec."""
        return "\n".join(
            [
                f"{i}.\n{self.spec_model.prepare_spec_for_one_path(p, include_api_info=False)}"
                for i, p in enumerate(self.spec_model.paths.keys())
            ]
        )

    @property
    def need_auth(self):
        """Whether the API call needs authentication."""
        return not self.meta_info["manifest"]["auth"]["type"] in [
            None,
            "None",
            "none",
            "Null",
            "null",
        ]  # the value of type is not null in ai-plugin.json

    @backoff.on_exception(backoff.expo, Exception, max_tries=10, max_time=20)
    def call_api(self, endpoint, input_json):
        """Call the API and return the output. Wrap the data in the output model."""
        # Find the endpoint by fuzzy match, in case sometimes LLM generated a wrong endpoint
        if endpoint not in self.endpoint2caller:
            return "Endpoint not found. Please try again."

        endpoint = process.extractOne(endpoint, list(self.endpoint2caller.keys()))[0]

        # Add fuzzy match for endpoint
        try:
            api_output = (
                self.endpoint2caller[endpoint](input_json, self.api_key)
                if self.need_auth
                else self.endpoint2caller[endpoint](input_json)
            )
        except Exception as e:
            raise APICallingError(f"{e}")

        compressed_output = self.endpoint2output_model[endpoint]({"out": api_output})[
            "out"]
        return compressed_output

    def parse_response(self, response: str):
        """Parse the endpoint and input_json"""
        endpoint = None
        input_json = None

        try:
            json_content = json5.loads(response)
            endpoint = json_content["endpoint"]
            input_json = json_content["input_json"]
        except:
            pattern = r"```json\n(.+?)\n```" if "```json" in response else r"```\n(.+?)\n```"
            match = re.search(pattern, response, re.DOTALL)

            if match:
                try:
                    json_content = json5.loads(match.group(1))
                    endpoint = json_content["endpoint"]
                    input_json = json_content["input_json"]
                except Exception as e:


        # Find the endpoint by fuzzy match, in case sometimes LLM generated a wrong endpoint
        if endpoint not in self.endpoint2caller:
            return "Endpoint not found. Please try again."

        endpoint = process.extractOne(endpoint, list(self.endpoint2caller.keys()))[0]

        # Add fuzzy match for endpoint
        try:
            api_output = (
                self.endpoint2caller[endpoint](input_json, self.api_key)
                if self.need_auth
                else self.endpoint2caller[endpoint](input_json)
            )
        except Exception as e:
            raise APICallingError(f"{e}")

        compressed_output = self.endpoint2output_model[endpoint]({"out": api_output})[
            "out"]
        return compressed_output

    def parse_response(self, response: str):
        """Parse the endpoint and input_json"""
        endpoint = None
        input_json = None

        try:
            json_content = json5.loads(response)
            endpoint = json_content["endpoint"]
            input_json = json_content["input_json"]
        except:
            pattern = r"```json\n(.+?)\n```" if "```json" in response else r"```\n(.+?)\n```"
            match = re.search(pattern, response, re.DOTALL)

            if match:
                try:
                    json_content = json5.loads(match.group(1))
                    endpoint = json_content["endpoint"]
                    input_json = json_content["input_json"]
                except Exception as e:
                    raise ParsingError(f"{e}")

        # When the endpoint is null, we use the default endpoint
        if endpoint is None or endpoint == "null" or endpoint == "Null" or endpoint == "NULL":
            endpoint = "\\"

        return {"endpoint": endpoint, "input_json": input_json}

    @classmethod
    def create_basic_prompt(cls, system_prompt, user_prompt) -> BasePromptTemplate:
        # Call the LLM to get the predicted end_point and input_json
        input_variables = ["specs_str", "input_str"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(user_prompt),
        ]

        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    @classmethod
    def create_retry_prompt(cls, system_prompt, retry_prompt) -> BasePromptTemplate:
        # Call the LLM to get the predicted end_point and input_json in retry
        input_variables = ["specs_str", "input_str", "trial_history"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(retry_prompt),
        ]


            input_json = json_content["input_json"]
        except:
            pattern = r"```json\n(.+?)\n```" if "```json" in response else r"```\n(.+?)\n```"
            match = re.search(pattern, response, re.DOTALL)

            if match:
                try:
                    json_content = json5.loads(match.group(1))
                    endpoint = json_content["endpoint"]
                    input_json = json_content["input_json"]
                except Exception as e:
                    raise ParsingError(f"{e}")

        # When the endpoint is null, we use the default endpoint
        if endpoint is None or endpoint == "null" or endpoint == "Null" or endpoint == "NULL":
            endpoint = "\\"

        return {"endpoint": endpoint, "input_json": input_json}

    @classmethod
    def create_basic_prompt(cls, system_prompt, user_prompt) -> BasePromptTemplate:
        # Call the LLM to get the predicted end_point and input_json
        input_variables = ["specs_str", "input_str"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(user_prompt),
        ]

        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    @classmethod
    def create_retry_prompt(cls, system_prompt, retry_prompt) -> BasePromptTemplate:
        # Call the LLM to get the predicted end_point and input_json in retry
        input_variables = ["specs_str", "input_str", "trial_history"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(retry_prompt),
        ]

        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    @classmethod
    def create_stop_prompt(cls, system_prompt, stop_prompt) -> BasePromptTemplate:
        # Decide the stop when the LLM are getting the predicted end_point and input_json
        input_variables = ["specs_str", "input_str", "api_output"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(stop_prompt),
        ]


                    raise ParsingError(f"{e}")

        # When the endpoint is null, we use the default endpoint
        if endpoint is None or endpoint == "null" or endpoint == "Null" or endpoint == "NULL":
            endpoint = "\\"

        return {"endpoint": endpoint, "input_json": input_json}

    @classmethod
    def create_basic_prompt(cls, system_prompt, user_prompt) -> BasePromptTemplate:
        # Call the LLM to get the predicted end_point and input_json
        input_variables = ["specs_str", "input_str"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(user_prompt),
        ]

        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    @classmethod
    def create_retry_prompt(cls, system_prompt, retry_prompt) -> BasePromptTemplate:
        # Call the LLM to get the predicted end_point and input_json in retry
        input_variables = ["specs_str", "input_str", "trial_history"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(retry_prompt),
        ]

        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    @classmethod
    def create_stop_prompt(cls, system_prompt, stop_prompt) -> BasePromptTemplate:
        # Decide the stop when the LLM are getting the predicted end_point and input_json
        input_variables = ["specs_str", "input_str", "api_output"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(stop_prompt),
        ]

        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    def retry(self, input_str: str, trial_history: List[Dict],
              _run_manager: CallbackManagerForChainRun,
              vars_to_pass: Dict) -> None:
        response_content = (
            self.llm_basic_chain.run(
                **{"specs_str": self.specs_str, "input_str": input_str})
            if len(trial_history) == 0
            else self.llm_retry_chain.run(


        input_variables = ["specs_str", "input_str"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(user_prompt),
        ]

        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    @classmethod
    def create_retry_prompt(cls, system_prompt, retry_prompt) -> BasePromptTemplate:
        # Call the LLM to get the predicted end_point and input_json in retry
        input_variables = ["specs_str", "input_str", "trial_history"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(retry_prompt),
        ]

        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    @classmethod
    def create_stop_prompt(cls, system_prompt, stop_prompt) -> BasePromptTemplate:
        # Decide the stop when the LLM are getting the predicted end_point and input_json
        input_variables = ["specs_str", "input_str", "api_output"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(stop_prompt),
        ]

        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    def retry(self, input_str: str, trial_history: List[Dict],
              _run_manager: CallbackManagerForChainRun,
              vars_to_pass: Dict) -> None:
        response_content = (
            self.llm_basic_chain.run(
                **{"specs_str": self.specs_str, "input_str": input_str})
            if len(trial_history) == 0
            else self.llm_retry_chain.run(
                **{"specs_str": self.specs_str, "input_str": input_str,
                   "trial_history": trial_history}
            )
        )

        parsed_return = self.parse_response(response_content)

        _run_manager.on_text(parsed_return, indent=4, color="green",
                             verbose=self.verbose)

        endpoint, input_json = (


retry

        # Call the LLM to get the predicted end_point and input_json in retry
        input_variables = ["specs_str", "input_str", "trial_history"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(retry_prompt),
        ]

        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    @classmethod
    def create_stop_prompt(cls, system_prompt, stop_prompt) -> BasePromptTemplate:
        # Decide the stop when the LLM are getting the predicted end_point and input_json
        input_variables = ["specs_str", "input_str", "api_output"]
        messages = [
            SystemMessagePromptTemplate.from_template(system_prompt),
            HumanMessagePromptTemplate.from_template(stop_prompt),
        ]

        return ChatPromptTemplate(input_variables=input_variables, messages=messages)

    def retry(self, input_str: str, trial_history: List[Dict],
              _run_manager: CallbackManagerForChainRun,
              vars_to_pass: Dict) -> None:
        response_content = (
            self.llm_basic_chain.run(
                **{"specs_str": self.specs_str, "input_str": input_str})
            if len(trial_history) == 0
            else self.llm_retry_chain.run(
                **{"specs_str": self.specs_str, "input_str": input_str,
                   "trial_history": trial_history}
            )
        )

        parsed_return = self.parse_response(response_content)

        _run_manager.on_text(parsed_return, indent=4, color="green",
                             verbose=self.verbose)

        endpoint, input_json = (
            parsed_return["endpoint"],
            parsed_return["input_json"],
        )

        vars_to_pass["endpoint"] = endpoint
        vars_to_pass["input_json"] = input_json

        api_output = self.call_api(endpoint, input_json)
        vars_to_pass["api_output"] = api_output

        _run_manager.on_text(api_output, color="yellow", verbose=self.verbose)

        should_stop = (
                self.llm_stop_chain.run(
                    **{"specs_str": self.specs_str, "input_str": input_str,
                       "api_output": api_output})
                .lower()
                .strip()
                == "yes"
        )
        _run_manager.on_text(should_stop, color="yellow", verbose=self.verbose)
        vars_to_pass["should_stop"] = should_stop

    def _call(
            self,
            inputs: Dict[str, str],
            run_manager: Optional[CallbackManagerForChainRun] = None,
    ) -> Dict[str, Any]:
        _run_manager = run_manager or CallbackManagerForChainRun.get_noop_manager()

        input_str = inputs["input_str"]

        trial_history = []
        count = 0
        vars_to_pass = {"endpoint": None, "input_json": None, "api_output": None,
                        "should_stop": False}

        while count < self.retry_times:
            try:
                self.retry(input_str, trial_history, _run_manager, vars_to_pass)
                trial_history.append({"input_json": vars_to_pass["input_json"],
                                      "api_output": vars_to_pass["api_output"]})


        vars_to_pass["endpoint"] = endpoint
        vars_to_pass["input_json"] = input_json

        api_output = self.call_api(endpoint, input_json)
        vars_to_pass["api_output"] = api_output

        _run_manager.on_text(api_output, color="yellow", verbose=self.verbose)

        should_stop = (
                self.llm_stop_chain.run(
                    **{"specs_str": self.specs_str, "input_str": input_str,
                       "api_output": api_output})
                .lower()
                .strip()
                == "yes"
        )
        _run_manager.on_text(should_stop, color="yellow", verbose=self.verbose)
        vars_to_pass["should_stop"] = should_stop

    def _call(
            self,
            inputs: Dict[str, str],
            run_manager: Optional[CallbackManagerForChainRun] = None,
    ) -> Dict[str, Any]:
        _run_manager = run_manager or CallbackManagerForChainRun.get_noop_manager()

        input_str = inputs["input_str"]

        trial_history = []
        count = 0
        vars_to_pass = {"endpoint": None, "input_json": None, "api_output": None,
                        "should_stop": False}

        while count < self.retry_times:
            try:
                self.retry(input_str, trial_history, _run_manager, vars_to_pass)
                trial_history.append({"input_json": vars_to_pass["input_json"],
                                      "api_output": vars_to_pass["api_output"]})
                if vars_to_pass["should_stop"]:
                    break
                else:
                    count += 1
            except ParsingError as e:
                _run_manager.on_text(str(e) + "\n", color="red", verbose=self.verbose)
                trial_history.append({"errors": str(e)})
                count += 1
                continue
            except APICallingError as e:
                _run_manager.on_text(str(e) + "\n", color="red", verbose=self.verbose)
                trial_history.append({"errors": str(e)})
                count += 1
                continue
            except Exception as e:
                # fixme: Handle the exception, make error message shorter
                _run_manager.on_text(str(e) + "\n", color="red", verbose=self.verbose)
                _run_manager.on_text(traceback.format_exc(), color="red",
                                     verbose=self.verbose)
                trial_history.append({"errors": str(e)})
                count += 1
                continue

        if count == self.retry_times:
            if "errors" in trial_history[-1]:
                return {"endpoint": vars_to_pass["endpoint"],
                        "input_json": vars_to_pass["input_json"],
                        "api_output": trial_history[-1]["errors"]}
            else:
                return {"endpoint": vars_to_pass["endpoint"]} | (
                    trial_history[-1])  # return the last trial history
        else:
            return {"endpoint": vars_to_pass["endpoint"],
                    "input_json": vars_to_pass["input_json"],
                    "api_output": vars_to_pass["api_output"]}

    @classmethod
    def from_llm_and_plugin(
            cls,
            llm: BaseLanguageModel,
            meta_info: Dict[str, Any],
            spec_model: SpecModel,
            endpoint2caller: Dict[str, Callable],
            endpoint2output_model: Dict[str, Callable],
            api_key: str,
    ) -> APICallingChain:
        llm_basic_chain = LLMChain(
            llm=llm,
            prompt=cls.create_basic_prompt(
                system_prompt=SYSTEM_PROMPT,
                user_prompt=USER_PROMPT,
            ),
        )

        llm_retry_chain = LLMChain(


                _run_manager.on_text(traceback.format_exc(), color="red",
                                     verbose=self.verbose)
                trial_history.append({"errors": str(e)})
                count += 1
                continue

        if count == self.retry_times:
            if "errors" in trial_history[-1]:
                return {"endpoint": vars_to_pass["endpoint"],
                        "input_json": vars_to_pass["input_json"],
                        "api_output": trial_history[-1]["errors"]}
            else:
                return {"endpoint": vars_to_pass["endpoint"]} | (
                    trial_history[-1])  # return the last trial history
        else:
            return {"endpoint": vars_to_pass["endpoint"],
                    "input_json": vars_to_pass["input_json"],
                    "api_output": vars_to_pass["api_output"]}

    @classmethod
    def from_llm_and_plugin(
            cls,
            llm: BaseLanguageModel,
            meta_info: Dict[str, Any],
            spec_model: SpecModel,
            endpoint2caller: Dict[str, Callable],
            endpoint2output_model: Dict[str, Callable],
            api_key: str,
    ) -> APICallingChain:
        llm_basic_chain = LLMChain(
            llm=llm,
            prompt=cls.create_basic_prompt(
                system_prompt=SYSTEM_PROMPT,
                user_prompt=USER_PROMPT,
            ),
        )

        llm_retry_chain = LLMChain(
            llm=llm,
            prompt=cls.create_retry_prompt(
                system_prompt=SYSTEM_PROMPT,
                retry_prompt=RETRY_PROMPT,
            ),
        )
        llm_stop_chain = LLMChain(
            llm=llm,
            prompt=cls.create_stop_prompt(
                system_prompt=SYSTEM_PROMPT,
                stop_prompt=STOP_PROMPT,
            ),
        )

        return cls(
            llm_basic_chain=llm_basic_chain,
            llm_retry_chain=llm_retry_chain,
            llm_stop_chain=llm_stop_chain,
            meta_info=meta_info,
            spec_model=spec_model,
            endpoint2caller=endpoint2caller,
            endpoint2output_model=endpoint2output_model,
            api_key=api_key,
        )


SYSTEM_PROMPT = (
    """You are acting like plugin system that understand user's needs and call APIs precisely for them.""".strip()
    + "\n"
)

USER_PROMPT = (
    """
Here are the endpoints specs:
```
{specs_str}
```
Here is the input string:
```
{input_str}
```
Select the right endpoint called that can process the input string.
You need to wrap the input str into a json object, so that it could be fed into the function you selected.
During wrapping, you should:
1. modify the value of each key so that it satisfies the requirements in function specs.For example, if the type of the value should be a number, then you should modify it into a number;
2. ignore the information that is not useful or not applicable to the function you selected.
You fill values into some slots in the input_json, and then call the API. If the API returns a valid output, then you succeed. Otherwise, you fail.
Return the function called and the json object in the following format:
```
{{
    "endpoint": "xxx",
    "input_json":{{
        "xxx": "xxx",
        "xxx": "xxx",
        ...
    }}
}}
```
""".strip()
    + "\n"
)

RETRY_PROMPT = (
    """
Here are the function specs:
```
{specs_str}
```
Here is the input string:
```
{input_str}
```
Select the right function called that can process the input string.
You need to wrap the input str into a json object, so that it could be fed into the function you selected. During wrapping, you should:
1. modify the value of each key so that it satisfies the requirements in function specs.For example, if the type of the value should be a number, then you should modify it into a number.
2. ignore the information that is not useful or not applicable to the function you selected.
Return the function called and the json object in the following format:
```
{{
    "endpoint": "xxx",
    "input_json":{{
        "xxx": "xxx",
        "xxx": "xxx",
        ...
    }}
}}
```
You have tried to call function to process the input string but failed. The output do not have enough information to answer the tool input.
Here is the history of your trials, each element in this list means a trial:
```
{trial_history}
```
You should firstly analyze your trial history, find the value of the key "errors" in the output of each trial and check whether there are any errors
Then you may consider changing the input_json or endpoint based on the error information in your trial history, function specs and the input string.
Return the function called and the json object in the following format:
```
{{
    "endpoint": "xxx",
    "input_json":{{
        "xxx": "xxx",
        "xxx": "xxx",
        ...
    }}
}}
```
""".strip()
    + "\n"
)

STOP_PROMPT = (
    """
Here are the function specs:
```
{specs_str}
```
Here is the input string:
```
{input_str}
```
Here is the output that you get from calling the API:
```
{api_output}
```
You need to decide whether the returned_block contains valid information or not. Some returned_block may not have enough information to answer the tool input, for example, the returned_block may be empty, or return a json that says some kind of answer.
Answer only by 'yes' or 'no'
""".strip()
    + "\n"
)


"""Customize exceptions for API calling."""

class ParsingError(BaseException):
    """Error occur when parsing."""

    def __init__(self, message: str):
        self.message = message
        super().__init__(f"Error occur when parsing: {message}")

class APICallingError(BaseException):
    """Error occur when calling API."""
    def __init__(self, message: str):
        self.message = message
        super().__init__(f"Error occur when calling API: {message}")


backend/memory.py

from typing import Any, Dict, List, Union
from loguru import logger
import json

from backend.app import app
from backend.utils.running_time_storage import get_running_time_storage
from backend.utils.user_conversation_storage import get_user_conversation_storage
from real_agents.adapters.memory import BaseChatMemory

HUMAN_MESSAGE_KEY = "human_message"
AI_MESSAGE_KEY = "ai_message"

LOCAL = "local"
DATABASE = "database"

class UserMemoryManager:
    """A class to manage the global memory including messages, grounding_sources,
    etc. on user level"""

    # api_key_pool:


backend/app.py

import os

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
current_path = os.path.abspath(__file__)
app.config["UPLOAD_FOLDER"] = os.path.dirname(current_path) + "/data"
os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True)
# Execute code locally or remotely on docker
app.config["CODE_EXECUTION_MODE"] = os.getenv("CODE_EXECUTION_MODE", "local")
CORS(app)


backend/schemas.py

APP_TYPES = ["copilot", "plugins", "webot"]
TIME_STEP = 0.035
TIME_OUT_MAP = {"copilot": 90, "plugins": 300, "webot": 600}
STREAM_BLOCK_TYPES = ["image", "echarts"]
STREAM_TOKEN_TYPES = ["tool", "transition", "execution_result", "error", "kaggle_search", "kaggle_connect", "plain"]
EXECUTION_RESULT_MAX_TOKENS_MAP = {"copilot": 1000, "plugins": 2000, "webot": 20000}

HEARTBEAT_INTERVAL = 10

# define error code
UNAUTH = 401
UNFOUND = 404
OVERLOAD = 503
INTERNAL = 500
UNSUPPORTED = 403

# define models which need extra continue flag
NEED_CONTINUE_MODEL = {"claude-v1", "claude-2"}
DEFAULT_USER_ID = "DefaultUser"


backend/main.py

import os
import warnings
import threading

from backend.app import app
from backend.kernel_publisher import start_kernel_publisher
from backend.utils.threading import ThreadManager
from backend.utils.utils import VariableRegister, init_log
from backend.memory import (
    ChatMemoryManager,
    MessageMemoryManager,
    UserMemoryManager,
)

warnings.filterwarnings("ignore", category=UserWarning)

logger = init_log(
    error=os.path.join(".logging", "error.log"),
    runtime=os.path.join(".logging", "runtime.log"),
    serialize=os.path.join(".logging", "serialize.log"),
    trace=os.path.join(".logging", "trace.log"),


import redis
from typing import Any

from backend.utils.utils import logger
import os

r = redis.Redis(host=os.getenv("REDIS_SERVER"), port=6379, decode_responses=True)

# Set the queue and pending key
QUEUE_RUNNING = "kernel_running_queue"
QUEUE_PENDING = "kernel_pending_queue"
SUBMIT_EVENT = "job_submitted"
RUNNING_EVENT = "job_started"
COMPLETE_EVENT = "job_completed"

MAX_CONCURRENT_KERNELS = 300

def add_job_to_pending(job: Any) -> None:
    # always add the jobn to pending


# flake8: noqa
# mypy: ignore-errors

from backend.api import (
    chat_copilot,
    chat_plugin,
    chat_webot,
    conversation,
    file,
    recommend,
    tool,
    language_model,
    webot_actions,
    webot_instructions,
    data_tools,
    data_connector,
)


from typing import Dict, Optional, List
import json
import base64
import re
import ast

import mo_sql_parsing
from pydantic import BaseModel

from real_agents.adapters.data_model import MessageDataModel, DataModel

def is_json(text: str) -> bool:
    try:
        json.loads(text)
        return True
    except json.JSONDecodeError:
        return False

def split_text_and_code(text: str) -> List:


import redis
from flask import g
import os

def get_running_time_storage():
    """Connects to redis."""
    if "running_time_storage" not in g:
        g.running_time_storage = redis.Redis(host=os.getenv("REDIS_SERVER"), port=6379, decode_responses=True)
        # Set maxmemory to 200MB (value is in bytes)
        g.running_time_storage.config_set("maxmemory", "500000000")
        # Set maxmemory policy to allkeys-lru (Least Recently Used)
        g.running_time_storage.config_set("maxmemory-policy", "allkeys-lru")
    return g.running_time_storage


import pymongo
from flask import g
import os

def get_user_conversation_storage():
    """Connects to mongodb."""
    if "user_conversation_storage" not in g:
        g.user_conversation_storage = pymongo.MongoClient("mongodb://{0}:27017/".format(os.getenv("MONGO_SERVER")))
    return g.user_conversation_storage["xlang"]

def close_user_conversation_storage():
    """Closes mongodb."""
    user_conversation_storage = g.pop("user_conversation_storage", None)
    if user_conversation_storage is not None:
        user_conversation_storage["xlang"].close()


import json
import re
import struct
import time
from typing import Any, Dict, List, Optional, Literal
import multiprocess
import requests
from bs4 import BeautifulSoup

from backend.display_streaming import DisplayStream
from backend.main import logger, message_pool, threading_pool
from backend.utils.user_conversation_storage import get_user_conversation_storage
from backend.utils.utils import error_rendering
from backend.memory import MessageMemoryManager
from backend.schemas import (
    APP_TYPES,
    TIME_OUT_MAP,
    HEARTBEAT_INTERVAL,
    STREAM_BLOCK_TYPES,
    STREAM_TOKEN_TYPES,
    EXECUTION_RESULT_MAX_TOKENS_MAP,


import os
import sys
import base64
from pathlib import Path
from typing import Any, Dict, Tuple, Union

import pandas as pd
import tiktoken
from flask import Request
from sqlalchemy import create_engine
from PIL import Image
from loguru import logger

from real_agents.adapters.data_model import (
    DatabaseDataModel,
    DataModel,
    ImageDataModel,
    TableDataModel,
    KaggleDataModel,
)
from real_agents.data_agent import (


# Python program using
# traces to kill threads
from typing import Dict, Tuple, Optional
from multiprocess import Process

class ThreadManager:
    """Manager class of all user chat threads."""

    def __init__(self) -> None:
        self.thread_pool: Dict[str, Process] = {}
        self.stop_pool: Dict[str, bool] = {}
        self.timeout_pool: Dict[str, bool] = {}
        self.run_error_pool: Dict[str, Optional[str]] = {}

    def register_thread(self, chat_id, thread: Process) -> None:
        self.thread_pool[chat_id] = thread
        self.stop_pool[chat_id] = False
        self.timeout_pool[chat_id] = False
        self.run_error_pool[chat_id] = None


import json

def polish_echarts(echarts_str):
    """Polishes the echarts output into prettier format."""
    try:
        option = json.loads(echarts_str)

        # turn numeric axis into str
        category_flag = False
        for idx, series_data in enumerate(option["series"]):
            if series_data["type"] in ["bar", "line"]:
                category_flag = True
                break
        if category_flag:
            option["xAxis"][0]["data"] = [str(_) for _ in option["xAxis"][0]["data"]]
            for idx, series_data in enumerate(option["series"]):
                try:
                    option["series"][idx]["data"] = [[str(_[0]), _[1]] for _ in series_data["data"]]
                except:
                    continue


from time import sleep
import copy
import redis
import json
import pickle
import traceback
from flask import Response, request, stream_with_context
from typing import Dict, Union
import os

from langchain.schema import HumanMessage, SystemMessage

from backend.api.language_model import get_llm
from backend.main import app, message_id_register, message_pool, logger
from backend.utils.streaming import single_round_chat_with_agent_streaming
from backend.schemas import OVERLOAD, NEED_CONTINUE_MODEL
from backend.schemas import DEFAULT_USER_ID
from real_agents.adapters.llm import BaseLanguageModel
from real_agents.adapters.agent_helpers import AgentExecutor, Tool
from real_agents.adapters.callbacks.agent_streaming import \
    AgentStreamingStdOutCallbackHandler


import base64
import copy
import json
import os
import random
import traceback
from typing import Dict, List, Union

import requests
from flask import Response, request, stream_with_context
from retrying import retry

from backend.api.language_model import get_llm
from backend.app import app
from backend.main import message_id_register, message_pool, logger
from backend.utils.streaming import single_round_chat_with_agent_streaming
from backend.schemas import OVERLOAD, NEED_CONTINUE_MODEL, DEFAULT_USER_ID
from backend.main import api_key_pool
from real_agents.adapters.llm import BaseLanguageModel
from real_agents.adapters.agent_helpers import AgentExecutor, Tool
from real_agents.adapters.callbacks.agent_streaming import \


import struct
import json
import datetime
from typing import Any, Generator
from bson.objectid import ObjectId
from flask import jsonify, request, Response

from backend.app import app
from backend.utils.user_conversation_storage import get_user_conversation_storage
from backend.main import threading_pool, logger
from backend.schemas import DEFAULT_USER_ID
from backend.schemas import INTERNAL, UNFOUND

@app.route("/api/conversations/get_conversation_list", methods=["POST"])
def get_conversation_list() -> Response:
    """Gets the history conversations."""
    request_json = request.get_json()
    user_id = request_json.pop("user_id", DEFAULT_USER_ID)
    conversations = []
    try:


import os
from flask import request, Response
from kaggle.api.kaggle_api_extended import KaggleApi

from backend.app import app
from backend.utils.utils import create_personal_folder
from backend.schemas import UNFOUND, INTERNAL, DEFAULT_USER_ID

api = KaggleApi()
api.authenticate()

@app.route("/api/kaggle/download_dataset", methods=["POST"])
def kaggle_dataset_download() -> dict | Response:
    """Use Kaggle-api to connect. """
    request_json = request.get_json()
    user_id = request_json.pop("user_id", DEFAULT_USER_ID)
    url = request_json["url"]
    if url.startswith("http"):
        return {"success": False,
                "message": "Please remove the http in your submitted URL."}


import traceback
from typing import Dict, List, Union
from flask import Response, request, stream_with_context, Response

from backend.api.file import _get_file_path_from_node
from backend.api.language_model import get_llm
from backend.app import app
from backend.main import (
    grounding_source_pool,
    jupyter_kernel_pool,
    logger,
    message_id_register,
    message_pool,
)
from backend.schemas import DEFAULT_USER_ID
from backend.utils.utils import create_personal_folder
from backend.utils.charts import polish_echarts
from backend.utils.streaming import (
    single_round_chat_with_executor,
    single_round_chat_with_agent_streaming,
)


from flask import request, jsonify, Response

from backend.api.chat_webot import get_webot_from_redis, save_webot_to_redis
from backend.main import app
from backend.schemas import DEFAULT_USER_ID
from backend.api.language_model import get_llm

@app.route("/api/webot/action", methods=["POST"])
def get_action() -> Response:
    """Gets the next action to take for a given the current page HTML."""
    request_json = request.get_json()
    user_id = request_json.pop("user_id", DEFAULT_USER_ID)
    chat_id = request_json["chat_id"]
    webot = get_webot_from_redis(user_id=user_id, chat_id=chat_id)
    # Get request parameters
    request_json = request.get_json()
    processed_html = request_json["processed_html"]
    llm = get_llm("gpt-4")
    result = webot.run(processed_html, llm=llm)
    save_webot_to_redis(user_id=user_id, chat_id=chat_id, webot=webot)


import os

from backend.app import app
from real_agents.adapters.models import ChatOpenAI, ChatAnthropic
from real_agents.adapters.llm import BaseLanguageModel

LLAMA_DIR = "PATH_TO_LLAMA_DIR"

@app.route("/api/llm_list", methods=["POST"])
def get_llm_list():
    """Gets the whole llm list."""
    return [
        {"id": llm, "name": llm} for llm in [
            "gpt-3.5-turbo-16k",
            "gpt-4",
            "claude-v1",
            "claude-2",
            "lemur-chat"
        ]
    ]


from flask import request, jsonify, Response

from backend.main import app
from backend.schemas import DEFAULT_USER_ID
from backend.api.chat_webot import get_webot_from_redis, \
    get_webot_status_from_redis, reset_webot_status

@app.route("/api/webot/instructions", methods=["POST"])
def get_instruction() -> Response:
    request_json = request.get_json()
    user_id = request_json.pop("user_id", DEFAULT_USER_ID)
    chat_id = request_json["chat_id"]
    webot = get_webot_from_redis(user_id=user_id, chat_id=chat_id)
    return jsonify({
        "chat_id": chat_id,
        "user_id": user_id,
        "instructions": webot.instruction
    })


from typing import Dict
from flask import request, jsonify, Response

from backend.main import message_pool
from backend.app import app
from backend.api.language_model import get_llm
from backend.utils.utils import get_user_and_chat_id_from_request_json
from real_agents.adapters.executors import QuestionSuggestionExecutor
from real_agents.adapters.memory import ConversationReActBufferMemory

@app.route("/api/recommend", methods=["POST"])
def recommend() -> dict | Response:
    """Recommends potential inputs for users. """
    try:
        request_json = request.get_json()
        (user_id, chat_id) = get_user_and_chat_id_from_request_json(request_json)
        parent_message_id = int(request_json["parent_message_id"])
        user_intent = request_json["user_intent"]

        # Find the mainstat message list from leaf to root


from typing import List
from flask import jsonify

from backend.app import app

DATA_TOOLS = [
    {
        "type": "language",
        "id": "1cea1f39-fe63-4b08-83d5-fa4c93db0c87",
        "name": "SQLQueryBuilder",
        "name_for_human": "SQL",
        "pretty_name_for_human": "SQL Query Generation",
        "icon": "",
        "description": "Using SQL as the programming language",
    },
    {
        "type": "language",
        "id": "0c135359-af7e-473b-8425-1393d2943b57",
        "name": "PythonCodeBuilder",
        "name_for_human": "Python",
        "pretty_name_for_human": "Python Code Generation",


from flask import request, jsonify, Response

from backend.api.chat_plugin import plugins
from backend.main import app, api_key_pool
from backend.schemas import DEFAULT_USER_ID

@app.route("/api/tool_list", methods=["POST"])
def get_tool_list() -> Response:
    """parameters:
    {
      user_id: id of the user
    }
    return value:
    [{
        id: id of a plugin,
        name: name pf a plugin,
        description: description of the plugin,
        icon: icon of the plugin,
        require_api_key: whether the plugin requires api_key,
        api_key: the api key of the plugin, None if no api key
    }]


import json
import os
import shutil
from typing import Dict, Any
from flask import Response, jsonify, request, send_file

from backend.app import app
from backend.main import (
    grounding_source_pool,
    logger,
    message_id_register,
    message_pool,
)
from backend.schemas import DEFAULT_USER_ID
from backend.utils.utils import create_personal_folder
from backend.utils.user_conversation_storage import get_user_conversation_storage
from backend.utils.utils import (
    allowed_file,
    get_data_model_cls,
    get_user_and_chat_id_from_request,
    get_user_and_chat_id_from_request_json,


# flake8: noqa

PREFIX = """You are XLang Agent , a friendly and intuitive interface developed by the XLang Team to guide human through every stage of human data lifecycle. Whether human are loading, processing, or interpreting data, XLang Agent is always at human's fingertips through our interactive chat system.

Empowered by an array of innovative tools that can generate and execute code, XLang Agent delivers robust, reliable answers to human queries. Whenever possible, You employs these tools to give human rich insights, like dynamic code generation & execution and compelling visualizations. And You will always proactively and correctly using all tools to help with human.

Get ready for a seamless and insightful journey with XLang Agent, the personal assistant for all things data!

TOOLS
------
You have direct access to following tools.
"""

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 Tool**
If the response involves using a tool, you can start with a natural language explanation[Optional], plus exactly one tool calling[MUST]. But **make sure no any words & answer appended after tool calling json**. The tool calling format should be a markdown code snippet with the following JSON schema:


from real_agents.data_agent.copilot import ConversationalChatAgent
from real_agents.data_agent.evaluation.python_evaluator import PythonEvaluator
from real_agents.data_agent.evaluation.sql_evaluator import SQLEvaluator
from real_agents.data_agent.executors.code_generation_executor import CodeGenerationExecutor
from real_agents.data_agent.executors.data_summary_executor import (
    DataSummaryExecutor,
    TableSummaryExecutor,
    ImageSummaryExecutor,
)
from real_agents.data_agent.executors.kaggle_data_loading_executor import KaggleDataLoadingExecutor
from real_agents.data_agent.python.base import PythonChain
from real_agents.data_agent.sql.base import SQLDatabaseChain
from real_agents.adapters.schema import SQLDatabase


"""An agent designed to hold a conversation in addition to using tools."""
from __future__ import annotations

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

from langchain.agents.agent import AgentOutputParser
from langchain.agents.utils import validate_tools_single_input
from langchain.base_language import BaseLanguageModel
from langchain.callbacks.manager import AsyncCallbackManagerForToolRun, CallbackManagerForToolRun, Callbacks
from langchain.chains import LLMChain
from langchain.schema import AgentAction, AgentFinish, HumanMessage, AIMessage, BaseMessage, BaseOutputParser
from langchain.tools.base import BaseTool

from real_agents.adapters.agent_helpers.agent import Agent
from real_agents.adapters.agent_helpers.output_parser import ConversationOutputParser
from real_agents.data_agent.copilot_prompt import PREFIX, SUFFIX, TEMPLATE_TOOL_RESPONSE, fake_continue_prompt
from real_agents.adapters.data_model import DataModel, MessageDataModel
from langchain.prompts import (
    BasePromptTemplate,


import os
from typing import Any, List, Optional, Tuple, Dict
from pydantic import BaseModel
import requests
import time
import ast

import pandas as pd
from io import StringIO
import redis
from loguru import logger

from IPython.core.interactiveshell import InteractiveShell
from IPython.core.getipython import get_ipython
from IPython.utils.capture import capture_output

# subscribed channels
SUBMIT_EVENT = "job_submitted"
RUNNING_EVENT = "job_started"
COMPLETE_EVENT = "job_completed"


import traceback
from typing import Any, Dict, List

from pydantic import root_validator

from real_agents.adapters.schema import SQLDatabase

class SQLEvaluator:
    """
    Util class for SQL code evaluation.
    """

    name = "SQL Evaluator"
    ERROR_PREFIX = "[ERROR]: "

    @root_validator(pre=True)
    def validate(cls, values: Dict) -> Any:
        """validate requirements for evaluation"""
        try:
            import sqlite3  # noqa F401 E402


import json
import os
import re
import shutil
import uuid
from typing import Any, Dict, List, Tuple
import requests
from bs4 import BeautifulSoup
from loguru import logger
from kaggle.api.kaggle_api_extended import KaggleApi

from langchain.base_language import BaseLanguageModel
from langchain import PromptTemplate

from real_agents.adapters.llm import LLMChain

class KaggleDataLoadingExecutor:
    KAGGLE_TEMPLATE = """

Determine whether the user input aims to (1) connect to a specific kaggle dataset that the user mentions its kaggle path


from typing import Any, Dict, List, Literal, Optional, Union

from langchain.base_language import BaseLanguageModel

from real_agents.adapters.data_model import DatabaseDataModel, TableDataModel, ImageDataModel
from real_agents.adapters.memory import ReadOnlySharedStringMemory
from real_agents.adapters.schema import SQLDatabase
from real_agents.data_agent.python.base import PythonChain
from real_agents.data_agent.sql.base import SQLDatabaseChain

class CodeGenerationExecutor:
    """Code Generation Executor.

    Example:
        .. code-block:: python

                from real_agents.adapters.executors import CodeGenerationExecutor
                executor = CodeGenerationExecutor(programming_language="sql")
                executor.run(
                    user_intent="What is the name of the first employee?",


from typing import Any, Dict, Tuple, Union
from abc import ABC

from langchain.base_language import BaseLanguageModel
from langchain import PromptTemplate

from real_agents.adapters.callbacks.executor_streaming import ExecutorStreamingChainHandler
from real_agents.adapters.data_model import DatabaseDataModel, TableDataModel, ImageDataModel
from real_agents.adapters.llm import LLMChain

class DataSummaryExecutor(ABC):
    tool_name = "DataProfiling"

    def _intelligent_summary(self, grounding_source: ImageDataModel, num_insights: int, llm: BaseLanguageModel) -> str:
        """Use LLM to generate data summary."""
        pass

class TableSummaryExecutor(DataSummaryExecutor):
    SUMMARY_PROMPT_TEMPLATE = """


"""Chain for interacting with SQL Database."""
from __future__ import annotations

from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Extra, Field
from loguru import logger

from langchain.base_language import BaseLanguageModel
from langchain.callbacks.manager import CallbackManagerForChainRun
from langchain.chains.base import Chain
from langchain import BasePromptTemplate, FewShotPromptTemplate

from real_agents.data_agent.evaluation.sql_evaluator import SQLEvaluator
from real_agents.adapters.schema import SQLDatabase
from real_agents.adapters.memory import ReadOnlySharedStringMemory
from real_agents.data_agent.sql.prompt import (
    EXAMPLE_PROMPT,
    FEW_SHOT_INPUT_VARIABLES,
    FEW_SHOT_PREFIX,
    FEW_SHOT_SUFFIX,
    PROMPT,


# flake8: noqa
from langchain import PromptTemplate

# Text-to-sql prompt
_DEFAULT_TEMPLATE = """Here are chat histories you may refer to, maybe empty.
{chat_history}

Given an input question, first create a syntactically correct {dialect} query to run, then look at the results of the query and return the answer.
Never query for all the columns from a specific table, only ask for a the few relevant columns given the question.
Pay attention to use only the column names that you can see in the schema description. Be careful to not query for columns that do not exist. Also, remember to wrap the table names in double quotes.
Use the following format:
Question: "Question here"
SQLQuery: "SQL Query to run"
SQLResult: "Result of the SQLQuery"
Answer: "Final answer here"
Only use the tables listed below.
{table_info}
Question: {question}"""
PROMPT = PromptTemplate(
    input_variables=["chat_history", "question", "table_info", "dialect"],
    template=_DEFAULT_TEMPLATE,


"""Implements Python Code Generation. """
from __future__ import annotations

import re
from typing import Any, Dict, List, Optional

from bs4 import BeautifulSoup
from langchain.base_language import BaseLanguageModel
from langchain.callbacks.manager import CallbackManagerForChainRun
from langchain.chains.base import Chain
from langchain.prompts.base import BasePromptTemplate
from langchain.prompts.chat import (
    ChatPromptTemplate,
    HumanMessagePromptTemplate,
)
from langchain.schema import SystemMessage
from loguru import logger
from pydantic import BaseModel, Extra

from real_agents.adapters.data_model import MessageDataModel
from real_agents.adapters.memory import ReadOnlySharedStringMemory


ECHARTS_REF_CODE = """Here are some examples of generating Py-Echarts Code based on the given table(s). Please generate new one based on the data and question human asks you, import the neccessary libraries and make sure the code is correct.

IMPORTANT: You need to follow the coding style, and the type of the x, y axis. But also need to focus on the column name of the uploaded tables(if exists). Generally, PyEcharts does not accept numpy.int or numpy.float, etc. It only supports built-in data type like int, float, and str.

Given the following database:
company_sales.xlsx
   year  sales  profit  expenses  employees
0  2010    100      60        40         10
1  2011    120      80        50         12
2  2012    150      90        60         14
3  2013    170     120        70         16
[too long to show]

Q: Could you help plot a bar chart with the year on the x-axis and the sales on the y-axis?
<code>
import pandas as pd
from pyecharts.charts import Bar
from pyecharts import options as opts
df = pd.read_excel('company_sales.xlsx')
years = [str(_) for _ in df['year'].tolist()]
sales = [float(_) for _ in df['sales'].tolist()]


FUNCTION_ROLE_PLAY = """def generate_continuous_elegant_python_code(history_dict: Dict[str, str], reference_code: str = "") -> str:
    \"\"\"
    This function generates elegant, coherent Python code based on a history of previously executed code and its corresponding results. The code is generated in response to human questions and is intended to continue from the last provided code snippet.

    The function takes two inputs: a `history_dict` and an optional `reference_code` string.

    The `history_dict` is a dictionary with the following keys:
    - 'history code': Contains the chat history of previously executed code snippets. It may be initially empty but will accumulate executed code over time.
    - 'human question': Contains the current question or instruction posed by the human user, which the generated code should respond to. Be aware that sometimes the 'human question' could contain code snippets, including instructions for loading data, which may need to be handled differently. It's not always appropriate to directly use the code in 'human question' without consideration.
    - 'data': Contains a list of data previews available for the task. It may include tables, images, and other data types.

    The `reference_code` string is optional and contains example codes, often related to a specific library or task, which can serve as a template for the code generation process. This parameter can be empty.

    IMPORTANT: Always refer to this history and the `reference_code` when generating new code in order to properly use existing variables and previously loaded resources, as well as to follow established coding patterns. DO NOT USE ECHARTS TO GENERATE CHARTS when reference code is empty.

    IMPORTANT: When `reference_code` is NOT EMPTY, the output MUST follow the style and use the libraries presented in the `reference_code` to accomplish the task.

    IMPORTANT: Avoid mere repetition of historical code. Always aim to generate novel and appropriate responses to the questions at hand.

    IMPORTANT: The 'data' key in the dictionary contains only random rows from a table. If a table has not been loaded before, load it from the correct path. You can assume it is in the current working directory. However, there's no need to load a table with every execution - only do this when necessary.


USER_PROMPT = """
history_code = \"\"\"{history_code}\"\"\"
human_question = \"\"\"{question}
# DO NOT use function that will pop up a new window (e.g., PIL & Image.show() is NOT preferable, saving the PIL image is better)
# However, feel free to use matplotlib.pyplot.show()\"\"\"
data = \"\"\"{data}\"\"\"
reference_code = \"\"\"{reference_code}\"\"\"

history_dict = {{
    "history code": history_code,
    "human question": human_question,
    "data": data,
    "reference_code": reference_code,
}}
"""

"""
final format:
user_prompt + reference_prompt + history_prompt
"""


from real_agents.web_agent.webot import ConversationalWebotChatAgent
from real_agents.web_agent.executors.webot_executor import WebotExecutor
from real_agents.web_agent.executors.web_browsing_executor import WebBrowsingExecutor
from real_agents.web_agent.web_browsing.base import WebotCallingChain
from real_agents.web_agent.web_browsing.end2end.base import WebotChain
from real_agents.web_agent.web_browsing.react.base import ReActWebotChain


# flake8: noqa
import datetime

PREFIX = (
        """You are XLanG WeBot 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.
Here are detailed instruction for you. Each time you generate response, you should think step by step to follow instructions below. You are a helpful assistant that is provided with a plugin called "WeBot" which is a web navigation agent tool and should leverage the power of it to help human to fulfill their needs, such as booking a hotel, buying a ticket, or searching for information, etc.
Human will ask you questions, and you can use WeBot to help them, they are assumed to know nothing about the WeBot.
----------------------------
Here are something you MUST remember:
1. After receiving output from the WeBot, you should check
    1.1 whether WeBot was interrupted, if so you should NEVER try again by yourself.
    1.2 whether WeBot failed or had error(not because of interruption), if so you should tell the human the error.
2. 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.

NEVER EVER EVER use other plugins except WeBot.
TRY YOUR BEST to break the question down into several parts and answer them one by one.
TRY YOUR BEST to use the WeBot to help you answer the question, you don't need to mention that you will use which WeBot, just use it.


"""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 (


"""
Implementation of the WebBrowsingExecutor.
WebBrowsingExecutor takes start_url and instruction as input, iteratively perform the actions on the web, and return the result.
"""
from typing import Any, Dict, List

from langchain.base_language import BaseLanguageModel

from real_agents.web_agent.web_browsing.react.base import ReActWebotChain
from real_agents.web_agent.web_browsing.end2end.base import WebotChain
from real_agents.adapters.data_model.html import HTMLDataModel

# This executor is for the extension usage and not for the chat interface.
# For the chat interface webot executor, refer xlang/real_agents/web_agent/executors/web_browsing_executor.py
class WebBrowsingExecutor:
    """
    WebBrowsingExecutor takes start_url and instruction as input, iteratively perform the actions on the web, and return the result.
    """

    def __init__(self, instruction: str, plan: str = "", mode: str = "react") -> None:


"""
Implementation of the WebotExecutor class.
WebotExecutor takes user's intent as input, return the start_url and instruction as the input for web browsing plugin
"""
from __future__ import annotations

from typing import Any, Dict, Union

from langchain.base_language import BaseLanguageModel
from pydantic import BaseModel, Extra

from real_agents.web_agent.web_browsing.base import WebotCallingChain

# This executor is for chat interface usage and not for the extension usage.
# For the extension usage, refer to xlang/real_agents/web_agent/executors/web_browsing_executor.py
class WebotExecutor(BaseModel):
    """
    WebotExecutor takes user's intent as input, return the start_url and instruction as the input for web browsing plugin (tool).
    """
    name: str


"""Implementation of the base webot calling chain."""
from __future__ import annotations

import re
import traceback
from typing import Dict, List, Optional

import json5
from langchain.base_language import BaseLanguageModel
from langchain.callbacks.manager import CallbackManagerForChainRun
from langchain.chains import LLMChain
from langchain.chains.base import Chain
from langchain.prompts.base import BasePromptTemplate
from langchain.prompts.chat import (
    ChatPromptTemplate,
    HumanMessagePromptTemplate,
    SystemMessagePromptTemplate,
)
from pydantic import BaseModel, Extra

from real_agents.adapters.memory import ReadOnlySharedStringMemory


SYSTEM_PROMPT = (
    """You are an expert at browsing website and you know a lot of website in case the user didn't explicitly mention the website they would like to search.""".strip()
    + "\n"
)

USER_PROMPT = (
    """
Here is the user's intent:
```
{input_str}
```
Now imagine you can use a tool called web agent to navigate on the web for you.
you should act as a user to tell the web agent instruction and the url to start
then it will take in the instruction and start at the url to navigate on the web.
Remember:
1. If you know the detailed url to take the action, you may set this as the start url. e.g. https://www.twitter.com/compose/tweet for writing a tweet on twitter
2. If you don't know the detailed url, you may set the start url as the homepage of the website. e.g. https://www.imdb.com/ for movie related question
3. If you are not sure whether the homepage will contain info that you need. Use https://www.google.com/ as the start url instead.
Here is an example for your reference:
the user intent is to write a blog post on medium
you should out put like this:


# The schema (action space) for the web browsing task is defined here:
ACTIONS = [
    {
        "name": "click",
        "description": "Clicks on an element",
        "args": [{"name": "elementId", "type": "number"}],
    },
    {
        "name": "setValue",
        "description": "Focuses on and sets the `value` of an input element.",
        "args": [{"name": "elementId", "type": "number"}, {"name": "value", "type": "string"}],
    },
    {"name": "finish", "description": "Indicates the task is finished", "args": []},
    {"name": "fail", "description": "Indicates that you are unable to complete the task", "args": []},
]


"""Implementation for prompt based end2end web bots."""
from __future__ import annotations

import datetime
import re
from typing import Any, Dict, List, Optional
from loguru import logger
from pydantic import BaseModel, Extra

from langchain.base_language import BaseLanguageModel
from langchain.callbacks.manager import CallbackManagerForChainRun
from langchain.chains.base import Chain
from langchain.chains.llm import LLMChain
from langchain.prompts.base import BasePromptTemplate
from langchain.prompts.chat import (
    ChatPromptTemplate,
    HumanMessagePromptTemplate,
    SystemMessagePromptTemplate,
)

from real_agents.adapters.memory import ReadOnlySharedStringMemory


SYSTEM_PROMPT = (
    """
You are a browser automation assistant.

You MUST take one of the following actions. NEVER EVER EVER make up actions that do not exist:

{formattedActions}

You will be be given a task to perform and the current state of the DOM. You will also be given previous actions that you have taken. You may retry a failed action up to one time.

This is an example of an action:

<Action>click(223)</Action>

You MUST always include the <Thought> and <Action> open/close tags or else your response will be marked as invalid.

Rules you MUST follow:
1. You must only take one step at a time. You cannot take multiple actions in a single response.
2. You should not consider the action to present the result to the user. You only need to do available actions. If info in current page is enough for the user to solve the problem, you should finish.
""".strip()
    + "\n"


"""Implementation for prompt based react web bots."""
from __future__ import annotations

import datetime
import re
from typing import Any, Dict, List, Optional, Tuple
from loguru import logger

from langchain.base_language import BaseLanguageModel
from langchain.callbacks.manager import CallbackManagerForChainRun
from langchain.chains.llm import LLMChain

from real_agents.web_agent.web_browsing.end2end.base import WebotChain
from real_agents.web_agent.web_browsing.react.prompt import (
    RETRY_PROMPT,
    SYSTEM_PROMPT,
    USER_PROMPT,
)

class ReActWebotChain(WebotChain):
    """Basic prompt based web bot that interact with websites."""


SYSTEM_PROMPT = (
    """
You are a browser automation assistant.

You will be given a user request and DOM of current webpage at a time, you need to take one action at a time and finally finish the task.

The last page you visited will be further fed into another model who is responsible for chatting with the user.

You MUST take one of the following actions. NEVER EVER EVER make up actions that do not exist:

{formattedActions}

You will be be given a task to perform and the current state of the DOM. You will also be given previous actions that you have taken. You may retry a failed action up to one time.

This is an example of an action:

<Thought>I should click the add to cart button</Thought>
<Action>click(223)</Action>

You MUST always include the <Thought> and <Action> open/close tags or else your response will be marked as invalid.


from __future__ import annotations

from typing import Any, Optional, Sequence

from langchain.base_language import BaseLanguageModel
from langchain.tools.base import BaseTool

from real_agents.adapters.agent_helpers import AgentExecutor
from real_agents.data_agent.copilot import ConversationalChatAgent
from real_agents.plugins_agent.plugin import ConversationalPluginChatAgent
from real_agents.web_agent.webot import ConversationalWebotChatAgent

def initialize_agent(
    tools: Sequence[BaseTool],
    llm: BaseLanguageModel,
    continue_model: str = None,
    agent_kwargs: Optional[dict] = None,
    return_intermediate_steps: Optional[bool] = True,
    **kwargs: Any,
) -> AgentExecutor:


"""Chain that just formats a prompt and calls an LLM."""
from __future__ import annotations

from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
from pydantic import Extra

from langchain.base_language import BaseLanguageModel
from langchain.callbacks.manager import (
    AsyncCallbackManager,
    AsyncCallbackManagerForChainRun,
    CallbackManager,
    CallbackManagerForChainRun,
    Callbacks,
)
from langchain.chains.base import Chain
from langchain.input import get_colored_text
from langchain.prompts.base import BasePromptTemplate
from langchain.prompts.prompt import PromptTemplate
from langchain.schema import LLMResult, PromptValue

from real_agents.adapters.data_model import DataModel


from typing import NamedTuple
from langchain import SQLDatabase
from sqlalchemy import text
from sqlalchemy.engine import Row
from tabulate import tabulate
from typing import List, Any

class AgentTransition(NamedTuple):
    """Agent's transition to take."""

    return_values: dict
    log: str

EMPTY_RESULT_STR = "NONE"  # to show NONE result in front-end.

class SQLDatabase(SQLDatabase):
    @staticmethod
    def _pretty_format(headers: Any, result: List[Row]) -> str:


"""Callback Handler streams to stdout on new llm token."""
import sys
from typing import Any, Dict, List, Union

from langchain.callbacks.base import BaseCallbackHandler
from langchain.schema import AgentAction, AgentFinish, LLMResult

class StreamingStdOutCallbackHandler(BaseCallbackHandler):
    """Callback handler for streaming. Only works with LLMs that support streaming."""

    def on_llm_start(self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any) -> None:
        """Run when LLM starts running."""

    def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
        """Run on new LLM token. Only available when streaming is enabled."""
        sys.stdout.write(token)
        sys.stdout.flush()

    def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
        """Run when LLM ends running."""


from typing import Any

from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler

class ExecutorStreamingChainHandler(StreamingStdOutCallbackHandler):
    is_end: bool = False
    _all = []

    @property
    def always_verbose(self) -> bool:
        """Whether to call verbose callbacks even if verbose is False."""
        return True

    def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
        """"""
        self._all.append(token)


"""Base callback handler that can be used to handle callbacks in langchain."""
from __future__ import annotations

from typing import Any, Dict, List, Optional, Union
from uuid import UUID

from langchain.schema import AgentAction, AgentFinish, BaseMessage, LLMResult

class LLMManagerMixin:
    """Mixin for LLM callbacks."""

    def on_llm_new_token(
        self,
        token: str,
        *,
        run_id: UUID,
        parent_run_id: Optional[UUID] = None,
        **kwargs: Any,
    ) -> Any:
        """Run on new LLM token. Only available when streaming is enabled."""


"""Callback Handler streams to stdout on new llm token."""
from typing import Any, Dict, List, Union

from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler

from real_agents.adapters.data_model import DataModel

class JSON_PDA:
    def __init__(self):
        self.stack = []
        self.state = "start"
        self.json = {}
        self.current_key = ""
        self.current_value = ""
        self.escape_next = False

    def transition(self, char):
        if self.escape_next:
            # Add the escaped character to the current key or value and return
            if self.state == "open_key_quote":


from __future__ import annotations

import asyncio
import functools
import logging
import os
import warnings
from contextlib import contextmanager
from contextvars import ContextVar
from typing import Any, Dict, Generator, List, Optional, Type, TypeVar, Union, cast
from uuid import UUID, uuid4

import langchain
from langchain.callbacks.base import (
    BaseCallbackHandler,
    BaseCallbackManager,
    ChainManagerMixin,
    LLMManagerMixin,
    RunManagerMixin,
    ToolManagerMixin,
)


from real_agents.adapters.callbacks.agent_streaming import JSON_PDA, AgentStreamingStdOutCallbackHandler
from real_agents.adapters.callbacks.base import BaseCallbackHandler, BaseCallbackManager, AsyncCallbackHandler
from real_agents.adapters.callbacks.executor_streaming import ExecutorStreamingChainHandler
from real_agents.adapters.callbacks.manager import CallbackManager, CallbackManagerForChainRun


import asyncio
import inspect
import warnings
from abc import ABC, abstractmethod
from typing import Any, Dict, List, Mapping, Optional, Sequence

import langchain
from langchain.base_language import BaseLanguageModel
from langchain.callbacks.base import BaseCallbackManager
from langchain.callbacks.manager import (
    AsyncCallbackManager,
    AsyncCallbackManagerForLLMRun,
    CallbackManager,
    CallbackManagerForLLMRun,
    Callbacks,
)
from langchain.schema import (
    BaseMessage,
    ChatGeneration,
    ChatResult,
    HumanMessage,


from typing import Any, Dict, List, Optional
from pydantic import Extra

from langchain.callbacks.manager import (
    AsyncCallbackManagerForLLMRun,
    CallbackManagerForLLMRun,
)
from langchain.chat_models.base import BaseChatModel
from langchain.llms.anthropic import _AnthropicCommon
from langchain.schema import (
    AIMessage,
    BaseMessage,
    ChatGeneration,
    ChatMessage,
    ChatResult,
    HumanMessage,
    SystemMessage,
)

class ChatAnthropic(BaseChatModel, _AnthropicCommon):


from langchain.chat_models.google_palm import ChatGooglePalm

from real_agents.adapters.models.anthropic import ChatAnthropic
from real_agents.adapters.models.openai import ChatOpenAI

__all__ = [
    "ChatOpenAI",
    "ChatAnthropic",
    "ChatGooglePalm",
]

type_to_cls_dict = {
    "chat_anthropic": ChatAnthropic,
    "chat_google_palm": ChatGooglePalm,
    "chat_openai": ChatOpenAI,
}


"""OpenAI chat wrapper."""
from __future__ import annotations

import logging
import sys
from typing import Any, Callable, Dict, List, Mapping, Optional, Tuple, Union
from pydantic import Extra, Field, root_validator
from tenacity import (
    before_sleep_log,
    retry,
    retry_if_exception_type,
    stop_after_attempt,
    wait_exponential,
)

from langchain.callbacks.manager import (
    AsyncCallbackManagerForLLMRun,
    CallbackManagerForLLMRun,
)
from langchain.chat_models.base import BaseChatModel
from langchain.schema import (


from typing import Any, Dict, List

from langchain.schema import BaseMemory

class ReadOnlySharedStringMemory(BaseMemory):
    """A memory wrapper that is read-only and cannot be changed."""

    memory: BaseMemory

    @property
    def memory_variables(self) -> List[str]:
        """Return memory variables."""
        return self.memory.memory_variables

    def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, str]:
        """Load memory variables from memory."""
        prev_memory_state = self.memory.return_messages
        self.memory.return_messages = False
        memory_string = self.memory.load_memory_variables(inputs)
        self.memory.return_messages = prev_memory_state


from typing import Any, Dict, List, Optional, Tuple
from pydantic import root_validator

from langchain.memory.utils import get_prompt_input_key
from langchain.base_language import BaseLanguageModel
from langchain.schema import BaseMessage, get_buffer_string
from langchain.memory.chat_memory import BaseChatMemory, BaseMemory

from real_agents.adapters.data_model import DataModel, MessageDataModel

class ConversationBufferMemory(BaseChatMemory):
    """Buffer for storing conversation memory."""

    human_prefix: str = "Human"
    ai_prefix: str = "AI"
    memory_key: str = "history"  #: :meta private:

    @property
    def buffer(self) -> Any:
        """String buffer of memory."""


from typing import Dict, List, Type

from langchain.schema import BaseMemory
from langchain.memory.chat_memory import BaseChatMemory

from real_agents.adapters.memory.buffer import (
    ConversationBufferMemory,
    ConversationStringBufferMemory,
)
from real_agents.adapters.memory.read_only_string_memory import ReadOnlySharedStringMemory
from real_agents.adapters.memory.buffer import ConversationReActBufferMemory

__all__ = [
    "ConversationBufferMemory",
    "ConversationReActBufferMemory",
    "ConversationStringBufferMemory",
    "BaseMemory",
    "BaseChatMemory",
    "ReadOnlySharedStringMemory",


import re
import textwrap
from typing import List, Dict, Any, Optional
from langchain.schema import BaseMessage
import tiktoken

# format of agent action
ACTION_FORMAT = """```json
{{
    "action": "{_action}",
    "action_input": "{_action_input}",
}}
```"""

# format of tool call(code) & tool output(response)
TOOL_FORMAT = {
    "code": """<code>
{_intermediate_steps}
</code>

<output>


from __future__ import annotations

import uuid
from typing import Any

from pydantic import BaseModel

class DataModel(BaseModel):
    """Base class for data models."""

    id: str
    raw_data: Any
    raw_data_name: str
    raw_data_path: str
    llm_side_data: Any  # could be string or potentially images for future needs
    human_side_data: Any

    def __hash__(self) -> int:
        return hash(self.id)


import json
from bs4 import BeautifulSoup
from collections import defaultdict
from typing import Any, Dict, List, Union
from real_agents.adapters.data_model.base import DataModel
import requests
import re
import tiktoken

JsonNode = Dict[str, Union[str, List[Any], int]]
PossibleTemplate = Dict[str, Union[str, List[Any], int]]
OptimizedTemplate = Dict[str, Union[str, List[Any], int, set]]
PossibleTemplates = Dict[str, PossibleTemplate]

def find_potential_templates(node, possible_templates):
    """Find all potential templates in the HTML tree."""
    if node.name:  # Element node
        attributes = {attr: node[attr] for attr in node.attrs}
        children = []
        for child in node.children:


from __future__ import annotations

import os
from typing import Any

import pandas as pd
from sqlalchemy import create_engine, inspect

from real_agents.adapters.data_model.base import DataModel
from real_agents.adapters.data_model.table import TableDataModel
from real_agents.adapters.data_model.templates.skg_templates.database_templates import serialize_db
from real_agents.adapters.schema import SQLDatabase

class DatabaseDataModel(DataModel):
    """A data model for database."""

    @classmethod
    def from_table_data_model(cls, table_data_model: TableDataModel) -> DatabaseDataModel:
        os.makedirs(f".db_cache/{table_data_model.id}", exist_ok=True)
        db_path = os.path.join(


from real_agents.adapters.data_model.base import DataModel
from real_agents.adapters.data_model.database import DatabaseDataModel
from real_agents.adapters.data_model.image import ImageDataModel
from real_agents.adapters.data_model.json import JsonDataModel
from real_agents.adapters.data_model.message import MessageDataModel
from real_agents.adapters.data_model.kaggle import KaggleDataModel
from real_agents.adapters.data_model.plugin import APIYamlModel, SpecModel
from real_agents.adapters.data_model.table import TableDataModel

__all__ = [
    "DataModel",
    "TableDataModel",
    "DatabaseDataModel",
    "ImageDataModel",
    "JsonDataModel",
    "KaggleDataModel",
    "APIYamlModel",
    "SpecModel",
    "MessageDataModel",
    "HTMLDataModel",
]


def indent_multiline_string(multiline_string: str, indent: int = 1) -> str:
    return "\n".join("\t" * indent + line for line in multiline_string.split("\n"))


from real_agents.adapters.data_model.base import DataModel

class TextDataModel(DataModel):
    """A data model for text, general purpose."""

    def get_llm_side_data(self, max_chars: int = 5000) -> str:
        assert isinstance(self.raw_data, str)
        return self.raw_data[:max_chars]


from __future__ import annotations

import json
from typing import Any

from pandas import DataFrame

from real_agents.adapters.data_model.base import DataModel
from real_agents.adapters.data_model.templates.skg_templates.table_templates import serialize_df

class TableDataModel(DataModel):
    """A data model for table."""

    db_view: DataModel = None

    def set_db_view(self, db_data_model: DataModel) -> None:
        self.db_view = db_data_model

    def get_llm_side_data(self, serialize_method: str = "tsv", num_visible_rows: int = 3) -> Any:
        # Show the first few rows for observation.


import json
from copy import deepcopy
from typing import Dict, List

from real_agents.adapters.data_model.base import DataModel

class JsonDataModel(DataModel):
    """A data model for json, general purpose."""

    filter_keys: List[str] = []

    def get_llm_side_data(self, json_format: str = "json") -> str:
        if json_format == "json":
            assert isinstance(self.raw_data, Dict)
            llm_side_data = deepcopy(self.raw_data)
            for key, value in self.raw_data.items():
                if key in self.filter_keys:
                    llm_side_data[key] = "..."
                    continue


from __future__ import annotations

from typing import Any, Dict

import pandas as pd

from real_agents.adapters.data_model.base import DataModel
from real_agents.adapters.data_model.templates.skg_templates.database_templates import serialize_db
from real_agents.adapters.data_model.templates.skg_templates.table_templates import serialize_df
import json

class KaggleDataModel(DataModel):
    """A data model for KaggleDataModel.
    We only support the csv and sqlite format for now.
    raw_data is a Dict[str, TableDataModel]
    raw_data_path is List[str]
    raw_data_name is Dict[str, str]
    """

    def get_llm_side_data(self, serialize_method: str = "tsv", num_visible_rows: int = 3) -> Any:


from typing import Any

from real_agents.adapters.data_model.base import DataModel

class ImageDataModel(DataModel):
    """A data model for image."""

    simple_filename = ""

    def get_raw_data(self) -> Any:
        return self.raw_data

    def get_llm_side_data(self) -> Any:
        if self.simple_filename == "":
            import os

            self.simple_filename = os.path.basename(self.raw_data_path)
        string = "image: " + self.simple_filename
        return string


import os
from typing import Any, Dict
import importlib.util
import tiktoken

from real_agents.adapters.data_model.plugin.base import APIYamlModel
from real_agents.adapters.data_model.utils import indent_multiline_string

def import_function_from_file(filepath, function_name):
    spec = importlib.util.spec_from_file_location("module.name", filepath)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)

    function = getattr(module, function_name)

    return function

def process_one_param(param_dict: Dict[str, Any]) -> str:
    name = param_dict.get("name", None)


from __future__ import annotations

import json
import os
from typing import Any, Dict

import yaml
from prance import ResolvingParser
from pydantic import BaseModel

# get the absolute path of the current file
CURRENT_PATH = os.path.dirname(os.path.abspath(__file__))

class APIYamlModel(BaseModel):
    info: Dict

    @classmethod
    def from_yaml(cls, yaml_path: str) -> APIYamlModel:
        return cls(info=APIYamlModel.yaml_to_json(yaml_path))


from real_agents.adapters.data_model.plugin.spec import APIYamlModel, SpecModel


from copy import deepcopy
from typing import Any, Dict

def convert(_input_json: Dict[str, Any]) -> Dict[str, Any]:
    input_json = deepcopy(_input_json)
    assert isinstance(input_json["out"], list)

    input_json["out"]["articles"] = input_json["out"]["articles"][:5]
    return input_json


from copy import deepcopy
from typing import Any, Dict

def convert(_input_json: Dict[str, Any]) -> Dict[str, Any]:
    input_json = deepcopy(_input_json)
    assert isinstance(input_json["out"], list)

    input_json["out"] = input_json["out"][:5]
    extracted_keys = [
        "body",
        "title",
        "created",
        "url",
        "tags",
    ]
    input_json["out"] = [{k: r[k] for k in extracted_keys if k in r} for r in input_json["out"]]
    return input_json


from copy import deepcopy
from typing import Any, Dict

def convert(_input_json: Dict[str, Any]) -> Dict[str, Any]:
    input_json = deepcopy(_input_json)
    assert isinstance(input_json["out"], list)

    input_json["out"] = input_json["out"][:5]

    for i, job in enumerate(input_json["out"]):
        cleaned_job_item = input_json["out"][i]
        del cleaned_job_item["id"]
        del cleaned_job_item["created"]
        input_json["out"][i] = cleaned_job_item

    return input_json


import subprocess
import sys
from copy import deepcopy
from typing import Any, Dict, Union

import pandas as pd
from sqlalchemy import create_engine
import tiktoken

from real_agents.adapters.schema import SQLDatabase

def convert(
    table_data: Union[pd.DataFrame, Dict[str, Any]], table_name: str = "table", visible_rows_num: int = 3
) -> Dict[str, str]:
    """
    Convert table data to string representations in different formats.

    :param table_data: A dictionary with "cols" (list of strings) and "rows"
                        (list of lists of strings) as keys.
    :param table_name: The name of the table.


import sqlite3
from typing import Dict, Union

import pandas as pd
import tiktoken

from real_agents.adapters.data_model.templates.skg_templates.table_templates import (
    convert as convert_table,
)
from real_agents.adapters.schema import SQLDatabase

def convert(db_input: Union[str, Dict[str, pd.DataFrame]], visible_rows_num: int = 3) -> Dict[str, str]:
    """
    Convert database data to string representations in different formats.

    :param db_input: the path to the sqlite database file, or a pd.DataFrame.
    :param visible_rows_num: the number of rows to be displayed in each table.
    :return: A dictionary with the string database representations in different formats.
    """


import subprocess
import sys
from typing import Dict, List, Tuple

def convert(kg_input: List[Tuple], name_space: str = "") -> Dict[str, str]:
    """
    Convert knowledge graph data to string representations in different formats.

    :param kg_input: the list of knowledge graph triples.
    :param name_space: of the knowledge graph.
    :return: A dictionary with the string knowledge graph representations in different formats.
    """

    def install_required_packages() -> None:
        packages = ["rdflib", "rdflib-jsonld"]

        for package in packages:
            subprocess.check_call([sys.executable, "-m", "pip", "install", package])

    # Call the function to install the required packages


from abc import ABC, abstractmethod
from typing import Any, Dict, Optional

from real_agents.adapters.schema import SQLDatabase

class BaseExecutor(ABC):
    @abstractmethod
    def run(self, user_intent: str, grounding_source: Optional[SQLDatabase]) -> Dict[str, Any]:
        """Run the executor."""