Skip to content

Commit

Permalink
feat: Add Runtime for tool calling (#1163)
Browse files Browse the repository at this point in the history
Co-authored-by: asher <[email protected]>
Co-authored-by: Wendong <[email protected]>
Co-authored-by: Isaac Jin <[email protected]>
Co-authored-by: Caelum Forder <[email protected]>
Co-authored-by: liuxukun2000 <[email protected]>
  • Loading branch information
6 people authored Nov 22, 2024
1 parent abfa406 commit 2f8881d
Show file tree
Hide file tree
Showing 24 changed files with 2,005 additions and 229 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -420,3 +420,7 @@ logs/
datasets_test/
evaluation_data/
camel_data/

#Benchmark
benchmark/gaia/Dataset
benchmark/gaia/results.jsonl
29 changes: 29 additions & 0 deletions camel/runtime/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
# 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.
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
from .base import BaseRuntime
from .configs import TaskConfig
from .docker_runtime import DockerRuntime
from .llm_guard_runtime import LLMGuardRuntime
from .remote_http_runtime import RemoteHttpRuntime

# TODO: Add Celery Runtime to support distributed computing,
# Rate Limiting, Load Balancing, etc.

__all__ = [
"BaseRuntime",
"DockerRuntime",
"RemoteHttpRuntime",
"LLMGuardRuntime",
"TaskConfig",
]
93 changes: 93 additions & 0 deletions camel/runtime/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
# 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.
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
import importlib
import io
import json
import logging
import os
import sys
from typing import Dict

import uvicorn
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

from camel.toolkits import BaseToolkit

logger = logging.getLogger(__name__)

sys.path.append(os.getcwd())

modules_functions = sys.argv[1:]

logger.info(f"Modules and functions: {modules_functions}")

app = FastAPI()


@app.exception_handler(Exception)
async def general_exception_handler(request: Request, exc: Exception):
return JSONResponse(
status_code=500,
content={
"detail": "Internal Server Error",
"error_message": str(exc),
},
)


for module_function in modules_functions:
try:
init_params = dict()
if "{" in module_function:
module_function, params = module_function.split("{")
params = "{" + params
init_params = json.loads(params)

module_name, function_name = module_function.rsplit(".", 1)

logger.info(f"Importing {module_name} and function {function_name}")

module = importlib.import_module(module_name)
function = getattr(module, function_name)
if isinstance(function, type) and issubclass(function, BaseToolkit):
function = function(**init_params).get_tools()

if not isinstance(function, list):
function = [function]

for func in function:

@app.post(f"/{func.get_function_name()}")
async def dynamic_function(data: Dict, func=func):
redirect_stdout = data.get('redirect_stdout', False)
if redirect_stdout:
sys.stdout = io.StringIO()
response_data = func.func(*data['args'], **data['kwargs'])
if redirect_stdout:
sys.stdout.seek(0)
output = sys.stdout.read()
sys.stdout = sys.__stdout__
return {
"output": json.dumps(response_data),
"stdout": output,
}
return {"output": json.dumps(response_data)}

except (ImportError, AttributeError) as e:
logger.error(f"Error importing {module_function}: {e}")


if __name__ == "__main__":
uvicorn.run("__main__:app", host="0.0.0.0", port=8000, reload=True)
45 changes: 45 additions & 0 deletions camel/runtime/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
# 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.
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
from abc import ABC, abstractmethod
from typing import Any, List, Union

from camel.toolkits import FunctionTool


class BaseRuntime(ABC):
r"""An abstract base class for all CAMEL runtimes."""

def __init__(self):
super().__init__()

self.tools_map = dict()

@abstractmethod
def add(
self,
funcs: Union[FunctionTool, List[FunctionTool]],
*args: Any,
**kwargs: Any,
) -> "BaseRuntime":
r"""Adds a new tool to the runtime."""
pass

@abstractmethod
def reset(self, *args: Any, **kwargs: Any) -> Any:
r"""Resets the runtime to its initial state."""
pass

def get_tools(self) -> List[FunctionTool]:
r"""Returns a list of all tools in the runtime."""
return list(self.tools_map.values())
56 changes: 56 additions & 0 deletions camel/runtime/configs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
# 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.
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
from typing import Dict, List, Optional, Union

from pydantic import BaseModel


class TaskConfig(BaseModel):
r"""A configuration for a task to run a command inside the container.
Arttributes:
cmd (str or list): Command to be executed
stdout (bool): Attach to stdout. (default::obj: `True`)
stderr (bool): Attach to stderr. (default::obj: `True`)
stdin (bool): Attach to stdin. (default::obj: `False`)
tty (bool): Allocate a pseudo-TTY. (default::obj: `False`)
privileged (bool): Run as privileged. (default::obj: `False`)
user (str): User to execute command as. (default::obj: `""`)
detach (bool): If true, detach from the exec command.
(default::obj: `False`)
stream (bool): Stream response data. (default::obj: `False`)
socket (bool): Return the connection socket to allow custom
read/write operations. (default::obj: `False`)
environment (dict or list): A dictionary or a list of strings in
the following format ``["PASSWORD=xxx"]`` or
``{"PASSWORD": "xxx"}``. (default::obj: `None`)
workdir (str): Path to working directory for this exec session.
(default::obj: `None`)
demux (bool): Return stdout and stderr separately. (default::obj:
`False`)
"""

cmd: Union[str, List[str]]
stdout: bool = True
stderr: bool = True
stdin: bool = False
tty: bool = False
privileged: bool = False
user: str = ""
detach: bool = False
stream: bool = False
socket: bool = False
environment: Optional[Union[Dict[str, str], List[str]]] = None
workdir: Optional[str] = None
demux: bool = False
Loading

0 comments on commit 2f8881d

Please sign in to comment.