From 13e4edef4486744b8c639e360dcf53d0b5a50e11 Mon Sep 17 00:00:00 2001 From: ryaneggz Date: Tue, 24 Jun 2025 00:19:51 -0500 Subject: [PATCH 1/5] init --- .example.env | 1 + .gitignore | 4 +++- README.md | 12 ++++++++++++ docker-compose.yml | 6 ++++-- 4 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 .example.env diff --git a/.example.env b/.example.env new file mode 100644 index 0000000..36200e3 --- /dev/null +++ b/.example.env @@ -0,0 +1 @@ +JUPYTER_TOKEN= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4a3021b..8b537cf 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ uploads downloads toolkilt/.venv/ toolkilt/__pycache__/ -toolkilt/tools/__pycache__/ \ No newline at end of file +toolkilt/tools/__pycache__/ +**/notebooks/ +**/.env* \ No newline at end of file diff --git a/README.md b/README.md index e26f4ce..d2666f8 100644 --- a/README.md +++ b/README.md @@ -178,4 +178,16 @@ curl -X POST http://localhost:8001/terminate -H "Content-Type: application/json" ## 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 ``` \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 2c1ed33..7d93268 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ version: '3' services: interpreter: container_name: interpreter - image: promptengineers/interpreter:latest + # image: promptengineers/interpreter:latest build: . restart: always ports: @@ -11,7 +11,9 @@ services: - "8001:8000" 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 8000"] volumes: - ./uploads:/tmp + - ./notebooks:/home/jovyan/.ipynb_checkpoints \ No newline at end of file From 34cbd4be80df1a2ee73b11af8a967dfb76571eab Mon Sep 17 00:00:00 2001 From: ryaneggz Date: Mon, 1 Sep 2025 14:38:35 -0600 Subject: [PATCH 2/5] init --- .gitignore | 3 ++- docker-compose.yml | 2 -- toolkit/interpreter.py | 23 ++++++++++++----------- toolkit/tools/__init__.py | 8 +++----- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 8b537cf..74a453f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ toolkilt/.venv/ toolkilt/__pycache__/ toolkilt/tools/__pycache__/ **/notebooks/ -**/.env* \ No newline at end of file +**/.env* +**/__pycache__/ \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 7d93268..a3c6f73 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3' - services: interpreter: container_name: interpreter diff --git a/toolkit/interpreter.py b/toolkit/interpreter.py index e33b4c0..5737b42 100644 --- a/toolkit/interpreter.py +++ b/toolkit/interpreter.py @@ -1,28 +1,29 @@ 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="http://localhost:8001") - 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") tools = toolkit.get_tools() result = tools[0].run({"session_id": "test", "code": "print('Hello, World!')"}) print(">>> Tools: ", tools) + print("\n\n") print(">>> Result: ", result) \ No newline at end of file diff --git a/toolkit/tools/__init__.py b/toolkit/tools/__init__.py index 4badc0e..8d09e1f 100644 --- a/toolkit/tools/__init__.py +++ b/toolkit/tools/__init__.py @@ -1,8 +1,9 @@ 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 ######################################################## @@ -256,8 +257,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(api_url="http://localhost:8001").execute(session_id="test", code="print('Hello, World!')") print(result) \ No newline at end of file From c9db91521018f428baa5e0aacf9bee470d041467 Mon Sep 17 00:00:00 2001 From: ryaneggz Date: Mon, 1 Sep 2025 15:03:25 -0600 Subject: [PATCH 3/5] Prepare for package --- Dockerfile | 1 + Makefile | 10 ++++++++++ docker-compose.yml | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 Makefile diff --git a/Dockerfile b/Dockerfile index aecb4f5..93c363e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,7 @@ 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 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..49d3c69 --- /dev/null +++ b/Makefile @@ -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 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index a3c6f73..81880cf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: interpreter: container_name: interpreter - # image: promptengineers/interpreter:latest + # image: ghcr.io/enso-labs/interpreter:latest build: . restart: always ports: From f012f58acb9a8518637c49ea5b3407601802322b Mon Sep 17 00:00:00 2001 From: ryaneggz Date: Mon, 1 Sep 2025 15:42:08 -0600 Subject: [PATCH 4/5] default to 8100 --- Dockerfile | 2 +- api.py | 2 +- docker-compose.yml | 4 ++-- toolkit/tools/__init__.py | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 93c363e..6ebd684 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,4 +12,4 @@ COPY README.md /home/jovyan/README.md EXPOSE 8888 # Expose an additional port for the FastAPI server -EXPOSE 8000 \ No newline at end of file +EXPOSE 8100 \ No newline at end of file diff --git a/api.py b/api.py index c74bbaa..52f421e 100644 --- a/api.py +++ b/api.py @@ -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))) diff --git a/docker-compose.yml b/docker-compose.yml index 81880cf..bad81da 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,12 +6,12 @@ services: 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=${JUPYTER_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 \ No newline at end of file diff --git a/toolkit/tools/__init__.py b/toolkit/tools/__init__.py index 8d09e1f..163cf59 100644 --- a/toolkit/tools/__init__.py +++ b/toolkit/tools/__init__.py @@ -1,3 +1,4 @@ +import os import httpx import logging from typing import List @@ -33,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]): From 7735ae955502ed4d11de2ecec6c1c151423cc0bc Mon Sep 17 00:00:00 2001 From: ryaneggz Date: Mon, 1 Sep 2025 15:49:30 -0600 Subject: [PATCH 5/5] missed a few --- README.md | 36 +++++++++++++++++++----------------- api.py | 2 +- toolkit/interpreter.py | 5 +++-- toolkit/tools/__init__.py | 2 +- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index d2666f8..d1d803e 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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!\")" }' @@ -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": { @@ -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"] }' @@ -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": { @@ -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": { @@ -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" @@ -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"] }' @@ -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())" }' @@ -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 @@ -172,7 +175,7 @@ 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" }' @@ -180,7 +183,6 @@ curl -X POST http://localhost:8001/terminate -H "Content-Type: application/json" # {"status":"success","message":"Session your_session_id terminated successfully."} ``` - ```bash docker run -d \ --name jupyter \ @@ -190,4 +192,4 @@ docker run -d \ -v "$PWD/notebooks:/home/jovyan/work" \ -v jupyter_data:/home/jovyan/.jupyter \ jupyter/base-notebook:latest -``` \ No newline at end of file +``` diff --git a/api.py b/api.py index 52f421e..3cd1616 100644 --- a/api.py +++ b/api.py @@ -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 diff --git a/toolkit/interpreter.py b/toolkit/interpreter.py index 5737b42..2ae1cb7 100644 --- a/toolkit/interpreter.py +++ b/toolkit/interpreter.py @@ -1,3 +1,4 @@ +import os from typing import List from pydantic import Field @@ -9,7 +10,7 @@ class InterpreterToolkit(BaseToolkit): """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.""" @@ -21,7 +22,7 @@ def get_tools(self) -> List[StructuredTool]: 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) diff --git a/toolkit/tools/__init__.py b/toolkit/tools/__init__.py index 163cf59..1f64473 100644 --- a/toolkit/tools/__init__.py +++ b/toolkit/tools/__init__.py @@ -258,5 +258,5 @@ def toolkit(self) -> List[StructuredTool]: ## Test ######################################################## if __name__ == "__main__": - result = Interpreter(api_url="http://localhost:8001").execute(session_id="test", code="print('Hello, World!')") + result = Interpreter().execute(session_id="test", code="print('Hello, World!')") print(result) \ No newline at end of file