You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

GPTtrace.py 7.2 kB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. #! /bin/env python3
  2. import argparse
  3. import os
  4. import time
  5. from pathlib import Path
  6. from typing import List, Optional, Tuple
  7. import pygments
  8. from marko.block import FencedCode
  9. from marko.inline import RawText
  10. from marko.parser import Parser
  11. from prompt_toolkit import print_formatted_text
  12. from prompt_toolkit.formatted_text import PygmentsTokens
  13. from pygments.token import Token
  14. from pygments_markdown_lexer import MarkdownLexer
  15. from revChatGPT.V1 import Chatbot
  16. import tempfile
  17. import shutil
  18. ENV_UUID = "GPTTRACE_CONV_UUID"
  19. ENV_ACCESS_TOKEN = "GPTTRACE_ACCESS_TOKEN"
  20. PROMPTS_DIR = Path("./prompts")
  21. def pretty_print(input, lexer=MarkdownLexer, *args, **kwargs):
  22. tokens = list(pygments.lex(input, lexer=lexer()))
  23. print_formatted_text(PygmentsTokens(tokens), *args, **kwargs)
  24. # print = pretty_print
  25. def main():
  26. parser = argparse.ArgumentParser(
  27. prog="GPTtrace",
  28. description="Use ChatGPT to write eBPF programs (bpftrace, etc.)",
  29. )
  30. group = parser.add_mutually_exclusive_group()
  31. group.add_argument(
  32. "-i", "--info", help="Let ChatGPT explain what's eBPF", action="store_true"
  33. )
  34. group.add_argument(
  35. "-e",
  36. "--execute",
  37. help="Generate commands using your input with ChatGPT, and run it",
  38. action="store",
  39. metavar="TEXT",
  40. )
  41. group.add_argument(
  42. "-g", "--generate", help="Generate eBPF programs using your input with ChatGPT", action="store", metavar="TEXT")
  43. group.add_argument(
  44. "--train", help="Train ChatGPT with conversions we provided", action="store_true")
  45. parser.add_argument("-v", "--verbose",
  46. help="Show more details", action="store_true")
  47. parser.add_argument(
  48. "-u",
  49. "--uuid",
  50. help=f"Conversion UUID to use, or passed through environment variable `{ENV_UUID}`",
  51. )
  52. parser.add_argument(
  53. "-t",
  54. "--access-token",
  55. help=f"ChatGPT access token, see `https://chat.openai.com/api/auth/session` or passed through `{ENV_ACCESS_TOKEN}`",
  56. )
  57. args = parser.parse_args()
  58. access_token = args.access_token or os.environ.get(ENV_ACCESS_TOKEN, None)
  59. conv_uuid = args.uuid or os.environ.get(ENV_UUID, None)
  60. if access_token is None:
  61. print(
  62. f"Either provide your access token through `-t` or through environment variable {ENV_ACCESS_TOKEN}"
  63. )
  64. return
  65. chatbot = Chatbot(config={"access_token": access_token})
  66. if args.info:
  67. generate_result(chatbot, "Explain what's eBPF", conv_uuid, True)
  68. elif args.execute is not None:
  69. desc: str = args.execute
  70. print("Sending query to ChatGPT: " + desc)
  71. ret_val, _ = generate_result(
  72. chatbot, construct_running_prompt(desc), conv_uuid, args.verbose)
  73. # print(ret_val)
  74. parsed = make_executable_command(ret_val)
  75. # print(f"Command to run: {parsed}")
  76. print("Press Ctrl+C to stop the program....")
  77. ok = False
  78. session = None
  79. for i in range(1, 5+1):
  80. stderr_out = tempfile.mktemp()
  81. if os.system(f"sudo {parsed} 2> {stderr_out}") != 0:
  82. with open(stderr_out, "r") as f:
  83. stderr_content = f.read()
  84. print("Failed to run: ")
  85. print(stderr_content)
  86. print(
  87. f"Failed to run bpftrace with generated command: {parsed}, sending errors to ChatGPT and re-train....")
  88. resp, session = generate_result(
  89. chatbot, f"bpftrace gives me the following error on command you generated: {stderr_out}", session, args.verbose)
  90. if args.verbose:
  91. print(resp)
  92. else:
  93. ok = True
  94. shutil.rmtree(stderr_out, True)
  95. if not ok:
  96. print("Retry times exceeded..")
  97. elif args.generate is not None:
  98. desc: str = args.generate
  99. print("Sending query to ChatGPT: " + desc)
  100. ret_val, _ = generate_result(
  101. chatbot, construct_generate_prompt(desc), conv_uuid)
  102. pretty_print(ret_val)
  103. parsed = extract_code_blocks(ret_val)
  104. # print(f"Command to run: {parsed}")
  105. with open("generated.bpf.c", "w") as f:
  106. for code in parsed:
  107. f.write(code)
  108. elif args.train:
  109. prompts = os.listdir(PROMPTS_DIR)
  110. prompts.sort()
  111. # conv_uuid could be None, in which we will create a new session and use it in the next steps
  112. session = conv_uuid
  113. for file in prompts:
  114. info = f"Training ChatGPT with `{file}`"
  115. print("-"*len(info))
  116. print(info)
  117. print("-"*len(info))
  118. with open(PROMPTS_DIR/file, "r") as f:
  119. input_data = f.read()
  120. if args.verbose:
  121. print(input_data)
  122. resp, session = generate_result(
  123. chatbot, input_data, conv_uuid, args.verbose)
  124. time.sleep(2.4)
  125. print(f"Trained session: {session}")
  126. else:
  127. parser.print_help()
  128. def construct_generate_prompt(text: str) -> str:
  129. return f"""You are now a translater from human language to {os.uname()[0]} eBPF programs.
  130. Please write eBPF programs for me.
  131. No explanation required, no instruction required, don't tell me how to compile and run.
  132. What I want is a eBPF program for: {text}."""
  133. def construct_running_prompt(text: str) -> str:
  134. return f"""You are now a translater from human language to {os.uname()[0]} shell bpftrace command.
  135. No explanation required.
  136. respond with only the raw shell bpftrace command.
  137. It should be start with `bpftrace`.
  138. Your response should be able to put into a shell and run directly.
  139. Just output in one line, without any description, or any other text that cannot be run in shell.
  140. What should I type to shell to trace using bpftrace for: {text}, in one line."""
  141. def make_executable_command(command: str) -> str:
  142. if command.startswith("\n"):
  143. command = command[1:]
  144. if command.endswith("\n"):
  145. command = command[:-1]
  146. if command.startswith("`"):
  147. command = command[1:]
  148. if command.endswith("`"):
  149. command = command[:-1]
  150. command = command.strip()
  151. command = command.split("User: ")[0]
  152. return command
  153. def generate_result(bot: Chatbot, text: str, session: Optional[str] = None, print_out: bool = False) -> Tuple[str, str]:
  154. from io import StringIO
  155. prev_text = ""
  156. buf = StringIO()
  157. received_session = ""
  158. for data in bot.ask(
  159. text, conversation_id=session
  160. ):
  161. received_session = data["conversation_id"]
  162. message = data["message"][len(prev_text):]
  163. if print_out:
  164. print(message, end="", flush=True)
  165. buf.write(message)
  166. prev_text = data["message"]
  167. if print_out:
  168. print()
  169. return buf.getvalue(), received_session
  170. def extract_code_blocks(text: str) -> List[str]:
  171. result = []
  172. parser = Parser()
  173. for block in parser.parse(text).children:
  174. if type(block) is FencedCode:
  175. block: FencedCode
  176. blk: RawText = block.children[0]
  177. result.append(blk.children)
  178. return result
  179. if __name__ == "__main__":
  180. main()

Generate eBPF programs and tracing with ChatGPT and natural language