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

Generate eBPF programs and tracing with ChatGPT and natural language