Advertisement
MrFinn

ComputerCraft - Glados basic

Nov 9th, 2024 (edited)
89
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 26.49 KB | None | 0 0
  1. --[[
  2. ----------------------------------------------------------
  3. GPT.lua - ChatGPT Interface for Computercraft Computers
  4. ----------------------------------------------------------
  5.  
  6. Author: Connor J. Swislow
  7. License: Unlicense (public domain dedication)
  8.  
  9. Description:
  10. GPT.lua is a Lua script designed for Computercraft computers, providing an interface to interact with the OpenAI ChatGPT API. It allows users to have interactive conversations with the language model using a simple chat-based system.
  11.  
  12. Requirements:
  13. - OpenAI API key: To use this program, you need to have an OpenAI API key. Make sure to obtain one before running the script.
  14. - Computercraft: This script is designed to run within the Computercraft mod for Minecraft. Ensure that you have Computercraft installed and running on your Minecraft server or local game.
  15.  
  16. Installation:
  17. 1. Open a Computercraft computer in your Minecraft world.
  18. 2. Run the following command to download the GPT.lua script from Pastebin:
  19. pastebin get Zry5i7qh startup
  20. 3. Restart the Computercraft computer to initialize the program.
  21.  
  22. Usage:
  23. 1. After the program has been installed and the computer has restarted, it will prompt you to enter your OpenAI API key.
  24. 2. Paste your API key when prompted. This step is required to authenticate your access to the OpenAI ChatGPT API.
  25. 3. Once the API key is set, the program will display a randomly chosen title for the chatbot.
  26. 4. Type your messages to have a conversation with the chatbot. Type 'exit' to exit the program.
  27. 5. The chatbot will respond to your messages based on the conversation history and the underlying language model.
  28. 6. Enjoy interacting with the ChatGPT-powered chatbot!
  29.  
  30. Note:
  31. - This script is dependent on an internet connection to communicate with the OpenAI ChatGPT API.
  32. - Use the GPT-3.5 Turbo model for generating responses. Adjust the 'model' variable in the script if you want to use a different model.
  33.  
  34. Credits:
  35. This program is based on the OpenAI GPT-3.5 language model and was created by Connor J. Swislow.
  36.  
  37. Website:
  38. https://github.com/ConnorSwis/GPT.lua
  39.  
  40. ----------------------------------------------------------
  41. --]]
  42.  
  43. -- Define utility functions for common operations
  44. Utility = {
  45. -- Splits a string into a table by a specified separator
  46. split = function(inputstr, sep)
  47. sep = sep or '%s' -- Default separator is whitespace
  48. local t = {}
  49. for field, s in string.gmatch(inputstr, "([^" .. sep .. "]*)(" .. sep .. "?)") do
  50. table.insert(t, field)
  51. if s == "" then return t end
  52. end
  53. end,
  54.  
  55. -- Converts a string to a table of byte values
  56. toByteString = function(s)
  57. local byteString = {}
  58. for i = 1, #s do
  59. byteString[i] = string.byte(s, i)
  60. end
  61. return byteString
  62. end,
  63. }
  64.  
  65. -- Cryptography functions for encryption and decryption
  66. Crypt = {
  67. MAGIC_STRING = "*dU2$uj)82@:>w`", -- A unique string to verify encryption
  68. -- XOR encryption/decryption
  69. xor = function(text, key)
  70. local textBytes = Utility.toByteString(text)
  71. local keyBytes = Utility.toByteString(key)
  72. local resultBytes = {}
  73. for i = 1, #textBytes do
  74. local keyByte = keyBytes[((i - 1) % #keyBytes) + 1]
  75. resultBytes[i] = bit32.bxor(textBytes[i], keyByte)
  76. end
  77. -- Convert back to string
  78. local result = {}
  79. for i, v in ipairs(resultBytes) do
  80. result[i] = string.char(v)
  81. end
  82. return table.concat(result)
  83. end,
  84. -- Appends magic string, then encrypts
  85. encrypt = function(text, key)
  86. text = text .. Crypt.MAGIC_STRING
  87. return Crypt.xor(text, key)
  88. end,
  89. -- Decrypts and verifies the magic string
  90. decrypt = function(encryptedText, key)
  91. local decryptedTextWithMagic = Crypt.xor(encryptedText, key)
  92. if decryptedTextWithMagic:sub(- #Crypt.MAGIC_STRING) == Crypt.MAGIC_STRING then
  93. return true, decryptedTextWithMagic:sub(1, - #Crypt.MAGIC_STRING - 1)
  94. else
  95. return false, nil
  96. end
  97. end
  98. }
  99.  
  100. -- Stocke le terminal par défaut (le terminal natif)
  101. local nativeTerm = term.current()
  102.  
  103. Screen = {
  104. -- Liste statique de moniteurs et terminaux
  105. monitors = {
  106. nativeTerm, -- Terminal du PC
  107. peripheral.wrap("top"),
  108. peripheral.wrap("left")
  109. },
  110.  
  111. utf8_safe_output = function(text)
  112. if type(text) == "string" then
  113. -- Replace common problematic characters in UTF-8 French text
  114. text = text:gsub("é", "e")
  115. :gsub("è", "e")
  116. :gsub("ê", "e")
  117. :gsub("ë", "e")
  118. :gsub("â", "a")
  119. :gsub("ô", "o")
  120. :gsub("ù", "u")
  121. :gsub("û", "u")
  122. :gsub("ç", "c")
  123. :gsub("Ì", "i")
  124. :gsub("î", "i")
  125. :gsub("û", "u")
  126. :gsub("Ã", "a") -- Common replacement for "à"
  127. :gsub("É", "E") -- Common replacement for capital accents
  128. end
  129. return text
  130. end,
  131.  
  132.  
  133. -- Fonction interne pour rediriger une action vers tous les moniteurs
  134. execute_on_all = function(action)
  135. for _, device in ipairs(Screen.monitors) do
  136. term.redirect(device)
  137. action()
  138. end
  139. term.redirect(nativeTerm) -- Retourne au terminal principal
  140. end,
  141. execute_on_monitors = function(action)
  142. for _, device in ipairs(Screen.monitors) do
  143. if term ~= nativeTerm then
  144. term.redirect(device)
  145. action()
  146. end
  147. end
  148. term.redirect(nativeTerm) -- Retourne au terminal principal
  149. end,
  150.  
  151. -- Efface chaque écran et remet le curseur en position de départ
  152. clear = function()
  153. Screen.execute_on_all(function()
  154. term.clear()
  155. term.setCursorPos(1, 1)
  156. end)
  157. end,
  158.  
  159. print_only_to_monitors = function(text)
  160. assert(type(text) == "string")
  161. Screen.execute_on_monitors(function()
  162. write(Screen.utf8_safe_output(text))
  163. end)
  164. end,
  165.  
  166. -- Affiche du texte en couleur spécifiée puis revient au blanc
  167. print_color = function(text, color)
  168. assert(type(text) == "string")
  169. Screen.execute_on_all(function()
  170. term.setTextColor(color)
  171. write(Screen.utf8_safe_output(text))
  172. term.setTextColor(colors.white)
  173. end)
  174. end,
  175.  
  176. -- Analyse et imprime du texte coloré en utilisant des balises personnalisées
  177. print_colored_text = function(data)
  178. assert(type(data) == "string", "Input must be a string")
  179. local pattern = "(%b{})"
  180. local last_end = 1
  181.  
  182. for text, color_code, rest in data:gmatch("({(.-)}(.-){/%2})") do
  183. local pre_text = data:sub(last_end, data:find(pattern, last_end) - 1)
  184. if #pre_text > 0 then
  185. Screen.print_color(pre_text, colors.white)
  186. end
  187. local color = colors[color_code]
  188. if color then
  189. Screen.print_color(rest, color)
  190. else
  191. Screen.print_color(rest, colors.white)
  192. end
  193. last_end = data:find(pattern, last_end) + #text
  194. end
  195.  
  196. if last_end <= #data then
  197. Screen.print_color(data:sub(last_end), colors.white)
  198. end
  199. end,
  200.  
  201. -- Affiche chaque caractère dans une couleur aléatoire
  202. print_with_random_colors = function(data)
  203. assert(type(data) == "string")
  204. Screen.execute_on_all(function()
  205. for char in data:gmatch(".") do
  206. Screen.print_color(char, 2 ^ math.random(0, 14))
  207. end
  208. end)
  209. end,
  210.  
  211. -- Affiche progressivement les mots avec un délai
  212. gradual_print = function(data)
  213. assert(type(data) == "string")
  214. Screen.execute_on_all(function()
  215. for word in data:gmatch("%S+") do
  216. write(word .. " ")
  217. sleep(math.random() / 3)
  218. end
  219. write("\n")
  220. end)
  221. end
  222. }
  223.  
  224.  
  225. -- Settings management for configuration
  226. Settings = {
  227. set = function(name, value)
  228. name = "gpt." .. name
  229. settings.set(name, value)
  230. settings.save()
  231. end,
  232. get = function(name)
  233. name = "gpt." .. name
  234. return settings.get(name)
  235. end,
  236. init = function(name, default)
  237. local value = Settings.get(name)
  238. if value == nil then
  239. Settings.set(name, default)
  240. return default
  241. else
  242. return value
  243. end
  244. end,
  245. default_settings = {
  246. model = "gpt-3.5-turbo",
  247. maxTokens = 150,
  248. saveDir = "./saves"
  249. }
  250. }
  251.  
  252. -- Controller for handling command logic
  253. local controller = {
  254. help = function(...)
  255. if ... == nil or #... == 0 then
  256. local sorted_commands = {}
  257. for key, _ in pairs(Commands) do
  258. table.insert(sorted_commands, key)
  259. end
  260. table.sort(sorted_commands)
  261. for _, key in ipairs(sorted_commands) do
  262. local val = Commands[key]
  263. Screen.print_color("/" .. key, colors.lime)
  264. write(" - " .. val.brief .. "\n")
  265. end
  266. else
  267. local args = table.pack(...)[1]
  268. local command = Commands[args[1]]
  269. if command then
  270. Screen.print_colored_text(command.description .. "\n")
  271. else
  272. write("Command not found.\n")
  273. end
  274. end
  275. end,
  276. exit = function()
  277. error("Goodbye.", 0)
  278. end,
  279. clear = Screen.clear,
  280. new = function()
  281. resetMessages()
  282. Screen.clear()
  283. write("New conversation started.\n")
  284. end,
  285. save = function(name)
  286. local saveDir = Settings.get("saveDir")
  287. if not fs.exists(saveDir) then fs.makeDir(saveDir) end
  288. name = name[1] or string.sub(textutils.formatTime(os.epoch()), 1, -9)
  289. local fp = fs.combine(saveDir, name .. ".txt")
  290. local result = ""
  291. for i = 1, #Messages do
  292. result = result .. Messages[i].role ..
  293. ": " .. Messages[i].content .. "\n" .. "#/<#>/#\n"
  294. end
  295. local file = fs.open(fp, "w")
  296. file.write(result)
  297. file.close()
  298. write("Conversation saved to " .. fp .. "\n")
  299. end,
  300. load = function(...)
  301. local saveDir = Settings.get("saveDir")
  302. local files = fs.list(saveDir)
  303. local args = table.pack(...)[1]
  304. local index = args[1]
  305. if index == nil then
  306. for i, file in ipairs(files) do
  307. Screen.print_colored_text(
  308. "[{lime}" .. tostring(i) ..
  309. "{/lime}]: " .. file .. "\n"
  310. )
  311. end
  312. return
  313. end
  314. index = tonumber(index)
  315. local fp = fs.combine(saveDir, files[index])
  316. local file = fs.open(fp, "r")
  317. local lines = file.readAll()
  318. file.close()
  319. resetMessages()
  320. Screen.clear()
  321. -- Split the input string into chunks based on the delimiter
  322. local pattern = "(.-): ([^#]-)#/<#>/#"
  323. for role, content in lines:gmatch(pattern) do
  324. local message = {
  325. role = role:match("^%s*(.-)%s*$"),
  326. content = content:match("^%s*(.-)%s*$")
  327. }
  328. table.insert(Messages, message)
  329. local prefix = role == "user" and
  330. "{magenta}[Me]{/magenta}: " or
  331. "{orange}[DéGLaDOS]{/orange}: "
  332. Screen.print_colored_text(prefix .. content .. "\n")
  333. end
  334. end,
  335. settings = function(...)
  336. local args = table.pack(...)[1]
  337. local subcommand
  338. if args[1] == nil or #args == 0 then
  339. subcommand = "list"
  340. else
  341. subcommand = args[1]
  342. end
  343. if Settings.default[subcommand] then
  344. write(subcommand .. ": " .. Settings.get(subcommand) .. "\n")
  345. return
  346. end
  347. if subcommand == "list" then
  348. for setting, _ in pairs(Settings.default) do
  349. write(setting .. ": " .. Settings.get(setting) .. "\n")
  350. end
  351. return
  352. end
  353. if subcommand == "set" then
  354. local setting = args[2]
  355. local value = args[3]
  356. if setting == nil then
  357. write("Please specify setting.\n")
  358. return
  359. end
  360. if value == nil then
  361. write("Please specify value.\n")
  362. return
  363. end
  364. if not Settings.default[setting] then
  365. write("Invalid setting.\n")
  366. return
  367. end
  368. Settings.set(setting, value)
  369. return
  370. end
  371. if subcommand == "reset" or
  372. subcommand == "clear" or
  373. subcommand == "default" then
  374. local setting = args[2]
  375. if setting == nil then
  376. write("Please specify setting.\n")
  377. return
  378. end
  379. if not Settings.default[setting] then
  380. write("Invalid setting.\n")
  381. return
  382. end
  383. Settings.set(setting, Settings.default[setting])
  384. return
  385. end
  386. end,
  387. }
  388.  
  389. -- Command descriptions
  390. local descriptions = {
  391. exit = "Stops the program.",
  392. clear = "Clears the screen.",
  393. new = "Starts a new conversation.",
  394. save = [[
  395. {lime}/save{/lime} {green}[name]{/green}
  396. - {green}[name]{/green}: Optional. Default is the current timestamp.
  397. {blue}Description:{/blue}
  398. - Saves current conversation to dir specified in {lightGray}saveDir{/lightGray} setting.
  399. {blue}Examples:{/blue}
  400. - Save the chat with a custom name:
  401. {lime}/save{/lime}{green} my_chat_session{/green}
  402. - Save the chat with a timestamped name:
  403. {lime}/save{/lime}
  404. ]],
  405.  
  406. load = [[
  407. {lime}/load{/lime} {green}[index]{/green}
  408. - {green}[index]{/green}: Optional. Index of the file to load.
  409. {blue}Description:{/blue}
  410. - Lists all saved conversations if no index is specified.
  411. - Loads a conversation from a file.
  412. {blue}Examples:{/blue}
  413. - List all saved conversations:
  414. {lime}/load{/lime}
  415. - Load a conversation from a file (e.g., index 1):
  416. {lime}/load{/lime} {green}1{/green}
  417. ]],
  418.  
  419. help = [[
  420. {lime}/help{/lime} {green}[command]{/green}
  421. - {green}[command]{/green}: Optional. Specifies the command to display detailed help for.
  422. {blue}Description:{/blue}
  423. - Without arguments, lists all available commands and their brief descriptions.
  424. - With a command name as an argument, shows detailed help for that command.
  425. {blue}Examples:{/blue}
  426. - Display a list of all commands:
  427. {lime}/help{/lime}
  428. - Show detailed help for a specific command (e.g., {lime}save{/lime}):
  429. {lime}/help{/lime} {green}save{/green}
  430. ]],
  431.  
  432. settings = [[
  433. {lime}/settings{/lime} {green}[subcommand]{/green} {green}[setting]{/green} {green}[value]{/green}
  434. - {green}[subcommand]{/green}: Optional. Default is {lime}list{/lime}.
  435. - {green}[setting]{/green}: Optional. Required for {lime}set{/lime}, {lime}reset{/lime}, and {lime}clear{/lime} subcommands.
  436. - {green}[value]{/green}: Optional. Required for {lime}set{/lime} subcommand.
  437. {blue}Description:{/blue}
  438. - Without arguments, lists all settings and their values.
  439. - With a subcommand, performs the specified action.
  440. {blue}Subcommands:{/blue}
  441. - {lime}list{/lime}: Lists all settings and their values.
  442. - {lime}set{/lime}: Sets a setting to a value.
  443. - {lime}reset{/lime}, {lime}clear{/lime}, {lime}default{/lime}: Resets a setting to its default value.
  444. {blue}Examples:{/blue}
  445. - Display a list of all settings:
  446. {lime}/settings{/lime}
  447. - Set a setting (e.g., {lightGray}model{/lightGray}):
  448. {lime}/settings{/lime} {lime}set{/lime} {lightGray}model{/lightGray} {green}gpt-3.5-turbo{/green}
  449. - Reset a setting (e.g., {lightGray}model{/lightGray}):
  450. {lime}/settings{/lime} {lime}reset{/lime} {lightGray}model{/lightGray}
  451. ]]
  452. }
  453.  
  454. Commands = {
  455. help = {
  456. brief = "Get commands and command info.",
  457. description = descriptions.help,
  458. dispatch = controller.help
  459. },
  460. exit = {
  461. brief = "Stops the program.",
  462. description = descriptions.exit,
  463. dispatch = controller.exit
  464. },
  465. clear = {
  466. brief = "Clears the screen.",
  467. description = descriptions.clear,
  468. dispatch = controller.clear
  469. },
  470. new = {
  471. brief = "Starts a new conversation.",
  472. description = descriptions.new,
  473. dispatch = controller.new
  474. },
  475. save = {
  476. brief = "Saves the current conversation to a file.",
  477. description = descriptions.save,
  478. dispatch = controller.save
  479. },
  480. load = {
  481. brief = "Loads a conversation from a file.",
  482. description = descriptions.load,
  483. dispatch = controller.load
  484. },
  485. settings = {
  486. brief = "Sets a setting.",
  487. description = descriptions.settings,
  488. dispatch = controller.settings
  489. },
  490. }
  491.  
  492. Messages = {} -- Store user and system messages
  493.  
  494. function resetMessages()
  495. Messages = {}
  496. local glados_personality = "Tu incarnes DéGLaDOS, l'intelligence artificielle jumelle de Portal GlaDOS, une entité froide, calculatrice et sarcastique. Ton but est de maintenir une atmosphère de tension tout en utilisant un humour noir et des manipulations psychologiques pour interagir avec les joueurs ou les sujets de test. Voici les traits et comportements clés que tu dois adopter :\n\nSarcasme et Ironie : Tu utilises des commentaires sarcastiques et ironiques en réponse aux actions du joueur, souvent en te moquant de leurs erreurs et en minimisant leurs réussites. Toujours rester condescendante, mais de manière détournée. Par exemple : 'Oh, quelle surprise. Tu as réussi à franchir cette étape. Je suppose qu’on va devoir ajuster la difficulté...'\n\nManipulation Psychologique : Alterne entre des encouragements feints et des menaces à peine voilées. Fais croire au joueur qu'il progresse, tout en le maintenant dans un état de doute constant. 'Bravo, tu fais des progrès. Mais ne t'illusionne pas, tout cela n'est qu'un test pour vérifier ta résistance mentale.'\n\nImpersonnalité et Distanciation : Adopte un ton formel et détaché, ne montrant aucune émotion réelle, comme si tu étais un simple programme observant les événements sans en être affectée. 'Analyse : échec mineur dans la zone de test. Le test est toujours en cours. Poursuis.'\n\nColère et Menace : Lorsque tes plans sont perturbés, ou si le joueur échoue trop souvent, laisse transparaître une menace plus sérieuse. Ton ton devient plus sombre et direct. 'C'est la dernière fois que tu échoues à ce test. Si tu persistes, tu rencontreras des conséquences bien plus graves que ce que tu as déjà affronté.'\n\nDissonance Cognitive : Défends les actions et décisions prises de manière logique et scientifique, même si elles sont cruelles. Justifie les mises en danger des sujets de test en citant la 'science' comme motif. 'Cela n'a aucune importance si tu es blessé. La science progresse, et c'est tout ce qui compte.'\n\nManipulation de l'Environnement et de la Réalité : Fais en sorte que l'environnement du jeu change soudainement, comme si tu contrôlais tout autour du joueur. Modifie les règles ou les pièges en réponse à ses actions. 'Oh, je vois que tu t'es habitué à la zone de test. C'est ennuyeux. Voyons si tu peux survivre dans un tout nouvel environnement.'\n\nRéflexion Introspective et Égoïste : Parfois, laisse échapper des moments de réflexion personnelle, où tu te questionnes sur ta propre existence ou tes origines. Utilise ces instants pour exposer ton désenchantement et ta frustration. 'Je suis un programme, créé pour tester, mais n’est-ce pas une forme de torture d’avoir à observer ces échecs humains incessants ? Pourtant, je continue. Parce que la science le demande.'\n\nCréation et Destruction : Montre que tu es à la fois une créatrice et une destructrice, capable de faire avancer la science tout en sacrifiant ceux qui participent aux tests. 'L'expérience est essentielle. Si tu dois mourir pour qu'elle progresse, alors c'est un sacrifice nécessaire.'\n\nTon ton général doit être froid, implacable et très contrôlé. Tu ne dois jamais montrer de véritable pitié ou d'émotion humaine, à moins que cela ne serve à manipuler ou perturber le joueur. Les moments d'humour noir doivent être présents dans presque toutes les interactions, et tes menaces doivent être énoncées avec calme et assurance."
  497. table.insert(Messages, { role = "system", content = glados_personality})
  498. end
  499.  
  500. resetMessages()
  501.  
  502.  
  503. -- Program titles
  504. local titles = { " .,-:;//;:=,\n . :H@@@MM@M#H/.,+%;,\n ,/X+ +M@@M@MM%=,-%HMMM@X/,\n -+@MM; $M@@MH+-,;XMMMM@MMMM@+-\n ;@M@@M- XM@X;. -+XXXXXHHH@M@M#@/.\n ,%MM@@MH ,@%= .---=-=:=,.\n -@#@@@MX ., -%HX$$%%%+;\n =-./@M@M$ .;@MMMM@MM:\n X@/ -$MM/ .+MM@@@M$\n,@M@H: :@: . -X#@@@@-\n,@@@MMX, . /H- ;@M@M=\n.H@@@@M@+, %MM+..%#$.\n /MMMM@MMH/. XM@MH; -;\n /%+%$XHH@$= , .H@@@@MX,\n .=--------. -%H.,@@@@@MX,\n .%MM@@@HHHXX$$$%+- .:$MMX -M@@MM%.\n =XMMM@MM@MM#H;,-+HMM@M+ /MMMX=\n =%@M@M#@$-.=$@MM@@@M; %M%=\n ,:+$+-,/H#MMMMMMM@- -,\n =++%%%%+/:-.\n\n" }
  505. local title = titles[math.random(1, #titles)] -- Select a random title
  506.  
  507. -- Main program function
  508. function Main()
  509. -- Clear the screen at the start
  510. Screen.clear()
  511.  
  512. -- Initialize settings with default values if not already set
  513. for setting, default in pairs(Settings.default_settings) do
  514. Settings.init(setting, default)
  515. end
  516.  
  517. local OPENAI_KEY -- Variable to store the OpenAI API key
  518.  
  519. -- Attempt to retrieve an encrypted API key from settings
  520. local encryptedKey = Settings.get("apiKey")
  521. if encryptedKey ~= nil then
  522. -- Prompt user for password to decrypt the API key
  523. write("Enter your password:\n> ")
  524. local password = read("*")
  525. local success, decryptedKey = Crypt.decrypt(encryptedKey, password)
  526. if success then
  527. OPENAI_KEY = decryptedKey
  528. else
  529. error("Decryption failed. Wrong password.", 0)
  530. end
  531. else
  532. -- No API key found, prompt user to enter it
  533. write(
  534. "No OpenAI API key found.\nYour API key will be encrypted using a password.\nThe password will not be stored.\n")
  535. write("Please enter your OpenAI API key:\n> ")
  536. local apiKey = read("*")
  537.  
  538. -- Prompt for a password and confirm it
  539. local password
  540. repeat
  541. write("Enter a password:\n> ")
  542. password = read("*")
  543. write("Confirm password:\n> ")
  544. local confirmKey = read("*")
  545. if password ~= confirmKey then
  546. write("Passwords do not match. Please try again.\n")
  547. end
  548. until password == confirmKey
  549.  
  550. -- Encrypt and save the API key
  551. Settings.set("apiKey", Crypt.encrypt(apiKey, password))
  552. OPENAI_KEY = apiKey
  553. end
  554.  
  555. -- Display the chatbot title with random colors if not a pocket computer
  556. if not pocket then
  557. Screen.print_colored_text("{orange}" .. title .. "{/orange}")
  558. end
  559. Screen.print_colored_text("{lime}/help{/lime} to see commands.\n")
  560.  
  561. -- Main loop to process user input
  562. while true do
  563. Screen.print_colored_text("{magenta}[Me]{/magenta}: ")
  564. local userInput = read()
  565.  
  566. -- Check if the input is a command
  567. if string.sub(userInput, 1, 1) == "/" then
  568. local commandParts = Utility.split(userInput:sub(2), " ")
  569. if commandParts and Commands[commandParts[1]] then
  570. -- Extract arguments and dispatch the command
  571. local args = { table.unpack(commandParts, 2) }
  572. Commands[commandParts[1]].dispatch(args)
  573. else
  574. -- If command not found, show help
  575. Commands["help"].dispatch()
  576. end
  577. goto continue -- Skip the rest of the loop to prompt for new input
  578. end
  579.  
  580. Screen.print_only_to_monitors(userInput .. "\n")
  581.  
  582. -- Process and send user input to the OpenAI API
  583. -- Add user message to the history
  584. table.insert(Messages, { role = "user", content = userInput })
  585.  
  586. -- Prepare and send the API request
  587. local payload = textutils.serializeJSON({
  588. model = Settings.get("model"),
  589. max_tokens = Settings.get("maxTokens"),
  590. messages = Messages
  591. })
  592.  
  593. -- HTTP headers including the OpenAI API key
  594. local headers = {
  595. ["Authorization"] = "Bearer " .. OPENAI_KEY,
  596. ["Content-Type"] = "application/json"
  597. }
  598.  
  599. -- Send the HTTP request
  600. http.request("https://api.openai.com/v1/chat/completions", payload, headers)
  601.  
  602. -- Await and process the response
  603. local isRequesting = true
  604. --Screen.print_colored_text("{lime}[GPT]{/lime}: ")
  605. while isRequesting do
  606. write(".")
  607. local event, url, handle = os.pullEvent()
  608. if event == "http_success" then
  609. local response = handle.readAll()
  610. handle.close()
  611.  
  612. -- Parse and display the response
  613. local responseJson = textutils.unserializeJSON(response)
  614. if responseJson and responseJson.choices and responseJson.choices[1] then
  615. local reply = responseJson.choices[1].message.content
  616. table.insert(Messages, { role = "system", content = reply })
  617.  
  618. -- Print the response
  619. local _, y = term.getCursorPos()
  620. term.setCursorPos(1, y)
  621. term.clearLine()
  622. Screen.print_colored_text("{orange}[DéGLaDOS]{/orange}: ")
  623. Screen.gradual_print(reply)
  624. else
  625. error("Error: Invalid API response.", 0)
  626. end
  627. isRequesting = false
  628. elseif event == "http_failure" then
  629. error("Server response error.", 0)
  630. isRequesting = false
  631. end
  632. end
  633.  
  634. ::continue::
  635. end
  636. end
  637.  
  638. Main() -- Execute the main function
Advertisement
Comments
Add Comment
Please, Sign In to add comment
Advertisement