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

E2b integration #271

Merged
merged 10 commits into from
Feb 28, 2025
1 change: 1 addition & 0 deletions .env.testing
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ ENCRYPTION_KEY=
MAILCHIMP_API_KEY=
MAILCHIMP_LIST_ID=
REDIS_URL=redis://redis:6379/1
E2B_API_KEY=
2 changes: 1 addition & 1 deletion backend/routers/api/skill.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,6 @@ async def execute_skill(
if config.user_id:
manager.check_user_permissions(config, current_user.id)

output = executor.execute_skill(config.title, payload.user_prompt)
output = executor.execute_skill(config, payload.user_prompt)

return ExecuteSkillResponse(data=output)
52 changes: 40 additions & 12 deletions backend/services/skill_executor.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import json
import logging
import os

from agency_swarm import BaseTool
from e2b_code_interpreter import Sandbox

from backend import custom_skills
from backend.models.skill_config import SkillConfig
from backend.settings import settings
from backend.utils import get_chat_completion

Expand All @@ -27,13 +30,13 @@ class SkillExecutor:
"""
USER_PROMPT_PREFIX = "Return the function call parameters in JSON format based on the following user prompt: "

def execute_skill(self, skill_name: str, user_prompt: str):
def execute_skill(self, skill: SkillConfig, user_prompt: str):
"""
Import the skill from custom_skills package, initialize it (using GPT to fill in kwargs), and run it
"""
skill_class = self._get_skill_class(skill_name)
skill_class = BaseTool
skill_args = self._get_skill_arguments(json.dumps(skill_class.openai_schema), user_prompt)
return self._execute_skill(skill_class, skill_args)
return self._execute_skill(skill, skill_args)

def _get_skill_arguments(self, function_spec: str, user_prompt: str) -> str:
user_prompt = (
Expand All @@ -56,17 +59,42 @@ def _get_skill_class(skill_name: str) -> BaseTool:
raise RuntimeError(f"Skill not found: {skill_name}") from e

@staticmethod
def _execute_skill(skill_class: BaseTool, args: str) -> str | None:
if not skill_class:
return f"Error: Skill {skill_class.__name__} not found"
def _execute_skill(skill: SkillConfig, args: str) -> str | None:
if not skill:
return f"Error: Skill not found"

try:
# init skill
func = skill_class(**eval(args))
# get outputs from the skill
return func.run()
# Ensure args are properly parsed
parsed_args = json.loads(args)

# Initialize E2B sandbox
api_key = os.environ.get("E2B_API_KEY", "")
sandbox = Sandbox(api_key=api_key)

# Dynamically import the correct class using the skill id
try:

# Get the class source code dynamically
class_code = skill.content
except ModuleNotFoundError as e:
return f"Error: {str(e)}"

# Embed the class code and skill execution inside the sandbox
script = f"""
{class_code} # Embed the class code

# Initialize the skill with provided arguments
skill_instance = {skill.title}(**{parsed_args})

# Run the skill and print output
print(skill_instance.run())
"""

# Run script inside E2B sandbox
sandbox.commands.run("pip install agency_swarm")
result = sandbox.run_code(script)
return result.logs.stdout[0].strip()

except Exception as e:
error_message = f"Error: {e}"
if "For further information visit" in error_message:
error_message = error_message.split("For further information visit")[0]
return error_message
3 changes: 2 additions & 1 deletion backend/services/skill_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ def create_or_update_skill(self, config: SkillConfig, current_user_id: str) -> s
config.id = None

# Check permissions if updating existing skill
config_db = None
if config.id:
config_db = self.get_skill_config(config.id)
self.check_user_permissions(config_db, current_user_id)
Expand All @@ -284,7 +285,7 @@ def create_or_update_skill(self, config: SkillConfig, current_user_id: str) -> s
)

# Initialize the skill
self._initialize_skill(config)
# self._initialize_skill(config)

# Save the approved skill to storage
skill_id = self.storage.save(config)
Expand Down
1 change: 1 addition & 0 deletions backend/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class Settings(BaseSettings):
encryption_key: bytes = Field(default=b"")
mailchimp_api_key: str | None = Field(default=None)
mailchimp_list_id: str | None = Field(default=None)
e2b_api_key: str | None = Field(default=None)

model_config = SettingsConfigDict(env_file=".env")

Expand Down
Loading
Loading