Skip to content

Commit fea89a8

Browse files
committed
Better server, OS mode image fix, better profiles, Colab fix, SMS enhancement
1 parent 3211d97 commit fea89a8

File tree

21 files changed

+413
-176
lines changed

21 files changed

+413
-176
lines changed

interpreter/core/async_core.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import json
2+
import threading
3+
import time
4+
5+
from core import OpenInterpreter
6+
7+
8+
class AsyncOpenInterpreter(OpenInterpreter):
9+
def __init__(self, *args, **kwargs):
10+
super().__init__(*args, **kwargs)
11+
self.async_thread = None
12+
self.input_queue
13+
self.output_queue
14+
15+
async def input(self, chunk):
16+
"""
17+
Expects a chunk in streaming LMC format.
18+
"""
19+
try:
20+
chunk = json.loads(chunk)
21+
except:
22+
pass
23+
24+
if "start" in chunk:
25+
self.async_thread.join()
26+
elif "end" in chunk:
27+
if self.async_thread is None or not self.async_thread.is_alive():
28+
self.async_thread = threading.Thread(target=self.complete)
29+
self.async_thread.start()
30+
else:
31+
await self._add_to_queue(self._input_queue, chunk)
32+
33+
async def output(self, *args, **kwargs):
34+
# Your async output code here
35+
pass

interpreter/core/computer/contacts/contacts.py

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,81 @@
11
import platform
2+
23
from ..utils.run_applescript import run_applescript_capture
34

5+
46
class Contacts:
57
def __init__(self, computer):
68
self.computer = computer
79

8-
910
def get_phone_number(self, contact_name):
1011
"""
1112
Returns the phone number of a contact by name.
1213
"""
13-
if platform.system() != 'Darwin':
14+
if platform.system() != "Darwin":
1415
return "This method is only supported on MacOS"
15-
16-
script = f'''
17-
tell application "Contacts"
18-
set thePerson to first person whose name is "{contact_name}"
19-
set theNumber to value of first phone of thePerson
20-
return theNumber
16+
17+
script = f"""
18+
tell application "System Events" to tell process "Finder"
19+
open location "addressbook://"
20+
tell application "Contacts"
21+
set thePerson to first person whose name is "{contact_name}"
22+
if exists thePerson then
23+
set theNumber to value of first phone of thePerson
24+
return theNumber
25+
else
26+
return "Contact not found"
27+
end if
28+
end tell
2129
end tell
22-
'''
30+
"""
2331
stout, stderr = run_applescript_capture(script)
2432
# If the person is not found, we will try to find similar contacts
25-
if "Can’t get person" in stderr:
26-
names= self.get_full_names_from_first_name(contact_name)
27-
if names == "No contacts found":
28-
return "No contacts found"
33+
if "Can’t get person" in stderr or not stout:
34+
names = self.get_full_names_from_first_name(contact_name)
35+
if "No contacts found" in names or not names:
36+
raise Exception("Contact not found")
2937
else:
3038
# Language model friendly error message
31-
return f"A contact for '{contact_name}' was not found, perhaps one of these similar contacts might be what you are looking for? {names} \n Please try again and provide a more specific contact name."
39+
raise Exception(
40+
f"A contact for '{contact_name}' was not found, perhaps one of these similar contacts might be what you are looking for? {names} \n Please try again and provide a more specific contact name."
41+
)
3242
else:
33-
return stout.replace('\n', '')
34-
43+
return stout.replace("\n", "")
3544

3645
def get_email_address(self, contact_name):
3746
"""
3847
Returns the email address of a contact by name.
3948
"""
40-
if platform.system() != 'Darwin':
49+
if platform.system() != "Darwin":
4150
return "This method is only supported on MacOS"
42-
43-
script = f'''
51+
52+
script = f"""
4453
tell application "Contacts"
4554
set thePerson to first person whose name is "{contact_name}"
4655
set theEmail to value of first email of thePerson
4756
return theEmail
4857
end tell
49-
'''
58+
"""
5059
stout, stderr = run_applescript_capture(script)
5160
# If the person is not found, we will try to find similar contacts
5261
if "Can’t get person" in stderr:
53-
names= self.get_full_names_from_first_name(contact_name)
62+
names = self.get_full_names_from_first_name(contact_name)
5463
if names == "No contacts found":
5564
return "No contacts found"
5665
else:
5766
# Language model friendly error message
5867
return f"A contact for '{contact_name}' was not found, perhaps one of these similar contacts might be what you are looking for? {names} \n Please try again and provide a more specific contact name."
5968
else:
60-
return stout.replace('\n', '')
61-
69+
return stout.replace("\n", "")
6270

6371
def get_full_names_from_first_name(self, first_name):
6472
"""
6573
Returns a list of full names of contacts that contain the first name provided.
6674
"""
67-
if platform.system() != 'Darwin':
75+
if platform.system() != "Darwin":
6876
return "This method is only supported on MacOS"
69-
70-
script = f'''
77+
78+
script = f"""
7179
tell application "Contacts"
7280
set matchingPeople to every person whose name contains "{first_name}"
7381
set namesList to {{}}
@@ -76,10 +84,9 @@ def get_full_names_from_first_name(self, first_name):
7684
end repeat
7785
return namesList
7886
end tell
79-
'''
87+
"""
8088
names, _ = run_applescript_capture(script)
8189
if names:
8290
return names
8391
else:
8492
return "No contacts found."
85-

interpreter/core/computer/display/display.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from io import BytesIO
1111

1212
import requests
13+
from IPython.display import display
1314
from PIL import Image
1415

1516
from ...utils.lazy_import import lazy_import
@@ -19,7 +20,11 @@
1920
# from utils.get_active_window import get_active_window
2021

2122
# Lazy import of optional packages
22-
cv2 = lazy_import("cv2")
23+
try:
24+
cv2 = lazy_import("cv2")
25+
except:
26+
cv2 = None # Fixes colab error
27+
2328
pyautogui = lazy_import("pyautogui")
2429
np = lazy_import("numpy")
2530
plt = lazy_import("matplotlib.pyplot")
@@ -86,7 +91,6 @@ def screenshot(
8691
show=True,
8792
quadrant=None,
8893
active_app_only=True,
89-
force_image=False,
9094
combine_screens=True,
9195
):
9296
"""
@@ -133,7 +137,7 @@ def screenshot(
133137
)
134138
)
135139
message = format_to_recipient(
136-
"Taking a screenshot of the active app (recommended). To take a screenshot of the entire screen (uncommon), use computer.display.view(active_app_only=False).",
140+
"Taking a screenshot of the active app. To take a screenshot of the entire screen (uncommon), use computer.view(active_app_only=False).",
137141
"assistant",
138142
)
139143
print(message)
@@ -145,7 +149,7 @@ def screenshot(
145149
screen=screen, combine_screens=combine_screens
146150
) # this function uses pyautogui.screenshot which works fine for all OS (mac, linux and windows)
147151
message = format_to_recipient(
148-
"Taking a screenshot of the entire screen. This is not recommended. You (the language model assistant) will receive it with low resolution.\n\nTo maximize performance, use computer.display.view(active_app_only=True). This will produce an ultra high quality image of the active application.",
152+
"Taking a screenshot of the entire screen.\n\nTo focus on the active app, use computer.view(active_app_only=True).",
149153
"assistant",
150154
)
151155
print(message)
@@ -181,18 +185,12 @@ def screenshot(
181185
screenshot = screenshot.convert("RGB")
182186

183187
if show:
184-
# Show the image using matplotlib
188+
# Show the image using IPython display
185189
if isinstance(screenshot, list):
186190
for img in screenshot:
187-
plt.imshow(np.array(img))
188-
plt.show()
191+
display(img)
189192
else:
190-
plt.imshow(np.array(screenshot))
191-
192-
with warnings.catch_warnings():
193-
# It displays an annoying message about Agg not being able to display something or WHATEVER
194-
warnings.simplefilter("ignore")
195-
plt.show()
193+
display(screenshot)
196194

197195
return screenshot # this will be a list of combine_screens == False
198196

interpreter/core/computer/mouse/mouse.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import time
22
import warnings
33

4+
from IPython.display import display
5+
from PIL import Image
6+
47
from ...utils.lazy_import import lazy_import
58
from ..utils.recipient_utils import format_to_recipient
69

710
# Lazy import of optional packages
8-
cv2 = lazy_import(
9-
"cv2",
10-
)
11+
try:
12+
cv2 = lazy_import("cv2")
13+
except:
14+
cv2 = None # Fixes colab error
1115
np = lazy_import("numpy")
1216
pyautogui = lazy_import("pyautogui")
1317
plt = lazy_import("matplotlib.pyplot")
@@ -103,10 +107,8 @@ def move(self, *args, x=None, y=None, icon=None, text=None, screenshot=None):
103107
cv2.LINE_AA,
104108
)
105109

106-
plt.imshow(img_draw)
107-
with warnings.catch_warnings():
108-
warnings.simplefilter("ignore")
109-
plt.show()
110+
img_pil = Image.fromarray(img_draw)
111+
display(img_pil)
110112

111113
coordinates = [
112114
f"{i}: ({int(item['coordinates'][0]*self.computer.display.width)}, {int(item['coordinates'][1]*self.computer.display.height)}) "

interpreter/core/computer/sms/sms.py

Lines changed: 96 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,109 @@
1+
import datetime
2+
import os
3+
import plistlib
4+
import sqlite3
15
import subprocess
2-
import platform
3-
from ..utils.run_applescript import run_applescript
6+
import time
47

58

69
class SMS:
710
def __init__(self, computer):
811
self.computer = computer
9-
self.messages_app = "Messages"
10-
12+
self.database_path = self.resolve_database_path()
1113

14+
def resolve_database_path(self):
15+
if os.geteuid() == 0: # Running as root
16+
home_directory = os.path.expanduser(f"~{os.environ.get('SUDO_USER')}")
17+
else:
18+
home_directory = os.path.expanduser("~")
19+
return f"{home_directory}/Library/Messages/chat.db"
1220

1321
def send(self, to, message):
22+
message_escaped = message.replace('"', '\\"').replace("\\", "\\\\")
23+
script = f"""
24+
tell application "Messages"
25+
set targetBuddy to "{to}"
26+
send "{message_escaped}" to buddy targetBuddy of (service 1 whose service type is iMessage)
27+
end tell
1428
"""
15-
Sends an SMS message to the specified recipient using the Messages app.
16-
"""
17-
# Check if the operating system is MacOS, as this functionality is MacOS-specific.
18-
if platform.system() != 'Darwin':
19-
return "This method is only supported on MacOS"
20-
21-
# Remove any newline characters from the recipient number.
22-
to = to.replace("\n", "")
23-
# Escape double quotes in the message and recipient variables to prevent script errors.
24-
escaped_message = message.replace('"', '\\"')
25-
escaped_to = to.replace('"', '\\"')
29+
subprocess.run(["osascript", "-e", script], check=True)
30+
return "Message sent successfully"
2631

27-
script = f"""
28-
tell application "Messages"
29-
set targetBuddy to buddy "{escaped_to}" of service 1
30-
send "{escaped_message}" to targetBuddy
31-
end tell
32+
def get(self, contact=None, limit=10, substring=None):
33+
if not self.can_access_database():
34+
self.prompt_full_disk_access()
35+
36+
conn = sqlite3.connect(self.database_path)
37+
conn.row_factory = sqlite3.Row # Set row factory
38+
cursor = conn.cursor()
39+
query = """
40+
SELECT message.*, handle.id as sender FROM message
41+
LEFT JOIN handle ON message.handle_id = handle.ROWID
3242
"""
43+
params = []
44+
conditions = []
45+
46+
if contact:
47+
conditions.append("handle.id=?")
48+
params.append(contact)
49+
if substring:
50+
conditions.append("message.text LIKE ?")
51+
params.append(f"%{substring}%")
52+
if conditions:
53+
query += " WHERE " + " AND ".join(conditions)
54+
query += " ORDER BY message.date DESC"
55+
56+
cursor.execute(query, params)
57+
58+
# Parse plist data and make messages readable
59+
readable_messages = []
60+
while len(readable_messages) < limit:
61+
try:
62+
message = cursor.fetchone()
63+
if message is None:
64+
break
65+
message_dict = dict(message) # Convert row to dictionary
66+
text_data = message_dict.get("text")
67+
if text_data:
68+
try:
69+
# Try to parse as plist
70+
plist_data = plistlib.loads(text_data)
71+
text = plist_data.get("NS.string", "")
72+
except:
73+
# If plist parsing fails, use the raw string
74+
text = text_data
75+
if text: # Only add messages with content
76+
# Convert Apple timestamp to datetime
77+
date = datetime.datetime(2001, 1, 1) + datetime.timedelta(
78+
seconds=message_dict.get("date") / 10**9
79+
)
80+
sender = message_dict.get("sender")
81+
if message_dict.get("is_from_me") == 1:
82+
sender = "(Me)"
83+
readable_messages.append(
84+
{"date": date, "from": sender, "text": text}
85+
)
86+
except sqlite3.Error as e:
87+
break
88+
89+
conn.close()
90+
return readable_messages
91+
92+
def can_access_database(self):
3393
try:
34-
run_applescript(script)
35-
return "SMS message sent"
36-
except subprocess.CalledProcessError:
37-
return "An error occurred while sending the SMS. Please check the recipient number and try again."
94+
with open(self.database_path, "r"):
95+
return True
96+
except IOError:
97+
return False
98+
99+
def prompt_full_disk_access(self):
100+
script = """
101+
tell application "System Preferences"
102+
activate
103+
end tell
104+
delay 1
105+
tell application "System Events"
106+
display dialog "This application requires Full Disk Access to function properly.\\n\\nPlease follow these steps:\\n1. Open the Security & Privacy panel.\\n2. Go to the Full Disk Access section.\\n3. Click the lock icon and enter your password to make changes.\\n4. Click the '+' button and add your terminal application (e.g., Terminal, iTerm).\\n5. Restart the application after granting access." buttons {"OK"} default button "OK"
107+
end tell
108+
"""
109+
subprocess.run(["osascript", "-e", script], check=True)

0 commit comments

Comments
 (0)