|
- #! /bin/env python3
- import argparse
- import os
- import time
-
- from pathlib import Path
- from typing import List, Optional, Tuple
-
- import pygments
- from marko.block import FencedCode
- from marko.inline import RawText
- from marko.parser import Parser
- from prompt_toolkit import print_formatted_text
- from prompt_toolkit.formatted_text import PygmentsTokens
- from pygments.token import Token
- from pygments_markdown_lexer import MarkdownLexer
- from revChatGPT.V1 import Chatbot
-
- ENV_UUID = "GPTTRACE_CONV_UUID"
- ENV_ACCESS_TOKEN = "GPTTRACE_ACCESS_TOKEN"
-
- PROMPTS_DIR = Path("./prompts")
-
-
- def pretty_print(input, lexer=MarkdownLexer, *args, **kwargs):
- tokens = list(pygments.lex(input, lexer=lexer()))
- print_formatted_text(PygmentsTokens(tokens), *args, **kwargs)
-
-
- # print = pretty_print
- def main():
- parser = argparse.ArgumentParser(
- prog="GPTtrace",
- description="Use ChatGPT to write eBPF programs (bpftrace, etc.)",
- )
-
- group = parser.add_mutually_exclusive_group()
-
- group.add_argument(
- "-i", "--info", help="Let ChatGPT explain what's eBPF", action="store_true"
- )
- group.add_argument(
- "-e",
- "--execute",
- help="Generate commands using your input with ChatGPT, and run it",
- action="store",
- metavar="TEXT",
- )
- group.add_argument(
- "-g", "--generate", help="Generate eBPF programs using your input with ChatGPT", action="store", metavar="TEXT")
- group.add_argument(
- "--train", help="Train ChatGPT with conversions we provided", action="store_true")
-
- parser.add_argument("-v", "--verbose",
- help="Show more details", action="store_true")
-
- parser.add_argument(
- "-u",
- "--uuid",
- help=f"Conversion UUID to use, or passed through environment variable `{ENV_UUID}`",
- )
- parser.add_argument(
- "-t",
- "--access-token",
- help=f"ChatGPT access token, see `https://chat.openai.com/api/auth/session` or passed through `{ENV_ACCESS_TOKEN}`",
- )
- args = parser.parse_args()
-
- access_token = args.access_token or os.environ.get(ENV_ACCESS_TOKEN, None)
- conv_uuid = args.uuid or os.environ.get(ENV_UUID, None)
- if access_token is None:
- print(
- f"Either provide your access token through `-t` or through environment variable {ENV_ACCESS_TOKEN}"
- )
- return
- chatbot = Chatbot(config={"access_token": access_token})
- if args.info:
- generate_result(chatbot, "Explain what's eBPF", conv_uuid, True)
- elif args.execute is not None:
- desc: str = args.execute
- print("Sending query to ChatGPT: " + desc)
- ret_val, _ = generate_result(
- chatbot, construct_running_prompt(desc), conv_uuid, args.verbose)
- # print(ret_val)
- parsed = make_executable_command(ret_val)
- # print(f"Command to run: {parsed}")
- print("Press Ctrl+C to stop the program....")
- os.system("sudo " + parsed)
- elif args.generate is not None:
- desc: str = args.generate
- print("Sending query to ChatGPT: " + desc)
- ret_val, _ = generate_result(
- chatbot, construct_generate_prompt(desc), conv_uuid)
- pretty_print(ret_val)
- parsed = extract_code_blocks(ret_val)
- # print(f"Command to run: {parsed}")
- with open("generated.bpf.c", "w") as f:
- for code in parsed:
- f.write(code)
- elif args.train:
- prompts = os.listdir(PROMPTS_DIR)
- prompts.sort()
- # conv_uuid could be None, in which we will create a new session and use it in the next steps
- session = conv_uuid
- for file in prompts:
- info = f"Training ChatGPT with `{file}`"
- print("-"*len(info))
- print(info)
- print("-"*len(info))
- with open(PROMPTS_DIR/file, "r") as f:
- input_data = f.read()
- if args.verbose:
- print(input_data)
- resp, session = generate_result(
- chatbot, input_data, conv_uuid, args.verbose)
- time.sleep(2.4)
- print(f"Trained session: {session}")
- else:
- parser.print_help()
-
-
- def construct_generate_prompt(text: str) -> str:
- return f"""You are now a translater from human language to {os.uname()[0]} eBPF programs.
- Please write eBPF programs for me.
- No explanation required, no instruction required, don't tell me how to compile and run.
- What I want is a eBPF program for: {text}."""
-
-
- def construct_running_prompt(text: str) -> str:
- return f"""You are now a translater from human language to {os.uname()[0]} shell bpftrace command.
- No explanation required.
- respond with only the raw shell bpftrace command.
- It should be start with `bpftrace`.
- Your response should be able to put into a shell and run directly.
- Just output in one line, without any description, or any other text that cannot be run in shell.
- What should I type to shell to trace using bpftrace for: {text}, in one line."""
-
-
- def make_executable_command(command: str) -> str:
- if command.startswith("\n"):
- command = command[1:]
- if command.endswith("\n"):
- command = command[:-1]
- if command.startswith("`"):
- command = command[1:]
- if command.endswith("`"):
- command = command[:-1]
- command = command.strip()
- command = command.split("User: ")[0]
- return command
-
-
- def generate_result(bot: Chatbot, text: str, session: Optional[str] = None, print_out: bool = False) -> Tuple[str, str]:
- from io import StringIO
-
- prev_text = ""
- buf = StringIO()
- received_session = ""
- for data in bot.ask(
- text, conversation_id=session
- ):
- received_session = data["conversation_id"]
- message = data["message"][len(prev_text):]
- if print_out:
- print(message, end="", flush=True)
- buf.write(message)
- prev_text = data["message"]
- if print_out:
- print()
- return buf.getvalue(), received_session
-
-
- def extract_code_blocks(text: str) -> List[str]:
- result = []
- parser = Parser()
- for block in parser.parse(text).children:
- if type(block) is FencedCode:
- block: FencedCode
- blk: RawText = block.children[0]
- result.append(blk.children)
- return result
-
-
- if __name__ == "__main__":
- main()
|