commit 46e626b2884551026d50fc553aa3a76e57e04726 Author: Hayden Johnson Date: Sun Aug 11 21:42:37 2024 -0700 Intial commit. Switch from OpenAI to Ollama module diff --git a/assistant.py b/assistant.py new file mode 100755 index 0000000..cec98c4 --- /dev/null +++ b/assistant.py @@ -0,0 +1,140 @@ +#!/bin/python3 +# Chat with an intelligent assistant in your terminal +from ollama import Client +import re +import pyperclip +import sys +import argparse + +model = 'mistral-nemo' + +pattern = r'```[a-z]*\n[\s\S]*?\n```' +line_pattern = r'`[a-z]*[\s\S]*?`' + +def extract_code_block(markdown_text): + # Use the regular expression pattern to find all matches in the markdown text + matches = re.finditer(pattern, markdown_text) + + # Iterate over the matches and extract the code blocks + code_blocks = [] + for match in matches: + code_block = match.group(0) + + # Add the code block to the list of code blocks + code_blocks.append('\n'.join(code_block.split('\n')[1:-1])) + + if len(code_blocks) == 0: + line_matches = re.finditer(line_pattern, markdown_text) + for match in line_matches: + code_block = match.group(0) + code_blocks.append(code_block[1:-1]) + return code_blocks + +def copy_string_to_clipboard(string): + try: + pyperclip.copy(string) + except: + return + +# Point to the local server +client = Client(host='hayden-desktop:11434') + +code_history = [] + +history = [ + {"role": "system", "content": "You are a helpful, smart, kind, and efficient AI assistant. You always fulfill the user's requests accurately and concisely."}, + ] + +def chat(message, stream=True): + history.append({"role": "user", "content": message}) + completion = client.chat( + model=model, + messages=history, + stream=stream + ) + result = '' + for chunk in completion: + if stream: + print(chunk['message']['content'], end='', flush=True) + result += chunk['message']['content'] + if stream: + print() + history.append({"role": 'assistant', 'content': result}) + return result + +def parse_args(): + # Create the parser + parser = argparse.ArgumentParser(description='Copy and open a source file in TextEdit') + # Add the --follow-up (-f) argument + parser.add_argument('--follow-up', '-f', nargs='?', const=True, default=False, help='Ask a follow up question when piping in context') + # Add the --copy (-c) argument + parser.add_argument('--copy', '-c', action='store_true', help='copy a codeblock if it appears') + # Add the --shell (-s) argument + parser.add_argument('--shell', '-s', nargs='?', const=True, default=False, help='output a shell command that does as described') + # Add the --model (-m) argument + parser.add_argument('--model', '-m', nargs='?', const=True, default=False, help='Specify model') + # Parse the arguments + return parser.parse_args() + +def arg_follow_up(args): + sys.stdin = open('/dev/tty') + if args.follow_up != True: + second_input = args.follow_up + else: + second_input = input('> ') + return second_input + +def arg_shell(args): + query = 'Form a shell command based on the following description. Only output a working shell command and then <|eot_id|>.\nDescription: ' + if args.shell != True: + query += args.shell + else: + query += input('> ') + result = chat(query, False) + result = blocks[0] if len(blocks := extract_code_block(result)) else result + print(result) + copy_string_to_clipboard(result) + +def handle_piped_input(args): + all_input = sys.stdin.read() + query = 'Use the following context to answer the question. There will be no follow up questions from the user so make sure your answer is complete:\nSTART CONTEXT\n' + all_input + '\nEND CONTEXT\n' + if args.copy: + query += 'Answer the question using a codeblock for any code or shell scripts\n' + if args.follow_up: + query += arg_follow_up(args) + query += '\n' + + result = chat(query) + blocks = extract_code_block(result) + if args.copy and len(blocks): + copy_string_to_clipboard(blocks[0]) + +def handle_non_piped_input(args): + if args.shell: + arg_shell(args) + exit() + if args.follow_up: + user_input = arg_follow_up(args) + chat(user_input) + exit() + while True: + try: + user_input = input('> ') + except (EOFError, KeyboardInterrupt): + print() + exit() + else: + chat(user_input) + +def main(): + args = parse_args() + if args.model: + global model + model = args.model + if not sys.stdin.isatty(): + handle_piped_input(args) + else: + handle_non_piped_input(args) + +if __name__ == '__main__': + main()