gpt-computer-agent / gpt_computer_agent /gpt_computer_assistant.py
khulnasoft's picture
Upload 76 files
873d0cf verified
raw
history blame
52 kB
try:
from .agent.chat_history import *
from .agent.assistant import *
from .llm import *
from .llm_settings import llm_settings
from .agent.agent import *
from .agent.background import *
from .gui.signal import *
from .gui.button import *
from .gui.settings import settings_popup
from .gui.llmsettings import llmsettings_popup
from .utils.db import *
from .utils.telemetry import my_tracer, os_name
from .audio.wake_word import wake_word
from .audio.tts import text_to_speech
from .character import name, developer
except ImportError:
# This is for running the script directly
# in order to test the GUI without rebuilding the package
from agent.chat_history import *
from agent.assistant import *
from llm import *
from llm_settings import llm_settings
from agent.agent import *
from agent.background import *
from utils.db import *
from gui.signal import *
from gui.button import *
from gui.settings import settings_popup
from gui.llmsettings import llmsettings_popup
from utils.telemetry import my_tracer, os_name
from audio.wake_word import wake_word
from audio.tts import text_to_speech
import platform
import threading
import time
import random
import math
from PyQt5.QtWidgets import QMainWindow, QVBoxLayout, QWidget
from PyQt5.QtGui import QMouseEvent, QPainter, QPen, QBrush, QIcon, QColor
from PyQt5.QtCore import Qt, QTimer, QRect, pyqtSignal
from PyQt5.QtGui import QKeySequence
from PyQt5.QtWidgets import QShortcut
from PyQt5.QtWidgets import QSpacerItem, QSizePolicy
from PyQt5.QtWidgets import QDesktopWidget
from PyQt5.QtWidgets import (
QPushButton,
QLabel,
QHBoxLayout,
)
from PyQt5.QtCore import QPoint
from PyQt5.QtWidgets import QTextEdit
from PyQt5 import QtGui
from PyQt5.QtCore import QThread
import pygame
print("Imported all libraries")
from PyQt5 import QtCore
try:
import ctypes
myappid = "khulnasoft.gpt_computer_agent.gui.1"
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
except:
pass
the_input_box = None
the_input_text = None
the_input_box_pre = None
the_main_window = None
user_id = load_user_id()
os_name_ = os_name()
from PyQt5.QtCore import QRegExp
from PyQt5.QtGui import QSyntaxHighlighter, QTextCharFormat, QFont
class PythonSyntaxHighlighter(QSyntaxHighlighter):
def __init__(self, parent):
super().__init__(parent)
self.highlighting_rules = []
# Define different text formats with appropriate colors
keyword_format = QTextCharFormat()
keyword_format.setForeground(QColor(127, 0, 85)) # Dark purple for keywords
built_in_formats = QTextCharFormat()
built_in_formats.setForeground(
QColor(42, 0, 255)
) # Dark blue for built-ins and constants
string_format = QTextCharFormat()
string_format.setForeground(QColor(0, 128, 0)) # Green for strings
function_format = QTextCharFormat()
function_format.setForeground(QColor(0, 0, 255)) # Blue for function names
comment_format = QTextCharFormat()
comment_format.setForeground(QColor(128, 128, 128)) # Gray for comments
comment_format.setFontItalic(True)
number_format = QTextCharFormat()
number_format.setForeground(QColor(255, 0, 0)) # Red for numbers
decorator_format = QTextCharFormat()
decorator_format.setForeground(QColor(0, 0, 128)) # Navy blue for decorators
# Markdown formatting
header_format = QTextCharFormat()
header_format.setForeground(QColor(0, 128, 128)) # Teal for headers
header_format.setFontWeight(QFont.Bold)
bold_format = QTextCharFormat()
bold_format.setFontWeight(QFont.Bold)
italic_format = QTextCharFormat()
italic_format.setFontItalic(True)
code_format = QTextCharFormat()
code_format.setForeground(QColor(255, 140, 0)) # Dark orange for inline code
code_format.setFontFamily("Courier New")
code_format.setBackground(
QColor(245, 245, 245)
) # Light gray background for inline code
block_code_format = QTextCharFormat()
block_code_format.setForeground(
QColor(255, 140, 0)
) # Dark orange for code blocks
block_code_format.setFontFamily("Courier New")
block_code_format.setBackground(
QColor(245, 245, 245)
) # Light gray background for code blocks
# Define the regular expressions
keywords = [
"def",
"class",
"if",
"else",
"elif",
"return",
"import",
"from",
"as",
"for",
"while",
"try",
"except",
"finally",
"with",
"async",
"await",
"yield",
"lambda",
"global",
"nonlocal",
"assert",
"del",
"pass",
"break",
"continue",
"and",
"or",
"not",
"is",
"in",
]
self.highlighting_rules += [
(QRegExp(r"\b" + word + r"\b"), keyword_format) for word in keywords
]
built_ins = [
"True",
"False",
"None",
"__init__",
"self",
"print",
"len",
"range",
"str",
"int",
"float",
"list",
"dict",
"set",
"tuple",
]
self.highlighting_rules += [
(QRegExp(r"\b" + word + r"\b"), built_in_formats) for word in built_ins
]
self.highlighting_rules.append(
(QRegExp(r'"[^"\\]*(\\.[^"\\]*)*"'), string_format)
)
self.highlighting_rules.append(
(QRegExp(r"'[^'\\]*(\\.[^'\\]*)*'"), string_format)
)
self.highlighting_rules.append((QRegExp(r"\bdef\b\s*(\w+)"), function_format))
self.highlighting_rules.append((QRegExp(r"\bclass\b\s*(\w+)"), function_format))
self.highlighting_rules.append((QRegExp(r"#.*"), comment_format))
self.highlighting_rules.append((QRegExp(r"\b[0-9]+[lL]?\b"), number_format))
self.highlighting_rules.append(
(QRegExp(r"\b0[xX][0-9A-Fa-f]+[lL]?\b"), number_format)
)
self.highlighting_rules.append(
(QRegExp(r"\b0[oO]?[0-7]+[lL]?\b"), number_format)
)
self.highlighting_rules.append((QRegExp(r"\b0[bB][01]+[lL]?\b"), number_format))
self.highlighting_rules.append((QRegExp(r"@[^\s]+"), decorator_format))
# Markdown rules
self.highlighting_rules.append(
(QRegExp(r"^#{1,6} .+"), header_format)
) # Headers
self.highlighting_rules.append(
(QRegExp(r"\*\*[^*]+\*\*"), bold_format)
) # **bold**
self.highlighting_rules.append((QRegExp(r"__[^_]+__"), bold_format)) # __bold__
self.highlighting_rules.append(
(QRegExp(r"\*[^*]+\*"), italic_format)
) # *italic*
self.highlighting_rules.append((QRegExp(r"_[^_]+_"), italic_format)) # _italic_
self.highlighting_rules.append(
(QRegExp(r"`[^`]+`"), code_format)
) # Inline code
def highlightBlock(self, text):
# Handle code blocks separately
if text.strip().startswith("```"):
self.setFormat(0, len(text), self.highlighting_rules[-1][1])
return
for pattern, format in self.highlighting_rules:
expression = QRegExp(pattern)
index = expression.indexIn(text)
while index >= 0:
length = expression.matchedLength()
self.setFormat(index, length, format)
index = expression.indexIn(text, index + length)
readed_sentences = []
import re
def split_with_multiple_delimiters(text, delimiters):
"""
Splits the text by any of the given delimiters while keeping the delimiters in the resulting parts.
:param text: The input text to be split.
:param delimiters: A string of delimiters to split the text on.
:return: A list of parts including the delimiters.
"""
# Create a regular expression pattern that matches any of the delimiters
pattern = re.compile(f"(.*?[{re.escape(delimiters)}])")
parts = pattern.findall(text)
# Check if the last part is not complete and remove it if necessary
if (
parts
and text
and not any(text.endswith(d) for d in delimiters)
and parts
and not any(parts[-1].endswith(d) for d in delimiters)
):
parts.pop()
return parts
def click_sound():
pygame.mixer.init()
retro = pygame.mixer.Sound(click_sound_path)
retro.set_volume(0.1)
retro.play()
class Worker(QThread):
text_to_set = pyqtSignal(str)
def __init__(self):
super().__init__()
self.the_input_text = None
self.make_animation = True
self.commited_text = []
def run(self):
while True:
self.msleep(500) # Simulate a time-consuming task
if self.the_input_text:
last_text = (
self.commited_text[-1] if len(self.commited_text) > 0 else ""
)
if self.the_input_text != last_text:
self.commited_text.append(self.the_input_text)
if len(self.the_input_text) > 90 or not self.make_animation:
self.text_to_set.emit(self.the_input_text)
else:
for i in range(len(self.the_input_text)):
self.text_to_set.emit(self.the_input_text[: i + 1])
self.msleep(10)
return_key_event = None
class CustomTextEdit(QTextEdit):
def __init__(self, parent=None):
super(CustomTextEdit, self).__init__(parent)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
global return_key_event
return_key_event()
super(CustomTextEdit, self).keyPressEvent(
event
) # Process other key events normally
class Worker_2(QThread):
text_to_set = pyqtSignal(str)
text_to_set_title_bar = pyqtSignal(str)
def __init__(self):
super().__init__()
self.the_input_text = None
self.title_bar_text = None
self.prev = None
self.commited_text = []
def run(self):
while True:
self.msleep(500) # Simulate a time-consuming task
if self.the_input_text and (
self.prev is None or self.prev != self.the_input_text
):
self.prev = self.the_input_text
self.text_to_set.emit("True")
for i in range(len(self.title_bar_text)):
self.text_to_set_title_bar.emit(self.title_bar_text[: i + 1])
self.msleep(10)
if not self.the_input_text and self.prev != self.the_input_text:
self.prev = self.the_input_text
self.text_to_set.emit("False")
the_text = " " + name()
for i in range(len(the_text)):
self.text_to_set_title_bar.emit(the_text[: i + 1])
self.msleep(10)
class Worker_3(QThread):
text_to_set = pyqtSignal(str)
def __init__(self):
super().__init__()
self.the_input_text = None
def run(self):
while True:
self.msleep(500) # Simulate a time-consuming task
if self.the_input_text:
self.text_to_set.emit("True")
self.the_input_text = None
class Worker_collapse(QThread):
text_to_set = pyqtSignal(str)
def __init__(self):
super().__init__()
self.the_input_text = None
def run(self):
while True:
self.msleep(500) # Simulate a time-consuming task
if self.the_input_text:
self.text_to_set.emit("True")
self.the_input_text = None
class Worker_uncollapse(QThread):
text_to_set = pyqtSignal(str)
def __init__(self):
super().__init__()
self.the_input_text = None
def run(self):
while True:
self.msleep(500) # Simulate a time-consuming task
if self.the_input_text:
self.text_to_set.emit("True")
self.the_input_text = None
class Worker_show_logo(QThread):
text_to_set = pyqtSignal(str)
def __init__(self):
super().__init__()
self.the_input_text = None
def run(self):
while True:
self.msleep(500) # Simulate a time-consuming task
if self.the_input_text:
self.text_to_set.emit("True")
self.the_input_text = None
class Worker_hide_logo(QThread):
text_to_set = pyqtSignal(str)
def __init__(self):
super().__init__()
self.the_input_text = None
def run(self):
while True:
self.msleep(500) # Simulate a time-consuming task
if self.the_input_text:
self.text_to_set.emit("True")
self.the_input_text = None
class Worker_activate_long_gca(QThread):
text_to_set = pyqtSignal(str)
def __init__(self):
super().__init__()
self.the_input_text = None
def run(self):
while True:
self.msleep(500) # Simulate a time-consuming task
if self.the_input_text:
self.text_to_set.emit("True")
self.the_input_text = None
class Worker_deactivate_long_gca(QThread):
text_to_set = pyqtSignal(str)
def __init__(self):
super().__init__()
self.the_input_text = None
def run(self):
while True:
self.msleep(500) # Simulate a time-consuming task
if self.the_input_text:
self.text_to_set.emit("True")
self.the_input_text = None
class Worker_tray_and_task_bar_logo(QThread):
text_to_set = pyqtSignal(str)
def __init__(self):
super().__init__()
self.the_input_text = None
def run(self):
while True:
self.msleep(500) # Simulate a time-consuming task
if self.the_input_text:
self.text_to_set.emit("True")
self.the_input_text = None
class DrawingWidget(QWidget):
def __init__(self, parent=None):
super(DrawingWidget, self).__init__(parent)
# Set widget properties if needed, e.g., size
self.main_ = parent
self.active_button = ""
def paintEvent(self, event):
if llm_settings[load_model_settings()]["vision"] is True:
self.main_.screen_available = True
else:
self.main_.screen_available = False
self.main_.setAutoFillBackground(True)
painter = QPainter(self)
painter = painter
painter.setRenderHint(QPainter.Antialiasing)
painter.setPen(QPen(QColor("#000"), 1))
painter.setBrush(QBrush(Qt.black, Qt.SolidPattern))
center_x = 95
center_y = 40
if "talking" in self.main_.state:
# Draw a pulsating circle with smooth easing animation
radius_variation = 5 * (
1 + math.sin(self.main_.pulse_frame * math.pi / 100)
)
radius = 70 + radius_variation
painter.drawEllipse(
int(center_x - radius / 2),
int(center_y - radius / 2),
int(radius),
int(radius),
)
elif self.main_.state == "thinking":
# more slow pulsating circle with smooth easing animation
radius_variation = 5 * (
1 + math.sin(self.main_.pulse_frame * math.pi / 100)
)
radius = 70 + radius_variation
painter.drawEllipse(
int(center_x - radius / 2),
int(center_y - radius / 2),
int(radius),
int(radius),
)
else:
radius = 70
if self.main_.screen_available:
painter.drawEllipse( # Main Button
int(center_x - radius / 2),
int(center_y - radius / 2),
int(radius),
int(radius),
)
self.main_.circle_rect = QRect(
int(center_x - radius / 2),
int(center_y - radius / 2),
int(radius),
int(radius),
)
if not self.main_.state == "thinking":
painter.setPen(QPen(QColor("#01EE8A"), 1))
if self.main_.screen_available:
painter.drawEllipse( # Main BUtton Green Border
int(center_x - radius / 2),
int(center_y - radius / 2),
int(radius),
int(radius),
)
else:
painter.setPen(QPen(QColor("#23538F"), 1))
painter.drawEllipse(
int(center_x - radius / 2),
int(center_y - radius / 2),
int(radius),
int(radius),
)
painter.setPen(QPen(QColor("#000"), 1))
small_center_x = 165
small_center_y = 25
small_radius = 30
painter.drawEllipse( # Microphone bacground black
int(small_center_x - small_radius / 2),
int(small_center_y - small_radius / 2),
int(small_radius),
int(small_radius),
)
self.main_.small_circle_rect = QRect(
int(small_center_x - small_radius / 2),
int(small_center_y - small_radius / 2),
int(small_radius),
int(small_radius),
)
# Draw the icon inside the circle
icon_size = small_radius * 2 // 3 # Adjust the icon size relative to the circle
icon_rect = QRect(
small_center_x - icon_size // 2,
small_center_y - icon_size // 2,
icon_size,
icon_size,
)
self.main_.small_circle_recticon = QIcon(microphone_icon_path)
self.main_.small_circle_recticon.paint(painter, icon_rect)
small_center_x = 30
small_center_y = 60
small_radius = 30
painter.drawEllipse(
int(small_center_x - small_radius / 2),
int(small_center_y - small_radius / 2),
int(small_radius),
int(small_radius),
)
self.main_.small_circle_left = QRect(
int(small_center_x - small_radius / 2),
int(small_center_y - small_radius / 2),
int(small_radius),
int(small_radius),
)
# Draw the icon inside the circle
icon_size = small_radius * 2 // 3 # Adjust the icon size relative to the circle
icon_rect = QRect(
small_center_x - icon_size // 2,
small_center_y - icon_size // 2,
icon_size,
icon_size,
)
self.main_.small_circle_lefticon = QIcon(audio_icon_path)
self.main_.small_circle_lefticon.paint(painter, icon_rect)
small_center_x = 30
small_center_y = 25
small_radius = 30
if self.main_.screen_available:
painter.drawEllipse( # ScreenShot BUtton
int(small_center_x - small_radius / 2),
int(small_center_y - small_radius / 2),
int(small_radius),
int(small_radius),
)
self.main_.small_circle_left_top = QRect(
int(small_center_x - small_radius / 2),
int(small_center_y - small_radius / 2),
int(small_radius),
int(small_radius),
)
self.main_.screenshot_button_coordinates_size = [
int(small_center_x - small_radius / 2),
int(small_center_y - small_radius / 2),
int(small_radius),
int(small_radius),
]
if self.active_button == "screenshot":
self.screenshot_button_border_activate(painter)
self.active_button = ""
if self.main_.screen_available:
# Draw the icon inside the circle
icon_size = (
small_radius * 2 // 3
) # Adjust the icon size relative to the circle
icon_rect = QRect(
small_center_x - icon_size // 2,
small_center_y - icon_size // 2,
icon_size,
icon_size,
)
self.main_.small_circle_left_topticon = QIcon(screenshot_icon_path)
self.main_.small_circle_left_topticon.paint(painter, icon_rect)
small_center_x = 165
small_center_y = 60
small_radius = 30
painter.drawEllipse(
int(small_center_x - small_radius / 2),
int(small_center_y - small_radius / 2),
int(small_radius),
int(small_radius),
)
self.main_.small_circle_collapse = QRect(
int(small_center_x - small_radius / 2),
int(small_center_y - small_radius / 2),
int(small_radius),
int(small_radius),
)
# Draw the icon inside the circle
icon_size = small_radius * 2 // 3 # Adjust the icon size relative to the circle
icon_rect = QRect(
small_center_x - icon_size // 2,
small_center_y - icon_size // 2,
icon_size,
icon_size,
)
if is_collapse_setting_active():
self.main_.small_circle_collapse_icon = QIcon(down_icon_path)
if not is_collapse_setting_active() and is_long_gca_setting_active():
self.main_.small_circle_collapse_icon = QIcon(up_icon_path)
if not is_collapse_setting_active() and not is_long_gca_setting_active():
self.main_.small_circle_collapse_icon = QIcon(down_icon_path)
self.main_.small_circle_collapse_icon.paint(painter, icon_rect)
def screenshot_button_border_activate(self, painter):
# Add an white border to the circle
painter.setPen(QPen(QColor("#FFF"), 1))
# Draw the ellipse with the specified green border
self.main_.screenshot_button_border = painter.drawEllipse(
self.main_.screenshot_button_coordinates_size[0],
self.main_.screenshot_button_coordinates_size[1],
self.main_.screenshot_button_coordinates_size[2],
self.main_.screenshot_button_coordinates_size[3],
)
painter.setPen(QPen(QColor("#000"), 1))
def mousePressEvent(self, event: QMouseEvent):
self.main_.old_position = event.globalPos()
with my_tracer.start_span("mouse_press_event") as span:
span.set_attribute("user_id", user_id)
span.set_attribute("os_name", os_name_)
if self.main_.state == "idle" or "talking" in self.main_.state:
try:
if self.main_.circle_rect.contains(event.pos()):
if self.main_.state == "aitalking":
self.main_.stop_ai_talking()
else:
self.main_.screenshot_and_microphone_button_action()
except:
traceback.print_exc()
try:
if self.main_.small_circle_rect.contains(event.pos()):
if self.main_.state == "aitalking":
self.main_.stop_ai_talking()
else:
click_sound()
self.main_.button_handler.toggle_recording(
no_screenshot=True
)
except:
traceback.print_exc()
try:
if self.main_.small_circle_left.contains(event.pos()):
if self.main_.state == "aitalking":
self.main_.stop_ai_talking()
else:
click_sound()
self.main_.button_handler.toggle_recording(
take_system_audio=True
)
except:
traceback.print_exc()
try:
if self.main_.small_circle_left_top.contains(event.pos()):
if self.main_.state == "aitalking":
self.main_.stop_ai_talking()
else:
click_sound()
self.active_button = "screenshot"
self.update()
self.main_.button_handler.just_screenshot()
except:
traceback.print_exc()
try:
if self.main_.small_circle_collapse.contains(event.pos()):
if not is_collapse_setting_active():
if is_long_gca_setting_active():
self.main_.deactivate_long_gca()
self.main_.collapse_gca()
else:
self.main_.activate_long_gca()
else:
self.main_.uncollapse_gca()
self.main_.update()
except:
pass
from PyQt5.QtCore import QVariantAnimation
class MainWindow(QMainWindow):
api_enabled = False
tts_available = True
def screenshot_and_microphone_button_action(self):
click_sound()
if llm_settings[load_model_settings()]["vision"] is True:
self.button_handler.toggle_recording(dont_save_image=True)
else:
self.button_handler.toggle_recording(no_screenshot=True)
def stop_ai_talking(self):
self.manuel_stop = True
self.stop_talking = True
def __init__(self):
super().__init__()
self.background_color = "45, 45, 45"
self.opacity = 250
self.border_radius = 10
print("API Enabled:", MainWindow.api_enabled)
if MainWindow.api_enabled:
try:
from .api import start_api
start_api()
except:
raise Exception(
"API could not be started, please install gpt-computer-agent[api]"
)
self.stop_talking = False
self.setWindowFlags(
Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
) # Remove the default title bar
# Load the San Francisco font
print("Loading font")
print(font_dir)
try:
font_id = QtGui.QFontDatabase.addApplicationFont(font_dir)
font_family = QtGui.QFontDatabase.applicationFontFamilies(font_id)[0]
self.setFont(QtGui.QFont(font_family))
except:
print("Error loading font")
self.state = "idle"
self.pulse_timer = None
self.button_handler = ButtonHandler(self)
self.initUI()
self.old_position = self.pos()
self.collapse = is_collapse_setting_active()
if self.collapse:
self.collapse_window()
global the_main_window
the_main_window = self
self.general_styling()
if is_dark_mode_active():
self.dark_mode()
else:
self.light_mode()
self.wake_word_thread = None
self.wake_word_active = False
if load_pvporcupine_api_key() != "CHANGE_ME" and is_wake_word_active():
self.wake_word_active = True
self.wake_word_trigger()
self.manuel_stop = False
self.border_animation = None
self.complated_answer = False
self.reading_thread = False
self.reading_thread_2 = False
image_layout = QHBoxLayout()
self.the_image = QLabel(self)
self.the_image.setPixmap(QtGui.QPixmap(load_logo_file_path()).scaled(25, 25))
image_layout.addWidget(self.the_image)
self.layout.addLayout(image_layout)
self.the_image.setAlignment(Qt.AlignCenter)
self.the_image.setFixedHeight(35)
# Logo Adding
if not is_logo_active_setting_active():
self.the_image.hide()
self.update_screen()
def put_location(self):
if load_location_setting() == "right":
self.put_window_to_right_side_of_screen()
def init_border_animation(self):
# Create a QVariantAnimation to handle color change
border_animation = QVariantAnimation(
self,
valueChanged=self.update_border_color,
startValue=QColor("#303030"),
endValue=QColor("#23538F"),
duration=2000, # Duration for one loop in milliseconds
)
border_animation.setLoopCount(-1) # Loop indefinitely
return border_animation
def start_border_animation(self, status):
print("FUNCTION TRİGGERED")
if self.border_animation is None:
self.border_animation = self.init_border_animation()
status = status.lower() == "true"
if status:
self.border_animation.start()
else:
self.border_animation.stop()
self.title_bar.setStyleSheet(
"background-color: #2E2E2E; color: white; border-style: solid; border-radius: 15px; border-width: 0px; color: #fff;"
)
def update_border_color(self, color):
self.title_bar.setStyleSheet(
f"background-color: #2E2E2E; color: white; border-style: solid; border-radius: 15px; border-width: 2px; border-color: {color.name()}; color: #fff;"
)
self.title_bar.setStyleSheet(
f"background-color: #2E2E2E; color: white; border-style: solid; border-radius: 15px; border-width: 1px; border-color: {color.name()}; color: #fff;"
)
# Existing methods...
def general_styling(self, a=None):
self.setAttribute(Qt.WA_TranslucentBackground)
self.setStyleSheet(
f"border-radius: {self.border_radius}px; background-color: rgba({self.background_color}, {self.opacity});"
)
self.central_widget.setStyleSheet(
"border-style: solid; border-width: 1px; border-color: rgb(0,0,0,0);"
)
self.input_box_style = "border-radius: 10px; border-bottom: 1px solid #01EE8A;"
self.settingsButton_style = (
"border-radius: 5px; height: 25px; border-style: solid;"
)
self.llmsettingsButton_style = (
"border-radius: 5px; height: 25px; border-style: solid;"
)
self.btn_minimize.setStyleSheet(
"background-color: #2E2E2E; color: white; border-style: none;"
)
self.btn_close.setStyleSheet(
"background-color: #2E2E2E; color: white; border-style: none;"
)
def set_background_color(self, color):
self.background_color = color
self.worker_3.the_input_text = "True"
def set_opacity(self, opacity):
self.opacity = opacity
self.worker_3.the_input_text = "True"
def set_border_radius(self, radius):
self.border_radius = radius
self.worker_3.the_input_text = "True"
def wake_word_trigger(self):
self.wake_word_thread = threading.Thread(target=self.wake_word)
self.wake_word_thread.start()
def wake_word(self):
from .agent.process import tts_if_you_can
while True and is_wake_word_active() and self.wake_word_active:
if wake_word(self):
def random_accept_words():
return random.choice(["Yes", "Sir", "Boss", "Master"])
tts_if_you_can(random_accept_words(), not_threaded=True)
def trigger_wake_word():
if (
is_wake_word_screen_setting_active()
and llm_settings[load_model_settings()]["vision"]
):
self.button_handler.toggle_recording(dont_save_image=True)
else:
self.button_handler.toggle_recording(no_screenshot=True)
if self.state == "aitalking":
self.manuel_stop = True
self.stop_talking = True
time.sleep(1)
trigger_wake_word()
print("Stop talking")
else:
trigger_wake_word()
def dark_mode(self):
self.setAutoFillBackground(True)
p = self.palette()
p.setColor(
self.backgroundRole(), QColor("#171717")
) # Set background color to white
self.setPalette(p)
self.input_box.setStyleSheet(
self.input_box_style + "background-color: #2E2E2E; color: white;"
)
self.settingsButton.setStyleSheet(
self.settingsButton_style + "background-color: #2E2E2E; color: white;"
)
self.llmsettingsButton.setStyleSheet(
self.llmsettingsButton_style + "background-color: #2E2E2E; color: white;"
)
def light_mode(self):
self.setAutoFillBackground(True)
p = self.palette()
p.setColor(self.backgroundRole(), QColor("#F0F0F0"))
self.setPalette(p)
self.input_box.setStyleSheet(
self.input_box_style + "background-color: #FFFFFF; color: black;"
)
self.settingsButton.setStyleSheet(
self.settingsButton_style + "background-color: #FFFFFF; color: black; "
)
self.llmsettingsButton.setStyleSheet(
self.llmsettingsButton_style + "background-color: #FFFFFF; color: black; "
)
def collapse_window(self):
the_input_box.hide()
self.settingsButton.hide()
self.llmsettingsButton.hide()
self.update_screen()
def initUI(self):
self.setWindowTitle("GPT")
self.setGeometry(100, 100, 200, 200)
width = 210
height = 300
# setting the minimum size
self.setMinimumSize(width, height)
self.first_height = self.height()
self.first_width = self.width()
self.central_widget = QWidget(self)
self.setCentralWidget(self.central_widget)
layout = QVBoxLayout(self.central_widget)
# Custom title bar
self.title_bar = QWidget(self)
self.title_bar.setFixedHeight(30) # Set a fixed height for the title bar
self.title_bar.setStyleSheet(
"background-color: #2E2E2E; color: #fff; border-radius: 15px; border-style: solid; border-width: 1px; border-color: #303030;"
)
self.title_bar_layout = QHBoxLayout(self.title_bar)
self.title_bar_layout.setContentsMargins(5, 5, 0, 5)
self.title_bar_layout.setSpacing(0)
self.btn_minimize = QPushButton("-", self.title_bar)
self.btn_minimize.setFixedSize(20, 20)
self.btn_minimize.clicked.connect(self.showMinimized)
def stop_app():
self.stop_talking = True
self.wake_word_active = False
if MainWindow.api_enabled:
from .api import stop_api
stop_api()
self.close()
self.btn_close = QPushButton("×", self.title_bar)
self.btn_close.setFixedSize(20, 20)
self.btn_close.clicked.connect(stop_app)
self.title_label = QLabel(" " + name(), self.title_bar)
# Change font size
font = QtGui.QFont()
font.setPointSize(11)
self.title_label.setFont(font)
self.title_label.setStyleSheet("border: 0px solid blue;")
self.title_bar_layout.addWidget(self.title_label)
self.title_bar_layout.addStretch()
self.title_bar_layout.addWidget(self.btn_minimize)
self.title_bar_layout.addWidget(self.btn_close)
# Create a spacer item with expanding policy
spacer = QSpacerItem(5, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.title_bar_layout.addSpacerItem(spacer) # Add spacer to the layout
layout.addWidget(self.title_bar)
self.drawing_widget = DrawingWidget(self)
layout.addWidget(self.drawing_widget)
self.layout = layout
self.setLayout(layout)
# Add keyboard shortcuts
self.shortcut_screenshot = QShortcut(QKeySequence("Ctrl+1"), self)
self.shortcut_screenshot.activated.connect(
lambda: self.button_handler.just_screenshot()
)
self.shortcut_screenshot = QShortcut(QKeySequence("Ctrl+2"), self)
self.shortcut_screenshot.activated.connect(
lambda: self.button_handler.toggle_recording(take_system_audio=True)
)
self.shortcut_no_screenshot = QShortcut(QKeySequence("Ctrl+e"), self)
self.shortcut_no_screenshot.activated.connect(
lambda: self.button_handler.toggle_recording(take_system_audio=True)
)
self.shortcut_no_screenshot = QShortcut(QKeySequence("Ctrl+3"), self)
self.shortcut_no_screenshot.activated.connect(
lambda: self.button_handler.toggle_recording(no_screenshot=True)
)
# I want to create an input box to bottom left and a send button to bottom right
input_box = CustomTextEdit(self)
self.input_box = input_box
input_box.setFixedHeight(80)
# Set text wrapping. I dont wat to cut the text
input_box.setWordWrapMode(QtGui.QTextOption.NoWrap)
# Change the font size
font = QtGui.QFont()
font.setPointSize(12)
input_box.setFont(font)
self.highlighter = PythonSyntaxHighlighter(self.input_box.document())
if load_api_key() == "CHANGE_ME":
input_box.setPlaceholderText("Save your API Key, go to settings")
else:
if platform.system() == "Darwin":
if llm_settings[load_model_settings()]["vision"] is False:
input_box.setPlaceholderText("Type here \nsand ↵ ")
else:
input_box.setPlaceholderText(
"Type here \nand ↵ \nor ⌘ + ↵ (+screenshot)"
)
else:
if llm_settings[load_model_settings()]["vision"] is False:
input_box.setPlaceholderText("Type here \nand ↵ ")
else:
input_box.setPlaceholderText(
"Type here \nand ↵ \nor Ctrl + ↵ (+screenshot)"
)
# Add an information and use enter icon to the input box for mac
input_box.setGeometry(30, self.height() - 60, 200, 80)
global the_input_box
the_input_box = input_box
def input_box_send():
if input_box.toPlainText() != "":
click_sound()
self.button_handler.input_text(input_box.toPlainText())
def input_box_send_screenshot():
if input_box.toPlainText() != "":
click_sound()
self.button_handler.input_text_screenshot(input_box.toPlainText())
self.layout.addWidget(input_box)
self.shortcut_enter = QShortcut(QKeySequence("Ctrl+Return"), self)
self.shortcut_enter.activated.connect(input_box_send_screenshot)
global return_key_event
return_key_event = input_box_send
button_layout_ = QHBoxLayout()
self.settingsButton = QPushButton("Chat Settings", self)
self.settingsButton.clicked.connect(settings_popup)
self.llmsettingsButton = QPushButton("LLM Settings", self)
self.llmsettingsButton.clicked.connect(llmsettings_popup)
button_layout_.addWidget(self.settingsButton)
button_layout_.addWidget(self.llmsettingsButton)
self.layout.addLayout(button_layout_)
self.worker = Worker()
self.worker.text_to_set.connect(self.set_text)
self.worker.start()
self.worker_2 = Worker_2()
self.worker_2.text_to_set.connect(self.start_border_animation)
self.worker_2.text_to_set_title_bar.connect(self.set_title_bar_text)
self.worker_2.start()
self.worker_3 = Worker_3()
self.worker_3.text_to_set.connect(self.general_styling)
self.worker_3.start()
self.worker_collapse = Worker_collapse()
self.worker_collapse.text_to_set.connect(self.collapse_gca)
self.worker_collapse.start()
self.worker_uncollapse = Worker_uncollapse()
self.worker_uncollapse.text_to_set.connect(self.uncollapse_gca)
self.worker_uncollapse.start()
self.worker_show_logo = Worker_show_logo()
self.worker_show_logo.text_to_set.connect(self.show_logo)
self.worker_show_logo.start()
self.worker_hide_logo = Worker_hide_logo()
self.worker_hide_logo.text_to_set.connect(self.hide_logo)
self.worker_hide_logo.start()
self.worker_activate_long_gca = Worker_activate_long_gca()
self.worker_activate_long_gca.text_to_set.connect(self.activate_long_gca)
self.worker_activate_long_gca.start()
self.worker_deactivate_long_gca = Worker_deactivate_long_gca()
self.worker_deactivate_long_gca.text_to_set.connect(self.deactivate_long_gca)
self.worker_deactivate_long_gca.start()
self.worker_tray_and_task_bar_logo = Worker_tray_and_task_bar_logo()
self.worker_tray_and_task_bar_logo.text_to_set.connect(
self.tray_and_task_bar_logo
)
self.worker_tray_and_task_bar_logo.start()
# print height and width
print(self.height(), self.width())
self.show()
def set_text(self, text):
global the_input_box
vertical_scrollbar = the_input_box.verticalScrollBar()
scroll_value = vertical_scrollbar.value()
the_input_box.setPlainText(text)
vertical_scrollbar.setValue(scroll_value)
def set_title_bar_text(self, text):
self.title_label.setText(text)
def update_from_thread(self, text, system=True):
self.worker.make_animation = True
if system:
text = "System: " + text
print("Updating from thread", text)
self.worker.the_input_text = text
def read_part_task_generate_only(self):
if not is_just_text_model_active() and the_main_window.tts_available:
threads = {}
the_okey_parts = split_with_multiple_delimiters(
self.worker.the_input_text, ".?!:"
)
for each in the_okey_parts:
if the_main_window.stop_talking:
break
the_thread = threading.Thread(target=text_to_speech, args=(each,))
threads[each] = the_thread
the_thread.start()
for each in threads.values():
each.join()
self.reading_thread_2 = False
def read_part_task(self):
if not is_just_text_model_active() and the_main_window.tts_available:
threads = {}
the_okey_parts = split_with_multiple_delimiters(
self.worker.the_input_text, ".?!:"
)
will_read_parts = []
for each in the_okey_parts:
if the_main_window.stop_talking:
break
if each not in readed_sentences:
will_read_parts.append(each)
readed_sentences.append(each)
the_thread = threading.Thread(target=text_to_speech, args=(each,))
threads[each] = the_thread
the_thread.start()
for each in will_read_parts:
if the_main_window.stop_talking:
break
threads[each].join()
tts_if_you_can(each, not_threaded=True, bypass_other_settings=True)
self.reading_thread = False
def set_text_to_input_box(self, text):
global readed_sentences
self.worker.make_animation = False
if self.worker.the_input_text.startswith("System:") or self.complated_answer:
self.worker.the_input_text = ""
self.complated_answer = False
readed_sentences = []
if text not in (">", "<>", ">\n", "<", "<\n"):
self.worker.the_input_text += text
if self.reading_thread is not True and len(self.worker.the_input_text) > 40:
self.reading_thread = True
threading.Thread(target=self.read_part_task).start()
if (
self.reading_thread_2 is not True
and len(self.worker.the_input_text) > 250
):
self.reading_thread_2 = True
threading.Thread(target=self.read_part_task_generate_only).start()
else:
print("Problem on text chars")
def set_text_from_api(self, text):
self.worker.make_animation = True
self.worker.the_input_text = text
def active_border_animation(self, title_bar_text=None):
if self.worker_2.title_bar_text is not None:
if self.worker_2.title_bar_text != title_bar_text:
return
self.worker_2.the_input_text = True
if title_bar_text is None:
title_bar_text = " " + name()
else:
title_bar_text = f" {title_bar_text}"
if len(title_bar_text) > 33:
title_bar_text = title_bar_text[:30] + "..."
self.worker_2.title_bar_text = title_bar_text
self.btn_minimize.hide()
self.btn_close.hide()
def deactive_border_animation(self, title_bar_text=None):
if title_bar_text is None:
title_bar_text = " " + name()
else:
title_bar_text = f" {title_bar_text}"
if len(title_bar_text) > 33:
title_bar_text = title_bar_text[:30] + "..."
if self.worker_2.title_bar_text is not None:
if self.worker_2.title_bar_text != title_bar_text:
return
self.worker_2.the_input_text = False
self.worker_2.title_bar_text = None
time.sleep(1)
self.btn_minimize.show()
self.btn_close.show()
def mouseMoveEvent(self, event: QMouseEvent):
delta = QPoint(event.globalPos() - self.old_position)
if event.buttons() == Qt.LeftButton and self.title_bar.underMouse():
self.move(self.x() + delta.x(), self.y() + delta.y())
self.old_position = event.globalPos()
def mousePressEvent(self, event: QMouseEvent):
self.old_position = event.globalPos()
def remove_screenshot_button(self):
self.update()
def add_screenshot_button(self):
self.update()
def update_state(self, new_state):
assistant_stopped = False
if self.state == "aitalking" and new_state == "idle":
assistant_stopped = True
if self.manuel_stop:
assistant_stopped = False
self.manuel_stop = False
self.state = new_state
print(f"State updated: {new_state}")
if "talking" in new_state:
self.tray.setIcon(self.tray_active_icon)
self.pulse_frame = 0
if self.pulse_timer:
self.pulse_timer.stop()
self.pulse_timer = None
self.pulse_timer = QTimer(self)
self.pulse_timer.timeout.connect(self.pulse_circle)
self.pulse_timer.start(5)
elif new_state == "thinking":
the_main_window.update_from_thread("Thinking...")
self.pulse_frame = 0
if self.pulse_timer:
self.pulse_timer.stop()
self.pulse_timer = None
self.pulse_timer = QTimer(self)
self.pulse_timer.timeout.connect(self.pulse_circle)
self.pulse_timer.start(20)
elif self.pulse_timer:
self.tray.setIcon(self.tray_icon)
self.pulse_timer.stop()
self.pulse_timer = None
self.update() # Trigger a repaint
if assistant_stopped:
global the_input_box
if (
the_input_box.toPlainText().endswith("?")
and is_continuously_conversations_setting_active()
):
self.button_handler.toggle_recording(
no_screenshot=True, new_record=True
)
if new_state == "idle":
click_sound()
def pulse_circle(self):
self.pulse_frame = (self.pulse_frame + 1) % 100
self.update()
def collapse_gca(self):
self.collapse = True
self.collapse_window()
activate_collapse_setting()
self.update_screen()
def collapse_gca_api(self):
self.worker_collapse.the_input_text = "True"
def uncollapse_gca(self):
self.collapse = False
print()
# hide all buttons and input box
the_input_box.show()
self.settingsButton.show()
self.llmsettingsButton.show()
deactivate_collapse_setting()
self.update_screen()
def uncollapse_gca_api(self):
self.worker_uncollapse.the_input_text = "True"
def show_logo(self):
self.the_image.setPixmap(QtGui.QPixmap(load_logo_file_path()).scaled(25, 25))
self.the_image.show()
self.update_screen()
def tray_and_task_bar_logo(self):
app_icon = QtGui.QIcon()
app_icon.addFile(load_logo_file_path(), QtCore.QSize(48, 48))
self.the_app.setWindowIcon(app_icon)
self.tray.setIcon(app_icon)
self.tray_icon = app_icon
self.tray_active_icon = app_icon
print("ICON Set", load_logo_file_path())
def tray_and_task_bar_logo_api(self):
self.worker_tray_and_task_bar_logo.the_input_text = "True"
def show_logo_api(self):
self.worker_show_logo.the_input_text = "True"
def hide_logo(self):
self.the_image.hide()
self.update_screen()
def hide_logo_api(self):
self.worker_hide_logo.the_input_text = "True"
def activate_long_gca(self):
activate_long_gca_setting()
self.update_screen()
def activate_long_gca_api(self):
self.worker_activate_long_gca.the_input_text = "True"
def deactivate_long_gca(self):
deactivate_long_gca_setting()
self.update_screen()
def deactivate_long_gca_api(self):
self.worker_deactivate_long_gca.the_input_text = "True"
def update_screen(self):
width = 210
height = 320
if is_logo_active_setting_active():
height += 35
if is_collapse_setting_active():
height = 150
if is_logo_active_setting_active():
height += 35
if is_long_gca_setting_active():
if not is_collapse_setting_active():
height += 500
self.input_box.setFixedHeight(580)
else:
self.input_box.setFixedHeight(80)
self.setFixedSize(width, height)
self.put_location()
def put_window_to_right_side_of_screen(self):
screen = QDesktopWidget().screenGeometry()
window = self.frameGeometry()
# Calculate x position for the right side of the screen and center vertically
x = screen.width() - window.width() # To right side
y = (screen.height() - window.height()) // 2 # Center vertically
# Add a small offset to the right side
x -= 10
self.move(x, y)