Skip to content

Commit 1aa2e49

Browse files
committed
2 parents 342db59 + 268e8ec commit 1aa2e49

File tree

21 files changed

+724
-1804
lines changed

21 files changed

+724
-1804
lines changed

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,34 @@ Open Interpreter equips a [function-calling language model](https://platform.ope
364364

365365
We then stream the model's messages, code, and your system's outputs to the terminal as Markdown.
366366

367+
# Access Documentation Offline
368+
369+
The full [documentation](https://docs.openinterpreter.com/) is accessible on-the-go without the need for an internet connection.
370+
371+
[Node](https://nodejs.org/en) is a pre-requisite:
372+
373+
- Version 18.17.0 or any later 18.x.x version.
374+
- Version 20.3.0 or any later 20.x.x version.
375+
- Any version starting from 21.0.0 onwards, with no upper limit specified.
376+
377+
Install [Mintlify](https://mintlify.com/):
378+
379+
```bash
380+
npm i -g mintlify@latest
381+
```
382+
383+
Change into the docs directory and run the appropriate command:
384+
385+
```bash
386+
# Assuming you're at the project's root directory
387+
cd ./docs
388+
389+
# Run the documentation server
390+
mintlify dev
391+
```
392+
393+
A new browser window should open. The documentation will be available at [http://localhost:3000](http://localhost:3000) as long as the documentation server is running.
394+
367395
# Contributing
368396

369397
Thank you for your interest in contributing! We welcome involvement from the community.

docs/telemetry/telemetry.mdx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ If you prefer to opt out of telemetry, you can do this in two ways.
1212

1313
### Python
1414

15-
Set `anonymized_telemetry` to `false` on the `interpreter` object:
15+
Set `disable_telemetry` to `true` on the `interpreter` object:
1616

1717
```python
1818
from interpreter import interpreter
19-
interpreter.anonymized_telemetry = False
19+
interpreter.disable_telemetry = True
2020
```
2121

2222
### Terminal
@@ -29,20 +29,20 @@ interpreter --disable_telemetry
2929

3030
### Configuration File
3131

32-
Set `anonymized_telemetry` to `false`. This will persist to future terminal sessions:
32+
Set `disable_telemetry` to `true`. This will persist to future terminal sessions:
3333

3434
```yaml
35-
anonymized_telemetry: false
35+
disable_telemetry: true
3636
```
3737
3838
### Environment Variables
3939
40-
Set `ANONYMIZED_TELEMETRY` to `False` in your shell or server environment.
40+
Set `DISABLE_TELEMETRY` to `true` in your shell or server environment.
4141

4242
If you are running Open Interpreter on your local computer with `docker-compose` you can set this value in an `.env` file placed in the same directory as the `docker-compose.yml` file:
4343

4444
```
45-
ANONYMIZED_TELEMETRY=False
45+
DISABLE_TELEMETRY=true
4646
```
4747

4848
# What do you track?

interpreter/core/computer/computer.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ def __init__(self, interpreter):
4949
self.import_computer_api = False # Defaults to false
5050
self._has_imported_computer_api = False # Because we only want to do this once
5151

52+
self.import_skills = False
53+
self._has_imported_skills = False
54+
5255
# Shortcut for computer.terminal.languages
5356
@property
5457
def languages(self):

interpreter/core/computer/display/display.py

Lines changed: 107 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@
66
import warnings
77
from contextlib import redirect_stdout
88
from io import BytesIO
9-
9+
import io
10+
import subprocess
11+
from PIL import Image
1012
import requests
11-
1213
from ...utils.lazy_import import lazy_import
1314
from ..utils.recipient_utils import format_to_recipient
15+
import cv2
16+
from screeninfo import get_monitors # for getting info about connected monitors
17+
1418

1519
# Still experimenting with this
1620
# from utils.get_active_window import get_active_window
@@ -20,6 +24,7 @@
2024
np = lazy_import("numpy")
2125
plt = lazy_import("matplotlib.pyplot")
2226

27+
2328
from ..utils.computer_vision import find_text_in_image, pytesseract_get_text
2429

2530

@@ -56,20 +61,30 @@ def center(self):
5661
"""
5762
return self.width // 2, self.height // 2
5863

59-
def view(self, show=True, quadrant=None):
64+
def info(self):
65+
"""
66+
Returns a list of all connected montitor/displays and thir information
67+
"""
68+
return get_displays()
69+
70+
71+
def view(self, show=True, quadrant=None, screen=0, combine_screens=True
72+
):
6073
"""
6174
Redirects to self.screenshot
6275
"""
63-
return self.screenshot(show, quadrant)
76+
return self.screenshot(screen=screen, show=show, quadrant=quadrant, combine_screens=combine_screens)
6477

6578
# def get_active_window(self):
6679
# return get_active_window()
6780

6881
def screenshot(
69-
self, show=True, quadrant=None, active_app_only=False, force_image=False
82+
self, screen=0, show=True, quadrant=None, active_app_only=False, force_image=False,combine_screens=True
7083
):
7184
"""
7285
Shows you what's on the screen by taking a screenshot of the entire screen or a specified quadrant. Returns a `pil_image` `in case you need it (rarely). **You almost always want to do this first!**
86+
:param screen: specify which display; 0 for primary and 1 and above for secondary.
87+
:param combine_screens: If True, a collage of all display screens will be returned. Otherwise, a list of display screens will be returned.
7388
"""
7489
if not self.computer.emit_images and force_image == False:
7590
text = self.get_text_as_list_of_lists()
@@ -91,10 +106,7 @@ def screenshot(
91106
region = self.get_active_window()["region"]
92107
screenshot = pyautogui.screenshot(region=region)
93108
else:
94-
if platform.system() == "Darwin":
95-
screenshot = take_screenshot_to_pil()
96-
else:
97-
screenshot = pyautogui.screenshot()
109+
screenshot = take_screenshot_to_pil(screen=screen, combine_screens=combine_screens) # this function uses pyautogui.screenshot which works fine for all OS (mac, linux and windows)
98110
# message = format_to_recipient("Taking a screenshot of the entire screen. This is not recommended. You (the language model assistant) will recieve 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.", "assistant")
99111
# print(message)
100112

@@ -121,18 +133,26 @@ def screenshot(
121133

122134
# Open the image file with PIL
123135
# IPython interactive mode auto-displays plots, causing RGBA handling issues, possibly MacOS-specific.
124-
screenshot = screenshot.convert("RGB")
136+
if isinstance(screenshot, list):
137+
screenshot = [img.convert("RGB") for img in screenshot] # if screenshot is a list (i.e combine_screens=False).
138+
else:
139+
screenshot = screenshot.convert("RGB")
125140

126141
if show:
127142
# Show the image using matplotlib
128-
plt.imshow(np.array(screenshot))
143+
if isinstance(screenshot, list):
144+
for img in screenshot:
145+
plt.imshow(np.array(img))
146+
plt.show()
147+
else:
148+
plt.imshow(np.array(screenshot))
129149

130150
with warnings.catch_warnings():
131151
# It displays an annoying message about Agg not being able to display something or WHATEVER
132152
warnings.simplefilter("ignore")
133153
plt.show()
134154

135-
return screenshot
155+
return screenshot # this will be a list of combine_screens == False
136156

137157
def find(self, description, screenshot=None):
138158
if description.startswith('"') and description.endswith('"'):
@@ -260,22 +280,78 @@ def get_text_as_list_of_lists(self, screenshot=None):
260280
)
261281

262282

263-
import io
264-
import subprocess
265-
266-
from PIL import Image
267-
268-
269-
def take_screenshot_to_pil(filename="temp_screenshot.png"):
270-
# Capture the screenshot and save it to a temporary file
271-
subprocess.run(["screencapture", "-x", filename], check=True)
272-
273-
# Open the image file with PIL
274-
with open(filename, "rb") as f:
275-
image_data = f.read()
276-
image = Image.open(io.BytesIO(image_data))
277-
278-
# Optionally, delete the temporary file if you don't need it after loading
279-
os.remove(filename)
280-
281-
return image
283+
def take_screenshot_to_pil(screen=0, combine_screens=True):
284+
# Get information about all screens
285+
monitors = get_monitors()
286+
if screen == -1: # All screens
287+
288+
# Take a screenshot of each screen and save them in a list
289+
screenshots = [pyautogui.screenshot(region=(monitor.x, monitor.y, monitor.width, monitor.height)) for monitor in monitors]
290+
291+
if combine_screens:
292+
# Combine all screenshots horizontally
293+
total_width = sum([img.width for img in screenshots])
294+
max_height = max([img.height for img in screenshots])
295+
296+
# Create a new image with a size that can contain all screenshots
297+
new_img = Image.new('RGB', (total_width, max_height))
298+
299+
# Paste each screenshot into the new image
300+
x_offset = 0
301+
for i, img in enumerate(screenshots):
302+
# Convert PIL Image to OpenCV Image (numpy array)
303+
img_cv = np.array(img)
304+
img_cv = cv2.cvtColor(img_cv, cv2.COLOR_RGB2BGR)
305+
306+
# Convert new_img PIL Image to OpenCV Image (numpy array)
307+
new_img_cv = np.array(new_img)
308+
new_img_cv = cv2.cvtColor(new_img_cv, cv2.COLOR_RGB2BGR)
309+
310+
# Paste each screenshot into the new image using OpenCV
311+
new_img_cv[0:img_cv.shape[0], x_offset:x_offset+img_cv.shape[1]] = img_cv
312+
x_offset += img.width
313+
314+
# Add monitor labels using OpenCV
315+
font = cv2.FONT_HERSHEY_SIMPLEX
316+
font_scale = 4
317+
font_color = (255, 255, 255)
318+
line_type = 2
319+
320+
if i == 0:
321+
text = "Primary Monitor"
322+
else:
323+
text = f"Monitor {i}"
324+
325+
# Calculate the font scale that will fit the text perfectly in the center of the monitor
326+
text_size = cv2.getTextSize(text, font, font_scale, line_type)[0]
327+
font_scale = min(img.width / text_size[0], img.height / text_size[1])
328+
329+
# Recalculate the text size with the new font scale
330+
text_size = cv2.getTextSize(text, font, font_scale, line_type)[0]
331+
332+
# Calculate the position to center the text
333+
text_x = x_offset - img.width // 2 - text_size[0] // 2
334+
text_y = max_height // 2 - text_size[1] // 2
335+
336+
cv2.putText(new_img_cv, text, (text_x, text_y), font, font_scale, font_color, line_type)
337+
338+
# Convert new_img from OpenCV Image back to PIL Image
339+
new_img_cv = cv2.cvtColor(new_img_cv, cv2.COLOR_BGR2RGB)
340+
new_img = Image.fromarray(new_img_cv)
341+
342+
return new_img
343+
else:
344+
return screenshots
345+
elif screen > 0:
346+
# Take a screenshot of the selected screen
347+
return pyautogui.screenshot(region=(monitors[screen].x, monitors[screen].y, monitors[screen].width, monitors[screen].height))
348+
349+
else:
350+
# Take a screenshot of the primary screen
351+
return pyautogui.screenshot(region=(monitors[screen].x, monitors[screen].y, monitors[screen].width, monitors[screen].height))
352+
353+
354+
def get_displays():
355+
monitors = get_monitors()
356+
return monitors
357+

interpreter/core/computer/terminal/languages/jupyter_language.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,6 @@ class JupyterLanguage(BaseLanguage):
2626

2727
def __init__(self, computer):
2828
self.computer = computer
29-
# Filter out the following messages from IPKernelApp, to prevent the logs from showing up anytime someone presses CTRL+C
30-
if not DEBUG_MODE:
31-
ipkernel_logger = logging.getLogger('IPKernelApp')
32-
# Create a filter using a lambda function
33-
warning_filter = lambda record: not any(msg in record.getMessage() for msg in [
34-
"Parent appears to have exited, shutting down.",
35-
"Could not destroy zmq context"
36-
])
37-
# Add the filter to the logger
38-
ipkernel_logger.addFilter(warning_filter)
3929

4030
self.km = KernelManager(kernel_name="python3")
4131
self.km.start_kernel()

interpreter/core/computer/terminal/terminal.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,8 @@ def get_language(self, language):
3737
return None
3838

3939
def run(self, language, code, stream=False, display=False):
40-
if (
41-
language == "python"
42-
and self.computer.import_computer_api
43-
and "computer" in code
44-
):
45-
if not self.computer._has_imported_computer_api:
40+
if language == "python":
41+
if self.computer.import_computer_api and not self.computer._has_imported_computer_api and "computer" in code:
4642
self.computer._has_imported_computer_api = True
4743
# Give it access to the computer via Python
4844
self.computer.run(
@@ -51,6 +47,10 @@ def run(self, language, code, stream=False, display=False):
5147
display=self.computer.verbose,
5248
)
5349

50+
if self.computer.import_skills and not self.computer._has_imported_skills:
51+
self.computer._has_imported_skills = True
52+
self.computer.skills.import_skills()
53+
5454
if stream == False:
5555
# If stream == False, *pull* from _streaming_run.
5656
output_messages = []

0 commit comments

Comments
 (0)