erniecli.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. #!/usr/bin/python3
  2. import requests
  3. import json
  4. import sys
  5. import time
  6. import _thread
  7. import os
  8. import subprocess
  9. ERNIE_API_KEY = ""
  10. ERNIE_SECRET_KEY = ""
  11. OPENAI_URL = ""
  12. OPENAI_API_KEY = ""
  13. OPENAI_MODEL = ""
  14. # 后端可选“openai”或“ernie”
  15. BACKEND = ""
  16. PROMPT = """
  17. 你是一个Linux命令行专家,请将我的请求转换成一条可执行的bash命令。
  18. 只给一条命令,不要做任何解释或说明。
  19. 示例:
  20. 请求:显示系统版本信息。
  21. 输出:
  22. ```bash
  23. uname -a
  24. ```
  25. """
  26. # 设置API_KEY和SECRET_KEY
  27. def set_config():
  28. global ERNIE_API_KEY, ERNIE_SECRET_KEY, OPENAI_API_KEY, OPENAI_MODEL, OPENAI_URL, BACKEND
  29. if os.path.exists(os.path.expanduser('~/.config/erniecli/.ernierc')):
  30. get_config()
  31. elif not os.path.exists(os.path.expanduser('~/.config/erniecli')):
  32. os.makedirs(os.path.expanduser('~/.config/erniecli'))
  33. # 用黄色字体
  34. bidx = 0
  35. if BACKEND == "ernie":
  36. bidx = 1
  37. elif BACKEND == "openai":
  38. bidx = 2
  39. elif BACKEND == "":
  40. bidx = 0
  41. while True:
  42. choice_bidx = input("\033[1;33m请选择后端(0: None | 1: ernie | 2: openai)[current: {0}]:".format(bidx))
  43. if choice_bidx == '':
  44. bidx = bidx
  45. else:
  46. bidx = int(choice_bidx)
  47. if bidx == 1 or bidx == 2:
  48. break
  49. if bidx == 1:
  50. BACKEND = "ernie"
  51. elif bidx == 2:
  52. BACKEND = "openai"
  53. choice_ernie = input("\033[1;34m是否需要配置ernie?(y/N)")
  54. if choice_ernie == "y":
  55. apikey_value = input("\033[1;34m请输入API_KEY(当前值:"+ERNIE_API_KEY+"):")
  56. securekey_value = input("请输入SECRET_KEY(当前值"+ERNIE_SECRET_KEY+"):")
  57. if apikey_value != "":
  58. ERNIE_API_KEY = apikey_value.strip()
  59. if securekey_value != "":
  60. ERNIE_SECRET_KEY = securekey_value.strip()
  61. choice_openai = input("\033[1;36m是否需要配置openai?(y/N)")
  62. if choice_openai == "y":
  63. url_value = input("\033[1;36m请输入BASE URL(当前值:"+OPENAI_URL+"):")
  64. apikey_value = input("\033[1;36m请输入API_KEY(当前值:"+OPENAI_API_KEY+"):")
  65. model_value = input("请输入模型(当前值:"+OPENAI_MODEL+"):")
  66. if url_value != "":
  67. OPENAI_URL = url_value.strip()
  68. if apikey_value != "":
  69. OPENAI_API_KEY = apikey_value.strip()
  70. if model_value != "":
  71. OPENAI_MODEL = model_value.strip()
  72. with open(os.path.expanduser('~/.config/erniecli/.ernierc'), 'w', encoding='utf-8') as f:
  73. # 写入所有配置
  74. f.write("[GLOBAL]\n")
  75. f.write("BACKEND="+BACKEND+"\n")
  76. f.write("\n[ERNIE]\n")
  77. f.write("API_KEY="+ERNIE_API_KEY+"\n")
  78. f.write("SECRET_KEY="+ERNIE_SECRET_KEY+"\n")
  79. f.write("\n[OPENAI]\n")
  80. f.write("URL="+OPENAI_URL+"\n")
  81. f.write("API_KEY="+OPENAI_API_KEY+"\n")
  82. f.write("MODEL="+OPENAI_MODEL+"\n")
  83. print("\033[1;32m配置成功\033[0m")
  84. sys.exit(0)
  85. # 读取$HOME/.config/erniecli/.ernierc文件中API_KEY和SECRET_KEY
  86. def get_config():
  87. global ERNIE_API_KEY, ERNIE_SECRET_KEY, OPENAI_API_KEY, OPENAI_MODEL, OPENAI_URL, BACKEND
  88. config = os.path.expanduser('~/.config/erniecli/.ernierc')
  89. if not os.path.exists(config):
  90. print("\033[1;31m请进行使用erniecli进行配置\033[0m")
  91. sys.exit(0)
  92. # 读取配置文件,读取[global]的BACKEND
  93. group = "global"
  94. with open(config, 'r', encoding='utf-8') as f:
  95. for line in f.readlines():
  96. line = line.strip()
  97. if len(line) == 0 or line[0] == '#':
  98. continue
  99. elif line.startswith("["):
  100. group = line[1:-1]
  101. continue
  102. # 配置global
  103. if group == "GLOBAL":
  104. key, value = line.split('=')
  105. if key.strip() == "BACKEND":
  106. BACKEND = value.strip()
  107. if group == "ERNIE":
  108. key, value = line.split('=')
  109. if key.strip() == "API_KEY":
  110. ERNIE_API_KEY = value.strip()
  111. if key.strip() == "SECRET_KEY":
  112. ERNIE_SECRET_KEY = value.strip()
  113. if group == "OPENAI":
  114. key, value = line.split('=')
  115. if key.strip() == "API_KEY":
  116. OPENAI_API_KEY = value.strip()
  117. if key.strip() == "MODEL":
  118. OPENAI_MODEL = value.strip()
  119. if key.strip() == "URL":
  120. OPENAI_URL = value.strip()
  121. # 查询百度千帆
  122. def askERNIE(quest):
  123. url = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions?access_token=" + get_access_token()
  124. payload = json.dumps({
  125. "messages": [
  126. {
  127. "role": "user",
  128. "content": PROMPT+"我的问题是:{0}".format(quest)
  129. }
  130. ],
  131. "temperature": 0.95,
  132. "top_p": 0.8,
  133. "penalty_score": 1,
  134. "disable_search": False,
  135. "enable_citation": False,
  136. "response_format": "text"
  137. })
  138. headers = {
  139. 'Content-Type': 'application/json'
  140. }
  141. response = requests.request("POST", url, headers=headers, data=payload)
  142. return response.text
  143. def get_access_token():
  144. """
  145. 使用 AK,SK 生成鉴权签名(Access Token)
  146. :return: access_token,或是None(如果错误)
  147. """
  148. url = "https://aip.baidubce.com/oauth/2.0/token"
  149. params = {"grant_type": "client_credentials", "client_id": ERNIE_API_KEY, "client_secret": ERNIE_SECRET_KEY}
  150. return str(requests.post(url, params=params).json().get("access_token"))
  151. # 查询OpenAI接口,如赞同模型
  152. def askOpenAI(quest):
  153. global OPENAI_URL
  154. # 如果OPENAI_URL是/结尾,去掉最后的/
  155. if OPENAI_URL[-1] == '/':
  156. OPENAI_URL = OPENAI_URL[:-1]
  157. url = "{0}/v1/chat/completions".format(OPENAI_URL)
  158. payload = json.dumps({
  159. "model": OPENAI_MODEL,
  160. "messages": [
  161. {
  162. "role": "system",
  163. "content": PROMPT
  164. },
  165. {
  166. "role": "user",
  167. "content": quest
  168. }
  169. ],
  170. "temperature": 0.3
  171. })
  172. headers = {
  173. 'Content-Type': 'application/json',
  174. 'Authorization': 'Bearer '+OPENAI_API_KEY
  175. }
  176. response = requests.request("POST", url, headers=headers, data=payload)
  177. return response.text
  178. def parse(answer):
  179. answer = json.loads(answer)
  180. # 获取第一个结果
  181. if BACKEND=="ernie":
  182. result = answer['result']
  183. elif BACKEND=="openai":
  184. result = answer['choices'][0]['message']['content']
  185. lines = result.split('\n')
  186. # 获取lines中的```bash项编号
  187. try:
  188. index = lines.index('```bash')
  189. return lines[index+1]
  190. except ValueError:
  191. print("\033[1;31mAI没有找到可执行的命令\033[0m")
  192. sys.exit(0)
  193. def loading(lock):
  194. chars = ['⣾', '⣷', '⣯', '⣟', '⡿', '⢿', '⣻', '⣽']
  195. i = 0
  196. print('')
  197. while lock[0]:
  198. i = (i+1) % len(chars)
  199. print('\033[A%s %s' %
  200. (chars[i], lock[1] or '' if len(lock) >= 2 else ''))
  201. time.sleep(0.1)
  202. # 执行alias命令,获取输出结果保存为alias_result
  203. def alias():
  204. # 定义一个dict
  205. alias_result = {'egrep': 'egrep --color=auto'
  206. ,'grep': 'grep --color=auto'
  207. ,'fgrep': 'fgrep --color=auto'
  208. ,'grep': 'grep --color=auto'
  209. ,'l': 'ls -CF'
  210. ,'ll': 'ls -alF'
  211. ,'la': 'ls -A'
  212. ,'ls': 'ls --color=auto'}
  213. return alias_result
  214. def replaceAlias(cmd):
  215. # 获取alias_result
  216. alias_result = alias()
  217. # 获取cmd中的第一个单词
  218. cmd_first = cmd.split(' ')[0]
  219. # 如果cmd_first在alias_result中,则替换
  220. if cmd_first in alias_result:
  221. cmd = alias_result[cmd_first] + ' ' + cmd.replace(cmd_first, '')
  222. return cmd
  223. if __name__ == '__main__':
  224. # 如果没有参数
  225. if len(sys.argv) < 2:
  226. print("Copyright (c) 2024 Xiongwei Yu. Info-Terminal Copilot v1.0 \n\n \
  227. Usage: \n \
  228. erniecli command question : \"?? question\" or \"? question\" for short, quest a command and run\n \
  229. erniecli config : set your config\n \
  230. erniecli alias : show alias\n \
  231. erniecli version : show version")
  232. sys.exit(0)
  233. # 获取第一个参数
  234. cmd = sys.argv[1]
  235. if cmd == "config":
  236. set_config()
  237. # 设置??别名
  238. if cmd == "alias":
  239. print ("alias erniecli='erniecli.py'")
  240. print ("alias ??='erniecli.py command'")
  241. print ("alias ??='erniecli.py command'")
  242. print ("alias ?='erniecli.py command'")
  243. print ("alias ?='erniecli.py command'")
  244. # 显示版本信息
  245. if cmd == "version":
  246. # 紫色显示
  247. print("\033[1;95m终端助理 Version 0.1\n\033[0m")
  248. # 用绿色字体显示“基于文心一言的对话式命令行助理”
  249. print("\033[1;32m基于大语言模型的对话式终端助理\n可使用百度千帆文心大模型ERNIE-3.5-8K或其他OpenAI接口的大语言模型\n让命令行插上AI的翅膀🪽\033[0m")
  250. sys.exit(0)
  251. # 如果cmd为command,调用ask函数
  252. if cmd == "command":
  253. get_config()
  254. # 获取第二个参数
  255. # 如果第二个参数为空,则输出错误,用红色字体显示
  256. if len(sys.argv) < 3:
  257. print("\033[1;31m请输入你的意图\033[0m")
  258. sys.exit(0)
  259. # 获取后面的所有参数,并拼接成字符串
  260. question = ' '.join(sys.argv[2:])
  261. # question = sys.argv[2]
  262. # 如果question为空,则输出错误,用红色字体显示
  263. if question == "":
  264. print("\033[1;31m请输入你的意图\033[0m")
  265. sys.exit(0)
  266. # 调用ask函数,并输出结果
  267. # 显示转圈
  268. lock = [True, '']
  269. try:
  270. _thread.start_new_thread(loading, (lock,))
  271. except Exception as e:
  272. print(e)
  273. #使用绿色字体
  274. lock[1] ="\033[1;32m终端助理正在思考...\033[0m"
  275. # 百度千帆
  276. # answer = askERNIE(question)
  277. # cmd = parseERNIE(answer)
  278. if BACKEND=="ernie":
  279. query = askERNIE
  280. elif BACKEND=="openai":
  281. query = askOpenAI
  282. # OpenAI API
  283. answer = query(question)
  284. cmd = parse(answer)
  285. lock[0] = False
  286. print('\033[F\033[K',end = "\033[1;32m❯ \033[0m")
  287. print('\033[1;32m{0}\033[0m'.format(cmd))
  288. while True:
  289. choice = input('\033[1;32m? \033[0m\033[1;90m是否执行 ⬆ ? [Y/n]\033[0m')
  290. if choice == 'Y' or choice == 'y' or choice == '':
  291. sys.stdout.write('\033[A\r')
  292. sys.stdout.flush()
  293. sys.stdout.write('\033[K')
  294. sys.stdout.flush()
  295. # 执行命令,并输出结果
  296. # print('')
  297. cmd = replaceAlias(cmd)
  298. subprocess.run(cmd, shell=True)
  299. # print('')
  300. # os.system(cmd)
  301. break
  302. elif choice == 'N' or choice == 'n':
  303. print('已取消')
  304. sys.exit(0)
  305. else:
  306. print("请输入正确的选项")