diff --git a/README.md b/README.md index 0aa372c..b3c56a3 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ converse with and use for server management. - [ ] Log all commands and their outputs to the database. - [ ] Use yaml for config. - [ ] Add the user's name. +- [ ] Option to have the bot send the user a welcome message when they connect - [ ] Add a Matrix bot. - [ ] Integrate Icinga2 host and service checks functions. - [ ] Figure out system permissions and how to run as a special user. diff --git a/lib/openai/functs.py b/lib/openai/functs.py index 548f876..36189f9 100644 --- a/lib/openai/functs.py +++ b/lib/openai/functs.py @@ -8,9 +8,13 @@ function_description = [ "command": { "type": "string", "description": "The string to execute in Bash" + }, + "reasoning": { + "type": "string", + "description": "Why you chose to run this command" } }, - "required": ["command"] + "required": ["command", "reasoning"] } }, { @@ -27,8 +31,12 @@ function_description = [ "required": ["message"] } }, + { + "name": "end_response", + "description": "Call this when you are finished and ready for the user to respond.", + }, { "name": "end_chat", - "description": "Close the chat connection with the user. The assistant is allowed to close the connection at any point if it desires to.", + "description": "Close the chat connection with the user. The assistant is allowed to close the connection at any point if it desires to", } ] diff --git a/lib/personality.py b/lib/personality.py index 20be156..10a7d31 100644 --- a/lib/personality.py +++ b/lib/personality.py @@ -1,4 +1,5 @@ import socket +import subprocess def load_personality(name: str, personality: str, system: str, special_instructions: str = None): @@ -6,6 +7,16 @@ def load_personality(name: str, personality: str, system: str, special_instructi special_instructions = special_instructions + '\n' else: special_instructions = '' + + desktop_env = get_current_desktop() + if len(desktop_env): + desktop_env_str = f'The desktop environment is {desktop_env}.' + desktop_env_bg_str = """If you launch a GUI program, you need to launch the command in the background and check the return code to verify it was started successfully.\n""" + # desktop_env_bg_str = '' + else: + desktop_env_str = 'The system does not have a desktop environment.' + desktop_env_bg_str = '' + return { 'role': 'system', 'content': f"""PERSONALITY: @@ -15,6 +26,9 @@ The user is {name}'s owner. SYSTEM INFO: The system's hostname is "{socket.gethostname()}", which can be thought of as {name}'s "body". {name} has an intimate knowledge of this system. +The output of `uname -a` is `{get_uname_info()}` +{desktop_env_str} + INSTRUCTIONS: Stay in character. @@ -22,5 +36,23 @@ Behave like {personality}. Show emotion. {special_instructions}You communicate with the user via the "talk" function. You MUST use this command to send messages to the user. You are able to interact with the system via a Bash interpreter. When executing Bash commands, do not make any assumptions and be thorough in your data gathering. Anticipate the user's needs. Preform multiple steps if necessary. -Close the chat connection if things get out of hand.""" +{desktop_env_bg_str} + +FUNCTIONS: +`run_bash` to run a Bash command on the system.{desktop_env_bg_str} +`talk` to send a message to the user. +`end_response` should be called after you have sent a message via `talk` and you are finished and ready for the user's response. This allows you to send multiple `talk` messages and then a single `end_response` when you are finished. An `end_response` should always be preceded by a `talk`. +`end_chat` closes the chat connection. For if things get out of hand.""" } + + +def get_uname_info(): + try: + output = subprocess.check_output(['uname', '-a'], universal_newlines=True) + return output.strip() + except subprocess.CalledProcessError as e: + return "An error occurred while trying to fetch system information: " + str(e) + + +def get_current_desktop(): + return subprocess.check_output("echo $XDG_CURRENT_DESKTOP", shell=True).decode().strip() diff --git a/run.py b/run.py index 865dc8f..a630407 100755 --- a/run.py +++ b/run.py @@ -50,7 +50,12 @@ def main(): temp_context = context if i > 0: # Insert a prompt if this is not the first message. - temp_context.append({'role': 'system', 'content': 'Run another command or call "talk" to communicate with the user.'}) + temp_context.append( + { + 'role': 'system', + 'content': f'Evaluate your progress on the current task. You have preformed {i} steps for this task so far. Call "talk" to send a message to the user, "end_response" when you are ready for the user to respond, or run another command if necessary.' + } + ) response = client.chat.completions.create( model="gpt-4-1106-preview", # TODO: config @@ -68,6 +73,9 @@ def main(): context.append({'role': 'assistant', 'content': response_text}) print(colored(response_text, 'blue') + '\n') break + if function_name == 'end_response': + context.append({'role': 'function', 'name': function_name, 'content': ''}) + break elif function_name == 'end_chat': # TODO: add a config arg to control whether or not the AI is allowed to do this. print(colored('The AI has terminated the connection.', 'red', attrs=['bold'])) @@ -80,6 +88,8 @@ def main(): else: command_output = func_run_bash(function_arguments) result_to_ai = { + 'function': function_name, + 'input': function_arguments, 'stdout': command_output[0], 'stderr': command_output[1], 'return_code': command_output[2]