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)
    description = param_dict.get("description", None)
    required = param_dict.get("required", None)

    schema = param_dict.get("schema", {})
    type = schema.get("type", "UnknownType")
    value_choices = schema.get("enum", [])

    ret = (
        f"`{name}` ({type}, {'required' if required else 'optional'}): {description}."
        f"{'Examples:' + ','.join([str(_) for _ in value_choices]) if len(value_choices) > 0 else ''}"
    )
    return ret

def process_one_property(name: str, value_dict: Dict[str, Any]) -> str:
    description = value_dict.get("description", None)


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)
    description = param_dict.get("description", None)
    required = param_dict.get("required", None)

    schema = param_dict.get("schema", {})
    type = schema.get("type", "UnknownType")
    value_choices = schema.get("enum", [])

    ret = (
        f"`{name}` ({type}, {'required' if required else 'optional'}): {description}."
        f"{'Examples:' + ','.join([str(_) for _ in value_choices]) if len(value_choices) > 0 else ''}"
    )
    return ret

def process_one_property(name: str, value_dict: Dict[str, Any]) -> str:
    description = value_dict.get("description", None)
    required = value_dict.get("required", None)

    type = value_dict.get("type", "UnknownType")
    value_choices = value_dict.get("enum", [])

    ret = (
        f"`{name}` ({type}, {'required' if required else 'optional'}): {description}."
        f"{'Examples:' + ','.join(value_choices) if len(value_choices) > 0 else ''}"
    )
    return ret

class SpecModel:
    def __init__(self, yaml_path: str, model_name: str = "gpt-4") -> None:
        # fixme: Must move out the logic of yaml path
        self.yaml_path = yaml_path


    return function

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

    schema = param_dict.get("schema", {})
    type = schema.get("type", "UnknownType")
    value_choices = schema.get("enum", [])

    ret = (
        f"`{name}` ({type}, {'required' if required else 'optional'}): {description}."
        f"{'Examples:' + ','.join([str(_) for _ in value_choices]) if len(value_choices) > 0 else ''}"
    )
    return ret

def process_one_property(name: str, value_dict: Dict[str, Any]) -> str:
    description = value_dict.get("description", None)
    required = value_dict.get("required", None)

    type = value_dict.get("type", "UnknownType")
    value_choices = value_dict.get("enum", [])

    ret = (
        f"`{name}` ({type}, {'required' if required else 'optional'}): {description}."
        f"{'Examples:' + ','.join(value_choices) if len(value_choices) > 0 else ''}"
    )
    return ret

class SpecModel:
    def __init__(self, yaml_path: str, model_name: str = "gpt-4") -> None:
        # fixme: Must move out the logic of yaml path
        self.yaml_path = yaml_path
        self.full_spec = APIYamlModel.from_yaml(yaml_path).to_json()
        self.paths = self.full_spec["paths"]

        # Process the description
        enc = tiktoken.encoding_for_model(model_name)
        if "description" in self.full_spec["info"]:
            if len(self.full_spec["info"]["description"]) > 200:
                self.full_spec["info"]["description"] = enc.decode(
                    enc.encode(self.full_spec["info"]["description"])[:200]
                )

    def load_personnel_info(self, api_key: str):
        # Get the dir of the yaml file
        yaml_dir = os.path.dirname(self.yaml_path)


SpecModel

        f"`{name}` ({type}, {'required' if required else 'optional'}): {description}."
        f"{'Examples:' + ','.join([str(_) for _ in value_choices]) if len(value_choices) > 0 else ''}"
    )
    return ret

def process_one_property(name: str, value_dict: Dict[str, Any]) -> str:
    description = value_dict.get("description", None)
    required = value_dict.get("required", None)

    type = value_dict.get("type", "UnknownType")
    value_choices = value_dict.get("enum", [])

    ret = (
        f"`{name}` ({type}, {'required' if required else 'optional'}): {description}."
        f"{'Examples:' + ','.join(value_choices) if len(value_choices) > 0 else ''}"
    )
    return ret

class SpecModel:
    def __init__(self, yaml_path: str, model_name: str = "gpt-4") -> None:
        # fixme: Must move out the logic of yaml path
        self.yaml_path = yaml_path
        self.full_spec = APIYamlModel.from_yaml(yaml_path).to_json()
        self.paths = self.full_spec["paths"]

        # Process the description
        enc = tiktoken.encoding_for_model(model_name)
        if "description" in self.full_spec["info"]:
            if len(self.full_spec["info"]["description"]) > 200:
                self.full_spec["info"]["description"] = enc.decode(
                    enc.encode(self.full_spec["info"]["description"])[:200]
                )

    def load_personnel_info(self, api_key: str):
        # Get the dir of the yaml file
        yaml_dir = os.path.dirname(self.yaml_path)
        personnel_load_dir = os.path.join(yaml_dir, "personnel.py")

        if not os.path.exists(personnel_load_dir):
            return {}, {}

        # Reload openapi.yaml
        reload_openapi = import_function_from_file(personnel_load_dir, "reload_openapi")
        resolved_json, new_paths_json = reload_openapi(api_key, self.full_spec)
        self.full_spec = resolved_json
        self.full_spec["info"] = resolved_json["info"]
        self.paths = resolved_json["paths"]

        # Reload the endpoints functions
        reload_endpoints = import_function_from_file(personnel_load_dir, "reload_endpoints")
        new_endpoint2caller = reload_endpoints(new_paths_json)

        # Reload the endpoints datamodels
        # todo: Add reload datamodels function
        new_endpoints2output_model = {k: lambda x: x for k in new_paths_json}

        return new_endpoint2caller, new_endpoints2output_model

    def prepare_spec(self, include_params: bool = True) -> str:
        path_names = list(self.paths.keys())
        ret = self.prepare_spec_for_one_path(path_names[0], include_params=include_params)

        if len(path_names) > 1:
            ret += "\n"

        for path_name in path_names[1:]:
            ret += (
                self.prepare_spec_for_one_path(path_name, include_api_info=False, include_params=include_params) + "\n"
            )

        return ret

    def list_endpoints(self) -> str:
        ret = ""
        for ep, ep_spec in self.paths.items():
            assert len(ep_spec) == 1, "Support two request methods!"
            request_method = list(ep_spec.keys())[0]
            func_spec = ep_spec[request_method]
            desc = func_spec.get("summary", None)
            ret += f"`{ep}`: {desc}\n"
        return ret.strip()

    def prepare_spec_for_one_path(
        self,
        path_name: str,
        include_api_info: bool = True,
        include_params: bool = True,
    ) -> str:
        func_dict = self.paths[path_name]
        if "servers" in func_dict:
            del func_dict["servers"]

        rets = []
        for request_method in list(func_dict.keys()):
            candidate_inputs_str = ""
            func_spec = func_dict[request_method]

            # Only GET and DELETE are processed, others are not properly processed
            if request_method.lower() not in ["get", "post", "put", "patch", "delete"]:
                raise ValueError("Unknown request method")

            # TODO: not sure how to arrange input when post method has "parameters"
            func_summary = func_spec.get("summary", None)
            func_description = func_spec.get("description", None)
            candidate_inputs = func_spec.get("parameters", [])
            candidate_inputs_str += "\n".join(process_one_param(p) for p in candidate_inputs)

            if request_method.lower() == "post" and "requestBody" in func_spec:
                request_body = func_spec["requestBody"]
                assert "content" in request_body, "Must have content in requestBody"
                content_dict = request_body["content"]
                assert len(content_dict) == 1, "Support one content type"
                content_type = list(content_dict.keys())[0]
                content = content_dict[content_type]
                assert "schema" in content, "Must have schema in requestBody"
                if "properties" in content["schema"]:
                    properties = content["schema"]["properties"]
                    candidate_inputs_str += "\n".join(process_one_property(n, vd) for n, vd in properties.items())

            ret = ""
            if include_api_info:
                ret += f"""Name: {self.full_spec["info"]["title"]}\n{'Description: ' + self.full_spec["info"]['description'] if
                "description" in self.full_spec["info"] else ""}\n"""

            ret += f"""\tSummary: {func_summary}\n"""
            ret += f"""\tDescription: {func_description}\n"""
            candidate_inputs_str = "None" if len(candidate_inputs_str) == 0 else candidate_inputs_str
            ret += (
                f"""\tInput: \n{indent_multiline_string(candidate_inputs_str, indent=2)}\n""" if include_params else ""
            )
            rets.append(ret)

        return f"""Endpoint: {path_name}\n""" + "\n".join(rets)


        f"{'Examples:' + ','.join([str(_) for _ in value_choices]) if len(value_choices) > 0 else ''}"
    )
    return ret

def process_one_property(name: str, value_dict: Dict[str, Any]) -> str:
    description = value_dict.get("description", None)
    required = value_dict.get("required", None)

    type = value_dict.get("type", "UnknownType")
    value_choices = value_dict.get("enum", [])

    ret = (
        f"`{name}` ({type}, {'required' if required else 'optional'}): {description}."
        f"{'Examples:' + ','.join(value_choices) if len(value_choices) > 0 else ''}"
    )
    return ret

class SpecModel:
    def __init__(self, yaml_path: str, model_name: str = "gpt-4") -> None:
        # fixme: Must move out the logic of yaml path
        self.yaml_path = yaml_path
        self.full_spec = APIYamlModel.from_yaml(yaml_path).to_json()
        self.paths = self.full_spec["paths"]

        # Process the description
        enc = tiktoken.encoding_for_model(model_name)
        if "description" in self.full_spec["info"]:
            if len(self.full_spec["info"]["description"]) > 200:
                self.full_spec["info"]["description"] = enc.decode(
                    enc.encode(self.full_spec["info"]["description"])[:200]
                )

    def load_personnel_info(self, api_key: str):
        # Get the dir of the yaml file
        yaml_dir = os.path.dirname(self.yaml_path)
        personnel_load_dir = os.path.join(yaml_dir, "personnel.py")

        if not os.path.exists(personnel_load_dir):
            return {}, {}

        # Reload openapi.yaml
        reload_openapi = import_function_from_file(personnel_load_dir, "reload_openapi")
        resolved_json, new_paths_json = reload_openapi(api_key, self.full_spec)
        self.full_spec = resolved_json
        self.full_spec["info"] = resolved_json["info"]
        self.paths = resolved_json["paths"]

        # Reload the endpoints functions
        reload_endpoints = import_function_from_file(personnel_load_dir, "reload_endpoints")
        new_endpoint2caller = reload_endpoints(new_paths_json)


        f"{'Examples:' + ','.join(value_choices) if len(value_choices) > 0 else ''}"
    )
    return ret

class SpecModel:
    def __init__(self, yaml_path: str, model_name: str = "gpt-4") -> None:
        # fixme: Must move out the logic of yaml path
        self.yaml_path = yaml_path
        self.full_spec = APIYamlModel.from_yaml(yaml_path).to_json()
        self.paths = self.full_spec["paths"]

        # Process the description
        enc = tiktoken.encoding_for_model(model_name)
        if "description" in self.full_spec["info"]:
            if len(self.full_spec["info"]["description"]) > 200:
                self.full_spec["info"]["description"] = enc.decode(
                    enc.encode(self.full_spec["info"]["description"])[:200]
                )

    def load_personnel_info(self, api_key: str):
        # Get the dir of the yaml file
        yaml_dir = os.path.dirname(self.yaml_path)
        personnel_load_dir = os.path.join(yaml_dir, "personnel.py")

        if not os.path.exists(personnel_load_dir):
            return {}, {}

        # Reload openapi.yaml
        reload_openapi = import_function_from_file(personnel_load_dir, "reload_openapi")
        resolved_json, new_paths_json = reload_openapi(api_key, self.full_spec)
        self.full_spec = resolved_json
        self.full_spec["info"] = resolved_json["info"]
        self.paths = resolved_json["paths"]

        # Reload the endpoints functions
        reload_endpoints = import_function_from_file(personnel_load_dir, "reload_endpoints")
        new_endpoint2caller = reload_endpoints(new_paths_json)

        # Reload the endpoints datamodels
        # todo: Add reload datamodels function
        new_endpoints2output_model = {k: lambda x: x for k in new_paths_json}

        return new_endpoint2caller, new_endpoints2output_model

    def prepare_spec(self, include_params: bool = True) -> str:
        path_names = list(self.paths.keys())
        ret = self.prepare_spec_for_one_path(path_names[0], include_params=include_params)

        if len(path_names) > 1:
            ret += "\n"

        for path_name in path_names[1:]:
            ret += (
                self.prepare_spec_for_one_path(path_name, include_api_info=False, include_params=include_params) + "\n"
            )

        return ret

    def list_endpoints(self) -> str:
        ret = ""
        for ep, ep_spec in self.paths.items():
            assert len(ep_spec) == 1, "Support two request methods!"
            request_method = list(ep_spec.keys())[0]


        if not os.path.exists(personnel_load_dir):
            return {}, {}

        # Reload openapi.yaml
        reload_openapi = import_function_from_file(personnel_load_dir, "reload_openapi")
        resolved_json, new_paths_json = reload_openapi(api_key, self.full_spec)
        self.full_spec = resolved_json
        self.full_spec["info"] = resolved_json["info"]
        self.paths = resolved_json["paths"]

        # Reload the endpoints functions
        reload_endpoints = import_function_from_file(personnel_load_dir, "reload_endpoints")
        new_endpoint2caller = reload_endpoints(new_paths_json)

        # Reload the endpoints datamodels
        # todo: Add reload datamodels function
        new_endpoints2output_model = {k: lambda x: x for k in new_paths_json}

        return new_endpoint2caller, new_endpoints2output_model

    def prepare_spec(self, include_params: bool = True) -> str:
        path_names = list(self.paths.keys())
        ret = self.prepare_spec_for_one_path(path_names[0], include_params=include_params)

        if len(path_names) > 1:
            ret += "\n"

        for path_name in path_names[1:]:
            ret += (
                self.prepare_spec_for_one_path(path_name, include_api_info=False, include_params=include_params) + "\n"
            )

        return ret

    def list_endpoints(self) -> str:
        ret = ""
        for ep, ep_spec in self.paths.items():
            assert len(ep_spec) == 1, "Support two request methods!"
            request_method = list(ep_spec.keys())[0]
            func_spec = ep_spec[request_method]
            desc = func_spec.get("summary", None)
            ret += f"`{ep}`: {desc}\n"
        return ret.strip()

    def prepare_spec_for_one_path(
        self,
        path_name: str,
        include_api_info: bool = True,
        include_params: bool = True,
    ) -> str:
        func_dict = self.paths[path_name]
        if "servers" in func_dict:
            del func_dict["servers"]


        # Reload the endpoints datamodels
        # todo: Add reload datamodels function
        new_endpoints2output_model = {k: lambda x: x for k in new_paths_json}

        return new_endpoint2caller, new_endpoints2output_model

    def prepare_spec(self, include_params: bool = True) -> str:
        path_names = list(self.paths.keys())
        ret = self.prepare_spec_for_one_path(path_names[0], include_params=include_params)

        if len(path_names) > 1:
            ret += "\n"

        for path_name in path_names[1:]:
            ret += (
                self.prepare_spec_for_one_path(path_name, include_api_info=False, include_params=include_params) + "\n"
            )

        return ret

    def list_endpoints(self) -> str:
        ret = ""
        for ep, ep_spec in self.paths.items():
            assert len(ep_spec) == 1, "Support two request methods!"
            request_method = list(ep_spec.keys())[0]
            func_spec = ep_spec[request_method]
            desc = func_spec.get("summary", None)
            ret += f"`{ep}`: {desc}\n"
        return ret.strip()

    def prepare_spec_for_one_path(
        self,
        path_name: str,
        include_api_info: bool = True,
        include_params: bool = True,
    ) -> str:
        func_dict = self.paths[path_name]
        if "servers" in func_dict:
            del func_dict["servers"]

        rets = []
        for request_method in list(func_dict.keys()):
            candidate_inputs_str = ""
            func_spec = func_dict[request_method]

            # Only GET and DELETE are processed, others are not properly processed
            if request_method.lower() not in ["get", "post", "put", "patch", "delete"]:
                raise ValueError("Unknown request method")


        if len(path_names) > 1:
            ret += "\n"

        for path_name in path_names[1:]:
            ret += (
                self.prepare_spec_for_one_path(path_name, include_api_info=False, include_params=include_params) + "\n"
            )

        return ret

    def list_endpoints(self) -> str:
        ret = ""
        for ep, ep_spec in self.paths.items():
            assert len(ep_spec) == 1, "Support two request methods!"
            request_method = list(ep_spec.keys())[0]
            func_spec = ep_spec[request_method]
            desc = func_spec.get("summary", None)
            ret += f"`{ep}`: {desc}\n"
        return ret.strip()

    def prepare_spec_for_one_path(
        self,
        path_name: str,
        include_api_info: bool = True,
        include_params: bool = True,
    ) -> str:
        func_dict = self.paths[path_name]
        if "servers" in func_dict:
            del func_dict["servers"]

        rets = []
        for request_method in list(func_dict.keys()):
            candidate_inputs_str = ""
            func_spec = func_dict[request_method]

            # Only GET and DELETE are processed, others are not properly processed
            if request_method.lower() not in ["get", "post", "put", "patch", "delete"]:
                raise ValueError("Unknown request method")

            # TODO: not sure how to arrange input when post method has "parameters"
            func_summary = func_spec.get("summary", None)
            func_description = func_spec.get("description", None)
            candidate_inputs = func_spec.get("parameters", [])
            candidate_inputs_str += "\n".join(process_one_param(p) for p in candidate_inputs)

            if request_method.lower() == "post" and "requestBody" in func_spec:
                request_body = func_spec["requestBody"]
                assert "content" in request_body, "Must have content in requestBody"
                content_dict = request_body["content"]
                assert len(content_dict) == 1, "Support one content type"
                content_type = list(content_dict.keys())[0]
                content = content_dict[content_type]
                assert "schema" in content, "Must have schema in requestBody"
                if "properties" in content["schema"]:
                    properties = content["schema"]["properties"]
                    candidate_inputs_str += "\n".join(process_one_property(n, vd) for n, vd in properties.items())

            ret = ""
            if include_api_info:
                ret += f"""Name: {self.full_spec["info"]["title"]}\n{'Description: ' + self.full_spec["info"]['description'] if
                "description" in self.full_spec["info"] else ""}\n"""

            ret += f"""\tSummary: {func_summary}\n"""
            ret += f"""\tDescription: {func_description}\n"""
            candidate_inputs_str = "None" if len(candidate_inputs_str) == 0 else candidate_inputs_str
            ret += (
                f"""\tInput: \n{indent_multiline_string(candidate_inputs_str, indent=2)}\n""" if include_params else ""
            )
            rets.append(ret)

        return f"""Endpoint: {path_name}\n""" + "\n".join(rets)


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))

    @classmethod
    def from_json(cls, json_path: str) -> APIYamlModel:
        with open(json_path, "r") as json_file:
            json_data = json.load(json_file)
        return cls(info=json_data)

    def to_yaml(self) -> Dict:
        yaml_data = yaml.safe_dump(self.info, sort_keys=False)
        return yaml_data

    def to_json(self) -> Dict:
        return self.info

    @staticmethod
    def yaml_to_json(yaml_path: str) -> Dict:
        # Open the OpenAPI YAML file
        # Load the YAML contents into a Python dictionary
        # json_data = yaml.safe_load(yaml_file)
        # there are #/xxxx/yyyy reference in openapi.yaml
        parsed = ResolvingParser(yaml_path, backend="openapi-spec-validator", strict=False)
        json_data = json.loads(parsed.json())
        return json_data

    @staticmethod
    def json_to_yaml(json_path: str) -> Any:
        # Open the OpenAPI JSON file
        with open(json_path, "r") as json_file:
            json_data = json.load(json_file)
            yaml_data = yaml.dump(json_data)
            return yaml_data


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))

    @classmethod
    def from_json(cls, json_path: str) -> APIYamlModel:
        with open(json_path, "r") as json_file:
            json_data = json.load(json_file)
        return cls(info=json_data)

    def to_yaml(self) -> Dict:
        yaml_data = yaml.safe_dump(self.info, sort_keys=False)
        return yaml_data

    def to_json(self) -> Dict:
        return self.info

    @staticmethod
    def yaml_to_json(yaml_path: str) -> Dict:
        # Open the OpenAPI YAML file
        # Load the YAML contents into a Python dictionary
        # json_data = yaml.safe_load(yaml_file)
        # there are #/xxxx/yyyy reference in openapi.yaml


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))

    @classmethod
    def from_json(cls, json_path: str) -> APIYamlModel:
        with open(json_path, "r") as json_file:
            json_data = json.load(json_file)
        return cls(info=json_data)

    def to_yaml(self) -> Dict:
        yaml_data = yaml.safe_dump(self.info, sort_keys=False)
        return yaml_data

    def to_json(self) -> Dict:
        return self.info

    @staticmethod
    def yaml_to_json(yaml_path: str) -> Dict:
        # Open the OpenAPI YAML file
        # Load the YAML contents into a Python dictionary
        # json_data = yaml.safe_load(yaml_file)
        # there are #/xxxx/yyyy reference in openapi.yaml
        parsed = ResolvingParser(yaml_path, backend="openapi-spec-validator", strict=False)
        json_data = json.loads(parsed.json())
        return json_data

    @staticmethod
    def json_to_yaml(json_path: str) -> Any:


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))

    @classmethod
    def from_json(cls, json_path: str) -> APIYamlModel:
        with open(json_path, "r") as json_file:
            json_data = json.load(json_file)
        return cls(info=json_data)

    def to_yaml(self) -> Dict:
        yaml_data = yaml.safe_dump(self.info, sort_keys=False)
        return yaml_data

    def to_json(self) -> Dict:
        return self.info

    @staticmethod
    def yaml_to_json(yaml_path: str) -> Dict:
        # Open the OpenAPI YAML file
        # Load the YAML contents into a Python dictionary
        # json_data = yaml.safe_load(yaml_file)
        # there are #/xxxx/yyyy reference in openapi.yaml
        parsed = ResolvingParser(yaml_path, backend="openapi-spec-validator", strict=False)
        json_data = json.loads(parsed.json())
        return json_data

    @staticmethod
    def json_to_yaml(json_path: str) -> Any:
        # Open the OpenAPI JSON file
        with open(json_path, "r") as json_file:
            json_data = json.load(json_file)
            yaml_data = yaml.dump(json_data)


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))

    @classmethod
    def from_json(cls, json_path: str) -> APIYamlModel:
        with open(json_path, "r") as json_file:
            json_data = json.load(json_file)
        return cls(info=json_data)

    def to_yaml(self) -> Dict:
        yaml_data = yaml.safe_dump(self.info, sort_keys=False)
        return yaml_data

    def to_json(self) -> Dict:
        return self.info

    @staticmethod
    def yaml_to_json(yaml_path: str) -> Dict:
        # Open the OpenAPI YAML file
        # Load the YAML contents into a Python dictionary
        # json_data = yaml.safe_load(yaml_file)
        # there are #/xxxx/yyyy reference in openapi.yaml
        parsed = ResolvingParser(yaml_path, backend="openapi-spec-validator", strict=False)
        json_data = json.loads(parsed.json())
        return json_data

    @staticmethod
    def json_to_yaml(json_path: str) -> Any:
        # Open the OpenAPI JSON file
        with open(json_path, "r") as json_file:
            json_data = json.load(json_file)
            yaml_data = yaml.dump(json_data)
            return yaml_data


    info: Dict

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

    @classmethod
    def from_json(cls, json_path: str) -> APIYamlModel:
        with open(json_path, "r") as json_file:
            json_data = json.load(json_file)
        return cls(info=json_data)

    def to_yaml(self) -> Dict:
        yaml_data = yaml.safe_dump(self.info, sort_keys=False)
        return yaml_data

    def to_json(self) -> Dict:
        return self.info

    @staticmethod
    def yaml_to_json(yaml_path: str) -> Dict:
        # Open the OpenAPI YAML file
        # Load the YAML contents into a Python dictionary
        # json_data = yaml.safe_load(yaml_file)
        # there are #/xxxx/yyyy reference in openapi.yaml
        parsed = ResolvingParser(yaml_path, backend="openapi-spec-validator", strict=False)
        json_data = json.loads(parsed.json())
        return json_data

    @staticmethod
    def json_to_yaml(json_path: str) -> Any:
        # Open the OpenAPI JSON file
        with open(json_path, "r") as json_file:
            json_data = json.load(json_file)
            yaml_data = yaml.dump(json_data)
            return yaml_data


        return cls(info=json_data)

    def to_yaml(self) -> Dict:
        yaml_data = yaml.safe_dump(self.info, sort_keys=False)
        return yaml_data

    def to_json(self) -> Dict:
        return self.info

    @staticmethod
    def yaml_to_json(yaml_path: str) -> Dict:
        # Open the OpenAPI YAML file
        # Load the YAML contents into a Python dictionary
        # json_data = yaml.safe_load(yaml_file)
        # there are #/xxxx/yyyy reference in openapi.yaml
        parsed = ResolvingParser(yaml_path, backend="openapi-spec-validator", strict=False)
        json_data = json.loads(parsed.json())
        return json_data

    @staticmethod
    def json_to_yaml(json_path: str) -> Any:
        # Open the OpenAPI JSON file
        with open(json_path, "r") as json_file:
            json_data = json.load(json_file)
            yaml_data = yaml.dump(json_data)
            return yaml_data


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.
    :param visible_rows_num: The number of rows to be displayed in the representation.
    :return: A dictionary with the string table representations in different formats.
    """

    def install_required_packages() -> None:
        packages = ["tabulate", "prettytable"]

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

    # Call the function to install the required packages
    install_required_packages()
    from prettytable import PrettyTable

    # Handle situation when the table_data is already a dataframe, FIXME: this is a hack
    new_table_data = {}
    if isinstance(table_data, pd.DataFrame):
        new_table_data["cols"] = table_data.columns
        new_table_data["rows"] = table_data.values.tolist()
    table_data = new_table_data

    # Type check for table_data
    if not isinstance(table_data, dict) or "cols" not in table_data or "rows" not in table_data:
        raise TypeError("table_data must be a dictionary with 'cols' and 'rows' as keys.")

    table_data_for_observable = deepcopy(table_data)
    if len(table_data_for_observable["rows"]) > visible_rows_num:
        table_data_for_observable["rows"] = table_data_for_observable["rows"][:visible_rows_num]
        table_data_for_observable["rows"].append(["..."] * len(table_data_for_observable["cols"]))

    # Create dataframe from table_data
    df = pd.DataFrame(table_data_for_observable["rows"], columns=table_data_for_observable["cols"])

    # Generate tables in different formats
    markdown_table = df.to_markdown(index=False)
    html_table = df.to_html(index=False)
    latex_table = df.to_latex(index=False)
    csv_table = df.to_csv(index=False)
    tsv_table = df.to_csv(index=False, sep="\t")
    rest_table = df.to_string(index=False)

    def bbcode_mode_table(data_frame: pd.DataFrame) -> str:
        bbcode_table = "[table]\n"
        for row in data_frame.itertuples(index=False):
            bbcode_table += "[tr]\n"
            for value in row:
                bbcode_table += f"[td]{value}[/td]\n"
            bbcode_table += "[/tr]\n"
        bbcode_table += "[/table]"
        return bbcode_table

    def mediawiki_mode_table(data_frame: pd.DataFrame) -> str:
        mediawiki_table = '{| class="wikitable"\n|-\n'
        for col in data_frame.columns:
            mediawiki_table += f"! {col}\n"
        for row in data_frame.itertuples(index=False):
            mediawiki_table += "|-\n"
            for value in row:
                mediawiki_table += f"| {value}\n"
        mediawiki_table += "|}"
        return mediawiki_table

    def org_mode_table(data_frame: pd.DataFrame) -> str:
        org_table = (
            "| "
            + " | ".join(data_frame.columns)
            + " |\n|-"
            + " | -".join(["-" * len(col) for col in data_frame.columns])
            + " |\n"
        )
        for row in data_frame.itertuples(index=False):
            org_table += "| " + " | ".join([str(value) for value in row]) + " |\n"
        return org_table

    bbcode_table = bbcode_mode_table(df)
    mediawiki_table = mediawiki_mode_table(df)
    org_table = org_mode_table(df)

    pretty_table = PrettyTable()
    pretty_table.field_names = table_data["cols"]
    for row in table_data["rows"]:
        pretty_table.add_row(row)
    pretty_table = str(pretty_table)

    # New function to generate SQL table
    def sql_mode_table(data_frame: pd.DataFrame, _table_name: str) -> str:
        sql_table_str = f"CREATE TABLE {table_name}(\n"

        for col in data_frame.columns:
            sql_table_str += f"{col} text,\n"

        # Remove the last comma and add the primary key constraint
        sql_table_str = sql_table_str[:-2] + f",\nPRIMARY KEY ({data_frame.columns[0]})\n);"

        sql_table_str += "\n/*\n{} example rows:\n".format(len(data_frame))
        for i, _row in data_frame.iterrows():
            _row = "\t".join([str(_cell) for _cell in _row.to_list()])
            sql_table_str += f"{_row}\n"
        sql_table_str += "*/"

        return sql_table_str

    sql_table = sql_mode_table(df, table_name)

    # Return the representation in different formats as a dictionary
    return {
        "Markdown": markdown_table,
        "HTML": html_table,
        "LaTeX": latex_table,
        "CSV": csv_table,
        "TSV": tsv_table,
        "reStructuredText": rest_table,
        "BBCode": bbcode_table,
        "MediaWiki": mediawiki_table,
        "Org mode": org_table,
        "PrettyTable": pretty_table,
        "SQL": sql_table,
    }

def serialize_df(
    table_data: pd.DataFrame,
    table_name: str,
    table_path: str,
    serialize_method: str = "tsv",
    num_visible_rows: int = 3,
    max_tokens: int = 1000,
    data_dir_splitter: str = "backend/data/",
) -> str:
    """Convert dataframe to a string representation."""
    if serialize_method == "tsv":
        # Here it means ignore the "path/to/the/data/<user_id/" part of the path
        pretty_path = "/".join(table_path.split(data_dir_splitter)[-1].strip("/").split("/")[1:])
        string = (
            "Here are table columns and the first {} rows of the table from the path {}"
            '(only a small part of the whole table) called "{}":\n'.format(num_visible_rows, pretty_path, table_name)
        )
        string += table_data.head(num_visible_rows).to_csv(sep="\t", index=False)
        # Truncate the string if it is too long
        enc = tiktoken.get_encoding("cl100k_base")
        enc_tokens = enc.encode(string)
        if len(enc_tokens) > max_tokens:
            string = enc.decode(enc_tokens[:max_tokens])
    elif serialize_method == "database":
        engine = create_engine("sqlite:///:memory:")
        table_data.to_sql(table_name, engine)
        db = SQLDatabase(engine)
        # TODO: Now access the internal variable
        setattr(db, "_sample_rows_in_table_info", num_visible_rows)
        string = db.get_table_info()
    else:
        raise ValueError("Unknown serialization method.")
    return string


convert

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.
    :param visible_rows_num: The number of rows to be displayed in the representation.
    :return: A dictionary with the string table representations in different formats.
    """

    def install_required_packages() -> None:
        packages = ["tabulate", "prettytable"]

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

    # Call the function to install the required packages
    install_required_packages()
    from prettytable import PrettyTable

    # Handle situation when the table_data is already a dataframe, FIXME: this is a hack
    new_table_data = {}
    if isinstance(table_data, pd.DataFrame):
        new_table_data["cols"] = table_data.columns
        new_table_data["rows"] = table_data.values.tolist()
    table_data = new_table_data

    # Type check for table_data
    if not isinstance(table_data, dict) or "cols" not in table_data or "rows" not in table_data:
        raise TypeError("table_data must be a dictionary with 'cols' and 'rows' as keys.")

    table_data_for_observable = deepcopy(table_data)
    if len(table_data_for_observable["rows"]) > visible_rows_num:
        table_data_for_observable["rows"] = table_data_for_observable["rows"][:visible_rows_num]
        table_data_for_observable["rows"].append(["..."] * len(table_data_for_observable["cols"]))

    # Create dataframe from table_data
    df = pd.DataFrame(table_data_for_observable["rows"], columns=table_data_for_observable["cols"])

    # Generate tables in different formats
    markdown_table = df.to_markdown(index=False)
    html_table = df.to_html(index=False)
    latex_table = df.to_latex(index=False)
    csv_table = df.to_csv(index=False)
    tsv_table = df.to_csv(index=False, sep="\t")
    rest_table = df.to_string(index=False)

    def bbcode_mode_table(data_frame: pd.DataFrame) -> str:
        bbcode_table = "[table]\n"
        for row in data_frame.itertuples(index=False):
            bbcode_table += "[tr]\n"
            for value in row:
                bbcode_table += f"[td]{value}[/td]\n"
            bbcode_table += "[/tr]\n"
        bbcode_table += "[/table]"
        return bbcode_table

    def mediawiki_mode_table(data_frame: pd.DataFrame) -> str:
        mediawiki_table = '{| class="wikitable"\n|-\n'
        for col in data_frame.columns:
            mediawiki_table += f"! {col}\n"
        for row in data_frame.itertuples(index=False):
            mediawiki_table += "|-\n"
            for value in row:
                mediawiki_table += f"| {value}\n"
        mediawiki_table += "|}"
        return mediawiki_table

    def org_mode_table(data_frame: pd.DataFrame) -> str:
        org_table = (
            "| "
            + " | ".join(data_frame.columns)
            + " |\n|-"
            + " | -".join(["-" * len(col) for col in data_frame.columns])
            + " |\n"
        )
        for row in data_frame.itertuples(index=False):
            org_table += "| " + " | ".join([str(value) for value in row]) + " |\n"
        return org_table

    bbcode_table = bbcode_mode_table(df)
    mediawiki_table = mediawiki_mode_table(df)
    org_table = org_mode_table(df)

    pretty_table = PrettyTable()
    pretty_table.field_names = table_data["cols"]
    for row in table_data["rows"]:
        pretty_table.add_row(row)
    pretty_table = str(pretty_table)

    # New function to generate SQL table
    def sql_mode_table(data_frame: pd.DataFrame, _table_name: str) -> str:
        sql_table_str = f"CREATE TABLE {table_name}(\n"

        for col in data_frame.columns:
            sql_table_str += f"{col} text,\n"

        # Remove the last comma and add the primary key constraint
        sql_table_str = sql_table_str[:-2] + f",\nPRIMARY KEY ({data_frame.columns[0]})\n);"

        sql_table_str += "\n/*\n{} example rows:\n".format(len(data_frame))
        for i, _row in data_frame.iterrows():
            _row = "\t".join([str(_cell) for _cell in _row.to_list()])
            sql_table_str += f"{_row}\n"
        sql_table_str += "*/"

        return sql_table_str

    sql_table = sql_mode_table(df, table_name)

    # Return the representation in different formats as a dictionary
    return {
        "Markdown": markdown_table,
        "HTML": html_table,
        "LaTeX": latex_table,
        "CSV": csv_table,
        "TSV": tsv_table,
        "reStructuredText": rest_table,
        "BBCode": bbcode_table,
        "MediaWiki": mediawiki_table,
        "Org mode": org_table,
        "PrettyTable": pretty_table,
        "SQL": sql_table,
    }

def serialize_df(
    table_data: pd.DataFrame,
    table_name: str,
    table_path: str,
    serialize_method: str = "tsv",
    num_visible_rows: int = 3,
    max_tokens: int = 1000,
    data_dir_splitter: str = "backend/data/",
) -> str:
    """Convert dataframe to a string representation."""
    if serialize_method == "tsv":
        # Here it means ignore the "path/to/the/data/<user_id/" part of the path
        pretty_path = "/".join(table_path.split(data_dir_splitter)[-1].strip("/").split("/")[1:])
        string = (
            "Here are table columns and the first {} rows of the table from the path {}"
            '(only a small part of the whole table) called "{}":\n'.format(num_visible_rows, pretty_path, table_name)
        )
        string += table_data.head(num_visible_rows).to_csv(sep="\t", index=False)


        return sql_table_str

    sql_table = sql_mode_table(df, table_name)

    # Return the representation in different formats as a dictionary
    return {
        "Markdown": markdown_table,
        "HTML": html_table,
        "LaTeX": latex_table,
        "CSV": csv_table,
        "TSV": tsv_table,
        "reStructuredText": rest_table,
        "BBCode": bbcode_table,
        "MediaWiki": mediawiki_table,
        "Org mode": org_table,
        "PrettyTable": pretty_table,
        "SQL": sql_table,
    }

def serialize_df(
    table_data: pd.DataFrame,
    table_name: str,
    table_path: str,
    serialize_method: str = "tsv",
    num_visible_rows: int = 3,
    max_tokens: int = 1000,
    data_dir_splitter: str = "backend/data/",
) -> str:
    """Convert dataframe to a string representation."""
    if serialize_method == "tsv":
        # Here it means ignore the "path/to/the/data/<user_id/" part of the path
        pretty_path = "/".join(table_path.split(data_dir_splitter)[-1].strip("/").split("/")[1:])
        string = (
            "Here are table columns and the first {} rows of the table from the path {}"
            '(only a small part of the whole table) called "{}":\n'.format(num_visible_rows, pretty_path, table_name)
        )
        string += table_data.head(num_visible_rows).to_csv(sep="\t", index=False)
        # Truncate the string if it is too long
        enc = tiktoken.get_encoding("cl100k_base")
        enc_tokens = enc.encode(string)
        if len(enc_tokens) > max_tokens:
            string = enc.decode(enc_tokens[:max_tokens])
    elif serialize_method == "database":
        engine = create_engine("sqlite:///:memory:")
        table_data.to_sql(table_name, engine)
        db = SQLDatabase(engine)
        # TODO: Now access the internal variable
        setattr(db, "_sample_rows_in_table_info", num_visible_rows)
        string = db.get_table_info()
    else:
        raise ValueError("Unknown serialization method.")
    return string


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.
    :param visible_rows_num: The number of rows to be displayed in the representation.
    :return: A dictionary with the string table representations in different formats.
    """

    def install_required_packages() -> None:
        packages = ["tabulate", "prettytable"]

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

    # Call the function to install the required packages
    install_required_packages()
    from prettytable import PrettyTable

    # Handle situation when the table_data is already a dataframe, FIXME: this is a hack
    new_table_data = {}
    if isinstance(table_data, pd.DataFrame):
        new_table_data["cols"] = table_data.columns
        new_table_data["rows"] = table_data.values.tolist()
    table_data = new_table_data

    # Type check for table_data
    if not isinstance(table_data, dict) or "cols" not in table_data or "rows" not in table_data:
        raise TypeError("table_data must be a dictionary with 'cols' and 'rows' as keys.")

    table_data_for_observable = deepcopy(table_data)
    if len(table_data_for_observable["rows"]) > visible_rows_num:
        table_data_for_observable["rows"] = table_data_for_observable["rows"][:visible_rows_num]
        table_data_for_observable["rows"].append(["..."] * len(table_data_for_observable["cols"]))


    # Type check for table_data
    if not isinstance(table_data, dict) or "cols" not in table_data or "rows" not in table_data:
        raise TypeError("table_data must be a dictionary with 'cols' and 'rows' as keys.")

    table_data_for_observable = deepcopy(table_data)
    if len(table_data_for_observable["rows"]) > visible_rows_num:
        table_data_for_observable["rows"] = table_data_for_observable["rows"][:visible_rows_num]
        table_data_for_observable["rows"].append(["..."] * len(table_data_for_observable["cols"]))

    # Create dataframe from table_data
    df = pd.DataFrame(table_data_for_observable["rows"], columns=table_data_for_observable["cols"])

    # Generate tables in different formats
    markdown_table = df.to_markdown(index=False)
    html_table = df.to_html(index=False)
    latex_table = df.to_latex(index=False)
    csv_table = df.to_csv(index=False)
    tsv_table = df.to_csv(index=False, sep="\t")
    rest_table = df.to_string(index=False)

    def bbcode_mode_table(data_frame: pd.DataFrame) -> str:
        bbcode_table = "[table]\n"
        for row in data_frame.itertuples(index=False):
            bbcode_table += "[tr]\n"
            for value in row:
                bbcode_table += f"[td]{value}[/td]\n"
            bbcode_table += "[/tr]\n"
        bbcode_table += "[/table]"
        return bbcode_table

    def mediawiki_mode_table(data_frame: pd.DataFrame) -> str:
        mediawiki_table = '{| class="wikitable"\n|-\n'
        for col in data_frame.columns:
            mediawiki_table += f"! {col}\n"
        for row in data_frame.itertuples(index=False):
            mediawiki_table += "|-\n"
            for value in row:
                mediawiki_table += f"| {value}\n"
        mediawiki_table += "|}"
        return mediawiki_table

    def org_mode_table(data_frame: pd.DataFrame) -> str:
        org_table = (
            "| "
            + " | ".join(data_frame.columns)
            + " |\n|-"
            + " | -".join(["-" * len(col) for col in data_frame.columns])
            + " |\n"
        )


    df = pd.DataFrame(table_data_for_observable["rows"], columns=table_data_for_observable["cols"])

    # Generate tables in different formats
    markdown_table = df.to_markdown(index=False)
    html_table = df.to_html(index=False)
    latex_table = df.to_latex(index=False)
    csv_table = df.to_csv(index=False)
    tsv_table = df.to_csv(index=False, sep="\t")
    rest_table = df.to_string(index=False)

    def bbcode_mode_table(data_frame: pd.DataFrame) -> str:
        bbcode_table = "[table]\n"
        for row in data_frame.itertuples(index=False):
            bbcode_table += "[tr]\n"
            for value in row:
                bbcode_table += f"[td]{value}[/td]\n"
            bbcode_table += "[/tr]\n"
        bbcode_table += "[/table]"
        return bbcode_table

    def mediawiki_mode_table(data_frame: pd.DataFrame) -> str:
        mediawiki_table = '{| class="wikitable"\n|-\n'
        for col in data_frame.columns:
            mediawiki_table += f"! {col}\n"
        for row in data_frame.itertuples(index=False):
            mediawiki_table += "|-\n"
            for value in row:
                mediawiki_table += f"| {value}\n"
        mediawiki_table += "|}"
        return mediawiki_table

    def org_mode_table(data_frame: pd.DataFrame) -> str:
        org_table = (
            "| "
            + " | ".join(data_frame.columns)
            + " |\n|-"
            + " | -".join(["-" * len(col) for col in data_frame.columns])
            + " |\n"
        )
        for row in data_frame.itertuples(index=False):
            org_table += "| " + " | ".join([str(value) for value in row]) + " |\n"
        return org_table

    bbcode_table = bbcode_mode_table(df)
    mediawiki_table = mediawiki_mode_table(df)
    org_table = org_mode_table(df)

    pretty_table = PrettyTable()
    pretty_table.field_names = table_data["cols"]
    for row in table_data["rows"]:


        bbcode_table = "[table]\n"
        for row in data_frame.itertuples(index=False):
            bbcode_table += "[tr]\n"
            for value in row:
                bbcode_table += f"[td]{value}[/td]\n"
            bbcode_table += "[/tr]\n"
        bbcode_table += "[/table]"
        return bbcode_table

    def mediawiki_mode_table(data_frame: pd.DataFrame) -> str:
        mediawiki_table = '{| class="wikitable"\n|-\n'
        for col in data_frame.columns:
            mediawiki_table += f"! {col}\n"
        for row in data_frame.itertuples(index=False):
            mediawiki_table += "|-\n"
            for value in row:
                mediawiki_table += f"| {value}\n"
        mediawiki_table += "|}"
        return mediawiki_table

    def org_mode_table(data_frame: pd.DataFrame) -> str:
        org_table = (
            "| "
            + " | ".join(data_frame.columns)
            + " |\n|-"
            + " | -".join(["-" * len(col) for col in data_frame.columns])
            + " |\n"
        )
        for row in data_frame.itertuples(index=False):
            org_table += "| " + " | ".join([str(value) for value in row]) + " |\n"
        return org_table

    bbcode_table = bbcode_mode_table(df)
    mediawiki_table = mediawiki_mode_table(df)
    org_table = org_mode_table(df)

    pretty_table = PrettyTable()
    pretty_table.field_names = table_data["cols"]
    for row in table_data["rows"]:
        pretty_table.add_row(row)
    pretty_table = str(pretty_table)

    # New function to generate SQL table
    def sql_mode_table(data_frame: pd.DataFrame, _table_name: str) -> str:
        sql_table_str = f"CREATE TABLE {table_name}(\n"

        for col in data_frame.columns:
            sql_table_str += f"{col} text,\n"

        # Remove the last comma and add the primary key constraint
        sql_table_str = sql_table_str[:-2] + f",\nPRIMARY KEY ({data_frame.columns[0]})\n);"


            + " | ".join(data_frame.columns)
            + " |\n|-"
            + " | -".join(["-" * len(col) for col in data_frame.columns])
            + " |\n"
        )
        for row in data_frame.itertuples(index=False):
            org_table += "| " + " | ".join([str(value) for value in row]) + " |\n"
        return org_table

    bbcode_table = bbcode_mode_table(df)
    mediawiki_table = mediawiki_mode_table(df)
    org_table = org_mode_table(df)

    pretty_table = PrettyTable()
    pretty_table.field_names = table_data["cols"]
    for row in table_data["rows"]:
        pretty_table.add_row(row)
    pretty_table = str(pretty_table)

    # New function to generate SQL table
    def sql_mode_table(data_frame: pd.DataFrame, _table_name: str) -> str:
        sql_table_str = f"CREATE TABLE {table_name}(\n"

        for col in data_frame.columns:
            sql_table_str += f"{col} text,\n"

        # Remove the last comma and add the primary key constraint
        sql_table_str = sql_table_str[:-2] + f",\nPRIMARY KEY ({data_frame.columns[0]})\n);"

        sql_table_str += "\n/*\n{} example rows:\n".format(len(data_frame))
        for i, _row in data_frame.iterrows():
            _row = "\t".join([str(_cell) for _cell in _row.to_list()])
            sql_table_str += f"{_row}\n"
        sql_table_str += "*/"

        return sql_table_str

    sql_table = sql_mode_table(df, table_name)

    # Return the representation in different formats as a dictionary
    return {
        "Markdown": markdown_table,
        "HTML": html_table,
        "LaTeX": latex_table,
        "CSV": csv_table,
        "TSV": tsv_table,
        "reStructuredText": rest_table,
        "BBCode": bbcode_table,
        "MediaWiki": mediawiki_table,
        "Org mode": org_table,
        "PrettyTable": pretty_table,
        "SQL": sql_table,
    }

def serialize_df(


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.
    """
    if isinstance(db_input, str):
        conn = sqlite3.connect(db_input)
        cursor = conn.cursor()
        cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
        table_names = [name[0] for name in cursor.fetchall()]
        dfs = {table_name: pd.read_sql_query(f"SELECT * FROM {table_name}", conn) for table_name in table_names}
    elif isinstance(db_input, dict) and all(isinstance(df, pd.DataFrame) for df in db_input.values()):
        dfs = db_input
    else:
        raise ValueError("db_input should be either a SQLite database file path or a dictionary of pandas DataFrames")

    representations = {
        "Markdown": "",
        "HTML": "",
        "LaTeX": "",
        "CSV": "",
        "TSV": "",
        "reStructuredText": "",
        "BBCode": "",
        "MediaWiki": "",
        "Org mode": "",
        "PrettyTable": "",
        "SQL": "",
    }

    for table_name, df in dfs.items():
        table_data = {"cols": df.columns.tolist(), "rows": df.values.tolist()}
        table_representations = convert_table(table_data, table_name, visible_rows_num)
        for _format, table_representation in table_representations.items():
            representations[_format] += table_representation + "\n\n"

    return representations

def serialize_db(
    db: SQLDatabase,
    serialize_method: str = "database",
    num_visible_rows: int = 3,
    max_tokens: int = 1000,
) -> str:
    """Convert database engine to a string representation."""
    if serialize_method == "database":
        # TODO: Now access the internal variable
        setattr(db, "_sample_rows_in_table_info", num_visible_rows)
        string = db.get_table_info()
        # Truncate the string if it is too long
        enc = tiktoken.get_encoding("cl100k_base")
        enc_tokens = enc.encode(string)
        if len(enc_tokens) > max_tokens:
            string = enc.decode(enc_tokens[:max_tokens])
    else:
        raise ValueError("Unknown serialization method.")
    return string


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.
    """
    if isinstance(db_input, str):
        conn = sqlite3.connect(db_input)
        cursor = conn.cursor()
        cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
        table_names = [name[0] for name in cursor.fetchall()]
        dfs = {table_name: pd.read_sql_query(f"SELECT * FROM {table_name}", conn) for table_name in table_names}
    elif isinstance(db_input, dict) and all(isinstance(df, pd.DataFrame) for df in db_input.values()):
        dfs = db_input
    else:
        raise ValueError("db_input should be either a SQLite database file path or a dictionary of pandas DataFrames")

    representations = {
        "Markdown": "",
        "HTML": "",
        "LaTeX": "",
        "CSV": "",
        "TSV": "",
        "reStructuredText": "",
        "BBCode": "",
        "MediaWiki": "",
        "Org mode": "",
        "PrettyTable": "",
        "SQL": "",
    }

    for table_name, df in dfs.items():
        table_data = {"cols": df.columns.tolist(), "rows": df.values.tolist()}
        table_representations = convert_table(table_data, table_name, visible_rows_num)
        for _format, table_representation in table_representations.items():
            representations[_format] += table_representation + "\n\n"

    return representations

def serialize_db(
    db: SQLDatabase,
    serialize_method: str = "database",
    num_visible_rows: int = 3,
    max_tokens: int = 1000,
) -> str:
    """Convert database engine to a string representation."""
    if serialize_method == "database":
        # TODO: Now access the internal variable
        setattr(db, "_sample_rows_in_table_info", num_visible_rows)
        string = db.get_table_info()
        # Truncate the string if it is too long
        enc = tiktoken.get_encoding("cl100k_base")
        enc_tokens = enc.encode(string)
        if len(enc_tokens) > max_tokens:
            string = enc.decode(enc_tokens[:max_tokens])
    else:
        raise ValueError("Unknown serialization method.")


        "LaTeX": "",
        "CSV": "",
        "TSV": "",
        "reStructuredText": "",
        "BBCode": "",
        "MediaWiki": "",
        "Org mode": "",
        "PrettyTable": "",
        "SQL": "",
    }

    for table_name, df in dfs.items():
        table_data = {"cols": df.columns.tolist(), "rows": df.values.tolist()}
        table_representations = convert_table(table_data, table_name, visible_rows_num)
        for _format, table_representation in table_representations.items():
            representations[_format] += table_representation + "\n\n"

    return representations

def serialize_db(
    db: SQLDatabase,
    serialize_method: str = "database",
    num_visible_rows: int = 3,
    max_tokens: int = 1000,
) -> str:
    """Convert database engine to a string representation."""
    if serialize_method == "database":
        # TODO: Now access the internal variable
        setattr(db, "_sample_rows_in_table_info", num_visible_rows)
        string = db.get_table_info()
        # Truncate the string if it is too long
        enc = tiktoken.get_encoding("cl100k_base")
        enc_tokens = enc.encode(string)
        if len(enc_tokens) > max_tokens:
            string = enc.decode(enc_tokens[:max_tokens])
    else:
        raise ValueError("Unknown serialization method.")
    return string


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
    install_required_packages()
    from rdflib import Graph, Namespace, URIRef

    g = Graph()

    # Define a namespace for the knowledge graph
    kg_ns = Namespace(name_space)
    g.bind("kg", kg_ns)

    # Add the triples to the graph
    for s, p, o in kg_input:
        subject = URIRef(kg_ns[s])
        predicate = URIRef(kg_ns[p])
        object = URIRef(kg_ns[o])
        g.add((subject, predicate, object))

    # Serialize the graph into the desired format
    representations = {_format: g.serialize(format=_format) for _format in ["json-ld", "turtle", "n3", "nt"]}

    return representations


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
    install_required_packages()
    from rdflib import Graph, Namespace, URIRef

    g = Graph()

    # Define a namespace for the knowledge graph
    kg_ns = Namespace(name_space)
    g.bind("kg", kg_ns)

    # Add the triples to the graph
    for s, p, o in kg_input:
        subject = URIRef(kg_ns[s])
        predicate = URIRef(kg_ns[p])
        object = URIRef(kg_ns[o])
        g.add((subject, predicate, object))

    # Serialize the graph into the desired format
    representations = {_format: g.serialize(format=_format) for _format in ["json-ld", "turtle", "n3", "nt"]}


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


from typing import Any, Dict

from langchain.base_language import BaseLanguageModel
from langchain.prompts import (
    ChatPromptTemplate,
    HumanMessagePromptTemplate,
    MessagesPlaceholder,
    SystemMessagePromptTemplate,
)
from langchain.chains import ConversationChain

from real_agents.adapters.executors.base import BaseExecutor
from real_agents.adapters.memory import ConversationBufferMemory

class ChatExecutor(BaseExecutor):
    """Chat Executor."""

    _DEFAULT_TEMPLATE = "The following is a friendly conversation between a human and an AI. \
        The AI is talkative and provides lots of specific details from its context. \
        If the AI does not know the answer to a question, it truthfully says it does not know."
    output_key: str = "result"

    def __init__(self) -> None:
        """Initialize the executor"""
        self.memory = ConversationBufferMemory(return_messages=True)

    def run(
        self,
        user_intent: str,
        llm: BaseLanguageModel,
        verbose: bool = True,
    ) -> Dict[str, Any]:
        """Run the executor.

        Args:
            user_intent: User intent to execute.
            grounding_source: Grounding source to execute the program on.
            llm: Language model to use.
            verbose: Whether to print the logging.

        Returns:
            Result of string.
        """
        prompt = ChatPromptTemplate.from_messages(
            [
                SystemMessagePromptTemplate.from_template(self._DEFAULT_TEMPLATE),
                MessagesPlaceholder(variable_name="history"),
                HumanMessagePromptTemplate.from_template("{input}"),
            ]
        )
        method = ConversationChain(
            llm=llm,
            prompt=prompt,
            verbose=verbose,
            memory=self.memory,
        )
        result = method.predict(input=user_intent)
        output = {self.output_key: result}
        return output


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

from real_agents.adapters.executors.base import BaseExecutor
from real_agents.adapters.memory import ConversationBufferMemory

class ChatExecutor(BaseExecutor):
    """Chat Executor."""

    _DEFAULT_TEMPLATE = "The following is a friendly conversation between a human and an AI. \
        The AI is talkative and provides lots of specific details from its context. \
        If the AI does not know the answer to a question, it truthfully says it does not know."
    output_key: str = "result"

    def __init__(self) -> None:
        """Initialize the executor"""
        self.memory = ConversationBufferMemory(return_messages=True)

    def run(
        self,
        user_intent: str,
        llm: BaseLanguageModel,
        verbose: bool = True,
    ) -> Dict[str, Any]:
        """Run the executor.

        Args:
            user_intent: User intent to execute.
            grounding_source: Grounding source to execute the program on.
            llm: Language model to use.
            verbose: Whether to print the logging.

        Returns:
            Result of string.
        """
        prompt = ChatPromptTemplate.from_messages(
            [


run

    SystemMessagePromptTemplate,
)
from langchain.chains import ConversationChain

from real_agents.adapters.executors.base import BaseExecutor
from real_agents.adapters.memory import ConversationBufferMemory

class ChatExecutor(BaseExecutor):
    """Chat Executor."""

    _DEFAULT_TEMPLATE = "The following is a friendly conversation between a human and an AI. \
        The AI is talkative and provides lots of specific details from its context. \
        If the AI does not know the answer to a question, it truthfully says it does not know."
    output_key: str = "result"

    def __init__(self) -> None:
        """Initialize the executor"""
        self.memory = ConversationBufferMemory(return_messages=True)

    def run(
        self,
        user_intent: str,
        llm: BaseLanguageModel,
        verbose: bool = True,
    ) -> Dict[str, Any]:
        """Run the executor.

        Args:
            user_intent: User intent to execute.
            grounding_source: Grounding source to execute the program on.
            llm: Language model to use.
            verbose: Whether to print the logging.

        Returns:
            Result of string.
        """
        prompt = ChatPromptTemplate.from_messages(
            [
                SystemMessagePromptTemplate.from_template(self._DEFAULT_TEMPLATE),
                MessagesPlaceholder(variable_name="history"),
                HumanMessagePromptTemplate.from_template("{input}"),
            ]
        )
        method = ConversationChain(
            llm=llm,
            prompt=prompt,
            verbose=verbose,
            memory=self.memory,
        )
        result = method.predict(input=user_intent)
        output = {self.output_key: result}
        return output


from __future__ import annotations

from langchain.base_language import BaseLanguageModel
from langchain.chains.llm import LLMChain

from real_agents.adapters.executors.question_suggestion.base import QuestionSuggestionChainBase
from real_agents.adapters.executors.question_suggestion.prompts import QUESTION_SUGGESTION_PROMPT_CHAT_MEMORY

class QuestionSuggestionChainChatMemory(QuestionSuggestionChainBase):
    @classmethod
    def from_prompt(cls, llm: BaseLanguageModel) -> QuestionSuggestionChainChatMemory:
        """Load from user profile prompt."""
        llm_chain = LLMChain(llm=llm, prompt=QUESTION_SUGGESTION_PROMPT_CHAT_MEMORY)
        return cls(llm_chain=llm_chain)


from __future__ import annotations

from typing import Dict, List, Optional
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 real_agents.adapters.executors.question_suggestion.prompts import QUESTION_SUGGESTION_PROMPT_BASE

class QuestionSuggestionChainBase(Chain, BaseModel):
    """Question Suggestion by Language Models."""

    llm_chain: LLMChain
    output_key: str = "questions"

    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 self.llm_chain.prompt.input_variables

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

        :meta private:
        """
        return [self.output_key]

    def extract_questions(self, s: str) -> List[str]:
        components = s.split("\n")
        questions = []
        count = 1
        for c in components:
            if c.startswith(f"{count}"):
                questions.append(c.replace(f"{count}.", "").replace(f"{count}", "").strip())
                count += 1
        return questions

    def _call(
        self,
        inputs: Dict[str, str],
        run_manager: Optional[CallbackManagerForChainRun] = None,
    ) -> Dict[str, List[str]]:
        _run_manager = run_manager or CallbackManagerForChainRun.get_noop_manager()
        questions = self.llm_chain.predict(**inputs)
        _run_manager.on_text(questions, color="green", end="\n", verbose=False)
        return {self.output_keys[0]: self.extract_questions(questions)}

    @classmethod
    def from_prompt(cls, llm: BaseLanguageModel) -> QuestionSuggestionChainBase:
        """Load from base prompt."""
        llm_chain = LLMChain(llm=llm, prompt=QUESTION_SUGGESTION_PROMPT_BASE)
        return cls(llm_chain=llm_chain)


Config

from __future__ import annotations

from typing import Dict, List, Optional
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 real_agents.adapters.executors.question_suggestion.prompts import QUESTION_SUGGESTION_PROMPT_BASE

class QuestionSuggestionChainBase(Chain, BaseModel):
    """Question Suggestion by Language Models."""

    llm_chain: LLMChain
    output_key: str = "questions"

    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 self.llm_chain.prompt.input_variables

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

        :meta private:
        """
        return [self.output_key]

    def extract_questions(self, s: str) -> List[str]:
        components = s.split("\n")
        questions = []


from langchain.callbacks.manager import CallbackManagerForChainRun
from langchain.chains.base import Chain
from langchain.chains.llm import LLMChain

from real_agents.adapters.executors.question_suggestion.prompts import QUESTION_SUGGESTION_PROMPT_BASE

class QuestionSuggestionChainBase(Chain, BaseModel):
    """Question Suggestion by Language Models."""

    llm_chain: LLMChain
    output_key: str = "questions"

    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 self.llm_chain.prompt.input_variables

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

        :meta private:
        """
        return [self.output_key]

    def extract_questions(self, s: str) -> List[str]:
        components = s.split("\n")
        questions = []
        count = 1
        for c in components:
            if c.startswith(f"{count}"):
                questions.append(c.replace(f"{count}.", "").replace(f"{count}", "").strip())
                count += 1
        return questions

    def _call(


    """Question Suggestion by Language Models."""

    llm_chain: LLMChain
    output_key: str = "questions"

    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 self.llm_chain.prompt.input_variables

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

        :meta private:
        """
        return [self.output_key]

    def extract_questions(self, s: str) -> List[str]:
        components = s.split("\n")
        questions = []
        count = 1
        for c in components:
            if c.startswith(f"{count}"):
                questions.append(c.replace(f"{count}.", "").replace(f"{count}", "").strip())
                count += 1
        return questions

    def _call(
        self,
        inputs: Dict[str, str],
        run_manager: Optional[CallbackManagerForChainRun] = None,
    ) -> Dict[str, List[str]]:
        _run_manager = run_manager or CallbackManagerForChainRun.get_noop_manager()
        questions = self.llm_chain.predict(**inputs)
        _run_manager.on_text(questions, color="green", end="\n", verbose=False)
        return {self.output_keys[0]: self.extract_questions(questions)}


        extra = Extra.forbid
        arbitrary_types_allowed = True

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

        :meta private:
        """
        return self.llm_chain.prompt.input_variables

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

        :meta private:
        """
        return [self.output_key]

    def extract_questions(self, s: str) -> List[str]:
        components = s.split("\n")
        questions = []
        count = 1
        for c in components:
            if c.startswith(f"{count}"):
                questions.append(c.replace(f"{count}.", "").replace(f"{count}", "").strip())
                count += 1
        return questions

    def _call(
        self,
        inputs: Dict[str, str],
        run_manager: Optional[CallbackManagerForChainRun] = None,
    ) -> Dict[str, List[str]]:
        _run_manager = run_manager or CallbackManagerForChainRun.get_noop_manager()
        questions = self.llm_chain.predict(**inputs)
        _run_manager.on_text(questions, color="green", end="\n", verbose=False)
        return {self.output_keys[0]: self.extract_questions(questions)}

    @classmethod
    def from_prompt(cls, llm: BaseLanguageModel) -> QuestionSuggestionChainBase:
        """Load from base prompt."""
        llm_chain = LLMChain(llm=llm, prompt=QUESTION_SUGGESTION_PROMPT_BASE)
        return cls(llm_chain=llm_chain)


        return self.llm_chain.prompt.input_variables

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

        :meta private:
        """
        return [self.output_key]

    def extract_questions(self, s: str) -> List[str]:
        components = s.split("\n")
        questions = []
        count = 1
        for c in components:
            if c.startswith(f"{count}"):
                questions.append(c.replace(f"{count}.", "").replace(f"{count}", "").strip())
                count += 1
        return questions

    def _call(
        self,
        inputs: Dict[str, str],
        run_manager: Optional[CallbackManagerForChainRun] = None,
    ) -> Dict[str, List[str]]:
        _run_manager = run_manager or CallbackManagerForChainRun.get_noop_manager()
        questions = self.llm_chain.predict(**inputs)
        _run_manager.on_text(questions, color="green", end="\n", verbose=False)
        return {self.output_keys[0]: self.extract_questions(questions)}

    @classmethod
    def from_prompt(cls, llm: BaseLanguageModel) -> QuestionSuggestionChainBase:
        """Load from base prompt."""
        llm_chain = LLMChain(llm=llm, prompt=QUESTION_SUGGESTION_PROMPT_BASE)
        return cls(llm_chain=llm_chain)


        components = s.split("\n")
        questions = []
        count = 1
        for c in components:
            if c.startswith(f"{count}"):
                questions.append(c.replace(f"{count}.", "").replace(f"{count}", "").strip())
                count += 1
        return questions

    def _call(
        self,
        inputs: Dict[str, str],
        run_manager: Optional[CallbackManagerForChainRun] = None,
    ) -> Dict[str, List[str]]:
        _run_manager = run_manager or CallbackManagerForChainRun.get_noop_manager()
        questions = self.llm_chain.predict(**inputs)
        _run_manager.on_text(questions, color="green", end="\n", verbose=False)
        return {self.output_keys[0]: self.extract_questions(questions)}

    @classmethod
    def from_prompt(cls, llm: BaseLanguageModel) -> QuestionSuggestionChainBase:
        """Load from base prompt."""
        llm_chain = LLMChain(llm=llm, prompt=QUESTION_SUGGESTION_PROMPT_BASE)
        return cls(llm_chain=llm_chain)


from __future__ import annotations

from langchain.base_language import BaseLanguageModel
from langchain.chains.llm import LLMChain

from real_agents.adapters.executors.question_suggestion.base import QuestionSuggestionChainBase
from real_agents.adapters.executors.question_suggestion.prompts import QUESTION_SUGGESTION_PROMPT_USER_PROFILE

class QuestionSuggestionChainUserProfile(QuestionSuggestionChainBase):
    @classmethod
    def from_prompt(cls, llm: BaseLanguageModel) -> QuestionSuggestionChainUserProfile:
        """Load from user profile prompt."""
        llm_chain = LLMChain(llm=llm, prompt=QUESTION_SUGGESTION_PROMPT_USER_PROFILE)
        return cls(llm_chain=llm_chain)


from langchain import PromptTemplate

template_base = (
    "{input_string}\nPlease provide {num_questions} natural language questions related to the above contents, "
    "but very different from each other. These questions should be diverse, challenging, "
    "and targeted towards different perspectives. "
    "You should ask these questions like you would ask a human, "
    "but strictly follow the style of your role-playing character.\n"
    "Do not explicitly mention the provided contents; "
    "instead use natural language descriptions for them. "
    "The final result should be a numbered list.".strip() + "\n"
)

QUESTION_SUGGESTION_PROMPT_BASE = PromptTemplate(
    input_variables=["input_string", "num_questions"], template=template_base
)

template_user_profile = (
    "{input_string}\n--------------------\n"
    "{user_description}\n"
    "From now on, you should speak in a style that fully conforms to the given role. \n"
    "Please provide {num_questions} natural language questions related to the above database, "
    "but very different from each other. These questions should be diverse, challenging, "
    "and targeted towards different database tables and columns as well as query types. "
    "You should ask these questions like you would ask a human, "
    "but strictly follow the style of your role-playing character.\n"
    "Do not explicitly mention column or table names in the database; "
    "instead use natural language descriptions for them. "
    "The final result should be a numbered list.".strip() + "\n"
)

QUESTION_SUGGESTION_PROMPT_USER_PROFILE = PromptTemplate(
    input_variables=["input_string", "user_description", "num_questions"], template=template_user_profile
)

template_chat_memory = (
    "{input_string}\n--------------------\n"
    "Here is the conversation between Human and AI.\n"
    "{chat_memory}\n"
    "--------------------\n"
    "Please provide {num_questions} natural language questions related to the above contents, "
    "but very different from each other. These questions should be diverse, challenging, "
    "and targeted towards different perspectives.\n"
    "Keep each questions shorter than 15 words.\n"
    "You should ask these questions like you would ask a human, "
    "but strictly follow the style of your role-playing character.\n"
    "Do not explicitly mention the provided contents; "
    "instead use natural language descriptions for them. "
    "The final result should be a numbered list.".strip() + "\n"
)

QUESTION_SUGGESTION_PROMPT_CHAT_MEMORY = PromptTemplate(
    input_variables=["input_string", "chat_memory", "num_questions"], template=template_chat_memory
)


from typing import Any, Dict

from langchain.base_language import BaseLanguageModel
from langchain.schema import AIMessage, HumanMessage

from real_agents.adapters.memory import ConversationReActBufferMemory
from real_agents.adapters.executors.question_suggestion.chat_memory import QuestionSuggestionChainChatMemory
from real_agents.adapters.executors.question_suggestion.base import QuestionSuggestionChainBase
from real_agents.adapters.executors.question_suggestion.user_profile import QuestionSuggestionChainUserProfile

class QuestionSuggestionExecutor:
    def run(
        self,
        user_intent: str,
        llm: BaseLanguageModel,
        num_questions: int = 3,
        mode: str = "",
        user_profile: str = "",
        chat_memory: ConversationReActBufferMemory = ConversationReActBufferMemory(),
    ) -> Dict[str, Any]:
        if mode == "base":
            method = QuestionSuggestionChainBase.from_prompt(llm)
            inputs = {"input_string": user_intent, "num_questions": num_questions}
        elif mode == "user_profile":
            method = QuestionSuggestionChainUserProfile.from_prompt(llm)
            with open(user_profile) as f:
                inputs = {"input_string": user_intent, "num_questions": num_questions, "user_description": f.read()}
        elif mode == "chat_memory":
            method = QuestionSuggestionChainChatMemory.from_prompt(llm)
            raw_history = chat_memory.load_memory_variables({})["chat_history"]
            refine_history = []
            for msg in raw_history[-4:]:
                if isinstance(msg, HumanMessage):
                    refine_history.append(f"Human: {msg.content}")
                elif isinstance(msg, AIMessage):
                    refine_history.append(f"AI: {msg.content}")
            inputs = {
                "input_string": user_intent,
                "num_questions": num_questions,
                "chat_memory": "\n".join(refine_history),
            }
        else:
            raise ValueError(f"Mode {mode} is not supported")
        return method(inputs)