Module ai_shell.code_generate

Generate code for AI Shell using the docstrings as source data.

Currently, targets: - CLI interface - Toolkit - JsonSchema dict

Expand source code
"""
Generate code for AI Shell using the docstrings as source data.

Currently, targets:
- CLI interface
- Toolkit
- JsonSchema dict
"""

from ai_shell.code_generate.generate_cli import generate_the_cli
from ai_shell.code_generate.generate_schema import generate_the_schema
from ai_shell.code_generate.generate_toolkit import generate_the_toolkit

__all__ = ["generate_the_schema", "generate_the_toolkit", "generate_the_cli"]

Sub-modules

ai_shell.code_generate.generate_cli

Code generate reverse client

ai_shell.code_generate.generate_schema

Generates JsonSchema in form of a python file.

ai_shell.code_generate.generate_toolkit

Code generate reverse client

ai_shell.code_generate.method_to_jsonschema

Utility for generating jsonschema from class methods.

Functions

def generate_the_cli(target_file: str) ‑> None

Main entry point.

Args

target_file : str
The target file path for the generated code.
Expand source code
def generate_the_cli(target_file: str) -> None:
    """Main entry point.
    Args:
        target_file (str): The target file path for the generated code.
    """
    tools = ""
    for _ns, json_schema in schemas._SCHEMAS.items():
        for tool, _ in json_schema.items():
            tools += f'            "{tool}" : self.{tool},\n'

    meta: dict[str, dict[str, str]] = {}
    meta["headtail"] = {
        "module": "ai_shell.head_tail_tool",
        "class": "HeadTailTool",
    }
    meta["cat"] = {
        "module": "ai_shell.cat_tool",
        "class": "CatTool",
    }
    meta["cut"] = {
        "module": "ai_shell.cut_tool",
        "class": "CutTool",
    }
    meta["find"] = {
        "module": "ai_shell.find_tool",
        "class": "FindTool",
    }
    meta["git"] = {
        "module": "ai_shell.git_tool",
        "class": "GitTool",
    }
    meta["patch"] = {
        "module": "ai_shell.patch_tool",
        "class": "PatchTool",
    }
    meta["ls"] = {
        "module": "ai_shell.ls_tool",
        "class": "LsTool",
    }
    meta["grep"] = {
        "module": "ai_shell.grep_tool",
        "class": "GrepTool",
    }
    meta["token_counter"] = {
        "module": "ai_shell.token_tool",
        "class": "TokenCounterTool",
    }
    meta["todo"] = {
        "module": "ai_shell.todo_tool",
        "class": "TodoTool",
    }
    meta["insert"] = {
        "module": "ai_shell.insert_tool",
        "class": "InsertTool",
    }
    meta["replace"] = {
        "module": "ai_shell.replace_tool",
        "class": "ReplaceTool",
    }
    meta["rewrite"] = {
        "module": "ai_shell.rewrite_tool",
        "class": "RewriteTool",
    }
    meta["answer_collector"] = {
        "module": "ai_shell.answer_tool",
        "class": "AnswerCollectorTool",
    }
    meta["pytest"] = {
        "module": "ai_shell.pytest_tool",
        "class": "PytestTool",
    }

    header = """\"\"\"
Generated code, do not edit.
\"\"\"
import argparse
from ai_shell.utils.console_utils import pretty_console
from ai_shell.utils.config_manager import Config
from ai_shell.__about__ import __version__, __description__

CONFIG = Config()
# pylint: disable=unused-argument
"""

    for _key, value in meta.items():
        header += f"\nfrom {value['module']} import {value['class']}"
    header += "\n\n"

    middle = ""
    for ns, data in meta.items():
        middle += "\n"
        for method, method_data in schemas._SCHEMAS[ns].items():
            middle += "\n"
            middle += f"def {method}_command(args):\n"
            middle += f'    """Invoke {method}"""\n'
            middle += f"    tool = {data['class']}('.', CONFIG)\n"
            middle += f"    pretty_console(tool.{method}("
            for arg_name, _arg_details in cast(dict[str, Any], method_data["properties"]).items():
                if arg_name != "mime_type":
                    # don't know how to handle mime types in CLI yet.
                    middle += f"\n        {arg_name} = args.{arg_name},"
            middle += "\n    ))"

    argparse_part = """\n\ndef run():
        \"\"\"Create the main parser\"\"\"
        program = 'ais'
        parser = argparse.ArgumentParser(
        prog=program,
        allow_abbrev=False,
        description=__description__,
        epilog=f\"\"\"
    Examples:

        ais cat hello.py
\"\"\",
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    parser.add_argument(
        "-V",
        "--version",
        action="version",
        version=f"%(prog)s {__version__}",
        help="Show program's version number and exit.",
    )
        
        )
        subparsers = parser.add_subparsers(dest='subcommand', help='sub-command help')
    """
    for ns, _data in meta.items():
        for method, method_data in schemas._SCHEMAS[ns].items():
            escaped_method_description = (
                cast(str, method_data.get("description", "")).replace("'", "\\'").replace("\n", "\\n")
            )
            argparse_part += f'    # Create a parser for the "{method}" command\n'
            argparse_part += f"""    {method}_parser = subparsers.add_parser('{method}', help=\"\"\"{escaped_method_description}.\"\"\")\n"""
            for arg_name, arg_details in cast(dict[str, Any], method_data["properties"]).items():
                dashed_arg_name = arg_name.replace("_", "-")
                escaped_description = arg_details.get("description", "").replace("'", "\\'")

                argparse_part += f"\n    {method}_parser.add_argument('--{dashed_arg_name}', "
                bool_part = ""
                if arg_details["type"] == "boolean":
                    bool_part = "action='store_true', "

                if method_data.get("default"):
                    argparse_part += (
                        f"    dest='{arg_name}', {bool_part} default={method_data['default']},\n"
                        f"    help='{escaped_description}, defaults to {method_data['default']}')\n"
                    )
                else:
                    argparse_part += f'    dest=\'{arg_name}\', {bool_part} help="""{escaped_description}""")\n'

                # if list
                # cat_parser.add_argument('file_paths', nargs='+', type=str, help='Paths to the files to be concatenated')

            argparse_part += f"    {method}_parser.set_defaults(func={method}_command)\n\n"

    argparse_part += """        # Parse the arguments
        args = parser.parse_args()
    """

    footer = """
    # Execute the appropriate command
    if hasattr(args, 'func'):
        args.func(args)
    else:
        parser.print_help()

if __name__ == '__main__':
    run()
"""

    # Generate method code
    with open(target_file, "w", encoding="utf-8") as code:
        code.write(header)
        code.write(middle)
        code.write(argparse_part)
        code.write(footer)
def generate_the_schema(target_file: str) ‑> None

Main entrypoint.

Args

target_file : str
The target file path for the generated code.
Expand source code
def generate_the_schema(target_file: str) -> None:
    """Main entrypoint.

    Args:
        target_file (str): The target file path for the generated code.
    """
    # Setup logging for my_app
    # We will only setup a console handler
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    ch = logging.StreamHandler()
    ch.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
    logger.addHandler(ch)

    schemas = {}
    schemas["cat"] = convert_to_json_schema(ai_shell.CatTool)
    schemas["cut"] = convert_to_json_schema(ai_shell.CutTool)
    schemas["ed"] = convert_to_json_schema(ai_shell.EdTool)
    schemas["sed"] = convert_to_json_schema(ai_shell.SedTool)
    schemas["insert"] = convert_to_json_schema(ai_shell.InsertTool)
    schemas["replace"] = convert_to_json_schema(ai_shell.ReplaceTool)
    schemas["headtail"] = convert_to_json_schema(ai_shell.HeadTailTool)
    schemas["edlin"] = convert_to_json_schema(ai_shell.EdlinTool)
    schemas["find"] = convert_to_json_schema(ai_shell.FindTool)
    schemas["git"] = convert_to_json_schema(ai_shell.GitTool)
    schemas["patch"] = convert_to_json_schema(ai_shell.PatchTool)
    schemas["ls"] = convert_to_json_schema(ai_shell.LsTool)
    schemas["grep"] = convert_to_json_schema(ai_shell.GrepTool)
    schemas["pycat"] = convert_to_json_schema(ai_shell.PyCatTool)
    schemas["token_counter"] = convert_to_json_schema(ai_shell.TokenCounterTool)
    schemas["todo"] = convert_to_json_schema(ai_shell.TodoTool)
    schemas["answer_collector"] = convert_to_json_schema(ai_shell.AnswerCollectorTool)
    schemas["rewrite"] = convert_to_json_schema(ai_shell.RewriteTool)
    schemas["pytest"] = convert_to_json_schema(ai_shell.PytestTool)

    with open(target_file, "w", encoding="utf-8") as source:
        source.write('"""jsonschema for functions"""')
        source.write("\n\n")
        source.write(f"_SCHEMAS = {pformat(schemas, width=500, indent=2)}")
def generate_the_toolkit(target_file: str) ‑> None

Main entry point

Args

target_file : str
Target file to write the generated code.
Expand source code
def generate_the_toolkit(target_file: str) -> None:
    """Main entry point
    Args:
        target_file (str): Target file to write the generated code.
    """
    tools = ""
    for _ns, json_schema in schemas._SCHEMAS.items():
        for tool, _ in json_schema.items():
            tools += f'            "{tool}" : self.{tool},\n'

    header = f"""\"\"\"
Generate code, do not edit.
\"\"\"
from ai_shell.openai_support import ToolKitBase
from typing import Any, cast, Callable, Optional

from ai_shell.utils.config_manager import Config
from ai_shell.cat_tool import CatTool
from ai_shell.find_tool import FindTool
from ai_shell.git_tool import GitTool
from ai_shell.grep_tool import GrepTool
from ai_shell.ls_tool import LsTool
from ai_shell.token_tool import TokenCounterTool
from ai_shell.edlin_tool import EdlinTool
from ai_shell.pycat_tool import PyCatTool
from ai_shell.ed_tool import EdTool
from ai_shell.cut_tool import CutTool
from ai_shell.head_tail_tool import HeadTailTool
from ai_shell.patch_tool import PatchTool
from ai_shell.sed_tool import SedTool
from ai_shell.insert_tool import InsertTool
from ai_shell.replace_tool import ReplaceTool
from ai_shell.todo_tool import TodoTool
from ai_shell.answer_tool import AnswerCollectorTool
from ai_shell.rewrite_tool import RewriteTool
from ai_shell.pytest_tool import PytestTool

# pylint: disable=unused-argument

class ToolKit(ToolKitBase):
    \"\"\"AI Shell Toolkit\"\"\"\n\n
    def __init__(self, root_folder: str, token_model: str, global_max_lines: int, permitted_tools: list[str], config:Config) -> None:
        super().__init__(root_folder, token_model, global_max_lines, permitted_tools, config)
        self._lookup: dict[str, Callable[[dict[str, Any]], Any]] = {{
            {tools}
        }}
        # Stateful tool support. Useless assignment to make mypy happy
        self.tool_answer_collector = AnswerCollectorTool(self.root_folder, self.config)
"""

    prologue = {}
    prologue["cat"] = "tool = CatTool(self.root_folder, self.config)"
    prologue["headtail"] = "tool = HeadTailTool(self.root_folder, self.config)"
    prologue["cut"] = "tool = CutTool(self.root_folder, self.config)"
    prologue["pycat"] = "tool = PyCatTool(self.root_folder, self.config)"
    prologue["ed"] = "tool = EdTool(self.root_folder, self.config)"
    prologue["sed"] = "tool = SedTool(self.root_folder, self.config)"
    prologue["insert"] = "tool = InsertTool(self.root_folder, self.config)"
    prologue["replace"] = "tool = ReplaceTool(self.root_folder, self.config)"
    prologue["edlin"] = "tool = EdlinTool(self.root_folder, self.config)"
    prologue["find"] = "tool = FindTool(self.root_folder, self.config)"
    prologue["git"] = "tool = GitTool(self.root_folder, self.config)"
    prologue["patch"] = "tool = PatchTool(self.root_folder, self.config)"
    prologue["ls"] = "tool = LsTool(self.root_folder, self.config)"
    prologue["grep"] = "tool = GrepTool(self.root_folder, self.config)"
    prologue["token_counter"] = "tool = TokenCounterTool(self.root_folder, self.config)"  # nosec
    prologue["todo"] = "tool = TodoTool(self.root_folder, self.config)"
    prologue["answer_collector"] = "self.tool_answer_collector = AnswerCollectorTool(self.root_folder, self.config)"
    prologue["rewrite"] = "tool = RewriteTool(self.root_folder, self.config)"
    prologue["pytest"] = "tool = PytestTool(self.root_folder, self.config)"

    # Generate method code
    with open(target_file, "w", encoding="utf-8") as code:
        code.write(header)
        for ns, json_schema in schemas._SCHEMAS.items():
            pro = prologue[ns]
            method_code = generate_method_code(json_schema, pro, ns)
            code.write(method_code)