From ca0ead7b0c3be0e5f0a1cd7bf67a913bba71afb1 Mon Sep 17 00:00:00 2001 From: Simatwa Date: Thu, 8 Feb 2024 20:28:37 +0300 Subject: [PATCH] 1. RawDog: - Give consent to script execution - Execute script internally or externally - Choose python interpreter name --- docs/CHANGELOG.md | 14 +++++- requirements.txt | 3 +- setup.py | 2 +- src/pytgpt/__init__.py | 3 +- src/pytgpt/console.py | 66 +++++++++++++++++++++++-- src/pytgpt/utils.py | 109 ++++++++++++++++++++++++++++++----------- 6 files changed, 160 insertions(+), 37 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ee828f9..2f91703 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -315,7 +315,7 @@ Assume role of the LLM and give your response.* - Disable g4f version check. - Partial or full installation options. -## v0.4.0 +## v0.4.1 **What's new?** @@ -336,4 +336,14 @@ For instance:

- \ No newline at end of file + + + +## v0.4.2 + +**What's new?** + +1. RawDog: + - Give consent to script execution + - Execute script internally or externally + - Choose python interpreter name \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 3add46e..8da6d4d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,4 +8,5 @@ webchatgpt==0.2.7 GoogleBard1==2.1.1 colorama==0.4.6 g4f>=0.2.1.0 -pyyaml==6.0.1 \ No newline at end of file +pyyaml==6.0.1 +matplotlib \ No newline at end of file diff --git a/setup.py b/setup.py index 271fa93..cc3565e 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,7 @@ setup( name="python-tgpt", - version="0.4.1", + version="0.4.2", license="MIT", author="Smartwa", maintainer="Smartwa", diff --git a/src/pytgpt/__init__.py b/src/pytgpt/__init__.py index 5ecb3bc..2b973bf 100644 --- a/src/pytgpt/__init__.py +++ b/src/pytgpt/__init__.py @@ -1,14 +1,13 @@ from .utils import appdir import g4f -__version__ = "0.4.1" +__version__ = "0.4.2" __author__ = "Smartwa" __repo__ = "https://github.com/Simatwa/python-tgpt" tgpt_providers = [ "leo", "openai", - "fakeopen", "opengpt", "koboldai", "bard", diff --git a/src/pytgpt/console.py b/src/pytgpt/console.py index 6765f51..be38438 100644 --- a/src/pytgpt/console.py +++ b/src/pytgpt/console.py @@ -332,6 +332,9 @@ def __init__( chat_completion=False, ignore_working=False, rawdog=False, + external_exec=False, + confirm_script=False, + interpreter="python", *args, **kwargs, ): @@ -346,9 +349,16 @@ def __init__( getOr = lambda option, default: option if option else default if rawdog: - self.RawDog = RawDog(quiet=quiet) - intro = RawDog.intro_prompt - getpass.getuser = lambda:'RawDog' + + self.RawDog = RawDog( + quiet=quiet, + external_exec=external_exec, + confirm_script=confirm_script, + interpreter=interpreter, + prettify=True, + ) + intro = self.RawDog.intro_prompt + getpass.getuser = lambda: "RawDog" if provider == "leo": import pytgpt.leo as leo @@ -1131,6 +1141,25 @@ class ChatInteractive: is_flag=True, help="Generate and auto-execute Python scripts - (experimental)", ) + @click.option( + "-ex", + "--external-exec", + is_flag=True, + help="RawDog : Execute scripts with system's python interpreter", + ) + @click.option( + "-cs", + "--confirm-script", + is_flag=True, + help="RawDog : Give consent to generated scripts prior to execution", + ) + @click.option( + "-int", + "--interpreter", + default="python", + help="RawDog : Python's interpreter name", + envvar="python_interpreter", + ) @click.help_option("-h", "--help") def interactive( model, @@ -1162,6 +1191,9 @@ def interactive( chat_completion, ignore_working, rawdog, + external_exec, + confirm_script, + interpreter, ): """Chat with AI interactively (Default)""" this.clear_history_file(filepath, new) @@ -1185,6 +1217,9 @@ def interactive( chat_completion, ignore_working, rawdog=rawdog, + external_exec=external_exec, + confirm_script=confirm_script, + interpreter=interpreter, ) busy_bar.spin_index = busy_bar_index bot.code_theme = code_theme @@ -1392,6 +1427,25 @@ class ChatGenerate: is_flag=True, help="Generate and auto-execute Python scripts - (experimental)", ) + @click.option( + "-ex", + "--external-exec", + is_flag=True, + help="RawDog : Execute scripts with system's python interpreter", + ) + @click.option( + "-cs", + "--confirm-script", + is_flag=True, + help="RawDog : Give consent to generated scripts prior to execution", + ) + @click.option( + "-int", + "--interpreter", + default="python", + help="RawDog : Python's interpreter name", + envvar="python_interpreter", + ) @click.help_option("-h", "--help") def generate( model, @@ -1423,6 +1477,9 @@ def generate( with_copied, ignore_working, rawdog, + external_exec, + confirm_script, + interpreter, ): """Generate a quick response with AI""" bot = Main( @@ -1444,6 +1501,9 @@ def generate( quiet, ignore_working=ignore_working, rawdog=rawdog, + external_exec=external_exec, + confirm_script=confirm_script, + interpreter=interpreter, ) prompt = prompt if prompt else "" copied_placeholder = "{{copied}}" diff --git a/src/pytgpt/utils.py b/src/pytgpt/utils.py index 59ee83f..b6b0ce1 100644 --- a/src/pytgpt/utils.py +++ b/src/pytgpt/utils.py @@ -7,6 +7,9 @@ import datetime import re import sys +import click +from rich.markdown import Markdown +from rich.console import Console appdir = appdirs.AppDirs("pytgpt", "Smartwa") @@ -424,7 +427,41 @@ class RawDog: # Idea borrowed from https://github.com/AbanteAI/rawdog - intro_prompt = f""" + def __init__( + self, + quiet: bool = False, + external_exec: bool = False, + confirm_script: bool = False, + interpreter: str = "python", + prettify: bool = True, + ): + """Constructor + + Args: + quiet (bool, optional): Flag for control logging. Defaults to False. + external_exec (bool, optional): Execute scripts with system's python executable. Defaults to False. + confirm_script (bool, optional): Give consent to scripts prior to execution. Defaults to False. + interpreter (str, optional): Python's interpreter name. Defaults to Python. + prettify (bool, optional): Prettify the code on stdout. Defaults to True. + """ + if not quiet: + print( + "To get the most out of Rawdog. Ensure the following are installed:\n" + " 1. Python interpreter.\n" + " 2. Dependency:\n" + " - Matplotlib\n" + "Be alerted on the risk posed! (Experimental)\n" + "Use '--quiet' to suppress this message and code/logs stdout.\n" + ) + self.external_exec = external_exec + self.confirm_script = confirm_script + self.quiet = quiet + self.interpreter = interpreter + self.prettify = prettify + + @property + def intro_prompt(self): + return f""" You are a command-line coding assistant called Rawdog that generates and auto-executes Python scripts. A typical interaction goes like this: @@ -470,22 +507,21 @@ class RawDog: - ALWAYS Return your SCRIPT inside of a single pair of ``` delimiters. Only the console output of the first such SCRIPT is visible to the user, so make sure that it's complete and don't bother returning anything else. Current system : {platform.system()} -Python version : {run_system_command("python --version",exit_on_error=True,stdout_error=True)[1].stdout} +Python version : {run_system_command(f"{self.interpreter} --version",exit_on_error=True,stdout_error=True)[1].stdout.split(' ')[1]} Current directory : {os.getcwd()} Current Datetime : {datetime.datetime.now()} """ - def __init__(self, quiet: bool = False): - if not quiet: - print( - "To get the most out of Rawdog. Ensure the following are installed:\n" - " 1. Python interpreter.\n" - " 2. Dependency:\n" - " - Matplotlib\n" - "Be alerted on the risk posed! (Experimental)\n" - "Use '--quiet' to suppress this message and code/logs stdout.\n" - ) - self.quiet = quiet + def stdout(self, message: str) -> None: + """Stdout data + + Args: + message (str): Text to be printed + """ + if self.prettify: + Console().print(Markdown(message)) + else: + click.secho(message, fg="yellow") def log(self, message: str, category: str = "info"): """RawDog logger @@ -497,6 +533,7 @@ def log(self, message: str, category: str = "info"): if self.quiet: return + message = "[PYTGPT] - " + message if category == "error": logging.error(message) else: @@ -513,34 +550,50 @@ def main(self, response: str) -> None: """ code_blocks = re.findall(r"```python.*?```", response, re.DOTALL) if len(code_blocks) != 1: - print(response) + self.stdout(response) + else: raw_code = code_blocks[0] - raw_code_plus = re.sub(r"(```)(python)?", "", raw_code) - if not self.quiet: - print(raw_code_plus) + if self.confirm_script: + self.stdout(raw_code) + if not click.confirm("- Do you wish to execute this"): + return + + elif not self.quiet: + self.stdout(raw_code) + + raw_code_plus = re.sub(r"(```)(python)?", "", raw_code) - if "CONTINUE" in response: - self.log("Executing script") + if "CONTINUE" in response or self.external_exec: + self.log("Executing script externally") path_to_script = os.path.join(default_path, "execute_this.py") with open(path_to_script, "w") as fh: fh.write(raw_code_plus) - success, proc = run_system_command( - f"python {path_to_script}", exit_on_error=False, stdout_error=False - ) - if success: - self.log("Returning success feedback") - return f"LAST SCRIPT OUTPUT:\n{proc.stdout}" + if "CONTINUE" in response: + + success, proc = run_system_command( + f"{self.interpreter} {path_to_script}", + exit_on_error=False, + stdout_error=False, + ) + + if success: + self.log("Returning success feedback") + return f"LAST SCRIPT OUTPUT:\n{proc.stdout}" + else: + self.log("Returning error feedback", "error") + return f"PREVIOUS SCRIPT EXCEPTION:\n{proc.stderr}" else: - self.log("Returning error feedback", "error") - return f"PREVIOUS SCRIPT EXCEPTION:\n{proc.stderr}" + os.system(f"{self.interpreter} {path_to_script}") + else: try: + self.log("Executing script internally") exec(raw_code_plus) except Exception as e: self.log( "Exception occurred while executing script. Responding with error.", "error", ) - return str(e) + return f"PREVIOUS SCRIPT EXCEPTION:\n{str(e)}"