Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .example.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
JUPYTER_TOKEN=
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ uploads
downloads
toolkilt/.venv/
toolkilt/__pycache__/
toolkilt/tools/__pycache__/
toolkilt/tools/__pycache__/
**/notebooks/
**/.env*
**/__pycache__/
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ RUN pip install fastapi uvicorn python-multipart

# Copy the API script to the root directory
COPY api.py /home/jovyan/api.py
COPY README.md /home/jovyan/README.md

# Expose port 8888 for the Jupyter Notebook
EXPOSE 8888

# Expose an additional port for the FastAPI server
EXPOSE 8000
EXPOSE 8100
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.PHONY: build run test

build:
docker build --push -t ghcr.io/enso-labs/interpreter:latest .

docker_run:
docker run -d --name interpreter ghcr.io/enso-labs/interpreter:latest

test:
python -m unittest discover
46 changes: 30 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,20 @@ Unlock the potential of executing Python code safely and efficiently with the Pr
Our sandbox is designed with security as a top priority. By isolating execution environments, we mitigate risks associated with running untrusted code. This makes it an ideal solution for developers and organizations who need a safe and controlled way to execute scripts, test code snippets, or provide coding functionalities within their applications.

Key Features:
- 🛡️ **Secure Execution:** Run Python code in a sandboxed environment to ensure the applications remains secure and unaffected.
- 📁 **File Management:** Upload and manage files within isolated sessions to maintain data integrity and security.
- 📦 **Package Installation:** Install required Python packages per session without affecting the global environment.
- 🕹️ **Session Management:** Efficiently create, manage, and terminate sessions to maintain clean and organized execution spaces.

- 🛡️ **Secure Execution:** Run Python code in a sandboxed environment to ensure the applications remains secure and unaffected.
- 📁 **File Management:** Upload and manage files within isolated sessions to maintain data integrity and security.
- 📦 **Package Installation:** Install required Python packages per session without affecting the global environment.
- 🕹️ **Session Management:** Efficiently create, manage, and terminate sessions to maintain clean and organized execution spaces.

### Start Interpreter

```bash
docker-compose up --build
```

### Test the Langchain Toolkit

```bash
## Change Directory
cd toolkit
Expand All @@ -43,7 +46,7 @@ python interpreter.py
Simple Example

```bash
curl -X POST http://localhost:8001/execute -H "Content-Type: application/json" -d '{
curl -X POST http://localhost:8100/execute -H "Content-Type: application/json" -d '{
"session_id": "your_session_id",
"code": "print(\"Hello from Interpreter!\")"
}'
Expand All @@ -55,7 +58,7 @@ curl -X POST http://localhost:8001/execute -H "Content-Type: application/json" -
Example curl Request (Verify will error when numpy is not installed):

```bash
curl -X POST http://localhost:8001/execute -H "Content-Type: application/json" -d '{
curl -X POST http://localhost:8100/execute -H "Content-Type: application/json" -d '{
"session_id": "your_session_id",
"code": "import os\nimport numpy as np\na = int(os.getenv(\"VAR_A\"))\nb = int(os.getenv(\"VAR_B\"))\nc = int(os.getenv(\"VAR_C\"))\narray = np.array([a, b, c])\nresult = np.sum(array)\nprint(f\"Result of summing [{a}, {b}, {c}] is: {result}\")",
"env": {
Expand All @@ -72,7 +75,7 @@ curl -X POST http://localhost:8001/execute -H "Content-Type: application/json" -
Example curl Request (Install numpy and execute the code):

```bash
curl -X POST http://localhost:8001/install -H "Content-Type: application/json" -d '{
curl -X POST http://localhost:8100/install -H "Content-Type: application/json" -d '{
"session_id": "your_session_id",
"packages": ["numpy"]
}'
Expand All @@ -84,7 +87,7 @@ curl -X POST http://localhost:8001/install -H "Content-Type: application/json" -
Execute numpy with Env vars

```bash
curl -X POST http://localhost:8001/execute -H "Content-Type: application/json" -d '{
curl -X POST http://localhost:8100/execute -H "Content-Type: application/json" -d '{
"session_id": "your_session_id",
"code": "import os\nimport numpy as np\na = int(os.getenv(\"VAR_A\"))\nb = int(os.getenv(\"VAR_B\"))\nc = int(os.getenv(\"VAR_C\"))\narray = np.array([a, b, c])\nresult = np.sum(array)\nprint(f\"Result of summing [{a}, {b}, {c}] is: {result}\")",
"env": {
Expand All @@ -101,14 +104,14 @@ curl -X POST http://localhost:8001/execute -H "Content-Type: application/json" -
Example curl Request (Verify numpy has been uninstalled):

```bash
curl -X POST http://localhost:8001/terminate -H "Content-Type: application/json" -d '{
curl -X POST http://localhost:8100/terminate -H "Content-Type: application/json" -d '{
"session_id": "your_session_id"
}'

## Result
# {"status":"success","message":"Session your_session_id terminated successfully."}

curl -X POST http://localhost:8001/execute -H "Content-Type: application/json" -d '{
curl -X POST http://localhost:8100/execute -H "Content-Type: application/json" -d '{
"session_id": "your_session_id",
"code": "import os\nimport numpy as np\na = int(os.getenv(\"VAR_A\"))\nb = int(os.getenv(\"VAR_B\"))\nc = int(os.getenv(\"VAR_C\"))\narray = np.array([a, b, c])\nresult = np.sum(array)\nprint(f\"Result of summing [{a}, {b}, {c}] is: {result}\")",
"env": {
Expand All @@ -127,7 +130,7 @@ curl -X POST http://localhost:8001/execute -H "Content-Type: application/json" -
Upload a file for a specific session.

```bash
curl -X POST "http://localhost:8001/upload" \
curl -X POST "http://localhost:8100/upload" \
-H "accept: application/json" \
-F "session_id=your_session_id" \
-F "file=@data/AAPL.csv"
Expand All @@ -139,7 +142,7 @@ curl -X POST "http://localhost:8001/upload" \
Install pandas pacakage for created session to interact with csv.

```bash
curl -X POST http://localhost:8001/install -H "Content-Type: application/json" -d '{
curl -X POST http://localhost:8100/install -H "Content-Type: application/json" -d '{
"session_id": "your_session_id",
"packages": ["pandas"]
}'
Expand All @@ -151,7 +154,7 @@ curl -X POST http://localhost:8001/install -H "Content-Type: application/json" -
Execute to interact with csv

```bash
curl -X POST http://localhost:8001/execute -H "Content-Type: application/json" -d '{
curl -X POST http://localhost:8100/execute -H "Content-Type: application/json" -d '{
"session_id": "your_session_id",
"code": "import pandas as pd\nfile_path = \"/tmp/your_session_id/AAPL.csv\"\ndf = pd.read_csv(file_path)\nfirst_row = df.iloc[0]\nprint(first_row.to_json())"
}'
Expand All @@ -163,7 +166,7 @@ curl -X POST http://localhost:8001/execute -H "Content-Type: application/json" -
Download file from session

```bash
curl -X GET "http://localhost:8001/download?session_id=your_session_id&filename=AAPL.csv" -o AAPL_downloaded.csv
curl -X GET "http://localhost:8100/download?session_id=your_session_id&filename=AAPL.csv" -o AAPL_downloaded.csv

## Result
# File downloaded to workspace
Expand All @@ -172,10 +175,21 @@ curl -X GET "http://localhost:8001/download?session_id=your_session_id&filename=
Terminate session to uninstall pacakges and remove files.

```bash
curl -X POST http://localhost:8001/terminate -H "Content-Type: application/json" -d '{
curl -X POST http://localhost:8100/terminate -H "Content-Type: application/json" -d '{
"session_id": "your_session_id"
}'

## Result
# {"status":"success","message":"Session your_session_id terminated successfully."}
```
```

```bash
docker run -d \
--name jupyter \
--restart always \
--env-file .env.jupyter \
-p 8888:8888 \
-v "$PWD/notebooks:/home/jovyan/work" \
-v jupyter_data:/home/jovyan/.jupyter \
jupyter/base-notebook:latest
```
4 changes: 2 additions & 2 deletions api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import shutil
from typing import List, Dict

app = FastAPI()
app = FastAPI(name="Enso Labs Interpreter", docs_url="/")

class CodeExecutionRequest(BaseModel):
session_id: str
Expand Down Expand Up @@ -141,4 +141,4 @@ def download_file(session_id: str, filename: str):

if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
uvicorn.run(app, host=os.getenv("HOST", "0.0.0.0"), port=int(os.getenv("PORT", 8100)))
10 changes: 5 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
version: '3'

services:
interpreter:
container_name: interpreter
image: promptengineers/interpreter:latest
# image: ghcr.io/enso-labs/interpreter:latest
build: .
restart: always
ports:
- "8888:8888"
- "8001:8000"
- "8100:8100"
environment:
- JUPYTER_ENABLE_LAB=yes
- JUPYTER_TOKEN=${JUPYTER_TOKEN}
- PYTHONPATH=/home/jovyan
entrypoint: ["sh", "-c", "start-notebook.sh --NotebookApp.token='' & uvicorn api:app --host 0.0.0.0 --port 8000"]
entrypoint: ["sh", "-c", "start-notebook.sh --NotebookApp.token=${JUPYTER_TOKEN} & uvicorn api:app --host 0.0.0.0 --port 8100"]
volumes:
- ./uploads:/tmp
- ./notebooks:/home/jovyan/.ipynb_checkpoints
26 changes: 14 additions & 12 deletions toolkit/interpreter.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
import os
from typing import List
from pydantic import Field

from langchain_core.tools import BaseToolkit
from langchain_core.pydantic_v1 import Field
from langchain_community.tools import BaseTool
from langchain_community.tools import StructuredTool

from tools import Interpreter

class InterpreterToolkit(BaseToolkit):
"""Toolkit for the interpreter."""
"""Toolkit for the interpreter."""

api_url: str = Field(default="http://localhost:8001")
api_url: str = Field(default=os.getenv("INTERPRETER_URL", "http://localhost:8100"))

class Config:
"""Pydantic config."""
arbitrary_types_allowed = True
class Config:
"""Pydantic config."""
arbitrary_types_allowed = True

def get_tools(self) -> List[BaseTool]:
"""Get the tools in the toolkit."""
toolkit = Interpreter(api_url=self.api_url).toolkit()
return toolkit
def get_tools(self) -> List[StructuredTool]:
"""Get the tools in the toolkit."""
toolkit = Interpreter(api_url=self.api_url).toolkit()
return toolkit

if __name__ == "__main__":
toolkit = InterpreterToolkit(api_url="http://localhost:8001")
toolkit = InterpreterToolkit(api_url=os.getenv("INTERPRETER_URL", "http://localhost:8100"))
tools = toolkit.get_tools()
result = tools[0].run({"session_id": "test", "code": "print('Hello, World!')"})
print(">>> Tools: ", tools)
print("\n\n")
print(">>> Result: ", result)
11 changes: 5 additions & 6 deletions toolkit/tools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import os
import httpx
import logging
from typing import List
from pydantic import BaseModel, Field

from langchain.tools import StructuredTool
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_core.tools import ToolException

########################################################
Expand Down Expand Up @@ -32,7 +34,7 @@ class DownloadSchema(BaseModel):
## Class
########################################################
class Interpreter:
def __init__(self, api_url: str = "http://localhost:8000"):
def __init__(self, api_url: str = os.getenv("INTERPRETER_URL", "http://localhost:8100")):
self.api_url = api_url

def install(self, session_id: str, packages: List[str]):
Expand Down Expand Up @@ -256,8 +258,5 @@ def toolkit(self) -> List[StructuredTool]:
## Test
########################################################
if __name__ == "__main__":
result = Interpreter(api_url="http://localhost:8001").execute().run({
"session_id": "test",
"code": "print('Hello, World!')"
})
result = Interpreter().execute(session_id="test", code="print('Hello, World!')")
print(result)