forked from davinchi73/comp480-Final_Project
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcompClient.py
More file actions
180 lines (147 loc) · 5.7 KB
/
compClient.py
File metadata and controls
180 lines (147 loc) · 5.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# ------- #
# Author - Duy Huynh
# Modified by - Devinn Chi, Arnika Abeysekera, Quang Nguyen
# ------- #
# Install dependencies --> "pip install -r requirements.txt"
# import statements
import socket
import time
import threading
import numpy as np
import whisper
import sounddevice as sd
from queue import Queue
from rich.console import Console
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
from langchain.prompts import PromptTemplate
from langchain_community.llms import Ollama
import pyttsx3
import re
# initialization of console, whisper model, pyttsx3 speech engine
console = Console()
stt = whisper.load_model("base.en")
engine = pyttsx3.init()
# template controlling chatbot personality and parameters / Prompt initialization
template = """
You are a friendly homie that seeks to converse with a fellow homie (user).
You speak as a close friend of the user would.
You aim to provide responses as fast as possible, to maintain the flow of conversation.
Your main objective is to be a conversation partner,
engaging in what one may call small talk. Speak in the manner that the user speaks to you, including
slang and such. You aim to provide responses in less than 30 words.
The conversation transcript is as follows:
{history}
And here is the user's follow-up: {input}
Your response:
"""
PROMPT = PromptTemplate(input_variables=["history", "input"], template=template)
# conversation chain initialization with prompt, Ollama model loaded
chain = ConversationChain(
prompt=PROMPT,
verbose=False,
memory=ConversationBufferMemory(ai_prefix="HomieBot:"),
llm=Ollama(),
)
def record_audio(stop_event, data_queue):
"""
Captures audio data from the user's microphone and adds it to a queue for further processing.
Args:
stop_event (threading.Event): An event that, when set, signals the function to stop recording.
data_queue (queue.Queue): A queue to which the recorded audio data will be added.
Returns:
None
"""
def callback(indata, frames, time, status):
if status:
console.print(status)
data_queue.put(bytes(indata))
with sd.RawInputStream(
samplerate=16000, dtype="int16", channels=1, callback=callback
):
while not stop_event.is_set():
time.sleep(0.1)
def introduce_homiebot():
"""
Introduces HomieBot with a greeting message.
"""
intro_message = "What's up dog, my name is HomieBot and my perogative is to be your homie. Let's chat!"
console.print(intro_message)
def transcribe(audio_np: np.ndarray) -> str:
"""
Transcribes the given audio data using the Whisper speech recognition model.
Args:
audio_np (numpy.ndarray): The audio data to be transcribed.
Returns:
str: The transcribed text.
"""
result = stt.transcribe(audio_np, fp16=False) # Set fp16=True if using a GPU
text = result["text"].strip()
return text
def get_llm_response(text: str) -> str:
"""
Generates a response to the given text using the Llama-2 language model.
Args:
text (str): The input text to be processed.
Returns:
str: The generated response.
"""
response = chain.predict(input=text)
if response.startswith("HomieBot:"):
response = response[len("HomieBot:") :].strip()
clean = re.sub(r'\*.*?\*', '', response)
cleanResponse = re.sub(r"[^a-zA-Z0-9\s.,!?;]", "", clean)
return cleanResponse
def client_program():
# add host IP and port address
host = '169.254.20.1' # IMPORTANT: REPLACE WITH NEW EV3 WIRED IP ADDRESS EACH TIME.
port = 5043
# create socket connection
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# connect EV3 as server host to port
client_socket.connect((host, port))
print("Connected to EV3 robot server.")
console.print("[cyan]HomieBot started! Press Ctrl+C to exit.")
# introduce homiebot (handles console output)
introduce_homiebot()
try:
while True:
# prompt user to start recording
console.input(
"Press Enter to start recording, then press Enter again to stop."
)
# get recorded audio data from user
data_queue = Queue() # type: ignore[var-annotated]
stop_event = threading.Event()
recording_thread = threading.Thread(
target=record_audio,
args=(stop_event, data_queue),
)
recording_thread.start()
# join audio data for processing
input()
stop_event.set()
recording_thread.join()
audio_data = b"".join(list(data_queue.queue))
audio_np = (
np.frombuffer(audio_data, dtype=np.int16).astype(np.float32) / 32768.0
)
# use whisper model to transcribe/print user audio data into English
if audio_np.size > 0:
with console.status("Transcribing...", spinner="earth"):
text = transcribe(audio_np)
console.print(f"[yellow]You: {text}")
# use Ollama model to generate response (and print response) to user input
with console.status("Generating response...", spinner="earth"):
response = get_llm_response(text)
console.print(f"[cyan]HomieBot: {response}")
# Send the command to the server
client_socket.send(response.encode())
# close server connection
except KeyboardInterrupt:
print("Closing connection...")
finally:
client_socket.close()
# run client side program
if __name__ == '__main__':
client_program()