Skip to content

Commit

Permalink
Package management
Browse files Browse the repository at this point in the history
  • Loading branch information
KillianLucas committed Nov 11, 2024
1 parent 4880911 commit 4d7b2ee
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 121 deletions.
111 changes: 111 additions & 0 deletions archive/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
[tool.poetry]
name = "open-interpreter"
packages = [
{include = "interpreter"},
{include = "scripts"},
{include = "interpreter_1"},
]
version = "0.4.4" # Use "-rc1", "-rc2", etc. for pre-release versions
description = "Let language models run code"
authors = ["Killian Lucas <[email protected]>"]
readme = "README.md"

[tool.poetry.dependencies]

# Optional [os] dependencies
opencv-python = { version = "^4.8.1.78", optional = true }
plyer = { version = "^2.1.0", optional = true }
pywinctl = { version = "^0.3", optional = true }
pytesseract = { version = "^0.3.10", optional = true }
sentence-transformers = { version = "^2.5.1", optional = true }
nltk = { version = "^3.8.1", optional = true }
ipywidgets = { version = "^8.1.2", optional = true }
torch = { version = "^2.2.1", optional = true }
timm = { version = "^0.9.16", optional = true }

# Optional [safe] dependencies
semgrep = { version = "^1.52.0", optional = true }

# Optional [local] dependencies
transformers = { version = "4.41.2", optional = true }
einops = { version = "^0.8.0", optional = true }
torchvision = { version = "^0.18.0", optional = true }
easyocr = { version = "^1.7.1", optional = true }

# Optional [server] dependencies
janus = { version = "^1.0.0", optional = true }

# Required dependencies
python = ">=3.9,<4"
setuptools = "*"
astor = "^0.8.1"
git-python = "^1.0.3"
inquirer = "^3.1.3"
pyyaml = "^6.0.1"
rich = "^13.4.2"
six = "^1.16.0"
tokentrim = "^0.1.13"
wget = "^3.2"
psutil = "^5.9.6"
pyreadline3 = {version = "^3.4.1", markers = "sys_platform == 'win32'"}
html2image = "^2.0.4.3"
send2trash = "^1.8.2"
ipykernel = "^6.26.0"
jupyter-client = "^8.6.0"
matplotlib = "^3.8.2"
toml = "^0.10.2"
tiktoken = "^0.7.0"
platformdirs = "^4.2.0"
pydantic = "^2.6.4"
google-generativeai = "^0.7.1"
pyperclip = "^1.9.0"
yaspin = "^3.0.2"
shortuuid = "^1.0.13"
litellm = "^1.41.26"
starlette = ">=0.37.2,<0.42.0"
html2text = "^2024.2.26"
selenium = "^4.24.0"
webdriver-manager = "^4.0.2"
anthropic = "^0.37.1"
pyautogui = "^0.9.54"
typer = "^0.12.5"
fastapi = "^0.111.0"
uvicorn = "^0.30.1"
screeninfo = "^0.8.1"
pyte = "^0.8.2"
pygments = "^2.18.0"
tabulate = "^0.9.0"

[tool.poetry.extras]
os = ["opencv-python", "pyautogui", "plyer", "pywinctl", "pytesseract", "sentence-transformers", "ipywidgets", "timm"]
safe = ["semgrep"]
local = ["opencv-python", "pytesseract", "torch", "transformers", "einops", "torchvision", "easyocr"]
server = ["fastapi", "janus", "uvicorn"]

[tool.poetry.group.dev.dependencies]
black = "^23.10.1"
isort = "^5.12.0"
pre-commit = "^3.5.0"
pytest = "^7.4.0"
sniffio = "^1.3.0"
websockets = "^13.1"
pytest-asyncio = "<0.24.0"
pdoc = "^15.0.0"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.poetry.scripts]
interpreter = "interpreter.terminal_interface.start_terminal_interface:main"
wtf = "scripts.wtf:main"
interpreter-classic = "interpreter.terminal_interface.start_terminal_interface:main"
i = "interpreter_1.cli:main"

[tool.black]
target-version = ['py311']

[tool.isort]
profile = "black"
multi_line_output = 3
include_trailing_comma = true
10 changes: 4 additions & 6 deletions interpreter_1/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
from concurrent.futures import ThreadPoolExecutor
from typing import Any, Dict

from yaspin import yaspin
from yaspin.spinners import Spinners

from .misc.spinner import SimpleSpinner
from .profiles import Profile


Expand Down Expand Up @@ -144,7 +142,7 @@ def parse_args():

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

if args["input_message"] is None:
if args["input_message"] is None and sys.stdin.isatty():
from .misc.welcome import welcome_message

welcome_message(args)
asyncio.run(async_load())
interpreter.chat()
else:
print()
spinner = yaspin(Spinners.simpleDots, text="")
spinner = SimpleSpinner("")
spinner.start()
load_interpreter()
spinner.stop()
Expand Down
67 changes: 46 additions & 21 deletions interpreter_1/interpreter.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
import asyncio
import dataclasses
import json
import os
import platform
import sys
import threading
import time
import traceback
import uuid
from collections.abc import Callable
from datetime import datetime
from typing import Any, List, Optional, cast

from prompt_toolkit import PromptSession
from prompt_toolkit.formatted_text import HTML

prompt_session = PromptSession()
from readchar import readchar

try:
from enum import StrEnum
Expand Down Expand Up @@ -45,8 +40,8 @@
BetaToolResultBlockParam,
BetaToolUseBlockParam,
)
from yaspin import yaspin
from yaspin.spinners import Spinners

from .misc.spinner import SimpleSpinner

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

self._client = None
self._spinner = yaspin(Spinners.simpleDots, text="")
self._spinner = SimpleSpinner("")
self._prompt_session = None

def to_dict(self):
"""Convert current settings to dictionary"""
Expand Down Expand Up @@ -336,21 +332,23 @@ async def async_respond(self):
}
)

content_blocks = cast(list[BetaContentBlock], response.content)
tool_use_blocks = [b for b in content_blocks if b.type == "tool_use"]

# If there are no tool use blocks, we're done
if not tool_use_blocks:
break

user_approval = None
if getattr(self, "auto_run", False):
user_approval = "y"
else:
if not sys.stdin.isatty():
print(
"Error: Non-interactive environment requires auto_run=True"
"Error: Non-interactive environment requires auto_run=True to run tools"
)
exit(1)

content_blocks = cast(list[BetaContentBlock], response.content)
tool_use_blocks = [
b for b in content_blocks if b.type == "tool_use"
]

if len(tool_use_blocks) > 1:
# Check if all tools are pre-approved
all_approved = all(
Expand All @@ -360,7 +358,7 @@ async def async_respond(self):
user_approval = "y"
else:
print(f"\n\033[38;5;240mRun all actions above\033[0m?")
user_approval = input("\n(y/n/a): ").lower().strip()
user_approval = self._ask_user_approval()
elif len(tool_use_blocks) == 1:
tool_block = tool_use_blocks[0]
if self._is_tool_approved(tool_block):
Expand Down Expand Up @@ -394,21 +392,21 @@ async def async_respond(self):
else:
print(f"\n\033[38;5;240mRun tool?\033[0m")

user_approval = input("\n(y/n/a): ").lower().strip()
user_approval = self._ask_user_approval()

# Handle adding to allowed lists
if user_approval == "a":
if tool_block.name == "editor":
path = tool_block.input.get("path")
if path:
self.allowed_paths.add(path)
self.allowed_paths.append(path)
print(
f"\n\033[38;5;240mEdits to {path} will be auto-approved in this session.\033[0m\n"
)
else: # bash/computer tools
command = tool_block.input.get("command", "")
if command:
self.allowed_commands.add(command)
self.allowed_commands.append(command)
print(
f"\n\033[38;5;240mThe command '{command}' will be auto-approved in this session.\033[0m\n"
)
Expand Down Expand Up @@ -455,6 +453,23 @@ async def async_respond(self):
# (I can add this if you'd like, but focusing on the Anthropic path for now)
pass

def _ask_user_approval(self) -> str:
"""Ask user for approval to run a tool"""
# 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)
# Simpler y/n prompt
print(
"\n\033[38;5;240m(\033[0my\033[38;5;240m/\033[0mn\033[38;5;240m): \033[0m",
end="",
flush=True,
)
try:
user_approval = readchar().lower()
print(user_approval)
return user_approval
except KeyboardInterrupt:
print()
return "n"

def _handle_command(self, cmd: str, parts: list[str]) -> bool:
"""Handle / commands for controlling interpreter settings"""

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

if platform.system() == "Darwin": # macOS
Expand Down Expand Up @@ -638,7 +654,9 @@ def chat(self):
placeholder = HTML(
f"<{placeholder_color}>{placeholder_text}</{placeholder_color}>"
)
user_input = prompt_session.prompt(
if self._prompt_session is None:
self._prompt_session = PromptSession()
user_input = self._prompt_session.prompt(
"> ", placeholder=placeholder
).strip()
print()
Expand All @@ -651,7 +669,7 @@ def chat(self):
placeholder = HTML(
f'<{placeholder_color}>Use """ again to finish</{placeholder_color}>'
)
line = prompt_session.prompt(
line = self._prompt_session.prompt(
"", placeholder=placeholder
).strip()
if line == '"""':
Expand All @@ -667,6 +685,13 @@ def chat(self):
if self._handle_command(cmd, parts):
continue

if user_input == "":
if message_count in range(4, 7):
print("Error: Cat is asleep on Enter key\n")
else:
print("Error: No input provided\n")
continue

self.messages.append({"role": "user", "content": user_input})

for _ in self.respond():
Expand Down
41 changes: 41 additions & 0 deletions interpreter_1/misc/spinner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import sys
import threading
import time


class SimpleSpinner:
"""A simple text-based spinner for command line interfaces."""

def __init__(self, text=""):
self.spinner_cycle = [" ", ". ", ".. ", "..."]
self.text = text
self.keep_running = False
self.spinner_thread = None

def _spin(self):
while self.keep_running:
for frame in self.spinner_cycle:
if not self.keep_running:
break
# Clear the line and write the new frame
sys.stdout.write("\r" + self.text + frame)
sys.stdout.flush()
time.sleep(0.2) # Control animation speed

def start(self):
"""Start the spinner animation in a separate thread."""
if not self.spinner_thread:
self.keep_running = True
self.spinner_thread = threading.Thread(target=self._spin)
self.spinner_thread.daemon = True
self.spinner_thread.start()

def stop(self):
"""Stop the spinner animation and clear the line."""
self.keep_running = False
if self.spinner_thread:
self.spinner_thread.join()
self.spinner_thread = None
# Clear the spinner from the line
sys.stdout.write("\r" + " " * (len(self.text) + 3) + "\r")
sys.stdout.flush()
Loading

0 comments on commit 4d7b2ee

Please sign in to comment.