chatterbot_ollama.py (8877B)
1 from chatterbot import ChatBot 2 from chatterbot.conversation import Statement 3 from chatterbot.trainers import ChatterBotCorpusTrainer 4 import en_core_web_sm 5 6 import random 7 import serial 8 import time 9 import json 10 import simpleaudio as sa 11 import pyaudio 12 from vosk import Model as srModel 13 from vosk import KaldiRecognizer 14 from dimits import Dimits 15 import ollama 16 17 # Uncomment the following line to enable verbose logging 18 # import logging 19 # logging.basicConfig(level=logging.INFO) 20 21 # Open serial port 22 ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=1) 23 dt_model = Dimits("en_US-amy-medium") 24 25 srmodel = srModel("/home/khan/srmodels/vosk-model-small-en-us-0.15") 26 recognizer = KaldiRecognizer(srmodel, 16000) 27 28 # Create a new instance of a ChatBot 29 cbot = ChatBot( 30 'Sylvie', 31 storage_adapter='chatterbot.storage.SQLStorageAdapter', 32 logic_adapters=[ 33 'chatterbot.logic.MathematicalEvaluation', 34 # 'chatterbot.logic.TimeLogicAdapter', 35 'chatterbot.logic.BestMatch' 36 ], 37 database_uri='sqlite:///sylvie_db.sqlite3' 38 ) 39 40 learning_mode = False 41 ollama_mode = False 42 43 admin_name = "Princess Khan" 44 admin_pronoun = "She" 45 46 user_prefix = "You are Sylvie Deane who is a cute librarian from French Polynesia. You are 33 years old. You were born on April 12th, in 1991. Your job is to chat with me, " + admin_name + ". We have only just met on Tinder, and we are not sure about each other yet. I asked you: \n" + admin_name + ": '" 47 user_suffix = "?' Now write a short and concise response in English. \n Sylvie Deane:" 48 49 global_prefix = "" 50 global_suffix = "" 51 52 def get_feedback(): 53 text = input() 54 if 'yes' in text.lower(): 55 return True 56 elif 'no' in text.lower(): 57 return False 58 else: 59 print('Please type either "Yes" or "No"') 60 return get_feedback() 61 62 def send_command(user_text): 63 # Determine the command type based on the length of the input 64 if len(user_text.split()) > 10: 65 command_type = "1sm" # Use long command for speech 66 elif len(user_text.split()) > 5: 67 command_type = "1so" # Use medium command for speech 68 else: 69 command_type = "1sn" 70 71 # Send the mouth movement command to the robot 72 ser.write(command_type.encode()) # Use variable command_type 73 ser.write(b'\n') # Assuming commands are terminated with a newline character 74 print("[SYSTEM] Sent motor command") 75 76 def generate_and_save_tts(dt_model, text): 77 # Initialize Dimits with the desired voice model 78 dt = dt_model 79 # Convert text to audio and save it as a WAV file 80 audio_path = dt.text_2_audio_file(text, "tts_output", "/home/khan/sylvie-2024/python/conversation/llama", format="wav") 81 # Return None as the audio path (since it's saved to a file directly) 82 return audio_path 83 84 def play_audio_file(audio_path): 85 # Play the saved WAV file using simpleaudio 86 wave_obj = sa.WaveObject.from_wave_file(audio_path) 87 play_obj = wave_obj.play() 88 play_obj.wait_done() # Wait for the audio playback to finish 89 90 def ollama_get_text(ollama_prompt, max_attempts=3): 91 attempt = 1 92 93 final_prompt = global_prefix + ollama_prompt + global_suffix 94 print("Final prompt:", final_prompt) 95 96 while attempt <= max_attempts: 97 # Generate a response using the Ollama API 98 ollama_stream = ollama.chat( 99 model='phi3', 100 messages=[{'role': 'user', 'content': final_prompt}], 101 stream=True, 102 ) 103 104 generated_text = "" 105 for chunk in ollama_stream: 106 generated_text += chunk['message']['content'] 107 if len(generated_text) >= 75: 108 break 109 110 # Remove newline characters and trim whitespace 111 generated_text = generated_text.replace('\n', ' ').strip() 112 113 # Find the last occurrence of punctuation in the generated text 114 last_punctuation_index = max(generated_text.rfind('.'), generated_text.rfind('!'), generated_text.rfind('?')) 115 116 # Check if the response ends with punctuation 117 if last_punctuation_index != -1: 118 generated_text = generated_text[:last_punctuation_index+1] # Include the punctuation mark 119 else: 120 # If no punctuation found, use entire generated text 121 last_punctuation_index = len(generated_text) 122 123 # Check if the response is empty or incomplete 124 if generated_text: 125 if last_punctuation_index == len(generated_text) - 1: # Response ends with punctuation 126 print("Ollama Response:", generated_text) 127 return generated_text 128 else: 129 filler_words = ['um', 'well', 'you know', 'let me think', "I'm not sure", 'hmm', 'hold on', 'actually', 'that reminds me', 'by the way', 'oh, right', 'interesting', 'to be honest', 'give me a moment', 'pardon me', 'I suppose', 'it seems', 'so', 'in other words', 'in any case', 'anyway', 'as a matter of fact', 'on the other hand', 'as far as I know', 'it appears', 'nevermind'] 130 filler = random.choice(filler_words) 131 generated_text += f'... {filler}' 132 print("Ollama Response:", generated_text) 133 return generated_text 134 else: 135 print("Ollama Response: No response received. Attempt", attempt) 136 attempt += 1 137 138 print("Ollama Response: No response received after", max_attempts, "attempts.") 139 return None 140 141 142 def store_ollama_response_in_chatterbot(cbot, user_input, ollama_response): 143 input_statement = Statement(user_input) 144 response_statement = Statement(ollama_response) 145 cbot.learn_response(response_statement, input_statement) 146 147 # If training the chatbot for first time use, uncomment some of these lines depending on your need 148 #trainer = ChatterBotCorpusTrainer(cbot) 149 #trainer.train("chatterbot.corpus.english") 150 #trainer.train("./custom_corpus.yml") # Train with custom data 151 152 # The following loop will execute each time the user enters input 153 while True: 154 try: 155 user_input = input("Say something to Sylvie: ") 156 157 if user_input.lower() == "engage in learning mode": 158 print('Learning mode engaged.') 159 learning_mode = True 160 continue 161 elif user_input.lower() == "exit learning mode": 162 print('Exiting learning mode.') 163 learning_mode = False 164 continue 165 elif user_input.lower() == "engage in ollama mode": 166 print('Ollama mode engaged.') 167 ollama_mode = True 168 continue 169 elif user_input.lower() == "exit ollama mode": 170 print('Exiting Ollama mode.') 171 ollama_mode = False 172 continue 173 elif user_input.lower() == "enable prompt context": 174 print('Enabling the prompt context.') 175 global_prefix = user_prefix 176 global_suffix = user_suffix 177 continue 178 elif user_input.lower() == "disable prompt context": 179 print('Disabling the prompt context.') 180 global_prefix = "" 181 global_suffix = "" 182 continue 183 elif user_input.lower() == "enable microphone": 184 print('Microphone enabled.') 185 # To do 186 continue 187 elif user_input.lower() == "disable microphone": 188 print('Microphone disabled.') 189 # To do 190 continue 191 192 if learning_mode: 193 input_statement = Statement(user_input) 194 response = cbot.generate_response(input_statement) 195 print('\nIs "{}" a coherent response to "{}"? \n'.format(response.text, input_statement.text)) 196 if get_feedback() is False: 197 print('Please input the correct one.') 198 correct_response = Statement(text=input()) 199 cbot.learn_response(correct_response, input_statement) 200 print('Responses added to bot!') 201 202 elif ollama_mode: 203 ollama_response = ollama_get_text(user_input) 204 print("Ollama Response:", ollama_response) 205 store_ollama_response_in_chatterbot(cbot, user_input, ollama_response) 206 207 audio_path = generate_and_save_tts(dt_model, ollama_response) 208 209 send_command(ollama_response) 210 play_audio_file(audio_path) 211 #play_audio_file(generate_and_save_tts(dt_model, ollama_response)) 212 213 else: 214 bot_response = cbot.get_response(user_input) 215 print(bot_response) 216 217 audio_path = generate_and_save_tts(dt_model, bot_response.text) 218 219 send_command(bot_response.text) 220 play_audio_file(audio_path) 221 #play_audio_file(generate_and_save_tts(dt_model, bot_response.text)) 222 223 # Press ctrl-c or ctrl-d on the keyboard to exit 224 except (KeyboardInterrupt, EOFError, SystemExit): 225 break