Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add E2B Sandboxed Code Execution Integration #1397

Open
wants to merge 2 commits into
base: staging
Choose a base branch
from
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
2 changes: 2 additions & 0 deletions docs/_toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ parts:
title: YOLO
- file: source/reference/ai/stablediffusion
title: Stable Diffusion
- file: source/reference/ai/e2b-code-execution
title: E2B Sandboxed Code Execution

- file: source/reference/ai/custom-ai-function
title: Bring Your Own AI Function
Expand Down
36 changes: 36 additions & 0 deletions docs/source/reference/ai/e2b-code-execution.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Executing Code Generated by OpenAI Chat Completion Model
========================================================

This section provides an overview on how to use the E2B Sandbox and EvaDB to execute code snippets generated by ChatGPT / Chat Completion Model.

1. Getting a cursor and Create the E2BExec Function

.. code-block:: python

import evadb
cursor = evadb.connect().cursor()
cursor.query("DROP FUNCTION IF EXISTS E2BExec").execute()
cursor.query(f"CREATE FUNCTION IF NOT EXISTS E2BExec IMPL '<path>/e2b_exec.py'").execute()

Assume output of OpenAI Chat Completion model has been stored in a table named `CodeToExecute`
under the column `codejson`

The input that this function expects should look like

.. code-block:: json

{
"function_call":{
"name":"exec_code",
"arguments":"{\n \"code\": \"def fibonacci(n):\\n fibonacci_sequence = [0, 1]\\n while len(fibonacci_sequence) < n:\\n fibonacci_sequence.append(fibonacci_sequence[-1] + fibonacci_sequence[-2])\\n return fibonacci_sequence\\n\\nprint(fibonacci(100))\"\n}"
}
}

2. Using the following snippet it will execute each column and return stderr and stdout

.. code-block:: python

result = cursor.query("SELECT E2BExec(codejson) FROM CodeToExecute").execute()

.. note::
Remember to set your E2B API key as `E2B_API_KEY` environment variable
76 changes: 76 additions & 0 deletions evadb/functions/e2b_exec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# coding=utf-8
# Copyright 2018-2023 EvaDB
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json

import pandas as pd
from evadb.catalog.catalog_type import NdArrayType
from evadb.functions.abstract.abstract_function import AbstractFunction, InputType
from evadb.functions.decorators.decorators import setup, forward
from evadb.functions.decorators.io_descriptors.data_types import PandasDataframe
from e2b import Sandbox


class E2BExec(AbstractFunction):
"""
Takes input as a string like:
{
"function_call":{
"name":"exec_code",
"arguments":"{\n \"code\": \"def fibonacci(n):\\n fibonacci_sequence = [0, 1]\\n while len(fibonacci_sequence) < n:\\n fibonacci_sequence.append(fibonacci_sequence[-1] + fibonacci_sequence[-2])\\n return fibonacci_sequence\\n\\nprint(fibonacci(100))\"\n}"
}
}
This is the output from OpenAI Chat Completion model.
"""
@setup(cacheable=False, function_type="code_execution", batchable=True)
def setup(self, *args, **kwargs) -> None:
pass

@forward(
input_signatures=[
PandasDataframe(
columns=["data"],
column_types=[NdArrayType.STR],
column_shapes=[(1,)],
)
],
output_signatures=[
PandasDataframe(
columns=["stdout", "stderr"],
column_types=[NdArrayType.STR, ],
column_shapes=[(1,), (1, )],
)
],
)
def forward(self, frames: InputType) -> InputType:
self.sandbox = Sandbox()
output = []
stderrs = []
for code in frames['codejson']:
code_obj = json.loads(code)
code_text = json.loads(code_obj['function_call']['arguments'])['code']
self.sandbox.filesystem.write('/home/user/test.py', code_text)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hard coded, but since it is what is executed in the VM/sandbox. this is probably fine for now.

proc = self.sandbox.process.start('python /home/user/test.py')
out = proc.wait()
output.append(out.stdout)
stderrs.append(out.stderr)

return_df = pd.DataFrame({"stdout": output, "stderr": stderrs})
self.sandbox.close()
return return_df

@property
def name(self) -> str:
return 'E2BExec'