Skip to content

Commit 4d7b2ee

Browse files
committed
Package management
1 parent 4880911 commit 4d7b2ee

File tree

9 files changed

+243
-121
lines changed

9 files changed

+243
-121
lines changed

archive/pyproject.toml

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
[tool.poetry]
2+
name = "open-interpreter"
3+
packages = [
4+
{include = "interpreter"},
5+
{include = "scripts"},
6+
{include = "interpreter_1"},
7+
]
8+
version = "0.4.4" # Use "-rc1", "-rc2", etc. for pre-release versions
9+
description = "Let language models run code"
10+
authors = ["Killian Lucas <[email protected]>"]
11+
readme = "README.md"
12+
13+
[tool.poetry.dependencies]
14+
15+
# Optional [os] dependencies
16+
opencv-python = { version = "^4.8.1.78", optional = true }
17+
plyer = { version = "^2.1.0", optional = true }
18+
pywinctl = { version = "^0.3", optional = true }
19+
pytesseract = { version = "^0.3.10", optional = true }
20+
sentence-transformers = { version = "^2.5.1", optional = true }
21+
nltk = { version = "^3.8.1", optional = true }
22+
ipywidgets = { version = "^8.1.2", optional = true }
23+
torch = { version = "^2.2.1", optional = true }
24+
timm = { version = "^0.9.16", optional = true }
25+
26+
# Optional [safe] dependencies
27+
semgrep = { version = "^1.52.0", optional = true }
28+
29+
# Optional [local] dependencies
30+
transformers = { version = "4.41.2", optional = true }
31+
einops = { version = "^0.8.0", optional = true }
32+
torchvision = { version = "^0.18.0", optional = true }
33+
easyocr = { version = "^1.7.1", optional = true }
34+
35+
# Optional [server] dependencies
36+
janus = { version = "^1.0.0", optional = true }
37+
38+
# Required dependencies
39+
python = ">=3.9,<4"
40+
setuptools = "*"
41+
astor = "^0.8.1"
42+
git-python = "^1.0.3"
43+
inquirer = "^3.1.3"
44+
pyyaml = "^6.0.1"
45+
rich = "^13.4.2"
46+
six = "^1.16.0"
47+
tokentrim = "^0.1.13"
48+
wget = "^3.2"
49+
psutil = "^5.9.6"
50+
pyreadline3 = {version = "^3.4.1", markers = "sys_platform == 'win32'"}
51+
html2image = "^2.0.4.3"
52+
send2trash = "^1.8.2"
53+
ipykernel = "^6.26.0"
54+
jupyter-client = "^8.6.0"
55+
matplotlib = "^3.8.2"
56+
toml = "^0.10.2"
57+
tiktoken = "^0.7.0"
58+
platformdirs = "^4.2.0"
59+
pydantic = "^2.6.4"
60+
google-generativeai = "^0.7.1"
61+
pyperclip = "^1.9.0"
62+
yaspin = "^3.0.2"
63+
shortuuid = "^1.0.13"
64+
litellm = "^1.41.26"
65+
starlette = ">=0.37.2,<0.42.0"
66+
html2text = "^2024.2.26"
67+
selenium = "^4.24.0"
68+
webdriver-manager = "^4.0.2"
69+
anthropic = "^0.37.1"
70+
pyautogui = "^0.9.54"
71+
typer = "^0.12.5"
72+
fastapi = "^0.111.0"
73+
uvicorn = "^0.30.1"
74+
screeninfo = "^0.8.1"
75+
pyte = "^0.8.2"
76+
pygments = "^2.18.0"
77+
tabulate = "^0.9.0"
78+
79+
[tool.poetry.extras]
80+
os = ["opencv-python", "pyautogui", "plyer", "pywinctl", "pytesseract", "sentence-transformers", "ipywidgets", "timm"]
81+
safe = ["semgrep"]
82+
local = ["opencv-python", "pytesseract", "torch", "transformers", "einops", "torchvision", "easyocr"]
83+
server = ["fastapi", "janus", "uvicorn"]
84+
85+
[tool.poetry.group.dev.dependencies]
86+
black = "^23.10.1"
87+
isort = "^5.12.0"
88+
pre-commit = "^3.5.0"
89+
pytest = "^7.4.0"
90+
sniffio = "^1.3.0"
91+
websockets = "^13.1"
92+
pytest-asyncio = "<0.24.0"
93+
pdoc = "^15.0.0"
94+
95+
[build-system]
96+
requires = ["poetry-core>=1.0.0"]
97+
build-backend = "poetry.core.masonry.api"
98+
99+
[tool.poetry.scripts]
100+
interpreter = "interpreter.terminal_interface.start_terminal_interface:main"
101+
wtf = "scripts.wtf:main"
102+
interpreter-classic = "interpreter.terminal_interface.start_terminal_interface:main"
103+
i = "interpreter_1.cli:main"
104+
105+
[tool.black]
106+
target-version = ['py311']
107+
108+
[tool.isort]
109+
profile = "black"
110+
multi_line_output = 3
111+
include_trailing_comma = true

interpreter_1/cli.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
from concurrent.futures import ThreadPoolExecutor
55
from typing import Any, Dict
66

7-
from yaspin import yaspin
8-
from yaspin.spinners import Spinners
9-
7+
from .misc.spinner import SimpleSpinner
108
from .profiles import Profile
119

1210

@@ -144,7 +142,7 @@ def parse_args():
144142

145143
# If a different profile is specified, load it
146144
if args["profile"] != profile.profile_path:
147-
profile.load_from_profile(args["profile"])
145+
profile.load(args["profile"])
148146
# Update any values that weren't explicitly set in CLI
149147
for key, value in vars(profile).items():
150148
if key in args and args[key] is None:
@@ -199,15 +197,15 @@ async def async_load():
199197
sys.stdout.write("\r\033[K") # Clear entire line
200198
sys.stdout.flush()
201199

202-
if args["input_message"] is None:
200+
if args["input_message"] is None and sys.stdin.isatty():
203201
from .misc.welcome import welcome_message
204202

205203
welcome_message(args)
206204
asyncio.run(async_load())
207205
interpreter.chat()
208206
else:
209207
print()
210-
spinner = yaspin(Spinners.simpleDots, text="")
208+
spinner = SimpleSpinner("")
211209
spinner.start()
212210
load_interpreter()
213211
spinner.stop()

interpreter_1/interpreter.py

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
import asyncio
2-
import dataclasses
32
import json
43
import os
54
import platform
65
import sys
7-
import threading
8-
import time
96
import traceback
107
import uuid
11-
from collections.abc import Callable
128
from datetime import datetime
139
from typing import Any, List, Optional, cast
1410

1511
from prompt_toolkit import PromptSession
1612
from prompt_toolkit.formatted_text import HTML
17-
18-
prompt_session = PromptSession()
13+
from readchar import readchar
1914

2015
try:
2116
from enum import StrEnum
@@ -45,8 +40,8 @@
4540
BetaToolResultBlockParam,
4641
BetaToolUseBlockParam,
4742
)
48-
from yaspin import yaspin
49-
from yaspin.spinners import Spinners
43+
44+
from .misc.spinner import SimpleSpinner
5045

5146
# Local imports
5247
from .profiles import Profile
@@ -167,7 +162,8 @@ def __init__(self, profile=None):
167162
setattr(self, key, value)
168163

169164
self._client = None
170-
self._spinner = yaspin(Spinners.simpleDots, text="")
165+
self._spinner = SimpleSpinner("")
166+
self._prompt_session = None
171167

172168
def to_dict(self):
173169
"""Convert current settings to dictionary"""
@@ -336,21 +332,23 @@ async def async_respond(self):
336332
}
337333
)
338334

335+
content_blocks = cast(list[BetaContentBlock], response.content)
336+
tool_use_blocks = [b for b in content_blocks if b.type == "tool_use"]
337+
338+
# If there are no tool use blocks, we're done
339+
if not tool_use_blocks:
340+
break
341+
339342
user_approval = None
340343
if getattr(self, "auto_run", False):
341344
user_approval = "y"
342345
else:
343346
if not sys.stdin.isatty():
344347
print(
345-
"Error: Non-interactive environment requires auto_run=True"
348+
"Error: Non-interactive environment requires auto_run=True to run tools"
346349
)
347350
exit(1)
348351

349-
content_blocks = cast(list[BetaContentBlock], response.content)
350-
tool_use_blocks = [
351-
b for b in content_blocks if b.type == "tool_use"
352-
]
353-
354352
if len(tool_use_blocks) > 1:
355353
# Check if all tools are pre-approved
356354
all_approved = all(
@@ -360,7 +358,7 @@ async def async_respond(self):
360358
user_approval = "y"
361359
else:
362360
print(f"\n\033[38;5;240mRun all actions above\033[0m?")
363-
user_approval = input("\n(y/n/a): ").lower().strip()
361+
user_approval = self._ask_user_approval()
364362
elif len(tool_use_blocks) == 1:
365363
tool_block = tool_use_blocks[0]
366364
if self._is_tool_approved(tool_block):
@@ -394,21 +392,21 @@ async def async_respond(self):
394392
else:
395393
print(f"\n\033[38;5;240mRun tool?\033[0m")
396394

397-
user_approval = input("\n(y/n/a): ").lower().strip()
395+
user_approval = self._ask_user_approval()
398396

399397
# Handle adding to allowed lists
400398
if user_approval == "a":
401399
if tool_block.name == "editor":
402400
path = tool_block.input.get("path")
403401
if path:
404-
self.allowed_paths.add(path)
402+
self.allowed_paths.append(path)
405403
print(
406404
f"\n\033[38;5;240mEdits to {path} will be auto-approved in this session.\033[0m\n"
407405
)
408406
else: # bash/computer tools
409407
command = tool_block.input.get("command", "")
410408
if command:
411-
self.allowed_commands.add(command)
409+
self.allowed_commands.append(command)
412410
print(
413411
f"\n\033[38;5;240mThe command '{command}' will be auto-approved in this session.\033[0m\n"
414412
)
@@ -455,6 +453,23 @@ async def async_respond(self):
455453
# (I can add this if you'd like, but focusing on the Anthropic path for now)
456454
pass
457455

456+
def _ask_user_approval(self) -> str:
457+
"""Ask user for approval to run a tool"""
458+
# print("\n\033[38;5;240m(\033[0my\033[38;5;240m)es (\033[0mn\033[38;5;240m)o (\033[0ma\033[38;5;240m)lways approve this command: \033[0m", end="", flush=True)
459+
# Simpler y/n prompt
460+
print(
461+
"\n\033[38;5;240m(\033[0my\033[38;5;240m/\033[0mn\033[38;5;240m): \033[0m",
462+
end="",
463+
flush=True,
464+
)
465+
try:
466+
user_approval = readchar().lower()
467+
print(user_approval)
468+
return user_approval
469+
except KeyboardInterrupt:
470+
print()
471+
return "n"
472+
458473
def _handle_command(self, cmd: str, parts: list[str]) -> bool:
459474
"""Handle / commands for controlling interpreter settings"""
460475

@@ -521,6 +536,7 @@ def print_help():
521536
path = os.path.expanduser(self._profile.profile_path)
522537
if not os.path.exists(path):
523538
print(f"Profile does not exist yet. Current path would be: {path}")
539+
print("Use /profile save to create it")
524540
return True
525541

526542
if platform.system() == "Darwin": # macOS
@@ -638,7 +654,9 @@ def chat(self):
638654
placeholder = HTML(
639655
f"<{placeholder_color}>{placeholder_text}</{placeholder_color}>"
640656
)
641-
user_input = prompt_session.prompt(
657+
if self._prompt_session is None:
658+
self._prompt_session = PromptSession()
659+
user_input = self._prompt_session.prompt(
642660
"> ", placeholder=placeholder
643661
).strip()
644662
print()
@@ -651,7 +669,7 @@ def chat(self):
651669
placeholder = HTML(
652670
f'<{placeholder_color}>Use """ again to finish</{placeholder_color}>'
653671
)
654-
line = prompt_session.prompt(
672+
line = self._prompt_session.prompt(
655673
"", placeholder=placeholder
656674
).strip()
657675
if line == '"""':
@@ -667,6 +685,13 @@ def chat(self):
667685
if self._handle_command(cmd, parts):
668686
continue
669687

688+
if user_input == "":
689+
if message_count in range(4, 7):
690+
print("Error: Cat is asleep on Enter key\n")
691+
else:
692+
print("Error: No input provided\n")
693+
continue
694+
670695
self.messages.append({"role": "user", "content": user_input})
671696

672697
for _ in self.respond():

interpreter_1/misc/spinner.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import sys
2+
import threading
3+
import time
4+
5+
6+
class SimpleSpinner:
7+
"""A simple text-based spinner for command line interfaces."""
8+
9+
def __init__(self, text=""):
10+
self.spinner_cycle = [" ", ". ", ".. ", "..."]
11+
self.text = text
12+
self.keep_running = False
13+
self.spinner_thread = None
14+
15+
def _spin(self):
16+
while self.keep_running:
17+
for frame in self.spinner_cycle:
18+
if not self.keep_running:
19+
break
20+
# Clear the line and write the new frame
21+
sys.stdout.write("\r" + self.text + frame)
22+
sys.stdout.flush()
23+
time.sleep(0.2) # Control animation speed
24+
25+
def start(self):
26+
"""Start the spinner animation in a separate thread."""
27+
if not self.spinner_thread:
28+
self.keep_running = True
29+
self.spinner_thread = threading.Thread(target=self._spin)
30+
self.spinner_thread.daemon = True
31+
self.spinner_thread.start()
32+
33+
def stop(self):
34+
"""Stop the spinner animation and clear the line."""
35+
self.keep_running = False
36+
if self.spinner_thread:
37+
self.spinner_thread.join()
38+
self.spinner_thread = None
39+
# Clear the spinner from the line
40+
sys.stdout.write("\r" + " " * (len(self.text) + 3) + "\r")
41+
sys.stdout.flush()

0 commit comments

Comments
 (0)