2024-08-12 04:42:37 +00:00
#!/bin/python3
# Chat with an intelligent assistant in your terminal
from ollama import Client
import re
import pyperclip
import sys
import argparse
2024-09-03 09:38:36 +00:00
model = ' llama3.1:8b-instruct-q8_0 '
temp = 0.2
2024-08-12 04:42:37 +00:00
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
2024-09-25 17:26:05 +00:00
client = Client ( host = ' localhost:11434 ' )
2024-08-12 04:42:37 +00:00
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 ,
2024-09-03 09:38:36 +00:00
options = { " temperature " : temp } ,
2024-08-12 04:42:37 +00:00
messages = history ,
stream = stream
)
result = ' '
for chunk in completion :
if stream :
print ( chunk [ ' message ' ] [ ' content ' ] , end = ' ' , flush = True )
2024-09-03 09:38:36 +00:00
result + = chunk [ ' message ' ] [ ' content ' ]
if not stream :
result = completion [ ' message ' ] [ ' content ' ]
2024-08-12 04:42:37 +00:00
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 ' )
2024-09-03 09:38:36 +00:00
# Add the --temp (-t) argument
parser . add_argument ( ' --temp ' , ' -t ' , nargs = ' ? ' , const = True , default = False , help = ' Specify temperature ' )
2024-08-12 04:42:37 +00:00
# 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 ) :
2024-09-03 09:38:36 +00:00
query = ' Form a shell command based on the following description. Only output a working shell command . \n Description: '
2024-08-12 04:42:37 +00:00
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 ( )
2024-09-25 17:26:05 +00:00
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: \n START CONTEXT \n ' + all_input + ' \n END CONTEXT \n After you answer the question, reflect on your answer and determine if it answers the question correctly. '
2024-08-12 04:42:37 +00:00
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
2024-09-03 09:38:36 +00:00
if args . temp :
global temp
temp = float ( args . temp )
2024-08-12 04:42:37 +00:00
if not sys . stdin . isatty ( ) :
handle_piped_input ( args )
else :
handle_non_piped_input ( args )
if __name__ == ' __main__ ' :
main ( )