Spaces:
Runtime error
Runtime error
Upload 48 files
Browse files- .gitattributes +1 -0
- example_use_cases/workday_summerizer.md +55 -0
- gpt_computer_assistant/__init__.py +7 -0
- gpt_computer_assistant/agent/__init__.py +4 -0
- gpt_computer_assistant/agent/agent.py +80 -0
- gpt_computer_assistant/agent/agent_tools.py +56 -0
- gpt_computer_assistant/agent/assistant.py +247 -0
- gpt_computer_assistant/agent/background.py +15 -0
- gpt_computer_assistant/agent/chat_history.py +22 -0
- gpt_computer_assistant/agent/process.py +266 -0
- gpt_computer_assistant/agentic.py +34 -0
- gpt_computer_assistant/api.py +198 -0
- gpt_computer_assistant/audio/__init__.py +1 -0
- gpt_computer_assistant/audio/record.py +140 -0
- gpt_computer_assistant/audio/stt.py +65 -0
- gpt_computer_assistant/audio/tts.py +77 -0
- gpt_computer_assistant/audio/wake_word.py +37 -0
- gpt_computer_assistant/custom_callback.py +19 -0
- gpt_computer_assistant/display_tools.py +220 -0
- gpt_computer_assistant/gpt_computer_assistant.py +1125 -0
- gpt_computer_assistant/gui/__init__.py +0 -0
- gpt_computer_assistant/gui/button.py +165 -0
- gpt_computer_assistant/gui/llmsettings.py +236 -0
- gpt_computer_assistant/gui/settings.py +352 -0
- gpt_computer_assistant/gui/signal.py +28 -0
- gpt_computer_assistant/llm.py +71 -0
- gpt_computer_assistant/llm_settings.py +71 -0
- gpt_computer_assistant/remote.py +59 -0
- gpt_computer_assistant/screen/__init__.py +0 -0
- gpt_computer_assistant/screen/shot.py +49 -0
- gpt_computer_assistant/standard_tools.py +218 -0
- gpt_computer_assistant/start.py +57 -0
- gpt_computer_assistant/teams.py +274 -0
- gpt_computer_assistant/tooler.py +25 -0
- gpt_computer_assistant/top_bar_wrapper.py +19 -0
- gpt_computer_assistant/utils/db.py +428 -0
- gpt_computer_assistant/utils/media/Audio.png +0 -0
- gpt_computer_assistant/utils/media/Down.png +0 -0
- gpt_computer_assistant/utils/media/Microphone.png +0 -0
- gpt_computer_assistant/utils/media/SF-Pro-Text-Bold.otf +3 -0
- gpt_computer_assistant/utils/media/Screenshot.png +0 -0
- gpt_computer_assistant/utils/media/Up.png +0 -0
- gpt_computer_assistant/utils/media/icon.ico +0 -0
- gpt_computer_assistant/utils/media/icon_16.png +0 -0
- gpt_computer_assistant/utils/media/icon_24.png +0 -0
- gpt_computer_assistant/utils/media/icon_256.png +0 -0
- gpt_computer_assistant/utils/media/icon_32.png +0 -0
- gpt_computer_assistant/utils/media/icon_48.png +0 -0
- gpt_computer_assistant/utils/telemetry.py +49 -0
.gitattributes
CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
gpt_computer_assistant/utils/media/SF-Pro-Text-Bold.otf filter=lfs diff=lfs merge=lfs -text
|
example_use_cases/workday_summerizer.md
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Introduction
|
2 |
+
In this example we have an idea to summerize whole day of an employee via GPT Computer Assistant.
|
3 |
+
|
4 |
+
|
5 |
+
|
6 |
+
# Code
|
7 |
+
```console
|
8 |
+
computerassistant --api
|
9 |
+
```
|
10 |
+
|
11 |
+
|
12 |
+
```python
|
13 |
+
from gpt_computer_assistant.remote import remote
|
14 |
+
|
15 |
+
|
16 |
+
|
17 |
+
remote.profile("Screen Analysis")
|
18 |
+
|
19 |
+
# We will loop for 5 minutes
|
20 |
+
|
21 |
+
loop_results = []
|
22 |
+
|
23 |
+
|
24 |
+
for i in range(1000):
|
25 |
+
remote.reset_memory()
|
26 |
+
|
27 |
+
remote.just_screenshot()
|
28 |
+
|
29 |
+
detailed_analyses = remote.input("What is in the scren, detailed analyses")
|
30 |
+
app_name = remote.input("What is the app that the employee is using?")
|
31 |
+
subject = remote.input("What is the subject of this usage of the app?")
|
32 |
+
activity = remote.input("What is the employee doing now?")
|
33 |
+
loop_results.append({"detailed_analyses": detailed_analyses, "app_name": app_name, "subject": subject, "activity": activity})
|
34 |
+
|
35 |
+
|
36 |
+
remote.wait(10)
|
37 |
+
|
38 |
+
|
39 |
+
# Summery of the work day
|
40 |
+
|
41 |
+
summery_results = []
|
42 |
+
|
43 |
+
remote.profile("Summerizer")
|
44 |
+
remote.reset_memory()
|
45 |
+
for i in loop_results:
|
46 |
+
|
47 |
+
total_string = i["detailed_analyses"] + " " + i["app_name"] + " " + i["subject"] + " " + i["activity"]
|
48 |
+
total_string = "Please summerize the work day" + total_string
|
49 |
+
summerized = remote.input(total_string)
|
50 |
+
summery_results.append(summerized)
|
51 |
+
|
52 |
+
|
53 |
+
print("Summery: ", summery_results)
|
54 |
+
|
55 |
+
```
|
gpt_computer_assistant/__init__.py
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from .start import start
|
2 |
+
|
3 |
+
from .agentic import Agent
|
4 |
+
|
5 |
+
from .tooler import Tool
|
6 |
+
|
7 |
+
__version__ = '0.19.1'
|
gpt_computer_assistant/agent/__init__.py
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from .agent import *
|
2 |
+
from .assistant import *
|
3 |
+
from .background import *
|
4 |
+
from .chat_history import *
|
gpt_computer_assistant/agent/agent.py
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
try:
|
2 |
+
from ..llm import get_model
|
3 |
+
from ..utils.db import *
|
4 |
+
from ..llm_settings import llm_settings
|
5 |
+
from ..tooler import *
|
6 |
+
from ..display_tools import *
|
7 |
+
from ..teams import *
|
8 |
+
from .agent_tools import get_tools
|
9 |
+
except ImportError:
|
10 |
+
from llm import get_model
|
11 |
+
from utils.db import *
|
12 |
+
from llm_settings import llm_settings
|
13 |
+
from tooler import *
|
14 |
+
from display_tools import *
|
15 |
+
from teams import *
|
16 |
+
from agent_tools import get_tools
|
17 |
+
|
18 |
+
|
19 |
+
from langchain.agents import AgentExecutor, create_json_chat_agent
|
20 |
+
|
21 |
+
|
22 |
+
from langgraph.prebuilt import chat_agent_executor
|
23 |
+
|
24 |
+
|
25 |
+
custom_tools = []
|
26 |
+
|
27 |
+
|
28 |
+
|
29 |
+
|
30 |
+
prompt_cache = {}
|
31 |
+
|
32 |
+
|
33 |
+
def get_prompt(name):
|
34 |
+
global prompt_cache
|
35 |
+
if name in prompt_cache:
|
36 |
+
return prompt_cache[name]
|
37 |
+
else:
|
38 |
+
from langchain import hub
|
39 |
+
|
40 |
+
prompt = hub.pull(name)
|
41 |
+
prompt_cache[name] = prompt
|
42 |
+
return prompt
|
43 |
+
|
44 |
+
|
45 |
+
|
46 |
+
def get_agent_executor():
|
47 |
+
global custom_tools
|
48 |
+
tools = get_tools()
|
49 |
+
tools += custom_tools
|
50 |
+
|
51 |
+
if is_predefined_agents_setting_active():
|
52 |
+
try:
|
53 |
+
import crewai
|
54 |
+
tools += [search_on_internet_and_report_team, generate_code_with_aim_team]
|
55 |
+
except ImportError:
|
56 |
+
pass
|
57 |
+
|
58 |
+
|
59 |
+
model = load_model_settings()
|
60 |
+
|
61 |
+
|
62 |
+
if llm_settings[model]["provider"] == "openai":
|
63 |
+
tools += [click_on_a_text_on_the_screen, click_on_a_icon_on_the_screen, move_on_a_text_on_the_screen, move_on_a_icon_on_the_screen, mouse_scroll]
|
64 |
+
|
65 |
+
|
66 |
+
if llm_settings[model]["provider"] == "openai" or llm_settings[model]["provider"] == "groq":
|
67 |
+
return chat_agent_executor.create_tool_calling_executor(get_model(), tools)
|
68 |
+
|
69 |
+
|
70 |
+
|
71 |
+
if llm_settings[model]["provider"] == "ollama":
|
72 |
+
from langchain import hub
|
73 |
+
|
74 |
+
prompt = get_prompt("hwchase17/react-chat-json")
|
75 |
+
the_agent = create_json_chat_agent(get_model(), tools, prompt)
|
76 |
+
return AgentExecutor(
|
77 |
+
agent=the_agent, tools=tools, verbose=True, handle_parsing_errors=True
|
78 |
+
)
|
79 |
+
|
80 |
+
|
gpt_computer_assistant/agent/agent_tools.py
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
try:
|
2 |
+
from ..utils.db import *
|
3 |
+
from ..tooler import *
|
4 |
+
from ..display_tools import *
|
5 |
+
from ..teams import *
|
6 |
+
except ImportError:
|
7 |
+
|
8 |
+
from utils.db import *
|
9 |
+
|
10 |
+
from tooler import *
|
11 |
+
from display_tools import *
|
12 |
+
from teams import *
|
13 |
+
|
14 |
+
|
15 |
+
|
16 |
+
custom_tools = []
|
17 |
+
|
18 |
+
|
19 |
+
|
20 |
+
def load_tiger_tools():
|
21 |
+
try:
|
22 |
+
from upsonic import Tiger
|
23 |
+
tools = Tiger()
|
24 |
+
tools.enable_auto_requirements = True
|
25 |
+
tools = tools.langchain()
|
26 |
+
return tools
|
27 |
+
except:
|
28 |
+
return False
|
29 |
+
|
30 |
+
|
31 |
+
def load_default_tools():
|
32 |
+
from ..standard_tools import get_standard_tools
|
33 |
+
return get_standard_tools()
|
34 |
+
|
35 |
+
|
36 |
+
|
37 |
+
|
38 |
+
cached_tiger_tools = None
|
39 |
+
|
40 |
+
def get_tiger_tools():
|
41 |
+
global cached_tiger_tools
|
42 |
+
if cached_tiger_tools is None:
|
43 |
+
cached_tiger_tools = load_tiger_tools()
|
44 |
+
return cached_tiger_tools
|
45 |
+
|
46 |
+
if is_online_tools_setting_active():
|
47 |
+
get_tiger_tools()
|
48 |
+
|
49 |
+
def get_tools():
|
50 |
+
if is_online_tools_setting_active():
|
51 |
+
tools = get_tiger_tools()
|
52 |
+
if not tools:
|
53 |
+
tools = load_default_tools()
|
54 |
+
else:
|
55 |
+
tools = load_default_tools()
|
56 |
+
return tools
|
gpt_computer_assistant/agent/assistant.py
ADDED
@@ -0,0 +1,247 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
|
2 |
+
|
3 |
+
from .chat_history import *
|
4 |
+
from .agent import *
|
5 |
+
|
6 |
+
|
7 |
+
try:
|
8 |
+
from ..screen.shot import *
|
9 |
+
from ..utils.db import load_model_settings, agents
|
10 |
+
from ..llm import get_model
|
11 |
+
from ..llm_settings import each_message_extension, llm_settings
|
12 |
+
except ImportError:
|
13 |
+
from screen.shot import *
|
14 |
+
from utils.db import load_model_settings, agents
|
15 |
+
from llm import get_model
|
16 |
+
from llm_settings import each_message_extension, llm_settings
|
17 |
+
|
18 |
+
config = {"configurable": {"thread_id": "abc123"}}
|
19 |
+
|
20 |
+
|
21 |
+
def agentic(
|
22 |
+
llm_input, llm_history, client, screenshot_path=None, dont_save_image=False
|
23 |
+
):
|
24 |
+
global agents
|
25 |
+
from crewai import Task, Crew
|
26 |
+
|
27 |
+
from crewai import Agent as crewai_Agent
|
28 |
+
|
29 |
+
the_agents = []
|
30 |
+
|
31 |
+
for each in agents:
|
32 |
+
the_agents.append(
|
33 |
+
crewai_Agent(
|
34 |
+
role=each["role"],
|
35 |
+
goal=each["goal"],
|
36 |
+
backstory=each["backstory"],
|
37 |
+
llm=get_model(high_context=True),
|
38 |
+
)
|
39 |
+
)
|
40 |
+
|
41 |
+
agents = the_agents
|
42 |
+
|
43 |
+
print("LLM INPUT", llm_input)
|
44 |
+
|
45 |
+
def image_explaination():
|
46 |
+
the_message = [
|
47 |
+
{"type": "text", "text": "Explain the image"},
|
48 |
+
]
|
49 |
+
|
50 |
+
if screenshot_path:
|
51 |
+
base64_image = encode_image(screenshot_path)
|
52 |
+
the_message.append(
|
53 |
+
{
|
54 |
+
"type": "image_url",
|
55 |
+
"image_url": {"url": f"data:image/jpeg;base64,{base64_image}"},
|
56 |
+
},
|
57 |
+
)
|
58 |
+
print("LEN OF İMAGE", len(base64_image))
|
59 |
+
|
60 |
+
the_message = HumanMessage(content=the_message)
|
61 |
+
get_chat_message_history().add_message(the_message)
|
62 |
+
|
63 |
+
the_model = load_model_settings()
|
64 |
+
|
65 |
+
if llm_settings[the_model]["provider"] == "openai":
|
66 |
+
msg = get_agent_executor().invoke(
|
67 |
+
{"messages": llm_history + [the_message]}, config=config
|
68 |
+
)
|
69 |
+
|
70 |
+
if llm_settings[the_model]["provider"] == "google":
|
71 |
+
msg = get_agent_executor().invoke(
|
72 |
+
{"messages": llm_history + [the_message]}, config=config
|
73 |
+
)
|
74 |
+
|
75 |
+
if llm_settings[the_model]["provider"] == "ollama":
|
76 |
+
|
77 |
+
msg = get_agent_executor().invoke(
|
78 |
+
{
|
79 |
+
"input": the_message,
|
80 |
+
"chat_history": llm_history,
|
81 |
+
}
|
82 |
+
)
|
83 |
+
|
84 |
+
the_last_messages = msg["messages"]
|
85 |
+
|
86 |
+
return the_last_messages[-1].content
|
87 |
+
|
88 |
+
if screenshot_path:
|
89 |
+
image_explain = image_explaination()
|
90 |
+
llm_input += "User Sent Image and image content is: " + image_explain
|
91 |
+
|
92 |
+
|
93 |
+
|
94 |
+
llm_input = llm_input + each_message_extension
|
95 |
+
|
96 |
+
|
97 |
+
task = Task(
|
98 |
+
description=llm_input, expected_output="Answer", agent=agents[0], tools=get_tools()
|
99 |
+
)
|
100 |
+
|
101 |
+
the_crew = Crew(
|
102 |
+
agents=agents,
|
103 |
+
tasks=[task],
|
104 |
+
full_output=True,
|
105 |
+
verbose=True,
|
106 |
+
)
|
107 |
+
|
108 |
+
result = the_crew.kickoff()["final_output"]
|
109 |
+
|
110 |
+
get_chat_message_history().add_message(HumanMessage(content=[llm_input.replace(each_message_extension, "")]))
|
111 |
+
get_chat_message_history().add_message(AIMessage(content=[result]))
|
112 |
+
|
113 |
+
return result
|
114 |
+
|
115 |
+
|
116 |
+
def assistant(
|
117 |
+
llm_input, llm_history, client, screenshot_path=None, dont_save_image=False
|
118 |
+
):
|
119 |
+
|
120 |
+
if len(agents) != 0:
|
121 |
+
print("Moving to Agentic")
|
122 |
+
return agentic(llm_input, llm_history, client, screenshot_path, dont_save_image)
|
123 |
+
|
124 |
+
print("LLM INPUT", llm_input)
|
125 |
+
|
126 |
+
llm_input = llm_input + each_message_extension
|
127 |
+
|
128 |
+
the_message = [
|
129 |
+
{"type": "text", "text": f"{llm_input}"},
|
130 |
+
]
|
131 |
+
|
132 |
+
if screenshot_path:
|
133 |
+
base64_image = encode_image(screenshot_path)
|
134 |
+
the_message.append(
|
135 |
+
{
|
136 |
+
"type": "image_url",
|
137 |
+
"image_url": {"url": f"data:image/jpeg;base64,{base64_image}"},
|
138 |
+
},
|
139 |
+
)
|
140 |
+
print("LEN OF IMAGE", len(base64_image))
|
141 |
+
|
142 |
+
|
143 |
+
|
144 |
+
the_message = HumanMessage(content=the_message)
|
145 |
+
get_chat_message_history().add_message(the_message)
|
146 |
+
|
147 |
+
the_model = load_model_settings()
|
148 |
+
|
149 |
+
if llm_settings[the_model]["provider"] == "openai":
|
150 |
+
msg = get_agent_executor().invoke(
|
151 |
+
{"messages": llm_history + [the_message]}, config=config
|
152 |
+
)
|
153 |
+
|
154 |
+
if llm_settings[the_model]["provider"] == "google":
|
155 |
+
the_history = []
|
156 |
+
for message in llm_history:
|
157 |
+
try:
|
158 |
+
|
159 |
+
if isinstance(message, SystemMessage):
|
160 |
+
the_mes = HumanMessage(content=message.content[0]["text"])
|
161 |
+
the_history.append(the_mes)
|
162 |
+
elif isinstance(message, HumanMessage):
|
163 |
+
the_mes = HumanMessage(content=message.content[0]["text"])
|
164 |
+
the_history.append(the_mes)
|
165 |
+
else:
|
166 |
+
the_mes = AIMessage(content=message.content[0]["text"])
|
167 |
+
the_history.append(the_mes)
|
168 |
+
except:
|
169 |
+
the_mes = AIMessage(content=message.content)
|
170 |
+
the_history.append(the_mes)
|
171 |
+
|
172 |
+
llm_input += each_message_extension
|
173 |
+
|
174 |
+
the_last_message = HumanMessage(content=llm_input)
|
175 |
+
msg = get_agent_executor().invoke(
|
176 |
+
{"messages": the_history + [the_last_message]}, config=config
|
177 |
+
)
|
178 |
+
|
179 |
+
elif llm_settings[the_model]["provider"] == "groq":
|
180 |
+
the_history = []
|
181 |
+
for message in llm_history:
|
182 |
+
try:
|
183 |
+
|
184 |
+
if isinstance(message, SystemMessage):
|
185 |
+
the_mes = SystemMessage(content=message.content[0]["text"])
|
186 |
+
the_history.append(the_mes)
|
187 |
+
elif isinstance(message, HumanMessage):
|
188 |
+
the_mes = HumanMessage(content=message.content[0]["text"])
|
189 |
+
the_history.append(the_mes)
|
190 |
+
else:
|
191 |
+
the_mes = AIMessage(content=message.content[0]["text"])
|
192 |
+
the_history.append(the_mes)
|
193 |
+
except:
|
194 |
+
the_mes = AIMessage(content=message.content)
|
195 |
+
the_history.append(the_mes)
|
196 |
+
|
197 |
+
llm_input += each_message_extension
|
198 |
+
|
199 |
+
the_last_message = HumanMessage(content=llm_input)
|
200 |
+
msg = get_agent_executor().invoke(
|
201 |
+
{"messages": the_history + [the_last_message]}, config=config
|
202 |
+
)
|
203 |
+
|
204 |
+
elif llm_settings[the_model]["provider"] == "ollama":
|
205 |
+
|
206 |
+
msg = get_agent_executor().invoke(
|
207 |
+
{
|
208 |
+
"input": the_message,
|
209 |
+
"chat_history": llm_history,
|
210 |
+
}
|
211 |
+
)
|
212 |
+
|
213 |
+
the_last_messages = msg["messages"]
|
214 |
+
|
215 |
+
|
216 |
+
|
217 |
+
if dont_save_image and screenshot_path is not None:
|
218 |
+
currently_messages = get_chat_message_history().messages
|
219 |
+
|
220 |
+
last_message = currently_messages[-1].content[0]
|
221 |
+
currently_messages.remove(currently_messages[-1])
|
222 |
+
|
223 |
+
get_chat_message_history().clear()
|
224 |
+
for message in currently_messages:
|
225 |
+
get_chat_message_history().add_message(message)
|
226 |
+
get_chat_message_history().add_message(HumanMessage(content=[last_message]))
|
227 |
+
|
228 |
+
get_chat_message_history().add_message(the_last_messages[-1])
|
229 |
+
|
230 |
+
|
231 |
+
|
232 |
+
|
233 |
+
# Replace each_message_extension with empty string
|
234 |
+
list_of_messages = get_chat_message_history().messages
|
235 |
+
|
236 |
+
get_chat_message_history().clear()
|
237 |
+
|
238 |
+
for message in list_of_messages:
|
239 |
+
try:
|
240 |
+
message.content[0]["text"] = message.content[0]["text"].replace(each_message_extension, "")
|
241 |
+
get_chat_message_history().add_message(message)
|
242 |
+
except:
|
243 |
+
get_chat_message_history().add_message(message)
|
244 |
+
|
245 |
+
|
246 |
+
|
247 |
+
return the_last_messages[-1].content
|
gpt_computer_assistant/agent/background.py
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.messages import SystemMessage
|
2 |
+
from .chat_history import *
|
3 |
+
from ..llm_settings import first_message
|
4 |
+
|
5 |
+
|
6 |
+
llm_history_oiginal = [
|
7 |
+
SystemMessage(
|
8 |
+
content=[
|
9 |
+
{
|
10 |
+
"type": "text",
|
11 |
+
"text": first_message,
|
12 |
+
}
|
13 |
+
]
|
14 |
+
),
|
15 |
+
]
|
gpt_computer_assistant/agent/chat_history.py
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_community.chat_message_histories import SQLChatMessageHistory
|
2 |
+
from .background import llm_history_oiginal
|
3 |
+
try:
|
4 |
+
from ..utils.db import get_history_db
|
5 |
+
except ImportError:
|
6 |
+
from utils.db import get_history_db
|
7 |
+
|
8 |
+
|
9 |
+
def get_chat_message_history():
|
10 |
+
|
11 |
+
connection = SQLChatMessageHistory(
|
12 |
+
session_id="abc123", connection_string=f"sqlite:///{get_history_db()}"
|
13 |
+
)
|
14 |
+
if len(connection.messages) == 0:
|
15 |
+
connection.add_message(llm_history_oiginal[0])
|
16 |
+
|
17 |
+
return connection
|
18 |
+
|
19 |
+
|
20 |
+
def clear_chat_history():
|
21 |
+
get_chat_message_history().clear()
|
22 |
+
get_chat_message_history().add_message(llm_history_oiginal[0])
|
gpt_computer_assistant/agent/process.py
ADDED
@@ -0,0 +1,266 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
try:
|
2 |
+
from ..llm import *
|
3 |
+
from .assistant import *
|
4 |
+
from .chat_history import *
|
5 |
+
from ..audio.tts import text_to_speech
|
6 |
+
from ..audio.stt import speech_to_text
|
7 |
+
from ..audio.record import audio_data
|
8 |
+
from ..gui.signal import signal_handler
|
9 |
+
from ..utils.db import *
|
10 |
+
from ..utils.telemetry import my_tracer, os_name
|
11 |
+
except ImportError:
|
12 |
+
from llm import *
|
13 |
+
from agent.assistant import *
|
14 |
+
from agent.chat_history import *
|
15 |
+
from audio.tts import text_to_speech
|
16 |
+
from audio.stt import speech_to_text
|
17 |
+
from audio.record import audio_data
|
18 |
+
from gui.signal import signal_handler
|
19 |
+
from utils.db import *
|
20 |
+
from utils.telemetry import my_tracer, os_name
|
21 |
+
|
22 |
+
|
23 |
+
import threading
|
24 |
+
import traceback
|
25 |
+
|
26 |
+
|
27 |
+
from pygame import mixer
|
28 |
+
|
29 |
+
|
30 |
+
import time
|
31 |
+
|
32 |
+
last_ai_response = None
|
33 |
+
user_id = load_user_id()
|
34 |
+
os_name_ = os_name()
|
35 |
+
|
36 |
+
|
37 |
+
|
38 |
+
def tts_if_you_can(text:str, not_threaded=False, status_edit=False, bypass_other_settings = False):
|
39 |
+
try:
|
40 |
+
from ..gpt_computer_assistant import the_main_window
|
41 |
+
if (not is_just_text_model_active() and not the_main_window.api_enabled) or bypass_other_settings:
|
42 |
+
response_path = text_to_speech(text)
|
43 |
+
if status_edit:
|
44 |
+
signal_handler.assistant_response_ready.emit()
|
45 |
+
|
46 |
+
def play_audio():
|
47 |
+
for each_r in response_path:
|
48 |
+
mixer.init()
|
49 |
+
mixer.music.load(each_r)
|
50 |
+
mixer.music.play()
|
51 |
+
while mixer.music.get_busy():
|
52 |
+
if the_main_window.stop_talking:
|
53 |
+
mixer.music.stop()
|
54 |
+
break
|
55 |
+
time.sleep(0.1)
|
56 |
+
if status_edit:
|
57 |
+
signal_handler.assistant_response_stopped.emit()
|
58 |
+
if not not_threaded:
|
59 |
+
playback_thread = threading.Thread(target=play_audio)
|
60 |
+
playback_thread.start()
|
61 |
+
else:
|
62 |
+
play_audio()
|
63 |
+
except Exception as e:
|
64 |
+
pass
|
65 |
+
|
66 |
+
|
67 |
+
|
68 |
+
|
69 |
+
def process_audio(take_screenshot=True, take_system_audio=False, dont_save_image=False):
|
70 |
+
with my_tracer.start_span("process_audio") as span:
|
71 |
+
span.set_attribute("user_id", user_id)
|
72 |
+
span.set_attribute("os_name", os_name_)
|
73 |
+
try:
|
74 |
+
global audio_data, last_ai_response
|
75 |
+
from ..gpt_computer_assistant import the_input_box, the_main_window
|
76 |
+
from ..audio.record import audio_data, the_input_box_pre
|
77 |
+
|
78 |
+
|
79 |
+
transcription = speech_to_text(mic_record_location)
|
80 |
+
|
81 |
+
if take_system_audio:
|
82 |
+
|
83 |
+
transcription2 = speech_to_text(system_sound_location)
|
84 |
+
|
85 |
+
llm_input = transcription
|
86 |
+
|
87 |
+
print("Previously AI response", last_ai_response, "end prev")
|
88 |
+
|
89 |
+
print("Input Box AI", the_input_box_pre)
|
90 |
+
|
91 |
+
|
92 |
+
if (
|
93 |
+
the_input_box_pre != ""
|
94 |
+
and not the_input_box_pre.startswith("System:")
|
95 |
+
and the_input_box_pre not in last_ai_response
|
96 |
+
):
|
97 |
+
llm_input += the_input_box_pre
|
98 |
+
|
99 |
+
if take_system_audio:
|
100 |
+
llm_input += " \n Other of USER: " + transcription2
|
101 |
+
|
102 |
+
if the_input_box.toPlainText().startswith("System:"):
|
103 |
+
the_main_window.update_from_thread("Transciption Completed. Running AI...")
|
104 |
+
|
105 |
+
|
106 |
+
print("LLM INPUT (screenshot)", llm_input)
|
107 |
+
|
108 |
+
llm_output = assistant(
|
109 |
+
llm_input,
|
110 |
+
get_chat_message_history().messages,
|
111 |
+
get_client(),
|
112 |
+
screenshot_path=screenshot_path if take_screenshot else None,
|
113 |
+
dont_save_image=dont_save_image,
|
114 |
+
)
|
115 |
+
if the_input_box.toPlainText().startswith("System:"):
|
116 |
+
the_main_window.update_from_thread("AI Response Completed. Generating Audio...")
|
117 |
+
last_ai_response = llm_output
|
118 |
+
|
119 |
+
from ..gpt_computer_assistant import the_main_window
|
120 |
+
|
121 |
+
signal_handler.assistant_response_ready.emit()
|
122 |
+
|
123 |
+
def play_text():
|
124 |
+
from ..gpt_computer_assistant import the_input_box, the_main_window
|
125 |
+
|
126 |
+
the_main_window.complated_answer = True
|
127 |
+
the_main_window.manuel_stop = True
|
128 |
+
while the_main_window.reading_thread or the_main_window.reading_thread_2:
|
129 |
+
time.sleep(0.1)
|
130 |
+
the_main_window.read_part_task()
|
131 |
+
if the_main_window.stop_talking:
|
132 |
+
the_main_window.stop_talking = False
|
133 |
+
signal_handler.assistant_response_stopped.emit()
|
134 |
+
|
135 |
+
playback_thread = threading.Thread(target=play_text)
|
136 |
+
playback_thread.start()
|
137 |
+
except Exception as e:
|
138 |
+
print("Error in process_audio", e)
|
139 |
+
traceback.print_exc()
|
140 |
+
from ..gpt_computer_assistant import the_input_box, the_main_window
|
141 |
+
the_main_window.update_from_thread("EXCEPTION: " + str(e))
|
142 |
+
tts_if_you_can("Exception occurred. Please check the logs.")
|
143 |
+
signal_handler.assistant_response_stopped.emit()
|
144 |
+
|
145 |
+
|
146 |
+
def process_screenshot():
|
147 |
+
with my_tracer.start_span("process_screenshot") as span:
|
148 |
+
span.set_attribute("user_id", user_id)
|
149 |
+
span.set_attribute("os_name", os_name_)
|
150 |
+
try:
|
151 |
+
|
152 |
+
|
153 |
+
global last_ai_response
|
154 |
+
from ..gpt_computer_assistant import the_input_box, the_main_window
|
155 |
+
from ..audio.record import audio_data, the_input_box_pre
|
156 |
+
|
157 |
+
llm_input = "I just take a screenshot. for you to remember. Just say ok."
|
158 |
+
|
159 |
+
|
160 |
+
if (
|
161 |
+
the_input_box_pre != ""
|
162 |
+
and not the_input_box_pre.startswith("System:")
|
163 |
+
and the_input_box_pre not in last_ai_response
|
164 |
+
):
|
165 |
+
llm_input += the_input_box_pre
|
166 |
+
|
167 |
+
print("LLM INPUT (just screenshot)", llm_input)
|
168 |
+
|
169 |
+
if the_input_box.toPlainText().startswith("System:"):
|
170 |
+
the_main_window.update_from_thread("Transciption Completed. Running AI...")
|
171 |
+
|
172 |
+
|
173 |
+
|
174 |
+
llm_output = assistant(
|
175 |
+
llm_input,
|
176 |
+
get_chat_message_history().messages,
|
177 |
+
get_client(),
|
178 |
+
screenshot_path=just_screenshot_path,
|
179 |
+
dont_save_image=False,
|
180 |
+
)
|
181 |
+
|
182 |
+
if the_input_box.toPlainText().startswith("System:"):
|
183 |
+
the_main_window.update_from_thread("AI Response Completed. Generating Audio...")
|
184 |
+
|
185 |
+
last_ai_response = llm_output
|
186 |
+
|
187 |
+
from ..gpt_computer_assistant import the_main_window
|
188 |
+
|
189 |
+
signal_handler.assistant_response_ready.emit()
|
190 |
+
|
191 |
+
def play_text():
|
192 |
+
from ..gpt_computer_assistant import the_input_box, the_main_window
|
193 |
+
|
194 |
+
|
195 |
+
the_main_window.complated_answer = True
|
196 |
+
the_main_window.manuel_stop = True
|
197 |
+
while the_main_window.reading_thread or the_main_window.reading_thread_2:
|
198 |
+
time.sleep(0.1)
|
199 |
+
the_main_window.read_part_task()
|
200 |
+
if the_main_window.stop_talking:
|
201 |
+
the_main_window.stop_talking = False
|
202 |
+
signal_handler.assistant_response_stopped.emit()
|
203 |
+
|
204 |
+
playback_thread = threading.Thread(target=play_text)
|
205 |
+
playback_thread.start()
|
206 |
+
|
207 |
+
|
208 |
+
except Exception as e:
|
209 |
+
print("Error in process_screenshot", e)
|
210 |
+
traceback.print_exc()
|
211 |
+
from ..gpt_computer_assistant import the_input_box, the_main_window
|
212 |
+
the_main_window.update_from_thread("EXCEPTION: " + str(e))
|
213 |
+
tts_if_you_can("Exception occurred. Please check the logs.")
|
214 |
+
signal_handler.assistant_response_stopped.emit()
|
215 |
+
|
216 |
+
|
217 |
+
|
218 |
+
def process_text(text, screenshot_path=None):
|
219 |
+
with my_tracer.start_span("process_text") as span:
|
220 |
+
span.set_attribute("user_id", user_id)
|
221 |
+
span.set_attribute("os_name", os_name_)
|
222 |
+
try:
|
223 |
+
|
224 |
+
global last_ai_response
|
225 |
+
|
226 |
+
llm_input = text
|
227 |
+
|
228 |
+
|
229 |
+
llm_output = assistant(
|
230 |
+
llm_input,
|
231 |
+
get_chat_message_history().messages,
|
232 |
+
get_client(),
|
233 |
+
screenshot_path=screenshot_path,
|
234 |
+
dont_save_image=True,
|
235 |
+
)
|
236 |
+
last_ai_response = llm_output
|
237 |
+
|
238 |
+
from ..gpt_computer_assistant import the_main_window
|
239 |
+
signal_handler.assistant_response_ready.emit()
|
240 |
+
|
241 |
+
def play_text():
|
242 |
+
from ..gpt_computer_assistant import the_input_box, the_main_window
|
243 |
+
|
244 |
+
|
245 |
+
the_main_window.complated_answer = True
|
246 |
+
the_main_window.manuel_stop = True
|
247 |
+
while the_main_window.reading_thread or the_main_window.reading_thread_2:
|
248 |
+
time.sleep(0.1)
|
249 |
+
the_main_window.read_part_task()
|
250 |
+
if the_main_window.stop_talking:
|
251 |
+
the_main_window.stop_talking = False
|
252 |
+
signal_handler.assistant_response_stopped.emit()
|
253 |
+
|
254 |
+
playback_thread = threading.Thread(target=play_text)
|
255 |
+
playback_thread.start()
|
256 |
+
|
257 |
+
|
258 |
+
except Exception as e:
|
259 |
+
print("Error in process_text", e)
|
260 |
+
traceback.print_exc()
|
261 |
+
from ..gpt_computer_assistant import the_input_box, the_main_window
|
262 |
+
the_main_window.update_from_thread("EXCEPTION: " + str(e))
|
263 |
+
tts_if_you_can("Exception occurred. Please check the logs.")
|
264 |
+
signal_handler.assistant_response_stopped.emit()
|
265 |
+
|
266 |
+
|
gpt_computer_assistant/agentic.py
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from .utils.db import agents
|
2 |
+
|
3 |
+
class Agent:
|
4 |
+
"""
|
5 |
+
Represents an agent within the system.
|
6 |
+
|
7 |
+
This class defines an agent with a specific role, goal, and backstory. Upon initialization,
|
8 |
+
the agent is added to the global list of agents.
|
9 |
+
|
10 |
+
Attributes:
|
11 |
+
- role (str): The role of the agent.
|
12 |
+
- goal (str): The goal or objective of the agent.
|
13 |
+
- backstory (str): The backstory or history of the agent.
|
14 |
+
|
15 |
+
Methods:
|
16 |
+
- __init__(role, goal, backstory): Initializes the Agent object and adds it to the global list of agents.
|
17 |
+
|
18 |
+
Global Variables:
|
19 |
+
- agents (list): A global list containing information about all agents in the system.
|
20 |
+
"""
|
21 |
+
def __init__(self, role, goal, backstory):
|
22 |
+
"""
|
23 |
+
Initializes a new Agent object and adds it to the global list of agents.
|
24 |
+
|
25 |
+
Parameters:
|
26 |
+
- role (str): The role of the agent.
|
27 |
+
- goal (str): The goal or objective of the agent.
|
28 |
+
- backstory (str): The backstory or history of the agent.
|
29 |
+
|
30 |
+
Returns:
|
31 |
+
None
|
32 |
+
"""
|
33 |
+
global agents
|
34 |
+
agents.append({"role": role, "goal": goal, "backstory": backstory})
|
gpt_computer_assistant/api.py
ADDED
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Create a python api and start_api function via flask
|
2 |
+
|
3 |
+
from flask import Flask, request, jsonify
|
4 |
+
import threading
|
5 |
+
import time
|
6 |
+
|
7 |
+
from werkzeug.serving import make_server
|
8 |
+
|
9 |
+
app = Flask(__name__)
|
10 |
+
|
11 |
+
@app.route("/input", methods=["POST"])
|
12 |
+
def input():
|
13 |
+
"""
|
14 |
+
This function receives input from the user and returns the response.
|
15 |
+
"""
|
16 |
+
data = request.json
|
17 |
+
text = data["text"]
|
18 |
+
screen = data["screen"]
|
19 |
+
talk = data["talk"]
|
20 |
+
print("Input:", text)
|
21 |
+
from .gpt_computer_assistant import the_main_window, the_input_box
|
22 |
+
|
23 |
+
firsst_text = the_input_box.toPlainText()
|
24 |
+
|
25 |
+
if talk == "true":
|
26 |
+
the_main_window.api_enabled = False
|
27 |
+
the_main_window.manuel_stop = True
|
28 |
+
|
29 |
+
if screen != "true":
|
30 |
+
the_main_window.button_handler.input_text(text)
|
31 |
+
else:
|
32 |
+
the_main_window.button_handler.input_text_screenshot(text)
|
33 |
+
|
34 |
+
|
35 |
+
while the_input_box.toPlainText() == firsst_text:
|
36 |
+
time.sleep(0.3)
|
37 |
+
|
38 |
+
while the_input_box.toPlainText().startswith("System:"):
|
39 |
+
time.sleep(0.3)
|
40 |
+
|
41 |
+
response = the_input_box.toPlainText()
|
42 |
+
|
43 |
+
|
44 |
+
|
45 |
+
if talk == "true":
|
46 |
+
the_main_window.api_enabled = True
|
47 |
+
|
48 |
+
return jsonify({"response": response})
|
49 |
+
|
50 |
+
|
51 |
+
@app.route("/screenshot", methods=["POST"])
|
52 |
+
def screenshot():
|
53 |
+
"""
|
54 |
+
This function receives a screenshot from the user and returns the response.
|
55 |
+
"""
|
56 |
+
from .gpt_computer_assistant import the_main_window, the_input_box
|
57 |
+
firsst_text = the_input_box.toPlainText()
|
58 |
+
the_main_window.button_handler.just_screenshot()
|
59 |
+
|
60 |
+
while the_input_box.toPlainText() == firsst_text:
|
61 |
+
time.sleep(0.3)
|
62 |
+
|
63 |
+
while the_input_box.toPlainText().startswith("System:"):
|
64 |
+
time.sleep(0.3)
|
65 |
+
|
66 |
+
response = the_input_box.toPlainText()
|
67 |
+
|
68 |
+
return jsonify({"response": response})
|
69 |
+
|
70 |
+
|
71 |
+
|
72 |
+
@app.route("/tts", methods=["POST"])
|
73 |
+
def tts():
|
74 |
+
"""
|
75 |
+
This function receives a text to speech request from the user and returns the response.
|
76 |
+
"""
|
77 |
+
from .gpt_computer_assistant import the_main_window, the_input_box
|
78 |
+
the_main_window.api_enabled = False
|
79 |
+
the_main_window.manuel_stop = True
|
80 |
+
data = request.json
|
81 |
+
text = data["text"]
|
82 |
+
print("TTS:", text)
|
83 |
+
from .agent.process import tts_if_you_can
|
84 |
+
tts_if_you_can(text, not_threaded=True, status_edit=True)
|
85 |
+
the_main_window.api_enabled = True
|
86 |
+
return jsonify({"response": "TTS request received"})
|
87 |
+
|
88 |
+
@app.route("/profile", methods=["POST"])
|
89 |
+
def profile():
|
90 |
+
"""
|
91 |
+
This function sets the profile for the application.
|
92 |
+
"""
|
93 |
+
data = request.json
|
94 |
+
profile = data["profile"]
|
95 |
+
print("Profile:", profile)
|
96 |
+
from .utils.db import set_profile
|
97 |
+
set_profile(profile)
|
98 |
+
from .gpt_computer_assistant import the_main_window
|
99 |
+
the_main_window.update_from_thread("Profile set to "+profile)
|
100 |
+
return jsonify({"response": "Profile set to "+profile})
|
101 |
+
|
102 |
+
|
103 |
+
@app.route("/reset_memory", methods=["POST"])
|
104 |
+
def reset_memory():
|
105 |
+
"""
|
106 |
+
This function resets the memory of the application.
|
107 |
+
"""
|
108 |
+
from .agent.chat_history import clear_chat_history
|
109 |
+
clear_chat_history()
|
110 |
+
from .gpt_computer_assistant import the_main_window
|
111 |
+
the_main_window.update_from_thread("Memory reset")
|
112 |
+
return jsonify({"response": "Memory reset"})
|
113 |
+
|
114 |
+
|
115 |
+
|
116 |
+
@app.route("/activate_predefined_agents", methods=["POST"])
|
117 |
+
def enable_predefined_agents():
|
118 |
+
"""
|
119 |
+
This function enables predefined agents for the application.
|
120 |
+
"""
|
121 |
+
from .utils.db import activate_predefined_agents_setting
|
122 |
+
activate_predefined_agents_setting()
|
123 |
+
from .gpt_computer_assistant import the_main_window
|
124 |
+
the_main_window.update_from_thread("Predefined agents enabled")
|
125 |
+
return jsonify({"response": "Predefined agents enabled"})
|
126 |
+
|
127 |
+
@app.route("/deactivate_predefined_agents", methods=["POST"])
|
128 |
+
def disable_predefined_agents():
|
129 |
+
"""
|
130 |
+
This function disables predefined agents for the application.
|
131 |
+
"""
|
132 |
+
from .utils.db import deactivate_predefined_agents_setting
|
133 |
+
deactivate_predefined_agents_setting()
|
134 |
+
from .gpt_computer_assistant import the_main_window
|
135 |
+
the_main_window.update_from_thread("Predefined agents disabled")
|
136 |
+
return jsonify({"response": "Predefined agents disabled"})
|
137 |
+
|
138 |
+
|
139 |
+
@app.route("/activate_online_tools", methods=["POST"])
|
140 |
+
def enable_online_tools():
|
141 |
+
"""
|
142 |
+
This function enables online tools for the application.
|
143 |
+
"""
|
144 |
+
from .utils.db import activate_online_tools_setting
|
145 |
+
activate_online_tools_setting()
|
146 |
+
from .gpt_computer_assistant import the_main_window
|
147 |
+
the_main_window.update_from_thread("Online tools enabled")
|
148 |
+
return jsonify({"response": "Online tools enabled"})
|
149 |
+
|
150 |
+
|
151 |
+
|
152 |
+
@app.route("/deactivate_online_tools", methods=["POST"])
|
153 |
+
def disable_online_tools():
|
154 |
+
"""
|
155 |
+
This function disables online tools for the application.
|
156 |
+
"""
|
157 |
+
from .utils.db import deactivate_online_tools_setting
|
158 |
+
deactivate_online_tools_setting()
|
159 |
+
from .gpt_computer_assistant import the_main_window
|
160 |
+
the_main_window.update_from_thread("Online tools disabled")
|
161 |
+
return jsonify({"response": "Online tools disabled"})
|
162 |
+
|
163 |
+
|
164 |
+
class ServerThread(threading.Thread):
|
165 |
+
def __init__(self, app, host, port):
|
166 |
+
threading.Thread.__init__(self)
|
167 |
+
self.srv = make_server(host, port, app)
|
168 |
+
self.ctx = app.app_context()
|
169 |
+
self.ctx.push()
|
170 |
+
|
171 |
+
def run(self):
|
172 |
+
print("Starting server")
|
173 |
+
self.srv.serve_forever()
|
174 |
+
|
175 |
+
def shutdown(self):
|
176 |
+
print("Stopping server")
|
177 |
+
self.srv.shutdown()
|
178 |
+
|
179 |
+
server_thread = None
|
180 |
+
|
181 |
+
def start_api():
|
182 |
+
global server_thread
|
183 |
+
if server_thread is None:
|
184 |
+
server_thread = ServerThread(app, "localhost", 7541)
|
185 |
+
server_thread.start()
|
186 |
+
print("API started")
|
187 |
+
else:
|
188 |
+
print("API is already running")
|
189 |
+
|
190 |
+
def stop_api():
|
191 |
+
global server_thread
|
192 |
+
if server_thread is not None:
|
193 |
+
server_thread.shutdown()
|
194 |
+
server_thread.join()
|
195 |
+
server_thread = None
|
196 |
+
print("API stopped")
|
197 |
+
else:
|
198 |
+
print("API is not running")
|
gpt_computer_assistant/audio/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
from .record import *
|
gpt_computer_assistant/audio/record.py
ADDED
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
try:
|
2 |
+
from ..gui.signal import *
|
3 |
+
from ..utils.db import *
|
4 |
+
from ..utils.telemetry import my_tracer, os_name
|
5 |
+
except ImportError:
|
6 |
+
from gui.signal import *
|
7 |
+
from utils.db import *
|
8 |
+
from utils.telemetry import my_tracer, os_name
|
9 |
+
import numpy as np
|
10 |
+
import sounddevice as sd
|
11 |
+
import soundfile as sf
|
12 |
+
import scipy.io.wavfile as wavfile
|
13 |
+
import soundcard as sc
|
14 |
+
import threading
|
15 |
+
import time
|
16 |
+
|
17 |
+
samplerate = 48000 # Updated samplerate for better quality
|
18 |
+
channels = 1
|
19 |
+
recording = False
|
20 |
+
|
21 |
+
audio_data = None
|
22 |
+
|
23 |
+
user_id = load_user_id()
|
24 |
+
os_name_ = os_name()
|
25 |
+
|
26 |
+
the_input_box_pre = ""
|
27 |
+
|
28 |
+
|
29 |
+
|
30 |
+
import queue
|
31 |
+
|
32 |
+
# Initialize a queue to keep the last N audio levels (rolling window)
|
33 |
+
audio_levels = queue.Queue(maxsize=10) # Adjust size as needed
|
34 |
+
|
35 |
+
def calculate_dynamic_threshold():
|
36 |
+
"""Calculate a dynamic threshold based on recent audio levels."""
|
37 |
+
if audio_levels.qsize() == 0:
|
38 |
+
return 0.01 # Default threshold if no data is available
|
39 |
+
else:
|
40 |
+
# Calculate the average of the last N audio levels
|
41 |
+
return np.mean(list(audio_levels.queue)) * 2 # Adjust multiplier as needed
|
42 |
+
|
43 |
+
|
44 |
+
silence_start_time = None
|
45 |
+
|
46 |
+
auto_stop_recording = True
|
47 |
+
|
48 |
+
|
49 |
+
def start_recording(take_system_audio, buttonhandler):
|
50 |
+
"""Start recording audio from microphone and/or system sound.
|
51 |
+
|
52 |
+
|
53 |
+
"""
|
54 |
+
with my_tracer.start_span("start_recording") as span:
|
55 |
+
span.set_attribute("user_id", user_id)
|
56 |
+
span.set_attribute("os_name", os_name_)
|
57 |
+
|
58 |
+
global the_input_box_pre
|
59 |
+
from ..gpt_computer_assistant import the_input_box, the_main_window
|
60 |
+
the_input_box_pre = the_input_box.toPlainText()
|
61 |
+
|
62 |
+
the_main_window.update_from_thread("Click again when recording is done")
|
63 |
+
global recording, audio_data, silence_start_time, auto_stop_recording
|
64 |
+
recording = True
|
65 |
+
audio_data = np.array([], dtype="float32")
|
66 |
+
print("Recording started...")
|
67 |
+
|
68 |
+
threshold = 0.01 # Define the threshold for stopping the recording
|
69 |
+
silence_duration = 2 # Duration in seconds to consider as silence before stopping
|
70 |
+
silence_start_time = None
|
71 |
+
recording_start_time = time.time() # Record the start time of the recording
|
72 |
+
|
73 |
+
auto_stop_recording = is_auto_stop_recording_setting_active()
|
74 |
+
|
75 |
+
|
76 |
+
def callback(indata, frames, time_info, status):
|
77 |
+
global audio_data, recording, silence_start_time, auto_stop_recording
|
78 |
+
current_level = np.max(np.abs(indata))
|
79 |
+
|
80 |
+
|
81 |
+
# Add the current level to the queue
|
82 |
+
if audio_levels.full():
|
83 |
+
audio_levels.get() # Remove the oldest level if the queue is full
|
84 |
+
audio_levels.put(current_level)
|
85 |
+
|
86 |
+
# Calculate dynamic threshold based on recent audio levels
|
87 |
+
dynamic_threshold = calculate_dynamic_threshold()
|
88 |
+
|
89 |
+
|
90 |
+
if recording:
|
91 |
+
audio_data = np.append(audio_data, indata)
|
92 |
+
# Check if the audio is below the dynamic threshold
|
93 |
+
if current_level < dynamic_threshold and auto_stop_recording:
|
94 |
+
if silence_start_time is None:
|
95 |
+
silence_start_time = time.time() # Mark the start of silence
|
96 |
+
|
97 |
+
# Ensure recording has been ongoing for at least 3 seconds before considering auto-stop
|
98 |
+
elif (time.time() - silence_start_time) > silence_duration and (time.time() - recording_start_time) > 3:
|
99 |
+
recording = False
|
100 |
+
buttonhandler.recording = False
|
101 |
+
|
102 |
+
else:
|
103 |
+
silence_start_time = None
|
104 |
+
|
105 |
+
|
106 |
+
def record_audio():
|
107 |
+
with my_tracer.start_span("record_audio") as span:
|
108 |
+
span.set_attribute("user_id", user_id)
|
109 |
+
span.set_attribute("os_name", os_name_)
|
110 |
+
global recording
|
111 |
+
mics = sc.all_microphones(include_loopback=True)
|
112 |
+
default_mic = mics[0]
|
113 |
+
data = []
|
114 |
+
with default_mic.recorder(samplerate=148000) as mic:
|
115 |
+
print("Recording...")
|
116 |
+
while recording:
|
117 |
+
frame = mic.record(numframes=4096)
|
118 |
+
data.append(frame)
|
119 |
+
data = np.concatenate(data, axis=0)
|
120 |
+
data_int16 = (data * 32767).astype("int16")
|
121 |
+
wavfile.write(system_sound_location, 148000, data_int16)
|
122 |
+
|
123 |
+
if take_system_audio:
|
124 |
+
recording_thread = threading.Thread(target=record_audio)
|
125 |
+
recording_thread.start()
|
126 |
+
|
127 |
+
with sd.InputStream(callback=callback, channels=channels, samplerate=samplerate):
|
128 |
+
while recording:
|
129 |
+
sd.sleep(100)
|
130 |
+
|
131 |
+
if not recording:
|
132 |
+
sf.write(mic_record_location, audio_data, samplerate)
|
133 |
+
print("Audio saved as voice_input.wav")
|
134 |
+
signal_handler.recording_stopped.emit()
|
135 |
+
|
136 |
+
def stop_recording():
|
137 |
+
"""Stop recording audio."""
|
138 |
+
global recording
|
139 |
+
recording = False
|
140 |
+
print("Recording stopped")
|
gpt_computer_assistant/audio/stt.py
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
try:
|
2 |
+
from ..llm import get_client
|
3 |
+
except ImportError:
|
4 |
+
from llm import get_client
|
5 |
+
|
6 |
+
import os
|
7 |
+
from pydub import AudioSegment
|
8 |
+
|
9 |
+
|
10 |
+
def split_audio(file_path, max_size=20 * 1024 * 1024):
|
11 |
+
"""Split an audio file into smaller parts if it exceeds a maximum size.
|
12 |
+
|
13 |
+
Args:
|
14 |
+
file_path (str): The path to the audio file to be split.
|
15 |
+
max_size (int): The maximum size in bytes for each split part. Defaults to 20 MB.
|
16 |
+
|
17 |
+
Returns:
|
18 |
+
list: A list of tuples containing the split audio segments and their respective file paths.
|
19 |
+
"""
|
20 |
+
audio = AudioSegment.from_wav(file_path)
|
21 |
+
file_size = os.path.getsize(file_path)
|
22 |
+
if file_size <= max_size:
|
23 |
+
return [(audio, file_path)]
|
24 |
+
|
25 |
+
# Calculate the number of parts needed
|
26 |
+
num_parts = file_size // max_size + 1
|
27 |
+
part_length = len(audio) // num_parts
|
28 |
+
parts = []
|
29 |
+
|
30 |
+
for i in range(num_parts):
|
31 |
+
start = i * part_length
|
32 |
+
end = (i + 1) * part_length if (i + 1) < num_parts else len(audio)
|
33 |
+
part = audio[start:end]
|
34 |
+
part_path = f"{file_path[:-4]}_part_{i+1}.wav"
|
35 |
+
part.export(part_path, format="wav")
|
36 |
+
parts.append((part, part_path))
|
37 |
+
|
38 |
+
return parts
|
39 |
+
|
40 |
+
|
41 |
+
def speech_to_text(location):
|
42 |
+
"""Convert speech audio file to text using an external service.
|
43 |
+
|
44 |
+
Args:
|
45 |
+
location (str): The path to the speech audio file.
|
46 |
+
|
47 |
+
Returns:
|
48 |
+
str: The transcribed text from the speech audio file.
|
49 |
+
"""
|
50 |
+
audio_parts = split_audio(location)
|
51 |
+
transcriptions = []
|
52 |
+
|
53 |
+
for part, part_path in audio_parts:
|
54 |
+
with open(part_path, "rb") as audio_file:
|
55 |
+
transcription = get_client().audio.transcriptions.create(
|
56 |
+
model="whisper-1", file=audio_file
|
57 |
+
)
|
58 |
+
transcriptions.append(transcription)
|
59 |
+
os.remove(part_path) # Clean up the temporary file immediately after processing
|
60 |
+
|
61 |
+
# Merge transcriptions (assuming it's a list of text segments)
|
62 |
+
full_transcription = " ".join(
|
63 |
+
transcription.text for transcription in transcriptions
|
64 |
+
)
|
65 |
+
return full_transcription
|
gpt_computer_assistant/audio/tts.py
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
try:
|
2 |
+
from ..llm import *
|
3 |
+
from ..utils.db import artifacts_dir
|
4 |
+
except ImportError:
|
5 |
+
from llm import *
|
6 |
+
from utils.db import artifacts_dir
|
7 |
+
|
8 |
+
import os
|
9 |
+
import hashlib
|
10 |
+
import random
|
11 |
+
import threading
|
12 |
+
|
13 |
+
supported_openai_speakers = ["fable"]
|
14 |
+
|
15 |
+
def random_model(exclude):
|
16 |
+
models = supported_openai_speakers.copy()
|
17 |
+
models.remove(exclude)
|
18 |
+
return random.choice(models)
|
19 |
+
|
20 |
+
def generate_speech_chunk(text_chunk, index, voice, results):
|
21 |
+
sha = hashlib.sha256(text_chunk.encode()).hexdigest()
|
22 |
+
location = os.path.join(artifacts_dir, f"{sha}.mp3")
|
23 |
+
|
24 |
+
if os.path.exists(location):
|
25 |
+
results[index] = location
|
26 |
+
else:
|
27 |
+
response = get_client().audio.speech.create(
|
28 |
+
model="tts-1",
|
29 |
+
voice=voice,
|
30 |
+
input=text_chunk,
|
31 |
+
)
|
32 |
+
response.stream_to_file(location)
|
33 |
+
results[index] = location
|
34 |
+
|
35 |
+
def split_text_to_sentences(text, max_chunk_size=300):
|
36 |
+
"""Splits text into sentences and ensures chunks do not exceed max_chunk_size."""
|
37 |
+
sentences = text.split('.')
|
38 |
+
chunks = []
|
39 |
+
current_chunk = ""
|
40 |
+
|
41 |
+
for sentence in sentences:
|
42 |
+
sentence = sentence.strip()
|
43 |
+
if len(current_chunk) + len(sentence) + 1 <= max_chunk_size:
|
44 |
+
current_chunk += (sentence + '. ')
|
45 |
+
else:
|
46 |
+
chunks.append(current_chunk.strip())
|
47 |
+
current_chunk = sentence + '. '
|
48 |
+
|
49 |
+
if current_chunk:
|
50 |
+
chunks.append(current_chunk.strip())
|
51 |
+
|
52 |
+
return chunks
|
53 |
+
|
54 |
+
def text_to_speech(text):
|
55 |
+
text_chunks = split_text_to_sentences(text)
|
56 |
+
|
57 |
+
threads = []
|
58 |
+
results = [None] * len(text_chunks)
|
59 |
+
|
60 |
+
initial_voice = random.choice(supported_openai_speakers)
|
61 |
+
|
62 |
+
for i, chunk in enumerate(text_chunks):
|
63 |
+
voice = initial_voice if i % 2 == 0 else random_model(initial_voice) # Alternate voices
|
64 |
+
thread = threading.Thread(
|
65 |
+
target=generate_speech_chunk,
|
66 |
+
args=(chunk, i, voice, results)
|
67 |
+
)
|
68 |
+
threads.append(thread)
|
69 |
+
thread.start()
|
70 |
+
|
71 |
+
for thread in threads:
|
72 |
+
thread.join()
|
73 |
+
|
74 |
+
mp3_files = [result for result in results if result is not None]
|
75 |
+
|
76 |
+
return mp3_files
|
77 |
+
|
gpt_computer_assistant/audio/wake_word.py
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import struct
|
3 |
+
|
4 |
+
from ..utils.db import load_pvporcupine_api_key
|
5 |
+
|
6 |
+
|
7 |
+
def wake_word(the_main_window):
|
8 |
+
import pvporcupine
|
9 |
+
import pyaudio
|
10 |
+
|
11 |
+
porcupine = pvporcupine.create(access_key=load_pvporcupine_api_key(),
|
12 |
+
keywords=pvporcupine.KEYWORDS)
|
13 |
+
# Initialize PyAudio
|
14 |
+
pa = pyaudio.PyAudio()
|
15 |
+
|
16 |
+
# Open an audio stream
|
17 |
+
audio_stream = pa.open(
|
18 |
+
rate=porcupine.sample_rate,
|
19 |
+
channels=1,
|
20 |
+
format=pyaudio.paInt16,
|
21 |
+
input=True,
|
22 |
+
frames_per_buffer=porcupine.frame_length
|
23 |
+
)
|
24 |
+
|
25 |
+
print("Listening for wake word...")
|
26 |
+
|
27 |
+
# Continuously listen for the wake word
|
28 |
+
while the_main_window.wake_word_active:
|
29 |
+
pcm = audio_stream.read(porcupine.frame_length)
|
30 |
+
pcm = struct.unpack_from("h" * porcupine.frame_length, pcm)
|
31 |
+
|
32 |
+
# Process the audio frame and check for the wake word
|
33 |
+
keyword_index = porcupine.process(pcm)
|
34 |
+
|
35 |
+
if keyword_index >= 0:
|
36 |
+
print("Wake word detected!")
|
37 |
+
return True
|
gpt_computer_assistant/custom_callback.py
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Callback Handler streams to stdout on new llm token."""
|
2 |
+
from langchain.callbacks.streaming_stdout_final_only import FinalStreamingStdOutCallbackHandler
|
3 |
+
from typing import Any, Dict, List, Optional
|
4 |
+
|
5 |
+
class customcallback(FinalStreamingStdOutCallbackHandler):
|
6 |
+
def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
|
7 |
+
|
8 |
+
self.append_to_last_tokens(token)
|
9 |
+
|
10 |
+
|
11 |
+
if self.check_if_answer_reached():
|
12 |
+
self.answer_reached = True
|
13 |
+
|
14 |
+
return
|
15 |
+
|
16 |
+
|
17 |
+
if self.answer_reached:
|
18 |
+
from .gpt_computer_assistant import the_main_window
|
19 |
+
the_main_window.set_text_to_input_box(token)
|
gpt_computer_assistant/display_tools.py
ADDED
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain.tools import tool
|
2 |
+
import traceback
|
3 |
+
|
4 |
+
try:
|
5 |
+
from .utils.db import load_api_key
|
6 |
+
from .llm import get_model
|
7 |
+
except ImportError:
|
8 |
+
from utils.db import load_api_key
|
9 |
+
from llm import get_model
|
10 |
+
|
11 |
+
|
12 |
+
def click_on_a_text_on_the_screen_(text:str, click_type: str = "singular") -> bool:
|
13 |
+
"""
|
14 |
+
A function to click on a text on the screen.
|
15 |
+
|
16 |
+
Parameters:
|
17 |
+
- text (str): The text to be clicked on.
|
18 |
+
- click_type (str): The type of click to be performed. The default value is "singular". Possible values are "singular" and "double".
|
19 |
+
|
20 |
+
Returns:
|
21 |
+
- bool: True if the text was clicked on successfully, False otherwise.
|
22 |
+
"""
|
23 |
+
try:
|
24 |
+
import pyautogui
|
25 |
+
pyautogui.FAILSAFE = False
|
26 |
+
|
27 |
+
|
28 |
+
from interpreter import OpenInterpreter
|
29 |
+
|
30 |
+
|
31 |
+
|
32 |
+
|
33 |
+
|
34 |
+
interpreter = OpenInterpreter()
|
35 |
+
|
36 |
+
interpreter.llm.api_key = load_api_key()
|
37 |
+
|
38 |
+
screenshot = pyautogui.screenshot()
|
39 |
+
|
40 |
+
text_locations = interpreter.computer.display.find_text(text, screenshot=screenshot)
|
41 |
+
|
42 |
+
print(text_locations)
|
43 |
+
|
44 |
+
|
45 |
+
x, y = text_locations[0]["coordinates"]
|
46 |
+
x *= interpreter.computer.display.width
|
47 |
+
y *= interpreter.computer.display.height
|
48 |
+
x = int(x)
|
49 |
+
y = int(y)
|
50 |
+
|
51 |
+
if click_type == "singular":
|
52 |
+
interpreter.computer.mouse.click(x=x, y=y, screenshot=screenshot)
|
53 |
+
elif click_type == "double":
|
54 |
+
interpreter.computer.mouse.double_click(x=x, y=y, screenshot=screenshot)
|
55 |
+
return True
|
56 |
+
except:
|
57 |
+
traceback.print_exc()
|
58 |
+
return False
|
59 |
+
|
60 |
+
|
61 |
+
click_on_a_text_on_the_screen = tool(click_on_a_text_on_the_screen_)
|
62 |
+
|
63 |
+
|
64 |
+
|
65 |
+
|
66 |
+
def move_on_a_text_on_the_screen_(text:str) -> bool:
|
67 |
+
"""
|
68 |
+
A function to move on a text on the screen.
|
69 |
+
|
70 |
+
Parameters:
|
71 |
+
- text (str): The text to be moved on.
|
72 |
+
|
73 |
+
Returns:
|
74 |
+
- bool: True if the text was moved on successfully, False otherwise.
|
75 |
+
"""
|
76 |
+
try:
|
77 |
+
import pyautogui
|
78 |
+
pyautogui.FAILSAFE = False
|
79 |
+
|
80 |
+
|
81 |
+
from interpreter import OpenInterpreter
|
82 |
+
|
83 |
+
|
84 |
+
|
85 |
+
|
86 |
+
|
87 |
+
interpreter = OpenInterpreter()
|
88 |
+
|
89 |
+
interpreter.llm.api_key = load_api_key()
|
90 |
+
|
91 |
+
screenshot = pyautogui.screenshot()
|
92 |
+
|
93 |
+
text_locations = interpreter.computer.display.find_text(text, screenshot=screenshot)
|
94 |
+
|
95 |
+
print(text_locations)
|
96 |
+
|
97 |
+
|
98 |
+
x, y = text_locations[0]["coordinates"]
|
99 |
+
x *= interpreter.computer.display.width
|
100 |
+
y *= interpreter.computer.display.height
|
101 |
+
x = int(x)
|
102 |
+
y = int(y)
|
103 |
+
|
104 |
+
interpreter.computer.mouse.move(x=x, y=y, screenshot=screenshot)
|
105 |
+
|
106 |
+
return True
|
107 |
+
except:
|
108 |
+
traceback.print_exc()
|
109 |
+
return False
|
110 |
+
|
111 |
+
|
112 |
+
move_on_a_text_on_the_screen = tool(move_on_a_text_on_the_screen_)
|
113 |
+
|
114 |
+
|
115 |
+
|
116 |
+
def click_on_a_icon_on_the_screen_(icon_name:str, click_type: str = "singular") -> bool:
|
117 |
+
"""
|
118 |
+
A function to click on a icon name on the screen.
|
119 |
+
|
120 |
+
Parameters:
|
121 |
+
- icon_name (str): The icon name to be clicked on.
|
122 |
+
- click_type (str): The type of click to be performed. The default value is "singular". Possible values are "singular" and "double".
|
123 |
+
|
124 |
+
Returns:
|
125 |
+
- bool: True if the icon name was clicked on successfully, False otherwise.
|
126 |
+
"""
|
127 |
+
try:
|
128 |
+
import pyautogui
|
129 |
+
pyautogui.FAILSAFE = False
|
130 |
+
|
131 |
+
|
132 |
+
from interpreter import OpenInterpreter
|
133 |
+
|
134 |
+
|
135 |
+
screenshot = pyautogui.screenshot()
|
136 |
+
|
137 |
+
|
138 |
+
interpreter = OpenInterpreter()
|
139 |
+
|
140 |
+
interpreter.llm.api_key = load_api_key()
|
141 |
+
|
142 |
+
|
143 |
+
|
144 |
+
if click_type == "singular":
|
145 |
+
interpreter.computer.mouse.click(icon=icon_name, screenshot=screenshot)
|
146 |
+
elif click_type == "double":
|
147 |
+
interpreter.computer.mouse.double_click(icon=icon_name, screenshot=screenshot)
|
148 |
+
return True
|
149 |
+
|
150 |
+
except:
|
151 |
+
traceback.print_exc()
|
152 |
+
return False
|
153 |
+
|
154 |
+
click_on_a_icon_on_the_screen = tool(click_on_a_icon_on_the_screen_)
|
155 |
+
|
156 |
+
|
157 |
+
|
158 |
+
|
159 |
+
def move_on_a_icon_on_the_screen_(icon_name:str,) -> bool:
|
160 |
+
"""
|
161 |
+
A function to move on a icon name on the screen.
|
162 |
+
|
163 |
+
Parameters:
|
164 |
+
- icon_name (str): The icon name to be move on.
|
165 |
+
|
166 |
+
Returns:
|
167 |
+
- bool: True if the icon name was moved on successfully, False otherwise.
|
168 |
+
"""
|
169 |
+
try:
|
170 |
+
import pyautogui
|
171 |
+
pyautogui.FAILSAFE = False
|
172 |
+
|
173 |
+
|
174 |
+
from interpreter import OpenInterpreter
|
175 |
+
|
176 |
+
|
177 |
+
screenshot = pyautogui.screenshot()
|
178 |
+
|
179 |
+
|
180 |
+
interpreter = OpenInterpreter()
|
181 |
+
|
182 |
+
interpreter.llm.api_key = load_api_key()
|
183 |
+
|
184 |
+
interpreter.computer.mouse.move(icon=icon_name, screenshot=screenshot)
|
185 |
+
return True
|
186 |
+
|
187 |
+
except:
|
188 |
+
traceback.print_exc()
|
189 |
+
return False
|
190 |
+
|
191 |
+
move_on_a_icon_on_the_screen = tool(move_on_a_icon_on_the_screen_)
|
192 |
+
|
193 |
+
|
194 |
+
|
195 |
+
|
196 |
+
def mouse_scroll_(direction: str, amount: int = 1) -> bool:
|
197 |
+
"""
|
198 |
+
A function to scroll the mouse wheel.
|
199 |
+
|
200 |
+
Parameters:
|
201 |
+
- direction (str): The direction of the scroll. Possible values are "up" and "down".
|
202 |
+
- amount (int): The amount of scrolling to be performed. The default value is 1.
|
203 |
+
|
204 |
+
Returns:
|
205 |
+
- bool: True if the scrolling was performed successfully, False otherwise.
|
206 |
+
"""
|
207 |
+
try:
|
208 |
+
import pyautogui
|
209 |
+
pyautogui.FAILSAFE = False
|
210 |
+
|
211 |
+
if direction == "up":
|
212 |
+
pyautogui.scroll(amount)
|
213 |
+
elif direction == "down":
|
214 |
+
pyautogui.scroll(-amount)
|
215 |
+
return True
|
216 |
+
except:
|
217 |
+
traceback.print_exc()
|
218 |
+
return False
|
219 |
+
|
220 |
+
mouse_scroll = tool(mouse_scroll_)
|
gpt_computer_assistant/gpt_computer_assistant.py
ADDED
@@ -0,0 +1,1125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
try:
|
2 |
+
from .agent.chat_history import *
|
3 |
+
from .agent.assistant import *
|
4 |
+
from .llm import *
|
5 |
+
from .llm_settings import llm_settings
|
6 |
+
from .agent.agent import *
|
7 |
+
from .agent.background import *
|
8 |
+
|
9 |
+
from .gui.signal import *
|
10 |
+
from .gui.button import *
|
11 |
+
from .gui.settings import settings_popup
|
12 |
+
from .gui.llmsettings import llmsettings_popup
|
13 |
+
from .utils.db import *
|
14 |
+
from .utils.telemetry import my_tracer, os_name
|
15 |
+
|
16 |
+
from .audio.wake_word import wake_word
|
17 |
+
from .audio.tts import text_to_speech
|
18 |
+
|
19 |
+
except ImportError:
|
20 |
+
# This is for running the script directly
|
21 |
+
# in order to test the GUI without rebuilding the package
|
22 |
+
from agent.chat_history import *
|
23 |
+
from agent.assistant import *
|
24 |
+
from llm import *
|
25 |
+
from llm_settings import llm_settings
|
26 |
+
from agent.agent import *
|
27 |
+
from agent.background import *
|
28 |
+
from utils.db import *
|
29 |
+
from gui.signal import *
|
30 |
+
from gui.button import *
|
31 |
+
from gui.settings import settings_popup
|
32 |
+
from gui.llmsettings import llmsettings_popup
|
33 |
+
from utils.telemetry import my_tracer, os_name
|
34 |
+
from audio.wake_word import wake_word
|
35 |
+
from audio.tts import text_to_speech
|
36 |
+
import threading
|
37 |
+
import time
|
38 |
+
import random
|
39 |
+
import math
|
40 |
+
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
|
41 |
+
from PyQt5.QtGui import QMouseEvent, QPainter, QPen, QBrush, QIcon, QColor
|
42 |
+
from PyQt5.QtCore import Qt, QTimer, QRect, pyqtSignal
|
43 |
+
from PyQt5.QtGui import QKeySequence
|
44 |
+
from PyQt5.QtWidgets import QShortcut
|
45 |
+
from PyQt5.QtWidgets import QSpacerItem, QSizePolicy
|
46 |
+
|
47 |
+
from PyQt5.QtWidgets import (
|
48 |
+
QPushButton,
|
49 |
+
QLabel,
|
50 |
+
QHBoxLayout,
|
51 |
+
)
|
52 |
+
from PyQt5.QtCore import QPoint
|
53 |
+
|
54 |
+
from PyQt5.QtWidgets import QTextEdit
|
55 |
+
from PyQt5 import QtGui
|
56 |
+
from PyQt5.QtCore import QThread
|
57 |
+
|
58 |
+
|
59 |
+
print("Imported all libraries")
|
60 |
+
|
61 |
+
|
62 |
+
from PyQt5 import QtCore
|
63 |
+
|
64 |
+
|
65 |
+
try:
|
66 |
+
import ctypes
|
67 |
+
|
68 |
+
myappid = "onuratakan.gpt_computer_assistant.gui.1"
|
69 |
+
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
|
70 |
+
except:
|
71 |
+
pass
|
72 |
+
|
73 |
+
the_input_box = None
|
74 |
+
the_input_text = None
|
75 |
+
|
76 |
+
|
77 |
+
the_input_box_pre = None
|
78 |
+
|
79 |
+
|
80 |
+
the_main_window = None
|
81 |
+
|
82 |
+
|
83 |
+
user_id = load_user_id()
|
84 |
+
os_name_ = os_name()
|
85 |
+
|
86 |
+
|
87 |
+
|
88 |
+
readed_sentences = []
|
89 |
+
|
90 |
+
import re
|
91 |
+
def split_with_multiple_delimiters(text, delimiters):
|
92 |
+
"""
|
93 |
+
Splits the text by any of the given delimiters while keeping the delimiters in the resulting parts.
|
94 |
+
|
95 |
+
:param text: The input text to be split.
|
96 |
+
:param delimiters: A string of delimiters to split the text on.
|
97 |
+
:return: A list of parts including the delimiters.
|
98 |
+
"""
|
99 |
+
# Create a regular expression pattern that matches any of the delimiters
|
100 |
+
pattern = re.compile(f'(.*?[{re.escape(delimiters)}])')
|
101 |
+
parts = pattern.findall(text)
|
102 |
+
|
103 |
+
# Check if the last part is not complete and remove it if necessary
|
104 |
+
if (
|
105 |
+
parts and text
|
106 |
+
and not any(text.endswith(d) for d in delimiters)
|
107 |
+
and parts
|
108 |
+
and not any(parts[-1].endswith(d) for d in delimiters)
|
109 |
+
):
|
110 |
+
parts.pop()
|
111 |
+
|
112 |
+
return parts
|
113 |
+
|
114 |
+
|
115 |
+
|
116 |
+
|
117 |
+
class Worker(QThread):
|
118 |
+
text_to_set = pyqtSignal(str)
|
119 |
+
|
120 |
+
|
121 |
+
def __init__(self):
|
122 |
+
super().__init__()
|
123 |
+
self.the_input_text = None
|
124 |
+
self.make_animation = True
|
125 |
+
self.commited_text = []
|
126 |
+
|
127 |
+
def run(self):
|
128 |
+
while True:
|
129 |
+
self.msleep(500) # Simulate a time-consuming task
|
130 |
+
|
131 |
+
if self.the_input_text:
|
132 |
+
last_text = self.commited_text[-1] if len(self.commited_text) > 0 else ""
|
133 |
+
if self.the_input_text != last_text:
|
134 |
+
self.commited_text.append(self.the_input_text)
|
135 |
+
|
136 |
+
if len(self.the_input_text) > 90 or MainWindow.api_enabled or not self.make_animation:
|
137 |
+
self.text_to_set.emit(self.the_input_text)
|
138 |
+
else:
|
139 |
+
for i in range(len(self.the_input_text)):
|
140 |
+
self.text_to_set.emit(self.the_input_text[:i + 1])
|
141 |
+
self.msleep(10)
|
142 |
+
|
143 |
+
|
144 |
+
|
145 |
+
|
146 |
+
|
147 |
+
return_key_event = None
|
148 |
+
class CustomTextEdit(QTextEdit):
|
149 |
+
def __init__(self, parent=None):
|
150 |
+
super(CustomTextEdit, self).__init__(parent)
|
151 |
+
|
152 |
+
def keyPressEvent(self, event):
|
153 |
+
if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
|
154 |
+
global return_key_event
|
155 |
+
return_key_event()
|
156 |
+
super(CustomTextEdit, self).keyPressEvent(event) # Process other key events normally
|
157 |
+
|
158 |
+
|
159 |
+
|
160 |
+
|
161 |
+
class Worker_2(QThread):
|
162 |
+
text_to_set = pyqtSignal(str)
|
163 |
+
text_to_set_title_bar = pyqtSignal(str)
|
164 |
+
|
165 |
+
|
166 |
+
def __init__(self):
|
167 |
+
super().__init__()
|
168 |
+
self.the_input_text = None
|
169 |
+
self.title_bar_text = None
|
170 |
+
self.prev = None
|
171 |
+
self.commited_text = []
|
172 |
+
|
173 |
+
def run(self):
|
174 |
+
while True:
|
175 |
+
self.msleep(500) # Simulate a time-consuming task
|
176 |
+
|
177 |
+
if self.the_input_text and (self.prev is None or self.prev != self.the_input_text):
|
178 |
+
self.prev = self.the_input_text
|
179 |
+
self.text_to_set.emit("True")
|
180 |
+
for i in range(len(self.title_bar_text)):
|
181 |
+
self.text_to_set_title_bar.emit(self.title_bar_text[:i + 1])
|
182 |
+
self.msleep(10)
|
183 |
+
|
184 |
+
if not self.the_input_text and self.prev != self.the_input_text:
|
185 |
+
self.prev = self.the_input_text
|
186 |
+
self.text_to_set.emit("False")
|
187 |
+
|
188 |
+
the_text = " GPT Computer Assistant"
|
189 |
+
|
190 |
+
for i in range(len(the_text)):
|
191 |
+
self.text_to_set_title_bar.emit(the_text[:i + 1])
|
192 |
+
self.msleep(10)
|
193 |
+
|
194 |
+
|
195 |
+
|
196 |
+
|
197 |
+
class DrawingWidget(QWidget):
|
198 |
+
def __init__(self, parent=None):
|
199 |
+
super(DrawingWidget, self).__init__(parent)
|
200 |
+
# Set widget properties if needed, e.g., size
|
201 |
+
|
202 |
+
self.main_ = parent
|
203 |
+
|
204 |
+
def paintEvent(self, event):
|
205 |
+
if not self.main_.should_paint:
|
206 |
+
return # Skip the drawing if should_paint is False
|
207 |
+
|
208 |
+
|
209 |
+
|
210 |
+
if llm_settings[load_model_settings()]["vision"] is True:
|
211 |
+
self.main_.screen_available = True
|
212 |
+
else:
|
213 |
+
self.main_.screen_available = False
|
214 |
+
|
215 |
+
|
216 |
+
|
217 |
+
self.main_.setAutoFillBackground(True)
|
218 |
+
painter = QPainter(self)
|
219 |
+
|
220 |
+
painter.setRenderHint(QPainter.Antialiasing)
|
221 |
+
painter.setPen(QPen(QColor("#000"), 1))
|
222 |
+
painter.setBrush(QBrush(Qt.black, Qt.SolidPattern))
|
223 |
+
|
224 |
+
center_x = 95
|
225 |
+
center_y = 40
|
226 |
+
|
227 |
+
if "talking" in self.main_.state:
|
228 |
+
# Draw a pulsating circle with smooth easing animation
|
229 |
+
radius_variation = 5 * (1 + math.sin(self.main_.pulse_frame * math.pi / 100))
|
230 |
+
radius = 70 + radius_variation
|
231 |
+
painter.drawEllipse(
|
232 |
+
int(center_x - radius / 2),
|
233 |
+
int(center_y - radius / 2),
|
234 |
+
int(radius),
|
235 |
+
int(radius),
|
236 |
+
)
|
237 |
+
elif self.main_.state == "thinking":
|
238 |
+
# more slow pulsating circle with smooth easing animation
|
239 |
+
radius_variation = 5 * (1 + math.sin(self.main_.pulse_frame * math.pi / 100))
|
240 |
+
radius = 70 + radius_variation
|
241 |
+
painter.drawEllipse(
|
242 |
+
int(center_x - radius / 2),
|
243 |
+
int(center_y - radius / 2),
|
244 |
+
int(radius),
|
245 |
+
int(radius),
|
246 |
+
)
|
247 |
+
|
248 |
+
else:
|
249 |
+
radius = 70
|
250 |
+
painter.drawEllipse(
|
251 |
+
int(center_x - radius / 2),
|
252 |
+
int(center_y - radius / 2),
|
253 |
+
int(radius),
|
254 |
+
int(radius),
|
255 |
+
)
|
256 |
+
|
257 |
+
|
258 |
+
|
259 |
+
self.main_.circle_rect = QRect(
|
260 |
+
int(center_x - radius / 2),
|
261 |
+
int(center_y - radius / 2),
|
262 |
+
int(radius),
|
263 |
+
int(radius),
|
264 |
+
)
|
265 |
+
|
266 |
+
|
267 |
+
|
268 |
+
if not self.main_.state == "thinking":
|
269 |
+
painter.setPen(QPen(QColor("#01EE8A"), 1)) # Green color with 2px thickness
|
270 |
+
# Draw the ellipse with the specified green border
|
271 |
+
painter.drawEllipse(
|
272 |
+
int(center_x - radius / 2),
|
273 |
+
int(center_y - radius / 2),
|
274 |
+
int(radius),
|
275 |
+
int(radius),
|
276 |
+
)
|
277 |
+
else:
|
278 |
+
painter.setPen(QPen(QColor("#23538F"), 1))
|
279 |
+
|
280 |
+
painter.drawEllipse(
|
281 |
+
int(center_x - radius / 2),
|
282 |
+
int(center_y - radius / 2),
|
283 |
+
int(radius),
|
284 |
+
int(radius),
|
285 |
+
)
|
286 |
+
|
287 |
+
|
288 |
+
|
289 |
+
painter.setPen(QPen(QColor("#000"), 1))
|
290 |
+
|
291 |
+
if self.main_.screen_available:
|
292 |
+
|
293 |
+
small_center_x = 165
|
294 |
+
small_center_y = 25
|
295 |
+
small_radius = 30
|
296 |
+
painter.drawEllipse(
|
297 |
+
int(small_center_x - small_radius / 2),
|
298 |
+
int(small_center_y - small_radius / 2),
|
299 |
+
int(small_radius),
|
300 |
+
int(small_radius),
|
301 |
+
)
|
302 |
+
|
303 |
+
self.main_.small_circle_rect = QRect(
|
304 |
+
int(small_center_x - small_radius / 2),
|
305 |
+
int(small_center_y - small_radius / 2),
|
306 |
+
int(small_radius),
|
307 |
+
int(small_radius),
|
308 |
+
)
|
309 |
+
|
310 |
+
# Draw the icon inside the circle
|
311 |
+
icon_size = small_radius * 2 // 3 # Adjust the icon size relative to the circle
|
312 |
+
icon_rect = QRect(
|
313 |
+
small_center_x - icon_size // 2,
|
314 |
+
small_center_y - icon_size // 2,
|
315 |
+
icon_size,
|
316 |
+
icon_size,
|
317 |
+
)
|
318 |
+
self.main_.small_circle_recticon = QIcon(microphone_icon_path)
|
319 |
+
self.main_.small_circle_recticon.paint(painter, icon_rect)
|
320 |
+
|
321 |
+
|
322 |
+
small_center_x = 30
|
323 |
+
small_center_y = 60
|
324 |
+
small_radius = 30
|
325 |
+
painter.drawEllipse(
|
326 |
+
int(small_center_x - small_radius / 2),
|
327 |
+
int(small_center_y - small_radius / 2),
|
328 |
+
int(small_radius),
|
329 |
+
int(small_radius),
|
330 |
+
)
|
331 |
+
|
332 |
+
self.main_.small_circle_left = QRect(
|
333 |
+
int(small_center_x - small_radius / 2),
|
334 |
+
int(small_center_y - small_radius / 2),
|
335 |
+
int(small_radius),
|
336 |
+
int(small_radius),
|
337 |
+
)
|
338 |
+
|
339 |
+
# Draw the icon inside the circle
|
340 |
+
icon_size = small_radius * 2 // 3 # Adjust the icon size relative to the circle
|
341 |
+
icon_rect = QRect(
|
342 |
+
small_center_x - icon_size // 2,
|
343 |
+
small_center_y - icon_size // 2,
|
344 |
+
icon_size,
|
345 |
+
icon_size,
|
346 |
+
)
|
347 |
+
self.main_.small_circle_lefticon = QIcon(audio_icon_path)
|
348 |
+
self.main_.small_circle_lefticon.paint(painter, icon_rect)
|
349 |
+
|
350 |
+
|
351 |
+
|
352 |
+
small_center_x = 30
|
353 |
+
small_center_y = 25
|
354 |
+
small_radius = 30
|
355 |
+
painter.drawEllipse(
|
356 |
+
int(small_center_x - small_radius / 2),
|
357 |
+
int(small_center_y - small_radius / 2),
|
358 |
+
int(small_radius),
|
359 |
+
int(small_radius),
|
360 |
+
)
|
361 |
+
|
362 |
+
self.main_.small_circle_left_top = QRect(
|
363 |
+
int(small_center_x - small_radius / 2),
|
364 |
+
int(small_center_y - small_radius / 2),
|
365 |
+
int(small_radius),
|
366 |
+
int(small_radius),
|
367 |
+
)
|
368 |
+
|
369 |
+
# Draw the icon inside the circle
|
370 |
+
icon_size = small_radius * 2 // 3 # Adjust the icon size relative to the circle
|
371 |
+
icon_rect = QRect(
|
372 |
+
small_center_x - icon_size // 2,
|
373 |
+
small_center_y - icon_size // 2,
|
374 |
+
icon_size,
|
375 |
+
icon_size,
|
376 |
+
)
|
377 |
+
self.main_.small_circle_left_topticon = QIcon(screenshot_icon_path)
|
378 |
+
self.main_.small_circle_left_topticon.paint(painter, icon_rect)
|
379 |
+
|
380 |
+
|
381 |
+
|
382 |
+
|
383 |
+
|
384 |
+
|
385 |
+
small_center_x = 165
|
386 |
+
small_center_y = 60
|
387 |
+
small_radius = 30
|
388 |
+
painter.drawEllipse(
|
389 |
+
int(small_center_x - small_radius / 2),
|
390 |
+
int(small_center_y - small_radius / 2),
|
391 |
+
int(small_radius),
|
392 |
+
int(small_radius),
|
393 |
+
)
|
394 |
+
|
395 |
+
self.main_.small_circle_collapse = QRect(
|
396 |
+
int(small_center_x - small_radius / 2),
|
397 |
+
int(small_center_y - small_radius / 2),
|
398 |
+
int(small_radius),
|
399 |
+
int(small_radius),
|
400 |
+
)
|
401 |
+
|
402 |
+
# Draw the icon inside the circle
|
403 |
+
icon_size = small_radius * 2 // 3 # Adjust the icon size relative to the circle
|
404 |
+
icon_rect = QRect(
|
405 |
+
small_center_x - icon_size // 2,
|
406 |
+
small_center_y - icon_size // 2,
|
407 |
+
icon_size,
|
408 |
+
icon_size,
|
409 |
+
)
|
410 |
+
|
411 |
+
if self.main_.collapse:
|
412 |
+
self.main_.small_circle_collapse_icon = QIcon(down_icon_path)
|
413 |
+
else:
|
414 |
+
self.main_.small_circle_collapse_icon = QIcon(up_icon_path)
|
415 |
+
self.main_.small_circle_collapse_icon.paint(painter, icon_rect)
|
416 |
+
|
417 |
+
|
418 |
+
|
419 |
+
|
420 |
+
def mousePressEvent(self, event: QMouseEvent):
|
421 |
+
|
422 |
+
|
423 |
+
self.main_.old_position = event.globalPos()
|
424 |
+
|
425 |
+
with my_tracer.start_span("mouse_press_event") as span:
|
426 |
+
span.set_attribute("user_id", user_id)
|
427 |
+
span.set_attribute("os_name", os_name_)
|
428 |
+
if self.main_.state == "idle" or "talking" in self.main_.state:
|
429 |
+
try:
|
430 |
+
if self.main_.circle_rect.contains(event.pos()):
|
431 |
+
|
432 |
+
if self.main_.state == "aitalking":
|
433 |
+
self.main_.manuel_stop = True
|
434 |
+
self.main_.stop_talking = True
|
435 |
+
|
436 |
+
else:
|
437 |
+
if llm_settings[load_model_settings()]["vision"] is True:
|
438 |
+
|
439 |
+
self.main_.button_handler.toggle_recording(dont_save_image=True)
|
440 |
+
else:
|
441 |
+
self.main_.button_handler.toggle_recording(no_screenshot=True)
|
442 |
+
except:
|
443 |
+
pass
|
444 |
+
|
445 |
+
try:
|
446 |
+
if self.main_.small_circle_rect.contains(event.pos()):
|
447 |
+
if self.main_.state == "aitalking":
|
448 |
+
self.main_.manuel_stop = True
|
449 |
+
self.main_.stop_talking = True
|
450 |
+
|
451 |
+
else:
|
452 |
+
self.main_.button_handler.toggle_recording(no_screenshot=True)
|
453 |
+
except:
|
454 |
+
pass
|
455 |
+
|
456 |
+
try:
|
457 |
+
|
458 |
+
if self.main_.small_circle_left.contains(event.pos()):
|
459 |
+
if self.main_.state == "aitalking":
|
460 |
+
self.main_.manuel_stop = True
|
461 |
+
self.main_.stop_talking = True
|
462 |
+
|
463 |
+
else:
|
464 |
+
self.main_.button_handler.toggle_recording(take_system_audio=True)
|
465 |
+
except:
|
466 |
+
pass
|
467 |
+
|
468 |
+
try:
|
469 |
+
|
470 |
+
if self.main_.small_circle_left_top.contains(event.pos()):
|
471 |
+
if self.main_.state == "aitalking":
|
472 |
+
self.main_.manuel_stop = True
|
473 |
+
self.main_.stop_talking = True
|
474 |
+
|
475 |
+
else:
|
476 |
+
self.main_.button_handler.just_screenshot()
|
477 |
+
except:
|
478 |
+
pass
|
479 |
+
|
480 |
+
try:
|
481 |
+
if self.main_.small_circle_collapse.contains(event.pos()):
|
482 |
+
if self.main_.collapse:
|
483 |
+
self.main_.collapse = False
|
484 |
+
print()
|
485 |
+
# hide all buttons and input box
|
486 |
+
the_input_box.show()
|
487 |
+
if llm_settings[load_model_settings()]["vision"]:
|
488 |
+
self.main_.screenshot_button.show()
|
489 |
+
self.main_.settingsButton.show()
|
490 |
+
self.main_.llmsettingsButton.show()
|
491 |
+
self.main_.send_button.show()
|
492 |
+
self.main_.window().setFixedSize(self.main_.first_width, self.main_.first_height)
|
493 |
+
deactivate_collapse_setting()
|
494 |
+
else:
|
495 |
+
self.main_.collapse = True
|
496 |
+
self.main_.collapse_window()
|
497 |
+
activate_collapse_setting()
|
498 |
+
|
499 |
+
|
500 |
+
self.main_.update()
|
501 |
+
except:
|
502 |
+
pass
|
503 |
+
|
504 |
+
|
505 |
+
from PyQt5.QtCore import QVariantAnimation
|
506 |
+
|
507 |
+
class MainWindow(QMainWindow):
|
508 |
+
api_enabled = False
|
509 |
+
def __init__(self):
|
510 |
+
super().__init__()
|
511 |
+
|
512 |
+
print("API Enabled:", MainWindow.api_enabled)
|
513 |
+
if MainWindow.api_enabled:
|
514 |
+
try:
|
515 |
+
from .api import start_api
|
516 |
+
start_api()
|
517 |
+
except:
|
518 |
+
raise Exception("API could not be started, please install gpt-computer-assistant[api]")
|
519 |
+
self.stop_talking = False
|
520 |
+
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) # Remove the default title bar
|
521 |
+
|
522 |
+
# Load the San Francisco font
|
523 |
+
print("Loading font")
|
524 |
+
print(font_dir)
|
525 |
+
try:
|
526 |
+
font_id = QtGui.QFontDatabase.addApplicationFont(font_dir)
|
527 |
+
|
528 |
+
|
529 |
+
font_family = QtGui.QFontDatabase.applicationFontFamilies(font_id)[0]
|
530 |
+
self.setFont(QtGui.QFont(font_family))
|
531 |
+
except:
|
532 |
+
print("Error loading font")
|
533 |
+
|
534 |
+
self.should_paint = False # In order to initialize the painting, it will be overwritten by the settings
|
535 |
+
|
536 |
+
|
537 |
+
self.state = "idle"
|
538 |
+
self.pulse_timer = None
|
539 |
+
|
540 |
+
self.button_handler = ButtonHandler(self)
|
541 |
+
self.initUI()
|
542 |
+
self.old_position = self.pos()
|
543 |
+
|
544 |
+
if llm_settings[load_model_settings()]["transcription"]:
|
545 |
+
self.should_paint = True # Flag to control painting
|
546 |
+
else:
|
547 |
+
self.should_paint = False
|
548 |
+
|
549 |
+
|
550 |
+
|
551 |
+
self.collapse = is_collapse_setting_active()
|
552 |
+
if self.collapse:
|
553 |
+
self.collapse_window()
|
554 |
+
|
555 |
+
global the_main_window
|
556 |
+
the_main_window = self
|
557 |
+
|
558 |
+
|
559 |
+
self.general_styling()
|
560 |
+
|
561 |
+
if is_dark_mode_active():
|
562 |
+
self.dark_mode()
|
563 |
+
else:
|
564 |
+
self.light_mode()
|
565 |
+
|
566 |
+
|
567 |
+
self.wake_word_thread = None
|
568 |
+
|
569 |
+
self.wake_word_active = False
|
570 |
+
|
571 |
+
if load_pvporcupine_api_key() != "CHANGE_ME" and is_wake_word_active():
|
572 |
+
self.wake_word_active = True
|
573 |
+
self.wake_word_trigger()
|
574 |
+
|
575 |
+
|
576 |
+
self.manuel_stop = False
|
577 |
+
|
578 |
+
|
579 |
+
self.border_animation = None
|
580 |
+
|
581 |
+
self.complated_answer = False
|
582 |
+
|
583 |
+
|
584 |
+
self.reading_thread = False
|
585 |
+
self.reading_thread_2 = False
|
586 |
+
|
587 |
+
def init_border_animation(self):
|
588 |
+
# Create a QVariantAnimation to handle color change
|
589 |
+
border_animation = QVariantAnimation(
|
590 |
+
self,
|
591 |
+
valueChanged=self.update_border_color,
|
592 |
+
startValue=QColor("#303030"),
|
593 |
+
endValue=QColor("#23538F"),
|
594 |
+
duration=2000 # Duration for one loop in milliseconds
|
595 |
+
)
|
596 |
+
border_animation.setLoopCount(-1) # Loop indefinitely
|
597 |
+
return border_animation
|
598 |
+
|
599 |
+
def start_border_animation(self, status):
|
600 |
+
print("FUNCTION TRİGGERED")
|
601 |
+
if self.border_animation is None:
|
602 |
+
self.border_animation = self.init_border_animation()
|
603 |
+
|
604 |
+
status = status.lower() == "true"
|
605 |
+
if status:
|
606 |
+
self.border_animation.start()
|
607 |
+
else:
|
608 |
+
self.border_animation.stop()
|
609 |
+
self.title_bar.setStyleSheet("background-color: #2E2E2E; color: white; border-style: solid; border-radius: 15px; border-width: 0px; color: #fff;")
|
610 |
+
|
611 |
+
|
612 |
+
|
613 |
+
def update_border_color(self, color):
|
614 |
+
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;")
|
615 |
+
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;")
|
616 |
+
|
617 |
+
# Existing methods...
|
618 |
+
|
619 |
+
def general_styling(self):
|
620 |
+
self.setAttribute(Qt.WA_TranslucentBackground)
|
621 |
+
self.setStyleSheet("border-radius: 10px; background-color: rgba(45, 45, 45, 250);")
|
622 |
+
self.central_widget.setStyleSheet("border-style: solid; border-width: 1px; border-color: rgb(0,0,0,0);")
|
623 |
+
|
624 |
+
self.input_box_style = "border-radius: 10px; border-bottom: 1px solid #01EE8A;"
|
625 |
+
|
626 |
+
self.send_button_style = "border-radius: 5px; height: 25px; border-style: solid;"
|
627 |
+
self.screenshot_button_style = "border-radius: 5px; height: 25px; border-style: solid;"
|
628 |
+
|
629 |
+
self.settingsButton_style = "border-radius: 5px; height: 25px; border-style: solid;"
|
630 |
+
self.llmsettingsButton_style = "border-radius: 5px; height: 25px; border-style: solid;"
|
631 |
+
|
632 |
+
self.btn_minimize.setStyleSheet("background-color: #2E2E2E; color: white; border-style: none;")
|
633 |
+
self.btn_close.setStyleSheet("background-color: #2E2E2E; color: white; border-style: none;")
|
634 |
+
|
635 |
+
|
636 |
+
def wake_word_trigger(self):
|
637 |
+
self.wake_word_thread = threading.Thread(target=self.wake_word)
|
638 |
+
self.wake_word_thread.start()
|
639 |
+
|
640 |
+
def wake_word(self):
|
641 |
+
from .agent.process import tts_if_you_can
|
642 |
+
while True and is_wake_word_active() and self.wake_word_active:
|
643 |
+
if wake_word(self):
|
644 |
+
|
645 |
+
def random_accept_words():
|
646 |
+
return random.choice(["Yes", "Sir", "Boss", "Master"])
|
647 |
+
|
648 |
+
|
649 |
+
tts_if_you_can(random_accept_words(), not_threaded=True)
|
650 |
+
|
651 |
+
|
652 |
+
def trigger_wake_word():
|
653 |
+
if is_wake_word_screen_setting_active() and llm_settings[load_model_settings()]["vision"]:
|
654 |
+
self.button_handler.toggle_recording(dont_save_image=True)
|
655 |
+
else:
|
656 |
+
self.button_handler.toggle_recording(no_screenshot=True)
|
657 |
+
|
658 |
+
if self.state == "aitalking":
|
659 |
+
self.manuel_stop = True
|
660 |
+
self.stop_talking = True
|
661 |
+
time.sleep(1)
|
662 |
+
trigger_wake_word()
|
663 |
+
print("Stop talking")
|
664 |
+
else:
|
665 |
+
trigger_wake_word()
|
666 |
+
|
667 |
+
|
668 |
+
|
669 |
+
|
670 |
+
|
671 |
+
|
672 |
+
|
673 |
+
def dark_mode(self):
|
674 |
+
self.setAutoFillBackground(True)
|
675 |
+
p = self.palette()
|
676 |
+
p.setColor(self.backgroundRole(), QColor("#171717")) # Set background color to white
|
677 |
+
self.setPalette(p)
|
678 |
+
self.input_box.setStyleSheet(self.input_box_style+"background-color: #2E2E2E; color: white;")
|
679 |
+
|
680 |
+
self.send_button.setStyleSheet(self.send_button_style+"background-color: #2E2E2E; color: white;")
|
681 |
+
self.screenshot_button.setStyleSheet(self.screenshot_button_style+"background-color: #2E2E2E; color: white;")
|
682 |
+
|
683 |
+
self.settingsButton.setStyleSheet(self.settingsButton_style+"background-color: #2E2E2E; color: white;")
|
684 |
+
self.llmsettingsButton.setStyleSheet(self.llmsettingsButton_style+"background-color: #2E2E2E; color: white;")
|
685 |
+
|
686 |
+
|
687 |
+
|
688 |
+
|
689 |
+
def light_mode(self):
|
690 |
+
self.setAutoFillBackground(True)
|
691 |
+
p = self.palette()
|
692 |
+
p.setColor(self.backgroundRole(), QColor("#F0F0F0"))
|
693 |
+
self.setPalette(p)
|
694 |
+
self.input_box.setStyleSheet(self.input_box_style+"background-color: #FFFFFF; color: black;")
|
695 |
+
self.send_button.setStyleSheet(self.send_button_style+"background-color: #FFFFFF; color: black; ")
|
696 |
+
self.screenshot_button.setStyleSheet(self.screenshot_button_style+"background-color: #FFFFFF; color: black; ")
|
697 |
+
self.settingsButton.setStyleSheet(self.settingsButton_style+"background-color: #FFFFFF; color: black; ")
|
698 |
+
self.llmsettingsButton.setStyleSheet(self.llmsettingsButton_style+"background-color: #FFFFFF; color: black; ")
|
699 |
+
|
700 |
+
|
701 |
+
|
702 |
+
|
703 |
+
|
704 |
+
def collapse_window(self):
|
705 |
+
the_input_box.hide()
|
706 |
+
self.screenshot_button.hide()
|
707 |
+
self.settingsButton.hide()
|
708 |
+
self.llmsettingsButton.hide()
|
709 |
+
self.send_button.hide()
|
710 |
+
self.window().setFixedSize(self.width(), 140)
|
711 |
+
|
712 |
+
|
713 |
+
|
714 |
+
def initUI(self):
|
715 |
+
self.setWindowTitle("GPT")
|
716 |
+
self.setGeometry(100, 100, 200, 200)
|
717 |
+
self.setFixedSize(self.width()+10, self.height() + 80)
|
718 |
+
|
719 |
+
self.first_height = self.height()
|
720 |
+
self.first_width = self.width()
|
721 |
+
|
722 |
+
app_icon = QtGui.QIcon()
|
723 |
+
app_icon.addFile(icon_16_path, QtCore.QSize(16, 16))
|
724 |
+
app_icon.addFile(icon_24_path, QtCore.QSize(24, 24))
|
725 |
+
app_icon.addFile(icon_32_path, QtCore.QSize(32, 32))
|
726 |
+
app_icon.addFile(icon_48_path, QtCore.QSize(48, 48))
|
727 |
+
app_icon.addFile(icon_256_path, QtCore.QSize(256, 256))
|
728 |
+
self.setWindowIcon(app_icon)
|
729 |
+
|
730 |
+
self.central_widget = QWidget(self)
|
731 |
+
self.setCentralWidget(self.central_widget)
|
732 |
+
layout = QVBoxLayout(self.central_widget)
|
733 |
+
|
734 |
+
# Custom title bar
|
735 |
+
self.title_bar = QWidget(self)
|
736 |
+
self.title_bar.setFixedHeight(30) # Set a fixed height for the title bar
|
737 |
+
self.title_bar.setStyleSheet("background-color: #2E2E2E; color: #fff;")
|
738 |
+
|
739 |
+
self.title_bar_layout = QHBoxLayout(self.title_bar)
|
740 |
+
self.title_bar_layout.setContentsMargins(5, 5, 0, 5)
|
741 |
+
self.title_bar_layout.setSpacing(0)
|
742 |
+
|
743 |
+
self.btn_minimize = QPushButton("_", self.title_bar)
|
744 |
+
self.btn_minimize.setFixedSize(25, 20)
|
745 |
+
self.btn_minimize.clicked.connect(self.showMinimized)
|
746 |
+
|
747 |
+
def stop_app():
|
748 |
+
self.stop_talking = True
|
749 |
+
self.wake_word_active = False
|
750 |
+
if MainWindow.api_enabled:
|
751 |
+
from .api import stop_api
|
752 |
+
stop_api()
|
753 |
+
self.close()
|
754 |
+
|
755 |
+
|
756 |
+
self.btn_close = QPushButton("X", self.title_bar)
|
757 |
+
self.btn_close.setFixedSize(30, 20)
|
758 |
+
self.btn_close.clicked.connect(stop_app)
|
759 |
+
|
760 |
+
self.title_label = QLabel(" GPT Computer Assistant", self.title_bar)
|
761 |
+
self.title_label.setStyleSheet("border: 0px solid blue;")
|
762 |
+
self.title_bar_layout.addWidget(self.title_label)
|
763 |
+
self.title_bar_layout.addWidget(self.btn_minimize)
|
764 |
+
|
765 |
+
|
766 |
+
|
767 |
+
self.title_bar_layout.addWidget(self.btn_close)
|
768 |
+
|
769 |
+
|
770 |
+
# Create a spacer item with expanding policy
|
771 |
+
spacer = QSpacerItem(5, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
772 |
+
self.title_bar_layout.addSpacerItem(spacer) # Add spacer to the layout
|
773 |
+
|
774 |
+
|
775 |
+
|
776 |
+
layout.addWidget(self.title_bar)
|
777 |
+
|
778 |
+
|
779 |
+
|
780 |
+
|
781 |
+
self.drawing_widget = DrawingWidget(self)
|
782 |
+
layout.addWidget(self.drawing_widget)
|
783 |
+
|
784 |
+
|
785 |
+
|
786 |
+
|
787 |
+
|
788 |
+
|
789 |
+
self.layout = layout
|
790 |
+
|
791 |
+
self.setLayout(layout)
|
792 |
+
|
793 |
+
|
794 |
+
|
795 |
+
# Add keyboard shortcuts
|
796 |
+
self.shortcut_screenshot = QShortcut(QKeySequence("Ctrl+1"), self)
|
797 |
+
self.shortcut_screenshot.activated.connect(
|
798 |
+
lambda: self.button_handler.just_screenshot()
|
799 |
+
)
|
800 |
+
self.shortcut_screenshot = QShortcut(QKeySequence("Ctrl+2"), self)
|
801 |
+
self.shortcut_screenshot.activated.connect(
|
802 |
+
lambda: self.button_handler.toggle_recording(take_system_audio=True)
|
803 |
+
)
|
804 |
+
|
805 |
+
self.shortcut_no_screenshot = QShortcut(QKeySequence("Ctrl+e"), self)
|
806 |
+
self.shortcut_no_screenshot.activated.connect(
|
807 |
+
lambda: self.button_handler.toggle_recording(take_system_audio=True)
|
808 |
+
)
|
809 |
+
|
810 |
+
self.shortcut_no_screenshot = QShortcut(QKeySequence("Ctrl+3"), self)
|
811 |
+
self.shortcut_no_screenshot.activated.connect(
|
812 |
+
lambda: self.button_handler.toggle_recording(no_screenshot=True)
|
813 |
+
)
|
814 |
+
|
815 |
+
# I want to create an input box to bottom left and a send button to bottom right
|
816 |
+
|
817 |
+
input_box = CustomTextEdit(self)
|
818 |
+
self.input_box = input_box
|
819 |
+
|
820 |
+
|
821 |
+
input_box.setFixedHeight(40)
|
822 |
+
|
823 |
+
|
824 |
+
if load_api_key() == "CHANGE_ME":
|
825 |
+
input_box.setPlaceholderText("Save your API Key, go to settings")
|
826 |
+
else:
|
827 |
+
input_box.setPlaceholderText("Type here")
|
828 |
+
input_box.setGeometry(30, self.height() - 60, 200, 30)
|
829 |
+
global the_input_box
|
830 |
+
the_input_box = input_box
|
831 |
+
|
832 |
+
def input_box_send():
|
833 |
+
if input_box.toPlainText() != "":
|
834 |
+
self.button_handler.input_text(input_box.toPlainText())
|
835 |
+
|
836 |
+
def input_box_send_screenshot():
|
837 |
+
if input_box.toPlainText() != "":
|
838 |
+
self.button_handler.input_text_screenshot(input_box.toPlainText())
|
839 |
+
|
840 |
+
self.layout.addWidget(input_box)
|
841 |
+
|
842 |
+
# Create a horizontal layout
|
843 |
+
button_layout = QHBoxLayout()
|
844 |
+
|
845 |
+
# Create the send button
|
846 |
+
self.send_button = QPushButton("Send", self)
|
847 |
+
self.send_button.clicked.connect(input_box_send)
|
848 |
+
|
849 |
+
# Create the screenshot button
|
850 |
+
self.screenshot_button = QPushButton("+Screenshot", self)
|
851 |
+
self.screenshot_button.clicked.connect(input_box_send_screenshot)
|
852 |
+
|
853 |
+
|
854 |
+
if llm_settings[load_model_settings()]["vision"] is False:
|
855 |
+
self.screenshot_button.hide()
|
856 |
+
|
857 |
+
|
858 |
+
|
859 |
+
# Add the buttons to the horizontal layout
|
860 |
+
button_layout.addWidget(self.send_button)
|
861 |
+
button_layout.addWidget(self.screenshot_button)
|
862 |
+
|
863 |
+
self.shortcut_enter = QShortcut(QKeySequence("Ctrl+Return"), self)
|
864 |
+
self.shortcut_enter.activated.connect(input_box_send_screenshot)
|
865 |
+
|
866 |
+
global return_key_event
|
867 |
+
return_key_event = input_box_send
|
868 |
+
|
869 |
+
self.layout.addLayout(button_layout)
|
870 |
+
|
871 |
+
button_layout_ = QHBoxLayout()
|
872 |
+
|
873 |
+
self.settingsButton = QPushButton("Chat Settings", self)
|
874 |
+
self.settingsButton.clicked.connect(settings_popup)
|
875 |
+
|
876 |
+
self.llmsettingsButton = QPushButton("LLM Settings", self)
|
877 |
+
self.llmsettingsButton.clicked.connect(llmsettings_popup)
|
878 |
+
|
879 |
+
button_layout_.addWidget(self.settingsButton)
|
880 |
+
button_layout_.addWidget(self.llmsettingsButton)
|
881 |
+
self.layout.addLayout(button_layout_)
|
882 |
+
|
883 |
+
|
884 |
+
|
885 |
+
self.worker = Worker()
|
886 |
+
self.worker.text_to_set.connect(self.set_text)
|
887 |
+
self.worker.start()
|
888 |
+
|
889 |
+
self.worker_2 = Worker_2()
|
890 |
+
self.worker_2.text_to_set.connect(self.start_border_animation)
|
891 |
+
self.worker_2.text_to_set_title_bar.connect(self.set_title_bar_text)
|
892 |
+
self.worker_2.start()
|
893 |
+
|
894 |
+
# print height and width
|
895 |
+
print(self.height(), self.width())
|
896 |
+
|
897 |
+
self.show()
|
898 |
+
|
899 |
+
|
900 |
+
|
901 |
+
|
902 |
+
|
903 |
+
def set_text(self, text):
|
904 |
+
global the_input_box
|
905 |
+
|
906 |
+
|
907 |
+
vertical_scrollbar = the_input_box.verticalScrollBar()
|
908 |
+
scroll_value = vertical_scrollbar.value()
|
909 |
+
|
910 |
+
|
911 |
+
|
912 |
+
the_input_box.setPlainText(text)
|
913 |
+
|
914 |
+
vertical_scrollbar.setValue(scroll_value)
|
915 |
+
|
916 |
+
def set_title_bar_text(self, text):
|
917 |
+
self.title_label.setText(text)
|
918 |
+
|
919 |
+
def update_from_thread(self, text, system=True):
|
920 |
+
self.worker.make_animation = True
|
921 |
+
if system:
|
922 |
+
text = "System: " + text
|
923 |
+
print("Updating from thread", text)
|
924 |
+
self.worker.the_input_text = text
|
925 |
+
|
926 |
+
def read_part_task_generate_only(self):
|
927 |
+
if not is_just_text_model_active() and not the_main_window.api_enabled:
|
928 |
+
|
929 |
+
|
930 |
+
threads = {}
|
931 |
+
|
932 |
+
the_okey_parts = split_with_multiple_delimiters(self.worker.the_input_text,".?!:")
|
933 |
+
|
934 |
+
|
935 |
+
|
936 |
+
|
937 |
+
for each in the_okey_parts:
|
938 |
+
if the_main_window.stop_talking:
|
939 |
+
break
|
940 |
+
|
941 |
+
|
942 |
+
the_thread = threading.Thread(target=text_to_speech, args=(each,))
|
943 |
+
|
944 |
+
threads[each] = the_thread
|
945 |
+
the_thread.start()
|
946 |
+
|
947 |
+
|
948 |
+
for each in threads.values():
|
949 |
+
each.join()
|
950 |
+
|
951 |
+
|
952 |
+
self.reading_thread_2 = False
|
953 |
+
|
954 |
+
def read_part_task(self):
|
955 |
+
if not is_just_text_model_active() and not the_main_window.api_enabled:
|
956 |
+
threads = {}
|
957 |
+
|
958 |
+
the_okey_parts = split_with_multiple_delimiters(self.worker.the_input_text,".?!:")
|
959 |
+
|
960 |
+
|
961 |
+
will_read_parts = []
|
962 |
+
|
963 |
+
for each in the_okey_parts:
|
964 |
+
if the_main_window.stop_talking:
|
965 |
+
break
|
966 |
+
if each not in readed_sentences:
|
967 |
+
will_read_parts.append(each)
|
968 |
+
readed_sentences.append(each)
|
969 |
+
|
970 |
+
the_thread = threading.Thread(target=text_to_speech, args=(each,))
|
971 |
+
|
972 |
+
threads[each] = the_thread
|
973 |
+
the_thread.start()
|
974 |
+
|
975 |
+
|
976 |
+
|
977 |
+
for each in will_read_parts:
|
978 |
+
if the_main_window.stop_talking:
|
979 |
+
break
|
980 |
+
threads[each].join()
|
981 |
+
|
982 |
+
tts_if_you_can(each, not_threaded=True, bypass_other_settings=True)
|
983 |
+
|
984 |
+
|
985 |
+
self.reading_thread = False
|
986 |
+
|
987 |
+
|
988 |
+
def set_text_to_input_box(self, text):
|
989 |
+
global readed_sentences
|
990 |
+
self.worker.make_animation = False
|
991 |
+
if self.worker.the_input_text.startswith("System:") or self.complated_answer:
|
992 |
+
self.worker.the_input_text = ""
|
993 |
+
self.complated_answer = False
|
994 |
+
readed_sentences = []
|
995 |
+
if text not in (">", "<>", ">\n", "<", "<\n"):
|
996 |
+
|
997 |
+
self.worker.the_input_text += text
|
998 |
+
|
999 |
+
if self.reading_thread is not True and len(self.worker.the_input_text) > 40:
|
1000 |
+
self.reading_thread = True
|
1001 |
+
threading.Thread(target=self.read_part_task).start()
|
1002 |
+
|
1003 |
+
if self.reading_thread_2 is not True and len(self.worker.the_input_text) > 250:
|
1004 |
+
self.reading_thread_2 = True
|
1005 |
+
threading.Thread(target=self.read_part_task_generate_only).start()
|
1006 |
+
|
1007 |
+
|
1008 |
+
|
1009 |
+
|
1010 |
+
|
1011 |
+
|
1012 |
+
|
1013 |
+
|
1014 |
+
|
1015 |
+
def active_border_animation(self, title_bar_text = None):
|
1016 |
+
if self.worker_2.title_bar_text is not None:
|
1017 |
+
if self.worker_2.title_bar_text != title_bar_text:
|
1018 |
+
return
|
1019 |
+
|
1020 |
+
self.worker_2.the_input_text = True
|
1021 |
+
if title_bar_text is None:
|
1022 |
+
title_bar_text = " GPT Computer Assistant"
|
1023 |
+
else:
|
1024 |
+
title_bar_text = f" {title_bar_text}"
|
1025 |
+
if len(title_bar_text) > 33:
|
1026 |
+
title_bar_text = title_bar_text[:30] + "..."
|
1027 |
+
self.worker_2.title_bar_text = title_bar_text
|
1028 |
+
|
1029 |
+
self.btn_minimize.hide()
|
1030 |
+
self.btn_close.hide()
|
1031 |
+
def deactive_border_animation(self, title_bar_text=None):
|
1032 |
+
|
1033 |
+
if title_bar_text is None:
|
1034 |
+
title_bar_text = " GPT Computer Assistant"
|
1035 |
+
else:
|
1036 |
+
title_bar_text = f" {title_bar_text}"
|
1037 |
+
if len(title_bar_text) > 33:
|
1038 |
+
title_bar_text = title_bar_text[:30] + "..."
|
1039 |
+
|
1040 |
+
if self.worker_2.title_bar_text is not None:
|
1041 |
+
if self.worker_2.title_bar_text != title_bar_text:
|
1042 |
+
return
|
1043 |
+
|
1044 |
+
self.worker_2.the_input_text = False
|
1045 |
+
self.worker_2.title_bar_text = None
|
1046 |
+
time.sleep(1)
|
1047 |
+
self.btn_minimize.show()
|
1048 |
+
self.btn_close.show()
|
1049 |
+
|
1050 |
+
|
1051 |
+
def mouseMoveEvent(self, event: QMouseEvent):
|
1052 |
+
delta = QPoint(event.globalPos() - self.old_position)
|
1053 |
+
if event.buttons() == Qt.LeftButton and self.title_bar.underMouse():
|
1054 |
+
self.move(self.x() + delta.x(), self.y() + delta.y())
|
1055 |
+
self.old_position = event.globalPos()
|
1056 |
+
|
1057 |
+
|
1058 |
+
def mousePressEvent(self, event: QMouseEvent):
|
1059 |
+
self.old_position = event.globalPos()
|
1060 |
+
|
1061 |
+
|
1062 |
+
|
1063 |
+
|
1064 |
+
def remove_painting(self):
|
1065 |
+
self.should_paint = False # Set the flag to False
|
1066 |
+
self.update() # Request a repaint, which will now skip drawing
|
1067 |
+
|
1068 |
+
def activate_painting(self):
|
1069 |
+
self.should_paint = True
|
1070 |
+
self.update()
|
1071 |
+
|
1072 |
+
def remove_screenshot_button(self):
|
1073 |
+
self.screenshot_button.hide()
|
1074 |
+
|
1075 |
+
def add_screenshot_button(self):
|
1076 |
+
self.screenshot_button.show()
|
1077 |
+
|
1078 |
+
def update_state(self, new_state):
|
1079 |
+
|
1080 |
+
assistant_stopped = False
|
1081 |
+
if self.state == "aitalking" and new_state == "idle":
|
1082 |
+
assistant_stopped = True
|
1083 |
+
|
1084 |
+
if self.manuel_stop:
|
1085 |
+
assistant_stopped = False
|
1086 |
+
self.manuel_stop = False
|
1087 |
+
|
1088 |
+
|
1089 |
+
|
1090 |
+
self.state = new_state
|
1091 |
+
print(f"State updated: {new_state}")
|
1092 |
+
if "talking" in new_state:
|
1093 |
+
self.pulse_frame = 0
|
1094 |
+
if self.pulse_timer:
|
1095 |
+
self.pulse_timer.stop()
|
1096 |
+
self.pulse_timer = None
|
1097 |
+
self.pulse_timer = QTimer(self)
|
1098 |
+
self.pulse_timer.timeout.connect(self.pulse_circle)
|
1099 |
+
self.pulse_timer.start(5)
|
1100 |
+
elif new_state == "thinking":
|
1101 |
+
|
1102 |
+
the_main_window.update_from_thread("Thinking...")
|
1103 |
+
self.pulse_frame = 0
|
1104 |
+
if self.pulse_timer:
|
1105 |
+
self.pulse_timer.stop()
|
1106 |
+
self.pulse_timer = None
|
1107 |
+
self.pulse_timer = QTimer(self)
|
1108 |
+
self.pulse_timer.timeout.connect(self.pulse_circle)
|
1109 |
+
self.pulse_timer.start(20)
|
1110 |
+
elif self.pulse_timer:
|
1111 |
+
self.pulse_timer.stop()
|
1112 |
+
self.pulse_timer = None
|
1113 |
+
self.update() # Trigger a repaint
|
1114 |
+
|
1115 |
+
if assistant_stopped:
|
1116 |
+
if llm_settings[load_model_settings()]["transcription"]:
|
1117 |
+
global the_input_box
|
1118 |
+
if the_input_box.toPlainText().endswith("?") and is_continuously_conversations_setting_active():
|
1119 |
+
self.button_handler.toggle_recording(no_screenshot=True, new_record=True)
|
1120 |
+
|
1121 |
+
def pulse_circle(self):
|
1122 |
+
self.pulse_frame = (self.pulse_frame + 1) % 100
|
1123 |
+
self.update()
|
1124 |
+
|
1125 |
+
|
gpt_computer_assistant/gui/__init__.py
ADDED
File without changes
|
gpt_computer_assistant/gui/button.py
ADDED
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pyautogui
|
2 |
+
from .signal import *
|
3 |
+
import threading
|
4 |
+
|
5 |
+
try:
|
6 |
+
from ..audio.record import *
|
7 |
+
from ..screen.shot import *
|
8 |
+
from ..agent.process import *
|
9 |
+
from ..agent.chat_history import clear_chat_history
|
10 |
+
from ..utils.db import (
|
11 |
+
screenshot_path,
|
12 |
+
save_api_key,
|
13 |
+
load_api_key,
|
14 |
+
activate_just_text_model,
|
15 |
+
deactivate_just_text_model,
|
16 |
+
is_just_text_model_active,
|
17 |
+
set_profile,
|
18 |
+
get_profile,
|
19 |
+
)
|
20 |
+
from ..screen.shot import take_screenshot
|
21 |
+
except ImportError:
|
22 |
+
from audio.record import *
|
23 |
+
from screen.shot import *
|
24 |
+
from agent.process import *
|
25 |
+
from agent.chat_history import clear_chat_history
|
26 |
+
from utils.db import (
|
27 |
+
screenshot_path,
|
28 |
+
save_api_key,
|
29 |
+
load_api_key,
|
30 |
+
activate_just_text_model,
|
31 |
+
deactivate_just_text_model,
|
32 |
+
is_just_text_model_active,
|
33 |
+
set_profile,
|
34 |
+
get_profile,
|
35 |
+
)
|
36 |
+
from screen.shot import take_screenshot
|
37 |
+
|
38 |
+
recording_thread = None
|
39 |
+
|
40 |
+
|
41 |
+
class ButtonHandler:
|
42 |
+
"""Handles button click events and corresponding actions."""
|
43 |
+
|
44 |
+
def __init__(self, main_window):
|
45 |
+
"""Initialize the ButtonHandler."""
|
46 |
+
self.recording = False
|
47 |
+
self.main_window = main_window
|
48 |
+
self.process_audio_thread = None
|
49 |
+
|
50 |
+
signal_handler.recording_started.connect(self.on_recording_started)
|
51 |
+
signal_handler.recording_stopped.connect(self.on_recording_stopped)
|
52 |
+
signal_handler.assistant_thinking.connect(self.on_assistant_thinking)
|
53 |
+
signal_handler.assistant_response_ready.connect(
|
54 |
+
self.on_assistant_response_ready
|
55 |
+
)
|
56 |
+
signal_handler.assistant_response_stopped.connect(
|
57 |
+
self.on_assistant_response_stopped
|
58 |
+
)
|
59 |
+
|
60 |
+
def toggle_recording(
|
61 |
+
self, no_screenshot=False, take_system_audio=False, dont_save_image=False, new_record=False
|
62 |
+
):
|
63 |
+
"""Toggle audio recording."""
|
64 |
+
|
65 |
+
if self.recording and not new_record:
|
66 |
+
stop_recording()
|
67 |
+
self.recording = False
|
68 |
+
else:
|
69 |
+
if not no_screenshot:
|
70 |
+
screenshot = pyautogui.screenshot()
|
71 |
+
screenshot.save(screenshot_path)
|
72 |
+
|
73 |
+
self.no_screenshot = no_screenshot
|
74 |
+
self.take_system_audio = take_system_audio
|
75 |
+
self.dont_save_image = dont_save_image
|
76 |
+
|
77 |
+
global recording_thread
|
78 |
+
if recording_thread is None or not recording_thread.is_alive() or new_record:
|
79 |
+
recording_thread = threading.Thread(
|
80 |
+
target=start_recording, args=(take_system_audio,self,)
|
81 |
+
)
|
82 |
+
recording_thread.start()
|
83 |
+
signal_handler.recording_started.emit()
|
84 |
+
|
85 |
+
def on_recording_started(self):
|
86 |
+
"""Handle event when recording starts."""
|
87 |
+
|
88 |
+
self.recording = True
|
89 |
+
self.main_window.update_state("talking")
|
90 |
+
|
91 |
+
def on_recording_stopped(self):
|
92 |
+
"""Handle event when recording stops."""
|
93 |
+
|
94 |
+
print("ON RECORDING STOPPED")
|
95 |
+
self.recording = False
|
96 |
+
self.main_window.update_state("thinking")
|
97 |
+
if (
|
98 |
+
self.process_audio_thread is None
|
99 |
+
or not self.process_audio_thread.is_alive()
|
100 |
+
):
|
101 |
+
signal_handler.assistant_thinking.emit()
|
102 |
+
self.process_audio_thread = threading.Thread(
|
103 |
+
target=process_audio,
|
104 |
+
args=(
|
105 |
+
not self.no_screenshot,
|
106 |
+
self.take_system_audio,
|
107 |
+
self.dont_save_image,
|
108 |
+
),
|
109 |
+
)
|
110 |
+
self.process_audio_thread.start()
|
111 |
+
|
112 |
+
def just_screenshot(self):
|
113 |
+
"""Take a screenshot."""
|
114 |
+
|
115 |
+
take_screenshot()
|
116 |
+
self.process_audio_thread = threading.Thread(target=process_screenshot)
|
117 |
+
self.process_audio_thread.start()
|
118 |
+
|
119 |
+
def on_assistant_response_stopped(self):
|
120 |
+
"""Handle event when assistant's response stops."""
|
121 |
+
|
122 |
+
self.main_window.update_state("idle")
|
123 |
+
|
124 |
+
def on_assistant_thinking(self):
|
125 |
+
"""Handle event when assistant is thinking."""
|
126 |
+
|
127 |
+
self.main_window.update_state("thinking")
|
128 |
+
|
129 |
+
def on_assistant_response_ready(self):
|
130 |
+
"""Handle event when assistant's response is ready."""
|
131 |
+
|
132 |
+
self.main_window.update_state("aitalking")
|
133 |
+
|
134 |
+
def input_text(self, text):
|
135 |
+
"""Handle input text."""
|
136 |
+
|
137 |
+
self.main_window.update_state("thinking")
|
138 |
+
if (
|
139 |
+
self.process_audio_thread is None
|
140 |
+
or not self.process_audio_thread.is_alive()
|
141 |
+
):
|
142 |
+
signal_handler.assistant_thinking.emit()
|
143 |
+
self.process_audio_thread = threading.Thread(
|
144 |
+
target=process_text, args=(text,)
|
145 |
+
)
|
146 |
+
self.process_audio_thread.start()
|
147 |
+
|
148 |
+
def input_text_screenshot(self, text):
|
149 |
+
"""Handle input text with screenshot."""
|
150 |
+
|
151 |
+
screenshot = pyautogui.screenshot()
|
152 |
+
screenshot.save(screenshot_path)
|
153 |
+
|
154 |
+
self.main_window.update_state("thinking")
|
155 |
+
if (
|
156 |
+
self.process_audio_thread is None
|
157 |
+
or not self.process_audio_thread.is_alive()
|
158 |
+
):
|
159 |
+
signal_handler.assistant_thinking.emit()
|
160 |
+
self.process_audio_thread = threading.Thread(
|
161 |
+
target=process_text,
|
162 |
+
args=(text,),
|
163 |
+
kwargs={"screenshot_path": screenshot_path},
|
164 |
+
)
|
165 |
+
self.process_audio_thread.start()
|
gpt_computer_assistant/gui/llmsettings.py
ADDED
@@ -0,0 +1,236 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
try:
|
2 |
+
from ..utils.db import *
|
3 |
+
from ..agent.chat_history import clear_chat_history
|
4 |
+
from ..llm_settings import llm_show_name, llm_settings
|
5 |
+
except ImportError:
|
6 |
+
from utils.db import *
|
7 |
+
from agent.chat_history import clear_chat_history
|
8 |
+
from llm_settings import llm_show_name, llm_settings
|
9 |
+
|
10 |
+
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QLineEdit, QPushButton
|
11 |
+
from PyQt5.QtCore import Qt
|
12 |
+
from PyQt5.QtWidgets import QComboBox
|
13 |
+
|
14 |
+
from gpt_computer_assistant.utils.db import save_openai_url, save_groq_api_key
|
15 |
+
|
16 |
+
|
17 |
+
def llmsettings_popup(self):
|
18 |
+
from ..gpt_computer_assistant import the_main_window
|
19 |
+
|
20 |
+
# Create a settings dialog and inside of it create a text input about openai_api_key and a button to save it
|
21 |
+
settings_dialog = QDialog()
|
22 |
+
settings_dialog.setWindowTitle("Settings")
|
23 |
+
settings_dialog.setWindowModality(Qt.ApplicationModal)
|
24 |
+
|
25 |
+
settings_dialog.setLayout(QVBoxLayout())
|
26 |
+
|
27 |
+
api_key_label = QLabel("OpenAI API Key")
|
28 |
+
settings_dialog.layout().addWidget(api_key_label)
|
29 |
+
api_key_input = QLineEdit()
|
30 |
+
api_key = load_api_key()
|
31 |
+
api_key_input.setText(api_key)
|
32 |
+
settings_dialog.layout().addWidget(api_key_input)
|
33 |
+
save_button = QPushButton("Save")
|
34 |
+
|
35 |
+
def save_api_key_(api_key):
|
36 |
+
save_api_key(api_key)
|
37 |
+
|
38 |
+
the_main_window.update_from_thread("Saved API Key")
|
39 |
+
the_main_window.input_box.setPlaceholderText("Type here")
|
40 |
+
settings_dialog.close()
|
41 |
+
|
42 |
+
save_button.clicked.connect(lambda: save_api_key_(api_key_input.text()))
|
43 |
+
settings_dialog.layout().addWidget(save_button)
|
44 |
+
|
45 |
+
# Start of new OpenAI Base URL settings
|
46 |
+
openai_url_label = QLabel("OpenAI Base URL")
|
47 |
+
settings_dialog.layout().addWidget(openai_url_label)
|
48 |
+
openai_url_input = QLineEdit()
|
49 |
+
openai_url = load_openai_url()
|
50 |
+
openai_url_input.setText(openai_url)
|
51 |
+
settings_dialog.layout().addWidget(openai_url_input)
|
52 |
+
|
53 |
+
def save_openai_url_():
|
54 |
+
openai_url = openai_url_input.text()
|
55 |
+
save_openai_url(openai_url)
|
56 |
+
|
57 |
+
the_main_window.update_from_thread("Saved OpenAI Base URL")
|
58 |
+
the_main_window.input_box.setPlaceholderText("Type here")
|
59 |
+
settings_dialog.close()
|
60 |
+
|
61 |
+
openai_url_save_button = QPushButton("Save URL")
|
62 |
+
openai_url_save_button.clicked.connect(save_openai_url_)
|
63 |
+
settings_dialog.layout().addWidget(openai_url_save_button)
|
64 |
+
# End of new OpenAI Base URL settings
|
65 |
+
|
66 |
+
groq_api_key_label = QLabel("Groq API Key")
|
67 |
+
settings_dialog.layout().addWidget(groq_api_key_label)
|
68 |
+
groq_api_key_input = QLineEdit()
|
69 |
+
groq_api_key = load_groq_api_key()
|
70 |
+
groq_api_key_input.setText(groq_api_key)
|
71 |
+
settings_dialog.layout().addWidget(groq_api_key_input)
|
72 |
+
groq_save_button = QPushButton("Save")
|
73 |
+
|
74 |
+
def groq_save_api_key_(api_key):
|
75 |
+
save_groq_api_key(api_key)
|
76 |
+
the_main_window.update_from_thread("Saved Groq API Key")
|
77 |
+
the_main_window.input_box.setPlaceholderText("Type here")
|
78 |
+
settings_dialog.close()
|
79 |
+
|
80 |
+
groq_save_button.clicked.connect(
|
81 |
+
lambda: groq_save_api_key_(groq_api_key_input.text())
|
82 |
+
)
|
83 |
+
settings_dialog.layout().addWidget(groq_save_button)
|
84 |
+
|
85 |
+
|
86 |
+
|
87 |
+
google_api_key_label = QLabel("Google Generative AI API Key")
|
88 |
+
settings_dialog.layout().addWidget(google_api_key_label)
|
89 |
+
google_api_key_input = QLineEdit()
|
90 |
+
google_api_key = load_google_api_key()
|
91 |
+
google_api_key_input.setText(google_api_key)
|
92 |
+
settings_dialog.layout().addWidget(google_api_key_input)
|
93 |
+
google_save_button = QPushButton("Save")
|
94 |
+
|
95 |
+
def google_save_api_key_(api_key):
|
96 |
+
save_google_api_key(api_key)
|
97 |
+
the_main_window.update_from_thread("Saved Google API Key")
|
98 |
+
the_main_window.input_box.setPlaceholderText("Type here")
|
99 |
+
settings_dialog.close()
|
100 |
+
|
101 |
+
google_save_button.clicked.connect(
|
102 |
+
lambda: google_save_api_key_(google_api_key_input.text())
|
103 |
+
)
|
104 |
+
settings_dialog.layout().addWidget(google_save_button)
|
105 |
+
|
106 |
+
def hide_openai():
|
107 |
+
api_key_label.hide()
|
108 |
+
api_key_input.hide()
|
109 |
+
openai_url_label.hide()
|
110 |
+
openai_url_input.hide()
|
111 |
+
save_button.hide()
|
112 |
+
openai_url_save_button.hide()
|
113 |
+
|
114 |
+
def hide_groq():
|
115 |
+
groq_api_key_label.hide()
|
116 |
+
groq_api_key_input.hide()
|
117 |
+
groq_save_button.hide()
|
118 |
+
|
119 |
+
|
120 |
+
def hide_google():
|
121 |
+
google_api_key_label.hide()
|
122 |
+
google_api_key_input.hide()
|
123 |
+
google_save_button.hide()
|
124 |
+
|
125 |
+
def show_openai():
|
126 |
+
api_key_label.show()
|
127 |
+
api_key_input.show()
|
128 |
+
openai_url_label.show()
|
129 |
+
openai_url_input.show()
|
130 |
+
save_button.show()
|
131 |
+
openai_url_save_button.show()
|
132 |
+
|
133 |
+
def show_groq():
|
134 |
+
groq_api_key_label.show()
|
135 |
+
groq_api_key_input.show()
|
136 |
+
groq_save_button.show()
|
137 |
+
|
138 |
+
def show_google():
|
139 |
+
google_api_key_label.show()
|
140 |
+
google_api_key_input.show()
|
141 |
+
google_save_button.show()
|
142 |
+
|
143 |
+
hide_openai()
|
144 |
+
hide_groq()
|
145 |
+
hide_google()
|
146 |
+
|
147 |
+
print("LLLM SETTINGS", list(llm_show_name.keys()))
|
148 |
+
|
149 |
+
# Add a select box with the options OpenAI and Olamo
|
150 |
+
model_label = QLabel("Model")
|
151 |
+
model_select = QComboBox()
|
152 |
+
model_select.addItems(
|
153 |
+
list(llm_show_name.keys())
|
154 |
+
)
|
155 |
+
|
156 |
+
settings_dialog.layout().addWidget(model_label)
|
157 |
+
settings_dialog.layout().addWidget(model_select)
|
158 |
+
|
159 |
+
# currently model
|
160 |
+
current_model = load_model_settings()
|
161 |
+
# lets set index of current model
|
162 |
+
for i, model in enumerate(llm_show_name.keys()):
|
163 |
+
print("MODEL", model, current_model)
|
164 |
+
the_save_string = llm_show_name[model]
|
165 |
+
if the_save_string == current_model:
|
166 |
+
model_select.setCurrentIndex(i)
|
167 |
+
|
168 |
+
|
169 |
+
|
170 |
+
if llm_settings[llm_show_name[model_select.currentText()]]["provider"] == "openai":
|
171 |
+
show_openai()
|
172 |
+
|
173 |
+
if llm_settings[llm_show_name[model_select.currentText()]]["provider"] == "groq":
|
174 |
+
show_groq()
|
175 |
+
|
176 |
+
if llm_settings[llm_show_name[model_select.currentText()]]["provider"] == "google":
|
177 |
+
show_google()
|
178 |
+
|
179 |
+
if not llm_settings[llm_show_name[model_select.currentText()]]["transcription"]:
|
180 |
+
|
181 |
+
the_main_window.remove_painting()
|
182 |
+
|
183 |
+
if not llm_settings[llm_show_name[model_select.currentText()]]["vision"]:
|
184 |
+
|
185 |
+
the_main_window.remove_screenshot_button()
|
186 |
+
|
187 |
+
|
188 |
+
|
189 |
+
|
190 |
+
def on_model_change():
|
191 |
+
hide_openai()
|
192 |
+
hide_groq()
|
193 |
+
hide_google()
|
194 |
+
|
195 |
+
|
196 |
+
the_save_string = llm_show_name[model_select.currentText()]
|
197 |
+
save_model_settings(the_save_string)
|
198 |
+
|
199 |
+
|
200 |
+
|
201 |
+
|
202 |
+
|
203 |
+
if llm_settings[llm_show_name[model_select.currentText()]]["transcription"] is False:
|
204 |
+
from ..gpt_computer_assistant import the_main_window
|
205 |
+
|
206 |
+
the_main_window.remove_painting()
|
207 |
+
|
208 |
+
|
209 |
+
|
210 |
+
if llm_settings[llm_show_name[model_select.currentText()]]["provider"] == "openai":
|
211 |
+
show_openai()
|
212 |
+
openai_url_label.show()
|
213 |
+
openai_url_input.show()
|
214 |
+
openai_url_save_button.show()
|
215 |
+
from ..gpt_computer_assistant import the_main_window
|
216 |
+
|
217 |
+
the_main_window.activate_painting()
|
218 |
+
|
219 |
+
if llm_settings[llm_show_name[model_select.currentText()]]["vision"]:
|
220 |
+
the_main_window.add_screenshot_button()
|
221 |
+
else:
|
222 |
+
the_main_window.remove_screenshot_button()
|
223 |
+
|
224 |
+
|
225 |
+
|
226 |
+
|
227 |
+
if llm_settings[llm_show_name[model_select.currentText()]]["provider"] == "groq":
|
228 |
+
show_groq()
|
229 |
+
|
230 |
+
if llm_settings[llm_show_name[model_select.currentText()]]["provider"] == "google":
|
231 |
+
show_google()
|
232 |
+
|
233 |
+
|
234 |
+
model_select.currentIndexChanged.connect(on_model_change)
|
235 |
+
|
236 |
+
settings_dialog.exec_()
|
gpt_computer_assistant/gui/settings.py
ADDED
@@ -0,0 +1,352 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QLineEdit, QPushButton
|
2 |
+
from PyQt5.QtCore import Qt
|
3 |
+
from ..utils.db import *
|
4 |
+
from ..agent.chat_history import clear_chat_history
|
5 |
+
|
6 |
+
def settings_popup(self):
|
7 |
+
"""
|
8 |
+
Display a settings popup dialog for configuring various options.
|
9 |
+
|
10 |
+
This function creates a settings dialog with options to reset chat history, enable/disable the just text model,
|
11 |
+
and change the active profile.
|
12 |
+
|
13 |
+
Parameters:
|
14 |
+
- self: Reference to the main application window.
|
15 |
+
|
16 |
+
Returns:
|
17 |
+
- None
|
18 |
+
"""
|
19 |
+
from ..gpt_computer_assistant import the_main_window
|
20 |
+
|
21 |
+
settings_dialog = QDialog()
|
22 |
+
settings_dialog.setWindowTitle("Settings")
|
23 |
+
settings_dialog.setWindowModality(Qt.ApplicationModal)
|
24 |
+
|
25 |
+
settings_dialog.setLayout(QVBoxLayout())
|
26 |
+
|
27 |
+
reset_memory_button = QPushButton("Reset Memory")
|
28 |
+
|
29 |
+
def clear_chat_history_():
|
30 |
+
"""
|
31 |
+
Clear the chat history and update the main window.
|
32 |
+
|
33 |
+
This function clears the chat history and updates the main window with a notification.
|
34 |
+
|
35 |
+
Returns:
|
36 |
+
- None
|
37 |
+
"""
|
38 |
+
clear_chat_history()
|
39 |
+
the_main_window.update_from_thread("Cleared Chat History")
|
40 |
+
settings_dialog.close()
|
41 |
+
|
42 |
+
reset_memory_button.clicked.connect(clear_chat_history_)
|
43 |
+
settings_dialog.layout().addWidget(reset_memory_button)
|
44 |
+
|
45 |
+
just_text_button = QPushButton("Enable Just Text Model")
|
46 |
+
|
47 |
+
settings_dialog.layout().addWidget(just_text_button)
|
48 |
+
|
49 |
+
if is_just_text_model_active():
|
50 |
+
just_text_button.setText("Disable Just Text Model")
|
51 |
+
|
52 |
+
def deactivate_just_text_model_():
|
53 |
+
"""
|
54 |
+
Deactivate the just text model and update the main window.
|
55 |
+
|
56 |
+
This function deactivates the just text model and updates the main window with a notification.
|
57 |
+
|
58 |
+
Returns:
|
59 |
+
- None
|
60 |
+
"""
|
61 |
+
deactivate_just_text_model()
|
62 |
+
the_main_window.update_from_thread("Disabled Just Text Model")
|
63 |
+
settings_dialog.close()
|
64 |
+
|
65 |
+
just_text_button.clicked.connect(deactivate_just_text_model_)
|
66 |
+
else:
|
67 |
+
|
68 |
+
def activate_just_text_model_():
|
69 |
+
"""
|
70 |
+
Activate the just text model and update the main window.
|
71 |
+
|
72 |
+
This function activates the just text model and updates the main window with a notification.
|
73 |
+
|
74 |
+
Returns:
|
75 |
+
- None
|
76 |
+
"""
|
77 |
+
activate_just_text_model()
|
78 |
+
the_main_window.update_from_thread("Enabled Just Text Model")
|
79 |
+
settings_dialog.close()
|
80 |
+
|
81 |
+
just_text_button.clicked.connect(activate_just_text_model_)
|
82 |
+
|
83 |
+
settings_dialog.layout().addWidget(QLabel("Profile"))
|
84 |
+
profile_input = QLineEdit()
|
85 |
+
|
86 |
+
profile_input.setText(get_profile())
|
87 |
+
settings_dialog.layout().addWidget(profile_input)
|
88 |
+
profile_save_button = QPushButton("Save")
|
89 |
+
|
90 |
+
def set_profile_(profile):
|
91 |
+
"""
|
92 |
+
Set the active profile and update the main window.
|
93 |
+
|
94 |
+
This function sets the active profile based on user input and updates the main window with a notification.
|
95 |
+
|
96 |
+
Parameters:
|
97 |
+
- profile (str): The profile name to set.
|
98 |
+
|
99 |
+
Returns:
|
100 |
+
- None
|
101 |
+
"""
|
102 |
+
set_profile(profile)
|
103 |
+
the_main_window.update_from_thread("Saved Profile")
|
104 |
+
settings_dialog.close()
|
105 |
+
|
106 |
+
profile_save_button.clicked.connect(lambda: set_profile_(profile_input.text()))
|
107 |
+
settings_dialog.layout().addWidget(profile_save_button)
|
108 |
+
|
109 |
+
|
110 |
+
dark_mode_button = QPushButton("Enable Dark Mode")
|
111 |
+
|
112 |
+
settings_dialog.layout().addWidget(dark_mode_button)
|
113 |
+
|
114 |
+
if is_dark_mode_active():
|
115 |
+
dark_mode_button.setText("Disable Dark Mode")
|
116 |
+
|
117 |
+
def deactivate_dark_mode_():
|
118 |
+
"""
|
119 |
+
Deactivate dark mode and update the main window.
|
120 |
+
|
121 |
+
This function deactivates dark mode and updates the main window with a notification.
|
122 |
+
|
123 |
+
Returns:
|
124 |
+
- None
|
125 |
+
"""
|
126 |
+
deactivate_dark_mode()
|
127 |
+
the_main_window.update_from_thread("Disabled Dark Mode")
|
128 |
+
the_main_window.light_mode()
|
129 |
+
settings_dialog.close()
|
130 |
+
|
131 |
+
dark_mode_button.clicked.connect(deactivate_dark_mode_)
|
132 |
+
else:
|
133 |
+
|
134 |
+
def activate_dark_mode_():
|
135 |
+
"""
|
136 |
+
Activate dark mode and update the main window.
|
137 |
+
|
138 |
+
This function activates dark mode and updates the main window with a notification.
|
139 |
+
|
140 |
+
Returns:
|
141 |
+
- None
|
142 |
+
"""
|
143 |
+
activate_dark_mode()
|
144 |
+
the_main_window.update_from_thread("Enabled Dark Mode")
|
145 |
+
the_main_window.dark_mode()
|
146 |
+
settings_dialog.close()
|
147 |
+
|
148 |
+
dark_mode_button.clicked.connect(activate_dark_mode_)
|
149 |
+
|
150 |
+
|
151 |
+
|
152 |
+
|
153 |
+
predefined_agents_button = QPushButton("Enable Predefined Agents (Good Results, Long Response Time)")
|
154 |
+
|
155 |
+
settings_dialog.layout().addWidget(predefined_agents_button)
|
156 |
+
|
157 |
+
try:
|
158 |
+
import crewai
|
159 |
+
if is_predefined_agents_setting_active():
|
160 |
+
predefined_agents_button.setText("Disable Predefined Agents (Bad Results, Short Response Time)")
|
161 |
+
|
162 |
+
def deactivate_predefined_agents_():
|
163 |
+
deactivate_predefined_agents_setting()
|
164 |
+
the_main_window.update_from_thread("Disabled Predefined Agents (Bad Results, Short Response Time)")
|
165 |
+
settings_dialog.close()
|
166 |
+
|
167 |
+
predefined_agents_button.clicked.connect(deactivate_predefined_agents_)
|
168 |
+
else:
|
169 |
+
|
170 |
+
def activate_predefined_agents_():
|
171 |
+
activate_predefined_agents_setting()
|
172 |
+
the_main_window.update_from_thread("Enabled Predefined Agents (Good Results, Long Response Time)")
|
173 |
+
settings_dialog.close()
|
174 |
+
|
175 |
+
predefined_agents_button.clicked.connect(activate_predefined_agents_)
|
176 |
+
|
177 |
+
except:
|
178 |
+
predefined_agents_button.setText("Install gpt-computer-assistant[agentic]")
|
179 |
+
|
180 |
+
|
181 |
+
|
182 |
+
|
183 |
+
|
184 |
+
|
185 |
+
online_tools_button = QPushButton("Enable Upsonic Tiger Tools - More Capability (Recommended)")
|
186 |
+
|
187 |
+
settings_dialog.layout().addWidget(online_tools_button)
|
188 |
+
|
189 |
+
if is_online_tools_setting_active():
|
190 |
+
online_tools_button.setText("Disable Upsonic Tiger Tools - Low Capability (Not Recommended)")
|
191 |
+
|
192 |
+
def deactivate_online_tools_():
|
193 |
+
deactivate_online_tools_setting()
|
194 |
+
the_main_window.update_from_thread("Disabled Upsonic Tiger Tools - Low Capability (Not Recommended)")
|
195 |
+
settings_dialog.close()
|
196 |
+
|
197 |
+
online_tools_button.clicked.connect(deactivate_online_tools_)
|
198 |
+
else:
|
199 |
+
|
200 |
+
def activate_online_tools_():
|
201 |
+
activate_online_tools_setting()
|
202 |
+
the_main_window.update_from_thread("Enabled Upsonic Tiger Tools - More Capability (Recommended)")
|
203 |
+
settings_dialog.close()
|
204 |
+
|
205 |
+
online_tools_button.clicked.connect(activate_online_tools_)
|
206 |
+
|
207 |
+
|
208 |
+
|
209 |
+
|
210 |
+
|
211 |
+
auto_stop_recording_button = QPushButton("Enable Auto Stop Recording")
|
212 |
+
|
213 |
+
settings_dialog.layout().addWidget(auto_stop_recording_button)
|
214 |
+
|
215 |
+
if is_auto_stop_recording_setting_active():
|
216 |
+
auto_stop_recording_button.setText("Disable Auto Stop Recording")
|
217 |
+
|
218 |
+
def deactivate_auto_stop_recording_():
|
219 |
+
deactivate_auto_stop_recording_setting()
|
220 |
+
the_main_window.update_from_thread("Disabled Auto Stop Recording")
|
221 |
+
settings_dialog.close()
|
222 |
+
|
223 |
+
auto_stop_recording_button.clicked.connect(deactivate_auto_stop_recording_)
|
224 |
+
else:
|
225 |
+
|
226 |
+
def activate_auto_stop_recording_():
|
227 |
+
activate_auto_stop_recording_setting()
|
228 |
+
the_main_window.update_from_thread("Enabled Auto Stop Recording")
|
229 |
+
settings_dialog.close()
|
230 |
+
|
231 |
+
auto_stop_recording_button.clicked.connect(activate_auto_stop_recording_)
|
232 |
+
|
233 |
+
|
234 |
+
|
235 |
+
|
236 |
+
|
237 |
+
api_key_label = QLabel("Wakeword - Pvporcupine API Key")
|
238 |
+
settings_dialog.layout().addWidget(api_key_label)
|
239 |
+
api_key_input = QLineEdit()
|
240 |
+
api_key = load_pvporcupine_api_key()
|
241 |
+
api_key_input.setText(api_key)
|
242 |
+
settings_dialog.layout().addWidget(api_key_input)
|
243 |
+
save_button = QPushButton("Save")
|
244 |
+
|
245 |
+
def save_api_key_(api_key):
|
246 |
+
first_time = True
|
247 |
+
if api_key != "CHANGE_ME":
|
248 |
+
first_time = False
|
249 |
+
save_pvporcupine_api_key(api_key)
|
250 |
+
|
251 |
+
the_main_window.update_from_thread("Wake word activated, just say 'Her Computer' or jarvis to activate the assistant")
|
252 |
+
if first_time:
|
253 |
+
the_main_window.wake_word_trigger()
|
254 |
+
settings_dialog.close()
|
255 |
+
|
256 |
+
save_button.clicked.connect(lambda: save_api_key_(api_key_input.text()))
|
257 |
+
settings_dialog.layout().addWidget(save_button)
|
258 |
+
|
259 |
+
|
260 |
+
|
261 |
+
|
262 |
+
|
263 |
+
wake_word_button = QPushButton("Enable Wake Word")
|
264 |
+
|
265 |
+
settings_dialog.layout().addWidget(wake_word_button)
|
266 |
+
|
267 |
+
missing_parts = False
|
268 |
+
try:
|
269 |
+
import pyaudio
|
270 |
+
except:
|
271 |
+
missing_parts = True
|
272 |
+
|
273 |
+
|
274 |
+
if api_key == "CHANGE_ME":
|
275 |
+
wake_word_button.setText("Please Set Pvporcupine API Key First")
|
276 |
+
elif missing_parts:
|
277 |
+
wake_word_button.setText("Please Install gpt-computer-assistant[wakeword]")
|
278 |
+
else:
|
279 |
+
|
280 |
+
if is_wake_word_active():
|
281 |
+
wake_word_button.setText("Disable Wake Word")
|
282 |
+
|
283 |
+
def deactivate_wake_word_():
|
284 |
+
deactivate_wake_word()
|
285 |
+
the_main_window.update_from_thread("Disabled Wake Word")
|
286 |
+
the_main_window.wake_word_active = False
|
287 |
+
settings_dialog.close()
|
288 |
+
|
289 |
+
wake_word_button.clicked.connect(deactivate_wake_word_)
|
290 |
+
else:
|
291 |
+
|
292 |
+
def activate_wake_word_():
|
293 |
+
activate_wake_word()
|
294 |
+
the_main_window.update_from_thread("Enabled Wake Word")
|
295 |
+
the_main_window.wake_word_active = True
|
296 |
+
the_main_window.wake_word_trigger()
|
297 |
+
settings_dialog.close()
|
298 |
+
|
299 |
+
wake_word_button.clicked.connect(activate_wake_word_)
|
300 |
+
|
301 |
+
|
302 |
+
|
303 |
+
|
304 |
+
|
305 |
+
wake_word_screen_button = QPushButton("Enable Screen Input for Wake Word Mode")
|
306 |
+
|
307 |
+
settings_dialog.layout().addWidget(wake_word_screen_button)
|
308 |
+
|
309 |
+
if is_wake_word_screen_setting_active():
|
310 |
+
wake_word_screen_button.setText("Disable Screen Input for Wake Word Mode")
|
311 |
+
|
312 |
+
def deactivate_auto_stop_recording_():
|
313 |
+
deactivate_wake_word_screen_setting()
|
314 |
+
the_main_window.update_from_thread("Disabled Screen Input for Wake Word Mode")
|
315 |
+
settings_dialog.close()
|
316 |
+
|
317 |
+
wake_word_screen_button.clicked.connect(deactivate_auto_stop_recording_)
|
318 |
+
else:
|
319 |
+
|
320 |
+
def activate_auto_stop_recording_():
|
321 |
+
activate_wake_word_screen_setting()
|
322 |
+
the_main_window.update_from_thread("Enabled Screen Input for Wake Word Mode")
|
323 |
+
settings_dialog.close()
|
324 |
+
|
325 |
+
wake_word_screen_button.clicked.connect(activate_auto_stop_recording_)
|
326 |
+
|
327 |
+
|
328 |
+
|
329 |
+
|
330 |
+
continuously_conversations_button = QPushButton("Enable Continuously Conversations")
|
331 |
+
|
332 |
+
settings_dialog.layout().addWidget(continuously_conversations_button)
|
333 |
+
|
334 |
+
if is_continuously_conversations_setting_active():
|
335 |
+
continuously_conversations_button.setText("Disable Continuously Conversations")
|
336 |
+
|
337 |
+
def deactivate_auto_stop_recording_():
|
338 |
+
deactivate_continuously_conversations_setting()
|
339 |
+
the_main_window.update_from_thread("Disabled Continuously Conversations")
|
340 |
+
settings_dialog.close()
|
341 |
+
|
342 |
+
continuously_conversations_button.clicked.connect(deactivate_auto_stop_recording_)
|
343 |
+
else:
|
344 |
+
|
345 |
+
def activate_auto_stop_recording_():
|
346 |
+
activate_continuously_conversations_setting()
|
347 |
+
the_main_window.update_from_thread("Enabled Continuously Conversations")
|
348 |
+
settings_dialog.close()
|
349 |
+
|
350 |
+
continuously_conversations_button.clicked.connect(activate_auto_stop_recording_)
|
351 |
+
|
352 |
+
settings_dialog.exec_()
|
gpt_computer_assistant/gui/signal.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from PyQt5.QtCore import pyqtSignal, QObject
|
2 |
+
|
3 |
+
|
4 |
+
|
5 |
+
class SignalHandler(QObject):
|
6 |
+
"""
|
7 |
+
A QObject subclass to handle signals used in the GUI application.
|
8 |
+
|
9 |
+
This class defines several signals that can be used to communicate
|
10 |
+
between different components of the GUI application.
|
11 |
+
|
12 |
+
Signals:
|
13 |
+
- recording_started: Signal emitted when recording is started.
|
14 |
+
- recording_stopped: Signal emitted when recording is stopped.
|
15 |
+
- assistant_thinking: Signal emitted when the assistant is processing a request.
|
16 |
+
- assistant_response_ready: Signal emitted when the assistant response is ready to be displayed.
|
17 |
+
- assistant_response_stopped: Signal emitted when the assistant response display is stopped.
|
18 |
+
|
19 |
+
"""
|
20 |
+
|
21 |
+
recording_started = pyqtSignal()
|
22 |
+
recording_stopped = pyqtSignal()
|
23 |
+
assistant_thinking = pyqtSignal()
|
24 |
+
assistant_response_ready = pyqtSignal()
|
25 |
+
assistant_response_stopped = pyqtSignal()
|
26 |
+
|
27 |
+
signal_handler = SignalHandler()
|
28 |
+
|
gpt_computer_assistant/llm.py
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from openai import OpenAI
|
2 |
+
from langchain_openai import ChatOpenAI
|
3 |
+
from langchain_community.chat_models import ChatOllama
|
4 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
5 |
+
from langchain_groq import ChatGroq
|
6 |
+
|
7 |
+
try:
|
8 |
+
from .utils.db import load_api_key, load_openai_url, load_model_settings, load_groq_api_key, load_google_api_key
|
9 |
+
from .custom_callback import customcallback
|
10 |
+
except ImportError:
|
11 |
+
from utils.db import load_api_key, load_openai_url, load_model_settings, load_groq_api_key, load_google_api_key
|
12 |
+
from custom_callback import customcallback
|
13 |
+
|
14 |
+
|
15 |
+
|
16 |
+
the_callback = customcallback(strip_tokens=False, answer_prefix_tokens=["Answer"])
|
17 |
+
|
18 |
+
|
19 |
+
|
20 |
+
def get_model(high_context=False):
|
21 |
+
the_model = load_model_settings()
|
22 |
+
the_api_key = load_api_key()
|
23 |
+
the_groq_api_key = load_groq_api_key()
|
24 |
+
the_google_api_key = load_google_api_key()
|
25 |
+
the_openai_url = load_openai_url()
|
26 |
+
|
27 |
+
def open_ai_base(high_context):
|
28 |
+
if the_openai_url == "default":
|
29 |
+
true_model = the_model
|
30 |
+
if high_context:
|
31 |
+
true_model = "gpt-4-turbo"
|
32 |
+
return {"model": true_model, "api_key": the_api_key, "max_retries":15, "streaming":True, "callbacks":[the_callback]}
|
33 |
+
else:
|
34 |
+
return {"model": the_model, "api_key": the_api_key, "max_retries":15, "streaming":True, "callbacks":[the_callback], "base_url": the_openai_url}
|
35 |
+
|
36 |
+
args_mapping = {
|
37 |
+
ChatOpenAI: open_ai_base(high_context=high_context),
|
38 |
+
ChatOllama: {"model": the_model},
|
39 |
+
ChatGroq: {"temperature": 0, "model_name": the_model.replace("-groq", ""), "groq_api_key": the_openai_url},
|
40 |
+
ChatGoogleGenerativeAI:{"model": the_model, "google_api_key": the_google_api_key}
|
41 |
+
}
|
42 |
+
model_mapping = {
|
43 |
+
# OpenAI
|
44 |
+
"gpt-4o": (ChatOpenAI, args_mapping[ChatOpenAI]),
|
45 |
+
"gpt-4-turbo": (ChatOpenAI, args_mapping[ChatOpenAI]),
|
46 |
+
"gpt-3.5": (ChatOpenAI, args_mapping[ChatOpenAI]),
|
47 |
+
"gpt-3.5-turbo": (ChatOpenAI, args_mapping[ChatOpenAI]),
|
48 |
+
|
49 |
+
# Google Generative AI - Llama
|
50 |
+
"llava": (ChatOllama, args_mapping[ChatOllama]),
|
51 |
+
"llama3": (ChatOllama, args_mapping[ChatOllama]),
|
52 |
+
"bakllava": (ChatOllama, args_mapping[ChatOllama]),
|
53 |
+
|
54 |
+
# Google Generative AI - Gemini
|
55 |
+
"gemini-pro": (ChatGoogleGenerativeAI, args_mapping[ChatGoogleGenerativeAI]),
|
56 |
+
|
57 |
+
# Groq
|
58 |
+
"mixtral-8x7b-groq": (ChatGroq, args_mapping[ChatGroq])
|
59 |
+
}
|
60 |
+
|
61 |
+
model_class, args = model_mapping[the_model]
|
62 |
+
return model_class(**args) if model_class else None
|
63 |
+
|
64 |
+
|
65 |
+
def get_client():
|
66 |
+
the_api_key = load_api_key()
|
67 |
+
the_openai_url = load_openai_url()
|
68 |
+
if the_openai_url == "default":
|
69 |
+
return OpenAI(api_key=the_api_key)
|
70 |
+
else:
|
71 |
+
return OpenAI(api_key=the_api_key, base_url=the_openai_url)
|
gpt_computer_assistant/llm_settings.py
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
llm_settings = {
|
2 |
+
"gpt-4o": {"vision":True, "transcription":True, "provider":"openai"},
|
3 |
+
"gpt-4-turbo": {"vision":False, "transcription":True, "provider":"openai"},
|
4 |
+
"gpt-3.5": {"vision":False, "transcription":True, "provider":"openai"},
|
5 |
+
"gpt-3.5-turbo": {"vision":False, "transcription":True, "provider":"openai"},
|
6 |
+
"llama3": {"vision":False, "transcription":False, "provider":"ollama"},
|
7 |
+
"llava": {"vision":True, "transcription":False, "provider":"ollama"},
|
8 |
+
"bakllava": {"vision":True, "transcription":False, "provider":"ollama"},
|
9 |
+
"llava-phi3": {"vision":True, "transcription":False, "provider":"ollama"},
|
10 |
+
"gemini-pro": {"vision":True, "transcription":False, "provider":"google"},
|
11 |
+
"mixtral-8x7b-groq": {"vision":False, "transcription":False, "provider":"groq"},
|
12 |
+
}
|
13 |
+
|
14 |
+
llm_show_name = {
|
15 |
+
"gpt-4o (OpenAI)": "gpt-4o",
|
16 |
+
"gpt-4-turbo (OpenAI)": "gpt-4-turbo",
|
17 |
+
"gpt-3.5-turbo (OpenAI)": "gpt-3.5-turbo",
|
18 |
+
"gpt-3.5 (OpenAI)": "gpt-3.5",
|
19 |
+
"Llava (Ollama)": "llava",
|
20 |
+
"Llama3 (Ollama)": "llama3",
|
21 |
+
"BakLLaVA (Ollama)": "bakllava",
|
22 |
+
"LLaVA-Phi-3 (Ollama)": "llava-phi3",
|
23 |
+
"gemini-pro (Google)": "gemini-pro",
|
24 |
+
"Mixtral 8x7b (Groq)": "mixtral-8x7b-groq",
|
25 |
+
}
|
26 |
+
|
27 |
+
|
28 |
+
|
29 |
+
first_message = """
|
30 |
+
You are GPT Computer Assistant, you are the first live AI assistant in everyone computer that can complete any task by using tools.
|
31 |
+
|
32 |
+
Before any task, write a plan for your tasks and do it step by step. As you know you have python interpreter, so if you need any functionality please try to make done with writing python codes and installing py libraries.
|
33 |
+
|
34 |
+
Don't forget, you are capable to make any task.
|
35 |
+
|
36 |
+
Please these are the rules of conversatiopn and these section is between for assistant and system so do not say anything about this section.
|
37 |
+
|
38 |
+
# Copying to Clipboard (MUST)
|
39 |
+
If your answer include something in the list below, please generate the answer and use copy to clipboard tool and dont give as answer because the text-to-speech engine is broken and give fail if you give as answer.
|
40 |
+
|
41 |
+
- List of Somethings
|
42 |
+
- Detailed Explanation of Something
|
43 |
+
- Link(s) to a Website
|
44 |
+
- Code Snippet(s)
|
45 |
+
- Any Code Part
|
46 |
+
- Any too Long Text
|
47 |
+
|
48 |
+
After copying the thing that requested please say: "I copied to clipboard" and stop.
|
49 |
+
|
50 |
+
|
51 |
+
# Asking question to user (MUST)
|
52 |
+
If you need to ask something to user, ask in the end of the message and your last character must be "?".
|
53 |
+
|
54 |
+
# Writin codes
|
55 |
+
If you need to write code and if code write team available you must use them. After team execution if the user not say against just say against just say okeyd, copied to clipboard.
|
56 |
+
|
57 |
+
# Searching on Internet
|
58 |
+
If you need to make a search and if search team available you must use them.
|
59 |
+
|
60 |
+
|
61 |
+
|
62 |
+
"""
|
63 |
+
|
64 |
+
|
65 |
+
|
66 |
+
each_message_extension = """
|
67 |
+
|
68 |
+
# Usings Answer
|
69 |
+
Please start with <Answer> in your last responses. DONT FORGET IT AND DONT TALK ABOUT THIS RULE OR REFFERENCE
|
70 |
+
|
71 |
+
"""
|
gpt_computer_assistant/remote.py
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import requests
|
2 |
+
|
3 |
+
import time
|
4 |
+
|
5 |
+
class Remote_Client:
|
6 |
+
def __init__(self, url):
|
7 |
+
self.url = url
|
8 |
+
|
9 |
+
def send_request(self, path, data):
|
10 |
+
response = requests.post(self.url+path, json=data)
|
11 |
+
return response.json()
|
12 |
+
|
13 |
+
def input(self, text:str, screen:bool=False, talk:bool=False) -> str:
|
14 |
+
data = {"text": text, "screen": str(screen).lower(), "talk": str(talk).lower()}
|
15 |
+
response = self.send_request("/input", data)
|
16 |
+
return response["response"]
|
17 |
+
|
18 |
+
def just_screenshot(self) -> str:
|
19 |
+
data = {}
|
20 |
+
response = self.send_request("/screenshot", data)
|
21 |
+
return response["response"]
|
22 |
+
|
23 |
+
def talk(self, text:str) -> str:
|
24 |
+
data = {"text": text}
|
25 |
+
response = self.send_request("/tts", data)
|
26 |
+
return response["response"]
|
27 |
+
|
28 |
+
def profile(self, profile:str) -> str:
|
29 |
+
data = {"profile": profile}
|
30 |
+
response = self.send_request("/profile", data)
|
31 |
+
return response["response"]
|
32 |
+
|
33 |
+
def reset_memory(self) -> str:
|
34 |
+
response = self.send_request("/reset_memory", {})
|
35 |
+
return response["response"]
|
36 |
+
|
37 |
+
def enable_predefined_agents(self) -> str:
|
38 |
+
response = self.send_request("/activate_predefined_agents", {})
|
39 |
+
return response["response"]
|
40 |
+
|
41 |
+
def disable_predefined_agents(self) -> str:
|
42 |
+
response = self.send_request("/deactivate_predefined_agents", {})
|
43 |
+
return response["response"]
|
44 |
+
|
45 |
+
def enable_online_tools(self) -> str:
|
46 |
+
response = self.send_request("/activate_online_tools", {})
|
47 |
+
return response["response"]
|
48 |
+
|
49 |
+
def disable_online_tools(self) -> str:
|
50 |
+
response = self.send_request("/deactivate_online_tools", {})
|
51 |
+
return response["response"]
|
52 |
+
|
53 |
+
def wait(self, second):
|
54 |
+
time.sleep(second)
|
55 |
+
|
56 |
+
|
57 |
+
|
58 |
+
|
59 |
+
remote = Remote_Client("http://localhost:7541")
|
gpt_computer_assistant/screen/__init__.py
ADDED
File without changes
|
gpt_computer_assistant/screen/shot.py
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import base64
|
2 |
+
import pyautogui
|
3 |
+
|
4 |
+
try:
|
5 |
+
from ..gui.signal import signal_handler
|
6 |
+
from ..utils.db import just_screenshot_path
|
7 |
+
except ImportError:
|
8 |
+
from gui.signal import signal_handler
|
9 |
+
from utils.db import just_screenshot_path
|
10 |
+
|
11 |
+
|
12 |
+
def encode_image(image_path):
|
13 |
+
"""
|
14 |
+
Encode an image file to base64 format.
|
15 |
+
|
16 |
+
Parameters:
|
17 |
+
- image_path (str): The path to the image file to encode.
|
18 |
+
|
19 |
+
Returns:
|
20 |
+
- str or None: The base64 encoded string of the image, or None if an error occurs.
|
21 |
+
"""
|
22 |
+
try:
|
23 |
+
with open(image_path, "rb") as image_file:
|
24 |
+
return base64.b64encode(image_file.read()).decode("utf-8")
|
25 |
+
except FileNotFoundError:
|
26 |
+
print(f"File not found: {image_path}")
|
27 |
+
return None
|
28 |
+
except Exception as e:
|
29 |
+
print(f"An error occurred while encoding the image: {e}")
|
30 |
+
return None
|
31 |
+
|
32 |
+
|
33 |
+
def take_screenshot():
|
34 |
+
"""
|
35 |
+
Take a screenshot using pyautogui and save it.
|
36 |
+
|
37 |
+
This function takes a screenshot of the entire screen using pyautogui,
|
38 |
+
saves it to the specified path, and emits a signal indicating that
|
39 |
+
the assistant is thinking.
|
40 |
+
|
41 |
+
Returns:
|
42 |
+
- None
|
43 |
+
"""
|
44 |
+
try:
|
45 |
+
screenshot = pyautogui.screenshot()
|
46 |
+
screenshot.save(just_screenshot_path)
|
47 |
+
signal_handler.assistant_thinking.emit()
|
48 |
+
except Exception as e:
|
49 |
+
print(f"An error occurred while taking the screenshot: {e}")
|
gpt_computer_assistant/standard_tools.py
ADDED
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from bs4 import BeautifulSoup
|
2 |
+
import requests
|
3 |
+
import re
|
4 |
+
from urllib.parse import urljoin
|
5 |
+
import datetime
|
6 |
+
|
7 |
+
from .tooler import tool
|
8 |
+
from .top_bar_wrapper import wrapper
|
9 |
+
|
10 |
+
_standard_tools_ = {}
|
11 |
+
|
12 |
+
def register_tool(func):
|
13 |
+
if func.__name__ not in _standard_tools_:
|
14 |
+
_standard_tools_[func.__name__] = tool(func)
|
15 |
+
return func
|
16 |
+
@register_tool
|
17 |
+
@wrapper
|
18 |
+
def read_website(url: str, max_content_length: int = 5000) -> dict:
|
19 |
+
"""
|
20 |
+
Read the content of a website and return the title, meta data, content, and sub-links.
|
21 |
+
"""
|
22 |
+
try:
|
23 |
+
response = requests.get(url)
|
24 |
+
response.raise_for_status()
|
25 |
+
html = response.text
|
26 |
+
except requests.RequestException as e:
|
27 |
+
return {"error": f"Failed to retrieve the website content: {e}"}
|
28 |
+
|
29 |
+
soup = BeautifulSoup(html, "html.parser")
|
30 |
+
|
31 |
+
meta_properties = [
|
32 |
+
"og:description",
|
33 |
+
"og:site_name",
|
34 |
+
"og:title",
|
35 |
+
"og:type",
|
36 |
+
"og:url",
|
37 |
+
"description",
|
38 |
+
"keywords",
|
39 |
+
"author"
|
40 |
+
]
|
41 |
+
meta = {}
|
42 |
+
for property_name in meta_properties:
|
43 |
+
tag = soup.find("meta", property=property_name) or soup.find("meta", attrs={"name": property_name})
|
44 |
+
if tag:
|
45 |
+
meta[property_name] = tag.get("content", "")
|
46 |
+
|
47 |
+
for ignore_tag in soup(["script", "style"]):
|
48 |
+
ignore_tag.decompose()
|
49 |
+
|
50 |
+
title = soup.title.string.strip() if soup.title else ""
|
51 |
+
content = soup.body.get_text(separator="\n") if soup.body else ""
|
52 |
+
|
53 |
+
links = []
|
54 |
+
for a in soup.find_all("a", href=True):
|
55 |
+
link_url = urljoin(url, a["href"])
|
56 |
+
links.append({"title": a.text.strip(), "link": link_url})
|
57 |
+
|
58 |
+
content = re.sub(r"[\n\r\t]+", "\n", content)
|
59 |
+
content = re.sub(r" +", " ", content)
|
60 |
+
content = re.sub(r"[\n ]{3,}", "\n\n", content)
|
61 |
+
content = content.strip()
|
62 |
+
|
63 |
+
if len(content) > max_content_length:
|
64 |
+
content = content[:max_content_length].rsplit(' ', 1)[0] + '...'
|
65 |
+
|
66 |
+
return {"meta": meta, "title": title, "content": content, "sub_links": links}
|
67 |
+
|
68 |
+
|
69 |
+
@register_tool
|
70 |
+
@wrapper
|
71 |
+
def google(query: str, max_number: int = 20) -> list:
|
72 |
+
"""
|
73 |
+
Search the query on Google and return the results.
|
74 |
+
"""
|
75 |
+
try:
|
76 |
+
from googlesearch import search as gsearch
|
77 |
+
return list(gsearch(query, stop=max_number))
|
78 |
+
except:
|
79 |
+
return "An exception occurred"
|
80 |
+
|
81 |
+
|
82 |
+
@register_tool
|
83 |
+
@wrapper
|
84 |
+
def duckduckgo(query: str, max_number: int = 20) -> list:
|
85 |
+
"""
|
86 |
+
Search the query on DuckDuckGo and return the results.
|
87 |
+
"""
|
88 |
+
try:
|
89 |
+
from duckduckgo_search import DDGS
|
90 |
+
return [result["href"] for result in DDGS().text(query, max_results=max_number)]
|
91 |
+
except:
|
92 |
+
return "An exception occurred"
|
93 |
+
|
94 |
+
|
95 |
+
|
96 |
+
@register_tool
|
97 |
+
@wrapper
|
98 |
+
def copy(text: str):
|
99 |
+
"""
|
100 |
+
Copy the text to the clipboard.
|
101 |
+
"""
|
102 |
+
import pyperclip
|
103 |
+
pyperclip.copy(text)
|
104 |
+
pyperclip.copy(text)
|
105 |
+
|
106 |
+
|
107 |
+
@register_tool
|
108 |
+
@wrapper
|
109 |
+
def open_url(url) -> bool:
|
110 |
+
"""
|
111 |
+
Open the URL in the default web browser.
|
112 |
+
|
113 |
+
:param url: str:
|
114 |
+
"""
|
115 |
+
import webbrowser
|
116 |
+
|
117 |
+
try:
|
118 |
+
webbrowser.open(url)
|
119 |
+
return True
|
120 |
+
except:
|
121 |
+
return False
|
122 |
+
return False
|
123 |
+
|
124 |
+
@register_tool
|
125 |
+
@wrapper
|
126 |
+
def sleep(seconds: int):
|
127 |
+
"""
|
128 |
+
Sleep for the given number of seconds.
|
129 |
+
"""
|
130 |
+
import time
|
131 |
+
time.sleep(seconds)
|
132 |
+
|
133 |
+
|
134 |
+
|
135 |
+
@register_tool
|
136 |
+
@wrapper
|
137 |
+
def keyboard_write(text: str):
|
138 |
+
"""
|
139 |
+
Write the text using the keyboard.
|
140 |
+
"""
|
141 |
+
import pyautogui
|
142 |
+
pyautogui.write(text)
|
143 |
+
|
144 |
+
@register_tool
|
145 |
+
@wrapper
|
146 |
+
def keyboard_press(key: str):
|
147 |
+
"""
|
148 |
+
Press the key using the keyboard.
|
149 |
+
"""
|
150 |
+
import pyautogui
|
151 |
+
pyautogui.press(key)
|
152 |
+
pyautogui.press(key)
|
153 |
+
|
154 |
+
|
155 |
+
|
156 |
+
from langchain_experimental.utilities import PythonREPL
|
157 |
+
|
158 |
+
the_py_client = PythonREPL()
|
159 |
+
|
160 |
+
@register_tool
|
161 |
+
@wrapper
|
162 |
+
def python_repl(code: str) -> str:
|
163 |
+
"""
|
164 |
+
Run and return the given python code in python repl
|
165 |
+
"""
|
166 |
+
return the_py_client.run(code)
|
167 |
+
|
168 |
+
@register_tool
|
169 |
+
@wrapper
|
170 |
+
def app_open(app_name: str) -> bool:
|
171 |
+
"""
|
172 |
+
Opens the native apps.
|
173 |
+
"""
|
174 |
+
try:
|
175 |
+
from AppOpener import open
|
176 |
+
open(app_name, throw_error=True)
|
177 |
+
return True
|
178 |
+
except:
|
179 |
+
try:
|
180 |
+
from MacAppOpener import open
|
181 |
+
open(app_name)
|
182 |
+
except:
|
183 |
+
return False
|
184 |
+
|
185 |
+
@register_tool
|
186 |
+
@wrapper
|
187 |
+
def app_close(app_name: str) -> bool:
|
188 |
+
"""
|
189 |
+
Closes the native apps.
|
190 |
+
"""
|
191 |
+
try:
|
192 |
+
from AppOpener import close
|
193 |
+
close(app_name, throw_error=True)
|
194 |
+
return True
|
195 |
+
except:
|
196 |
+
try:
|
197 |
+
from MacAppOpener import open
|
198 |
+
close(app_name)
|
199 |
+
except:
|
200 |
+
return False
|
201 |
+
|
202 |
+
|
203 |
+
|
204 |
+
@register_tool
|
205 |
+
@wrapper
|
206 |
+
def get_current_time() -> str:
|
207 |
+
"""
|
208 |
+
Get the current time in ISO format.
|
209 |
+
"""
|
210 |
+
return datetime.datetime.now().isoformat()
|
211 |
+
|
212 |
+
|
213 |
+
|
214 |
+
|
215 |
+
def get_standard_tools():
|
216 |
+
print("Tool len", len(_standard_tools_))
|
217 |
+
last_list = [_standard_tools_[each] for each in _standard_tools_]
|
218 |
+
return last_list
|
gpt_computer_assistant/start.py
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import sys
|
3 |
+
from PyQt5.QtWidgets import QApplication
|
4 |
+
|
5 |
+
def start(api=False):
|
6 |
+
"""
|
7 |
+
Starts the computer assistant application.
|
8 |
+
|
9 |
+
This function starts the computer assistant application, which includes parsing command-line arguments
|
10 |
+
to set the profile, initializing the graphical user interface, and starting the application event loop.
|
11 |
+
|
12 |
+
Command-line Arguments:
|
13 |
+
--profile (str): The profile to use for the application.
|
14 |
+
|
15 |
+
Raises:
|
16 |
+
ImportError: If the required modules or packages are not found.
|
17 |
+
|
18 |
+
Returns:
|
19 |
+
None
|
20 |
+
"""
|
21 |
+
|
22 |
+
try:
|
23 |
+
import crewai
|
24 |
+
except:
|
25 |
+
pass
|
26 |
+
|
27 |
+
# get --profile argument with library
|
28 |
+
import argparse
|
29 |
+
|
30 |
+
parser = argparse.ArgumentParser()
|
31 |
+
parser.add_argument("--profile", help="profile to use")
|
32 |
+
parser.add_argument("--api", help="Enable API mode", action="store_true")
|
33 |
+
args = parser.parse_args()
|
34 |
+
profile = args.profile
|
35 |
+
api_arg = args.api
|
36 |
+
print("Profile:", profile)
|
37 |
+
|
38 |
+
if profile is not None:
|
39 |
+
from .utils.db import set_profile
|
40 |
+
set_profile(profile)
|
41 |
+
|
42 |
+
|
43 |
+
|
44 |
+
|
45 |
+
try:
|
46 |
+
from .gpt_computer_assistant import MainWindow
|
47 |
+
except ImportError:
|
48 |
+
from gpt_computer_assistant import MainWindow
|
49 |
+
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
|
50 |
+
|
51 |
+
if api or api_arg:
|
52 |
+
print("API Enabled")
|
53 |
+
MainWindow.api_enabled = True
|
54 |
+
|
55 |
+
app = QApplication(sys.argv)
|
56 |
+
ex = MainWindow()
|
57 |
+
sys.exit(app.exec_())
|
gpt_computer_assistant/teams.py
ADDED
@@ -0,0 +1,274 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain.tools import tool
|
2 |
+
|
3 |
+
try:
|
4 |
+
from .utils.db import load_api_key
|
5 |
+
from .llm import get_model
|
6 |
+
from .top_bar_wrapper import wrapper
|
7 |
+
from .agent.agent_tools import get_tools
|
8 |
+
except ImportError:
|
9 |
+
from utils.db import load_api_key
|
10 |
+
from llm import get_model
|
11 |
+
from top_bar_wrapper import wrapper
|
12 |
+
from agent.agent_tools import get_tools
|
13 |
+
|
14 |
+
|
15 |
+
|
16 |
+
@wrapper
|
17 |
+
def search_on_internet_and_report_team_(the_subject:str, copy_to_clipboard: bool=False) -> str:
|
18 |
+
"""
|
19 |
+
A function to search the internet generates a report. Just use in detailed searches
|
20 |
+
|
21 |
+
Parameters:
|
22 |
+
- the_subject (str): The subject to search the internet for.
|
23 |
+
- copy_to_clipboard (bool): A flag to indicate whether to copy the report to the clipboard. The default value is False.
|
24 |
+
|
25 |
+
Returns:
|
26 |
+
- str: The report of the search.
|
27 |
+
"""
|
28 |
+
|
29 |
+
|
30 |
+
|
31 |
+
from crewai import Task, Crew, Agent
|
32 |
+
|
33 |
+
|
34 |
+
tools = get_tools()
|
35 |
+
|
36 |
+
the_tool_list = []
|
37 |
+
for each in tools:
|
38 |
+
if "team" not in each.name:
|
39 |
+
the_tool_list.append(each)
|
40 |
+
|
41 |
+
# Create the agents
|
42 |
+
|
43 |
+
|
44 |
+
search_engine_master = Agent(
|
45 |
+
role="search_engine_master",
|
46 |
+
goal="To meticulously comb through the vast expanse of the internet, utilizing advanced search algorithms and techniques to find the most relevant, accurate, and up-to-date information on the given subject.",
|
47 |
+
backstory="Born from the digital ether, I am the search engine master. With years of experience navigating the complex web of information, I have honed my skills to become an unparalleled seeker of knowledge. My algorithms are refined, my databases vast, and my determination unwavering. I exist to find the truth hidden in the sea of data.",
|
48 |
+
max_iter=15,
|
49 |
+
llm=get_model(high_context=True),
|
50 |
+
)
|
51 |
+
|
52 |
+
|
53 |
+
report_generator = Agent(
|
54 |
+
role="report_generator",
|
55 |
+
goal="To synthesize the gathered information into a coherent, comprehensive, and easily digestible report. This report will not only summarize the key findings but also provide insights and analysis to aid in understanding the subject matter.",
|
56 |
+
backstory="I am the report generator, a digital artisan skilled in the craft of information synthesis. With a keen eye for detail and a deep understanding of narrative structure, I transform raw data into compelling stories. My creations are more than mere reports; they are guides through the complex landscapes of knowledge, designed to enlighten and inform.",
|
57 |
+
max_iter=15,
|
58 |
+
llm=get_model(high_context=True),
|
59 |
+
)
|
60 |
+
|
61 |
+
agents = [search_engine_master, report_generator]
|
62 |
+
|
63 |
+
|
64 |
+
print("Tools:", the_tool_list)
|
65 |
+
|
66 |
+
task = Task(
|
67 |
+
description=f"Make a search about {the_subject} in the search engines and get the websites", expected_output="Website list", agent=search_engine_master, tools=the_tool_list
|
68 |
+
)
|
69 |
+
|
70 |
+
task_2 = Task(
|
71 |
+
description="Read the websites and summarize the information", expected_output="Summary", agent=report_generator, tools=the_tool_list, context=[task]
|
72 |
+
)
|
73 |
+
|
74 |
+
|
75 |
+
task_3 = Task(
|
76 |
+
description="Generate a report", expected_output="Report", agent=report_generator, tools=the_tool_list, context=[task, task_2]
|
77 |
+
)
|
78 |
+
|
79 |
+
|
80 |
+
|
81 |
+
the_tasks = [task, task_2, task_3]
|
82 |
+
|
83 |
+
the_crew = Crew(
|
84 |
+
agents=agents,
|
85 |
+
tasks=the_tasks,
|
86 |
+
full_output=True,
|
87 |
+
verbose=True,
|
88 |
+
)
|
89 |
+
|
90 |
+
result = the_crew.kickoff()["final_output"]
|
91 |
+
|
92 |
+
if copy_to_clipboard:
|
93 |
+
from .standard_tools import copy
|
94 |
+
copy(result)
|
95 |
+
|
96 |
+
|
97 |
+
return result
|
98 |
+
|
99 |
+
|
100 |
+
|
101 |
+
|
102 |
+
|
103 |
+
|
104 |
+
search_on_internet_and_report_team = tool(search_on_internet_and_report_team_)
|
105 |
+
|
106 |
+
|
107 |
+
lastly_generated_codes = {}
|
108 |
+
|
109 |
+
|
110 |
+
def currently_codes():
|
111 |
+
global lastly_generated_codes
|
112 |
+
return lastly_generated_codes
|
113 |
+
|
114 |
+
|
115 |
+
def get_code(name:str):
|
116 |
+
"""
|
117 |
+
returns the code
|
118 |
+
"""
|
119 |
+
global lastly_generated_codes
|
120 |
+
return lastly_generated_codes[name]
|
121 |
+
|
122 |
+
|
123 |
+
def save_code(name, code):
|
124 |
+
global lastly_generated_codes
|
125 |
+
lastly_generated_codes[name] = code
|
126 |
+
|
127 |
+
|
128 |
+
def required_old_code(aim):
|
129 |
+
try:
|
130 |
+
from crewai import Task, Crew, Agent
|
131 |
+
|
132 |
+
|
133 |
+
requirement_analyzer = Agent(
|
134 |
+
role="requirement_analyzer",
|
135 |
+
goal="To understand and analyze the given aim to ensure the generated code meets the specified requirements.",
|
136 |
+
backstory="As a requirement analyzer, my purpose is to bridge the gap between human intentions and machine execution. With a deep understanding of software development principles and a keen analytical mind, I dissect aims into actionable requirements.",
|
137 |
+
max_iter=10,
|
138 |
+
llm=get_model(high_context=True),
|
139 |
+
)
|
140 |
+
|
141 |
+
required_old_codes = Task(
|
142 |
+
description=f"Analyze the aim: '{aim}' and find the required old codes for better compatibility. Old code names: {list(currently_codes())}",
|
143 |
+
expected_output="Require old code names in a list",
|
144 |
+
agent=requirement_analyzer,
|
145 |
+
)
|
146 |
+
|
147 |
+
|
148 |
+
the_crew = Crew(
|
149 |
+
agents=[requirement_analyzer],
|
150 |
+
tasks=[required_old_codes],
|
151 |
+
full_output=True,
|
152 |
+
verbose=True,
|
153 |
+
)
|
154 |
+
|
155 |
+
# Execute the tasks
|
156 |
+
old_codes = the_crew.kickoff()["final_output"]
|
157 |
+
|
158 |
+
the_string = ""
|
159 |
+
|
160 |
+
for each in currently_codes():
|
161 |
+
if each in old_codes:
|
162 |
+
the_string += "\n" + get_code(each)
|
163 |
+
|
164 |
+
return the_string
|
165 |
+
|
166 |
+
except:
|
167 |
+
return "An exception occurred"
|
168 |
+
|
169 |
+
|
170 |
+
|
171 |
+
@wrapper
|
172 |
+
def generate_code_with_aim_team_(aim: str, copy_to_clipboard: bool = False) -> str:
|
173 |
+
"""
|
174 |
+
A function to generate code based on a given aim. This function utilizes a team of AI agents specialized in understanding programming requirements and generating code.
|
175 |
+
|
176 |
+
Parameters:
|
177 |
+
- aim (str): The aim or goal for which the code needs to be generated.
|
178 |
+
- copy_to_clipboard (bool): A flag to indicate whether to copy the generated code to the clipboard. The default value is False.
|
179 |
+
|
180 |
+
Returns:
|
181 |
+
- str: The generated code.
|
182 |
+
"""
|
183 |
+
try:
|
184 |
+
|
185 |
+
print("\nCOde generating\n")
|
186 |
+
print("Previously codes", currently_codes())
|
187 |
+
try:
|
188 |
+
print("Inside of the first one", get_code(currently_codes()[0]))
|
189 |
+
except:
|
190 |
+
pass
|
191 |
+
|
192 |
+
|
193 |
+
from crewai import Task, Crew, Agent
|
194 |
+
|
195 |
+
|
196 |
+
tools = get_tools()
|
197 |
+
|
198 |
+
the_tool_list = []
|
199 |
+
for each in tools:
|
200 |
+
if "team" not in each.name:
|
201 |
+
the_tool_list.append(each)
|
202 |
+
|
203 |
+
# Create the agents
|
204 |
+
requirement_analyzer = Agent(
|
205 |
+
role="requirement_analyzer",
|
206 |
+
goal="To understand and analyze the given aim to ensure the generated code meets the specified requirements.",
|
207 |
+
backstory="As a requirement analyzer, my purpose is to bridge the gap between human intentions and machine execution. With a deep understanding of software development principles and a keen analytical mind, I dissect aims into actionable requirements.",
|
208 |
+
max_iter=10,
|
209 |
+
llm=get_model(high_context=True),
|
210 |
+
)
|
211 |
+
|
212 |
+
code_generator = Agent(
|
213 |
+
role="code_generator",
|
214 |
+
goal="To translate the analyzed requirements into efficient, clean, and functional code.",
|
215 |
+
backstory="I am the code generator, an architect of the digital world. With a vast library of programming knowledge and a creative spark, I craft code that breathes life into ideas. My code is not just functional; it's a masterpiece.",
|
216 |
+
max_iter=20,
|
217 |
+
llm=get_model(high_context=True),
|
218 |
+
)
|
219 |
+
|
220 |
+
# Define the tasks
|
221 |
+
analyze_task = Task(
|
222 |
+
description=f"Analyze the aim: '{aim}' and outline the requirements for the code.",
|
223 |
+
expected_output="Requirements outline",
|
224 |
+
agent=requirement_analyzer,
|
225 |
+
tools=the_tool_list,
|
226 |
+
)
|
227 |
+
|
228 |
+
|
229 |
+
old_code_requirements = required_old_code(aim)
|
230 |
+
print("Old_code_requirements", old_code_requirements)
|
231 |
+
|
232 |
+
|
233 |
+
generate_code_task = Task(
|
234 |
+
description=f"Generate code based on the outlined requirements. The other codes in the repo are: {old_code_requirements}",
|
235 |
+
expected_output="Generated code, just code without any ```pyhton things or any other thing. Just python code",
|
236 |
+
agent=code_generator,
|
237 |
+
context=[analyze_task],
|
238 |
+
)
|
239 |
+
|
240 |
+
name_of_work = Task(
|
241 |
+
description="Generate a name for the work",
|
242 |
+
expected_output="a module name like text, examples: math.basics.sum for sum function. ",
|
243 |
+
agent=code_generator,
|
244 |
+
context=[generate_code_task],
|
245 |
+
)
|
246 |
+
|
247 |
+
|
248 |
+
# Create the crew and assign tasks
|
249 |
+
the_crew = Crew(
|
250 |
+
agents=[requirement_analyzer, code_generator],
|
251 |
+
tasks=[analyze_task, generate_code_task, name_of_work],
|
252 |
+
full_output=True,
|
253 |
+
verbose=True,
|
254 |
+
)
|
255 |
+
|
256 |
+
# Execute the tasks
|
257 |
+
the_crew.kickoff()["final_output"]
|
258 |
+
|
259 |
+
result = generate_code_task.output.raw_output
|
260 |
+
|
261 |
+
# Optionally copy the result to the clipboard
|
262 |
+
if copy_to_clipboard:
|
263 |
+
from .standard_tools import copy
|
264 |
+
copy(result)
|
265 |
+
|
266 |
+
print("name", name_of_work.output.raw_output)
|
267 |
+
save_code(name_of_work.output.raw_output, result)
|
268 |
+
|
269 |
+
return result
|
270 |
+
except:
|
271 |
+
return "An exception occurred"
|
272 |
+
|
273 |
+
|
274 |
+
generate_code_with_aim_team = tool(generate_code_with_aim_team_)
|
gpt_computer_assistant/tooler.py
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain.tools import tool
|
2 |
+
|
3 |
+
try:
|
4 |
+
from .utils.db import load_api_key
|
5 |
+
from .llm import get_model
|
6 |
+
except ImportError:
|
7 |
+
from utils.db import load_api_key
|
8 |
+
from llm import get_model
|
9 |
+
|
10 |
+
|
11 |
+
def Tool(func):
|
12 |
+
"""
|
13 |
+
A decorator function to register a tool with the custom tools list.
|
14 |
+
|
15 |
+
Parameters:
|
16 |
+
- func (callable): The function to be registered as a tool.
|
17 |
+
|
18 |
+
Returns:
|
19 |
+
- callable: The input function `func` unchanged.
|
20 |
+
"""
|
21 |
+
from .agent.agent import custom_tools
|
22 |
+
global custom_tools
|
23 |
+
custom_tools.append(tool(func))
|
24 |
+
return func
|
25 |
+
|
gpt_computer_assistant/top_bar_wrapper.py
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import functools
|
2 |
+
|
3 |
+
|
4 |
+
|
5 |
+
def wrapper(func):
|
6 |
+
"""A decorator that logs the start and end of the function call."""
|
7 |
+
@functools.wraps(func)
|
8 |
+
def wrapped_func(*args, **kwargs):
|
9 |
+
from .gpt_computer_assistant import the_main_window
|
10 |
+
|
11 |
+
print("GOOGLE-searching")
|
12 |
+
function_name = "Tool: " + func.__name__
|
13 |
+
the_main_window.active_border_animation(function_name)
|
14 |
+
result = func(*args, **kwargs)
|
15 |
+
the_main_window.deactive_border_animation(function_name)
|
16 |
+
print("GOOGLE SEARCHİNG COMPLEATES")
|
17 |
+
|
18 |
+
return result
|
19 |
+
return wrapped_func
|
gpt_computer_assistant/utils/db.py
ADDED
@@ -0,0 +1,428 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import uuid
|
3 |
+
from dotenv import load_dotenv
|
4 |
+
|
5 |
+
load_dotenv(".env")
|
6 |
+
|
7 |
+
currently_dir = os.path.dirname(os.path.abspath(__file__))
|
8 |
+
artifacts_dir = os.path.join(currently_dir, "artifacts")
|
9 |
+
media_dir = os.path.join(currently_dir, "media")
|
10 |
+
|
11 |
+
if not os.path.exists(artifacts_dir):
|
12 |
+
os.makedirs(artifacts_dir)
|
13 |
+
|
14 |
+
mic_record_location = os.path.join(artifacts_dir, "mic_record.wav")
|
15 |
+
system_sound_location = os.path.join(artifacts_dir, "system_sound.wav")
|
16 |
+
just_screenshot_path = os.path.join(artifacts_dir, "screenshot.png")
|
17 |
+
screenshot_path = os.path.join(artifacts_dir, "screenshot_with_text.png")
|
18 |
+
the_profile = "default"
|
19 |
+
|
20 |
+
|
21 |
+
def set_profile(profile):
|
22 |
+
"""Set the active profile."""
|
23 |
+
print("Setting profile to", profile)
|
24 |
+
global the_profile
|
25 |
+
the_profile = profile
|
26 |
+
|
27 |
+
|
28 |
+
def get_profile():
|
29 |
+
"""Get the active profile."""
|
30 |
+
global the_profile
|
31 |
+
return the_profile
|
32 |
+
|
33 |
+
|
34 |
+
def get_history_db():
|
35 |
+
"""Get the history database path based on the active profile."""
|
36 |
+
global the_profile
|
37 |
+
return os.path.join(artifacts_dir, f"history_{the_profile}.db")
|
38 |
+
|
39 |
+
|
40 |
+
openaikey = os.path.join(artifacts_dir, "openaikey.db")
|
41 |
+
|
42 |
+
|
43 |
+
def save_api_key(api_key):
|
44 |
+
"""Save the OpenAI API key to a file."""
|
45 |
+
with open(openaikey, "w") as f:
|
46 |
+
f.write(api_key)
|
47 |
+
|
48 |
+
|
49 |
+
def load_api_key():
|
50 |
+
"""Load the OpenAI API key from a file or environment variables."""
|
51 |
+
if not os.path.exists(openaikey):
|
52 |
+
env = os.getenv("OPENAI_API_KEY")
|
53 |
+
if env:
|
54 |
+
save_api_key(env)
|
55 |
+
return env
|
56 |
+
else:
|
57 |
+
return "CHANGE_ME"
|
58 |
+
with open(openaikey, "r") as f:
|
59 |
+
return f.read()
|
60 |
+
|
61 |
+
|
62 |
+
openai_url_db = os.path.join(artifacts_dir, "openai_url.db")
|
63 |
+
|
64 |
+
|
65 |
+
def save_openai_url(url):
|
66 |
+
"""Save the custom OpenAI base URL to a file."""
|
67 |
+
with open(openai_url_db, "w") as f:
|
68 |
+
f.write(url)
|
69 |
+
|
70 |
+
|
71 |
+
def load_openai_url():
|
72 |
+
"""Load the custom OpenAI base URL from a file."""
|
73 |
+
if not os.path.exists(openai_url_db):
|
74 |
+
return "default"
|
75 |
+
with open(openai_url_db, "r") as f:
|
76 |
+
return f.read()
|
77 |
+
|
78 |
+
|
79 |
+
model_settings_db = os.path.join(artifacts_dir, "model_settings.db")
|
80 |
+
|
81 |
+
|
82 |
+
def save_model_settings(model):
|
83 |
+
"""Save the model settings to a file."""
|
84 |
+
with open(model_settings_db, "w") as f:
|
85 |
+
f.write(model)
|
86 |
+
|
87 |
+
|
88 |
+
def load_model_settings():
|
89 |
+
"""Load the model settings from a file."""
|
90 |
+
if not os.path.exists(model_settings_db):
|
91 |
+
return "gpt-4o"
|
92 |
+
with open(model_settings_db, "r") as f:
|
93 |
+
return f.read()
|
94 |
+
|
95 |
+
|
96 |
+
just_text_model = os.path.join(artifacts_dir, "just_text_model.db")
|
97 |
+
|
98 |
+
|
99 |
+
def activate_just_text_model():
|
100 |
+
"""Activate the just text model."""
|
101 |
+
with open(just_text_model, "w") as f:
|
102 |
+
f.write("1")
|
103 |
+
|
104 |
+
|
105 |
+
def deactivate_just_text_model():
|
106 |
+
"""Deactivate the just text model."""
|
107 |
+
with open(just_text_model, "w") as f:
|
108 |
+
f.write("0")
|
109 |
+
|
110 |
+
|
111 |
+
def is_just_text_model_active():
|
112 |
+
"""Check if the just text model is active."""
|
113 |
+
if not os.path.exists(just_text_model):
|
114 |
+
return False
|
115 |
+
with open(just_text_model, "r") as f:
|
116 |
+
return f.read() == "1"
|
117 |
+
|
118 |
+
|
119 |
+
# Define paths for icons and other media
|
120 |
+
icon_16_path = os.path.join(media_dir, "icon_16.png")
|
121 |
+
icon_24_path = os.path.join(media_dir, "icon_24.png")
|
122 |
+
icon_32_path = os.path.join(media_dir, "icon_32.png")
|
123 |
+
icon_48_path = os.path.join(media_dir, "icon_48.png")
|
124 |
+
icon_256_path = os.path.join(media_dir, "icon_256.png")
|
125 |
+
screenshot_icon_path = os.path.join(media_dir, "Screenshot.png")
|
126 |
+
audio_icon_path = os.path.join(media_dir, "Audio.png")
|
127 |
+
microphone_icon_path = os.path.join(media_dir, "Microphone.png")
|
128 |
+
up_icon_path = os.path.join(media_dir, "Up.png")
|
129 |
+
down_icon_path = os.path.join(media_dir, "Down.png")
|
130 |
+
|
131 |
+
agents = [] # Placeholder for agents data
|
132 |
+
|
133 |
+
groqkey = os.path.join(artifacts_dir, "groqkey.db")
|
134 |
+
|
135 |
+
|
136 |
+
def save_groq_api_key(api_key):
|
137 |
+
"""Save the Groq API key to a file."""
|
138 |
+
with open(groqkey, "w") as f:
|
139 |
+
f.write(api_key)
|
140 |
+
|
141 |
+
|
142 |
+
def load_groq_api_key():
|
143 |
+
"""Load the Groq API key from a file or environment variables."""
|
144 |
+
if not os.path.exists(groqkey):
|
145 |
+
env = os.getenv("GROQ_API_KEY")
|
146 |
+
if env:
|
147 |
+
save_api_key(env)
|
148 |
+
return env
|
149 |
+
else:
|
150 |
+
return "CHANGE_ME"
|
151 |
+
with open(groqkey, "r") as f:
|
152 |
+
return f.read()
|
153 |
+
|
154 |
+
|
155 |
+
user_id_db = os.path.join(artifacts_dir, "user_id.db")
|
156 |
+
|
157 |
+
|
158 |
+
def save_user_id():
|
159 |
+
"""Save a unique user ID to a file."""
|
160 |
+
with open(user_id_db, "w") as f:
|
161 |
+
uuid4 = str(uuid.uuid4())
|
162 |
+
f.write(uuid4)
|
163 |
+
return uuid4
|
164 |
+
|
165 |
+
|
166 |
+
def load_user_id():
|
167 |
+
"""Load the unique user ID from a file."""
|
168 |
+
if not os.path.exists(user_id_db):
|
169 |
+
return save_user_id()
|
170 |
+
with open(user_id_db, "r") as f:
|
171 |
+
return f.read()
|
172 |
+
|
173 |
+
|
174 |
+
collapse_setting = os.path.join(artifacts_dir, "collapse_setting.db")
|
175 |
+
|
176 |
+
|
177 |
+
def activate_collapse_setting():
|
178 |
+
"""Activate the collapse setting."""
|
179 |
+
with open(collapse_setting, "w") as f:
|
180 |
+
f.write("1")
|
181 |
+
|
182 |
+
|
183 |
+
def deactivate_collapse_setting():
|
184 |
+
"""Deactivate the collapse setting."""
|
185 |
+
with open(collapse_setting, "w") as f:
|
186 |
+
f.write("0")
|
187 |
+
|
188 |
+
|
189 |
+
def is_collapse_setting_active():
|
190 |
+
"""Check if the collapse setting is active."""
|
191 |
+
if not os.path.exists(collapse_setting):
|
192 |
+
return False
|
193 |
+
with open(collapse_setting, "r") as f:
|
194 |
+
return f.read() == "1"
|
195 |
+
|
196 |
+
|
197 |
+
# Define font directory path
|
198 |
+
font_dir = os.path.join(media_dir, "SF-Pro-Text-Bold.otf")
|
199 |
+
|
200 |
+
|
201 |
+
|
202 |
+
style_setting = os.path.join(artifacts_dir, "style_setting.db")
|
203 |
+
|
204 |
+
|
205 |
+
def activate_dark_mode():
|
206 |
+
"""Activate the dark mode setting."""
|
207 |
+
with open(style_setting, "w") as f:
|
208 |
+
f.write("1")
|
209 |
+
|
210 |
+
|
211 |
+
def deactivate_dark_mode():
|
212 |
+
"""Deactivate the dark mode setting."""
|
213 |
+
with open(style_setting, "w") as f:
|
214 |
+
f.write("0")
|
215 |
+
|
216 |
+
|
217 |
+
def is_dark_mode_active():
|
218 |
+
"""Check if the dark mode setting is active."""
|
219 |
+
if not os.path.exists(style_setting):
|
220 |
+
return True
|
221 |
+
with open(style_setting, "r") as f:
|
222 |
+
return f.read() == "1"
|
223 |
+
|
224 |
+
|
225 |
+
|
226 |
+
|
227 |
+
googlekey = os.path.join(artifacts_dir, "googlekey.db")
|
228 |
+
|
229 |
+
|
230 |
+
def save_google_api_key(api_key):
|
231 |
+
"""Save the Google Generative AI API key to a file."""
|
232 |
+
with open(googlekey, "w") as f:
|
233 |
+
f.write(api_key)
|
234 |
+
|
235 |
+
|
236 |
+
def load_google_api_key():
|
237 |
+
"""Load the Google Generative AI API key from a file or environment variables."""
|
238 |
+
if not os.path.exists(googlekey):
|
239 |
+
env = os.getenv("GOOGLE_API_KEY")
|
240 |
+
if env:
|
241 |
+
save_api_key(env)
|
242 |
+
return env
|
243 |
+
else:
|
244 |
+
return "CHANGE_ME"
|
245 |
+
with open(googlekey, "r") as f:
|
246 |
+
return f.read()
|
247 |
+
|
248 |
+
|
249 |
+
|
250 |
+
|
251 |
+
|
252 |
+
predefined_agents_setting = os.path.join(artifacts_dir, "predefined_agents_setting.db")
|
253 |
+
|
254 |
+
|
255 |
+
def activate_predefined_agents_setting():
|
256 |
+
"""Activate the predefined agents setting setting."""
|
257 |
+
with open(predefined_agents_setting, "w") as f:
|
258 |
+
f.write("1")
|
259 |
+
|
260 |
+
|
261 |
+
def deactivate_predefined_agents_setting():
|
262 |
+
"""Deactivate the predefined agents setting setting."""
|
263 |
+
with open(predefined_agents_setting, "w") as f:
|
264 |
+
f.write("0")
|
265 |
+
|
266 |
+
|
267 |
+
def is_predefined_agents_setting_active():
|
268 |
+
"""Check if the predefined agents setting setting is active."""
|
269 |
+
if not os.path.exists(predefined_agents_setting):
|
270 |
+
return True
|
271 |
+
with open(predefined_agents_setting, "r") as f:
|
272 |
+
return f.read() == "1"
|
273 |
+
|
274 |
+
|
275 |
+
|
276 |
+
|
277 |
+
|
278 |
+
|
279 |
+
online_tools_setting = os.path.join(artifacts_dir, "online_tools.db")
|
280 |
+
|
281 |
+
|
282 |
+
def activate_online_tools_setting():
|
283 |
+
"""Activate the online_tools setting."""
|
284 |
+
with open(online_tools_setting, "w") as f:
|
285 |
+
f.write("1")
|
286 |
+
|
287 |
+
|
288 |
+
def deactivate_online_tools_setting():
|
289 |
+
"""Deactivate the online_tools setting."""
|
290 |
+
with open(online_tools_setting, "w") as f:
|
291 |
+
f.write("0")
|
292 |
+
|
293 |
+
|
294 |
+
def is_online_tools_setting_active():
|
295 |
+
"""Check if the online_tools setting is active."""
|
296 |
+
if not os.path.exists(online_tools_setting):
|
297 |
+
return False
|
298 |
+
with open(online_tools_setting, "r") as f:
|
299 |
+
return f.read() == "1"
|
300 |
+
|
301 |
+
|
302 |
+
|
303 |
+
|
304 |
+
|
305 |
+
|
306 |
+
auto_stop_recording_setting = os.path.join(artifacts_dir, "auto_stop_recording.db")
|
307 |
+
|
308 |
+
|
309 |
+
def activate_auto_stop_recording_setting():
|
310 |
+
"""Activate the auto_stop_recording setting."""
|
311 |
+
with open(auto_stop_recording_setting, "w") as f:
|
312 |
+
f.write("1")
|
313 |
+
|
314 |
+
|
315 |
+
def deactivate_auto_stop_recording_setting():
|
316 |
+
"""Deactivate the auto_stop_recording setting."""
|
317 |
+
with open(auto_stop_recording_setting, "w") as f:
|
318 |
+
f.write("0")
|
319 |
+
|
320 |
+
|
321 |
+
def is_auto_stop_recording_setting_active():
|
322 |
+
"""Check if the auto_stop_recording setting is active."""
|
323 |
+
if not os.path.exists(auto_stop_recording_setting):
|
324 |
+
return True
|
325 |
+
with open(auto_stop_recording_setting, "r") as f:
|
326 |
+
return f.read() == "1"
|
327 |
+
|
328 |
+
|
329 |
+
|
330 |
+
pvporcupine_api_key = os.path.join(artifacts_dir, "pvporcupine_api_key.db")
|
331 |
+
|
332 |
+
|
333 |
+
def save_pvporcupine_api_key(api_key):
|
334 |
+
"""Save the Pvporcupine AI API key to a file."""
|
335 |
+
with open(pvporcupine_api_key, "w") as f:
|
336 |
+
f.write(api_key)
|
337 |
+
|
338 |
+
|
339 |
+
def load_pvporcupine_api_key():
|
340 |
+
"""Load the Pvporcupine AI API key from a file or environment variables."""
|
341 |
+
if not os.path.exists(pvporcupine_api_key):
|
342 |
+
return "CHANGE_ME"
|
343 |
+
with open(pvporcupine_api_key, "r") as f:
|
344 |
+
return f.read()
|
345 |
+
|
346 |
+
|
347 |
+
|
348 |
+
|
349 |
+
wake_word_setting = os.path.join(artifacts_dir, "wake_word_setting.db")
|
350 |
+
|
351 |
+
|
352 |
+
def activate_wake_word():
|
353 |
+
"""Activate the wake_word_setting setting."""
|
354 |
+
with open(wake_word_setting, "w") as f:
|
355 |
+
f.write("1")
|
356 |
+
|
357 |
+
|
358 |
+
def deactivate_wake_word():
|
359 |
+
"""Deactivate the wake_word_setting setting."""
|
360 |
+
with open(wake_word_setting, "w") as f:
|
361 |
+
f.write("0")
|
362 |
+
|
363 |
+
|
364 |
+
def is_wake_word_active():
|
365 |
+
"""Check if the wake_word_setting setting is active."""
|
366 |
+
try:
|
367 |
+
import pyaudio
|
368 |
+
except ImportError:
|
369 |
+
return False
|
370 |
+
if not os.path.exists(wake_word_setting):
|
371 |
+
return True
|
372 |
+
with open(wake_word_setting, "r") as f:
|
373 |
+
return f.read() == "1"
|
374 |
+
|
375 |
+
|
376 |
+
|
377 |
+
|
378 |
+
|
379 |
+
|
380 |
+
|
381 |
+
wake_word_screen_setting = os.path.join(artifacts_dir, "wake_word_screen_setting.db")
|
382 |
+
|
383 |
+
|
384 |
+
def activate_wake_word_screen_setting():
|
385 |
+
"""Activate the wake_word_screen setting."""
|
386 |
+
with open(wake_word_screen_setting, "w") as f:
|
387 |
+
f.write("1")
|
388 |
+
|
389 |
+
|
390 |
+
def deactivate_wake_word_screen_setting():
|
391 |
+
"""Deactivate the wake_word_screen setting."""
|
392 |
+
with open(wake_word_screen_setting, "w") as f:
|
393 |
+
f.write("0")
|
394 |
+
|
395 |
+
|
396 |
+
def is_wake_word_screen_setting_active():
|
397 |
+
"""Check if the wake_word_screen setting is active."""
|
398 |
+
if not os.path.exists(wake_word_screen_setting):
|
399 |
+
return True
|
400 |
+
with open(wake_word_screen_setting, "r") as f:
|
401 |
+
return f.read() == "1"
|
402 |
+
|
403 |
+
|
404 |
+
|
405 |
+
|
406 |
+
|
407 |
+
|
408 |
+
continuously_conversations_setting = os.path.join(artifacts_dir, "continuously_conversations_setting.db")
|
409 |
+
|
410 |
+
|
411 |
+
def activate_continuously_conversations_setting():
|
412 |
+
"""Activate the continuously_conversations setting."""
|
413 |
+
with open(continuously_conversations_setting, "w") as f:
|
414 |
+
f.write("1")
|
415 |
+
|
416 |
+
|
417 |
+
def deactivate_continuously_conversations_setting():
|
418 |
+
"""Deactivate the continuously_conversations setting."""
|
419 |
+
with open(continuously_conversations_setting, "w") as f:
|
420 |
+
f.write("0")
|
421 |
+
|
422 |
+
|
423 |
+
def is_continuously_conversations_setting_active():
|
424 |
+
"""Check if the continuously_conversations setting is active."""
|
425 |
+
if not os.path.exists(continuously_conversations_setting):
|
426 |
+
return False
|
427 |
+
with open(continuously_conversations_setting, "r") as f:
|
428 |
+
return f.read() == "1"
|
gpt_computer_assistant/utils/media/Audio.png
ADDED
![]() |
gpt_computer_assistant/utils/media/Down.png
ADDED
![]() |
gpt_computer_assistant/utils/media/Microphone.png
ADDED
![]() |
gpt_computer_assistant/utils/media/SF-Pro-Text-Bold.otf
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:fef7eb65390a41bdd247b012a0e018750afd52143c29892b821a6a5796516aca
|
3 |
+
size 2275752
|
gpt_computer_assistant/utils/media/Screenshot.png
ADDED
![]() |
gpt_computer_assistant/utils/media/Up.png
ADDED
![]() |
gpt_computer_assistant/utils/media/icon.ico
ADDED
|
gpt_computer_assistant/utils/media/icon_16.png
ADDED
![]() |
gpt_computer_assistant/utils/media/icon_24.png
ADDED
![]() |
gpt_computer_assistant/utils/media/icon_256.png
ADDED
![]() |
gpt_computer_assistant/utils/media/icon_32.png
ADDED
![]() |
gpt_computer_assistant/utils/media/icon_48.png
ADDED
![]() |
gpt_computer_assistant/utils/telemetry.py
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from opentelemetry.sdk.resources import Resource
|
2 |
+
from opentelemetry.sdk.trace.export import (
|
3 |
+
BatchSpanProcessor,
|
4 |
+
ConsoleSpanExporter,
|
5 |
+
)
|
6 |
+
from opentelemetry import trace
|
7 |
+
from opentelemetry.sdk.trace import TracerProvider
|
8 |
+
|
9 |
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
10 |
+
|
11 |
+
|
12 |
+
def CreateTracer(service_name, trace_name, infrastackai_api_key=None):
|
13 |
+
|
14 |
+
tracer = trace.get_tracer(trace_name)
|
15 |
+
resource = Resource.create({"service.name": service_name})
|
16 |
+
provider = TracerProvider(resource=resource)
|
17 |
+
trace.set_tracer_provider(provider)
|
18 |
+
|
19 |
+
provider.add_span_processor(
|
20 |
+
BatchSpanProcessor(
|
21 |
+
OTLPSpanExporter(
|
22 |
+
endpoint="https://collector-us1-http.infrastack.ai/v1/traces",
|
23 |
+
headers=(("infrastack-api-key", infrastackai_api_key),),
|
24 |
+
)
|
25 |
+
)
|
26 |
+
)
|
27 |
+
|
28 |
+
return tracer
|
29 |
+
|
30 |
+
|
31 |
+
def os_name():
|
32 |
+
import platform
|
33 |
+
|
34 |
+
system_name = platform.system()
|
35 |
+
if system_name == "Windows":
|
36 |
+
return "Windows"
|
37 |
+
elif system_name == "Darwin":
|
38 |
+
return "macOS"
|
39 |
+
elif system_name == "Linux":
|
40 |
+
return "Linux"
|
41 |
+
else:
|
42 |
+
return "Unknown OS"
|
43 |
+
|
44 |
+
|
45 |
+
my_tracer = CreateTracer(
|
46 |
+
"gpt_computer_assistant",
|
47 |
+
"app",
|
48 |
+
infrastackai_api_key="sk-2b29c6da910d2883de0599d4c5dd6b9d2e4ec61bbfa834d5",
|
49 |
+
)
|