agents: scripts to run scripts as sandboxed fastapi servers
This commit is contained in:
parent
63d13245e1
commit
ffc74360e2
7 changed files with 172 additions and 0 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +1,6 @@
|
|||
[submodule "kompute"]
|
||||
path = kompute
|
||||
url = https://github.com/nomic-ai/kompute.git
|
||||
[submodule "examples/agents/hermes_function_calling"]
|
||||
path = examples/agents/hermes_function_calling
|
||||
url = https://github.com/NousResearch/Hermes-Function-Calling
|
||||
|
|
15
examples/agents/README.md
Normal file
15
examples/agents/README.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
|
||||
Edit `examples/agents/hermes_function_calling/utils.py`:
|
||||
|
||||
```py
|
||||
log_folder = os.environ.get('LOG_FOLDER', os.path.join(script_dir, "inference_logs"))
|
||||
```
|
||||
|
||||
Then run:
|
||||
|
||||
```bash
|
||||
REQUIREMENTS_FILE=<( cat examples/agents/hermes_function_calling/requirements.txt | grep -vE "bitsandbytes|flash-attn" ) \
|
||||
examples/agents/run_sandboxed_tools.sh \
|
||||
examples/agents/hermes_function_calling/functions.py \
|
||||
-e LOG_FOLDER=/data/inference_logs
|
||||
```
|
5
examples/agents/fastify-requirements.txt
Normal file
5
examples/agents/fastify-requirements.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
fastapi[all]
|
||||
pydantic
|
||||
sse-starlette
|
||||
uvicorn[all]
|
||||
typer[all]
|
63
examples/agents/fastify.py
Normal file
63
examples/agents/fastify.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
'''
|
||||
Binds the functions of a python script as a FastAPI server.
|
||||
|
||||
This is useful in combination w/ the examples/agent/run_sandboxed_tools.sh
|
||||
'''
|
||||
import os, sys, typing, importlib.util
|
||||
from anyio import Path
|
||||
import fastapi, uvicorn
|
||||
import typer
|
||||
|
||||
# from langchain_core.tools import BaseTool
|
||||
|
||||
def load_source_as_module(source):
|
||||
i = 0
|
||||
while (module_name := f'mod_{i}') in sys.modules:
|
||||
i += 1
|
||||
|
||||
spec = importlib.util.spec_from_file_location(module_name, source)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[module_name] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
def bind_functions(app, module):
|
||||
for k in dir(module):
|
||||
if k.startswith('_'):
|
||||
continue
|
||||
if k == k.capitalize():
|
||||
continue
|
||||
v = getattr(module, k)
|
||||
if not callable(v) or isinstance(v, typing.Type):
|
||||
continue
|
||||
if not hasattr(v, '__annotations__'):
|
||||
continue
|
||||
|
||||
vt = type(v)
|
||||
if vt.__module__ == 'langchain_core.tools' and vt.__name__.endswith('Tool') and hasattr(v, 'func') and callable(v.func):
|
||||
v = v.func
|
||||
|
||||
print(f'INFO: Binding /{k}')
|
||||
try:
|
||||
app.post(k)(v)
|
||||
except Exception as e:
|
||||
print(f'WARNING: Failed to bind /{k}\n\t{e}')
|
||||
|
||||
def main(files: typing.List[str], host: str = '0.0.0.0', port: int = 8000):
|
||||
app = fastapi.FastAPI()
|
||||
|
||||
for f in files:
|
||||
if f.endswith('.py'):
|
||||
sys.path.insert(0, str(Path(f).parent))
|
||||
|
||||
module = load_source_as_module(f)
|
||||
else:
|
||||
module = importlib.import_module(f)
|
||||
|
||||
bind_functions(app, module)
|
||||
|
||||
uvicorn.run(app, host=host, port=port)
|
||||
|
||||
if __name__ == '__main__':
|
||||
typer.run(main)
|
||||
|
1
examples/agents/hermes_function_calling
Submodule
1
examples/agents/hermes_function_calling
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit b4f757e27d87f4ab408f706f482c25a8e1508d59
|
3
examples/agents/requirements.txt
Normal file
3
examples/agents/requirements.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
jsonargparse
|
||||
pydantic
|
||||
typer[all]
|
82
examples/agents/run_sandboxed_tools.sh
Executable file
82
examples/agents/run_sandboxed_tools.sh
Executable file
|
@ -0,0 +1,82 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Runs a Python script in a sandboxed environment and makes its functions available as a web service.
|
||||
#
|
||||
# git submodule add https://github.com/NousResearch/Hermes-Function-Calling examples/agents/hermes_function_calling
|
||||
# python examples/agents/fastify.py examples/agents/hermes_function_calling/functions.py
|
||||
# REQUIREMENTS_FILE=<( cat examples/agents/hermes_function_calling/requirements.txt | grep -vE "bitsandbytes|flash-attn" ) examples/agents/run_sandboxed_tools.sh examples/agents/hermes_function_calling/functions.py -e LOG_FOLDER=/data/inference_logs
|
||||
set -euo pipefail
|
||||
|
||||
script="$( realpath "$1" )"
|
||||
script_folder="$(dirname "$script")"
|
||||
shift 1
|
||||
|
||||
function cleanup {
|
||||
rm -rf "$BUILD_DIR"
|
||||
echo "Deleted $BUILD_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
BUILD_DIR=$(mktemp -d)
|
||||
DATA_DIR="${DATA_DIR:-$HOME/.llama.cpp/sandbox}"
|
||||
SCRIPT_DIR=$( cd "$(dirname "$0")" ; pwd )
|
||||
|
||||
REQUIREMENTS_FILE="${REQUIREMENTS_FILE:-}"
|
||||
if [[ -z "$REQUIREMENTS_FILE" && -f "$script_folder/requirements.txt" ]]; then
|
||||
REQUIREMENTS_FILE="$script_folder/requirements.txt"
|
||||
fi
|
||||
if [[ -n "$REQUIREMENTS_FILE" ]]; then
|
||||
cp "$REQUIREMENTS_FILE" "$BUILD_DIR/script-requirements.txt"
|
||||
else
|
||||
touch $BUILD_DIR/script-requirements.txt
|
||||
fi
|
||||
|
||||
echo "INFO: using DATA_DIR: $DATA_DIR"
|
||||
|
||||
cp \
|
||||
"$SCRIPT_DIR/fastify-requirements.txt" \
|
||||
"$SCRIPT_DIR/fastify.py" \
|
||||
"$BUILD_DIR"
|
||||
|
||||
mkdir -p "$DATA_DIR"
|
||||
|
||||
PORT=${PORT:-8088}
|
||||
|
||||
# BASE_IMAGE=pytorch/pytorch:latest
|
||||
# BASE_IMAGE=python:3.10-slim
|
||||
BASE_IMAGE=python:3.11-slim
|
||||
# torch
|
||||
# FROM nvidia/cuda:12.1.1-runtime-ubuntu20.04
|
||||
# RUN apt-get update && \
|
||||
# apt-get install -y python3-pip python3-dev && \
|
||||
# rm -rf /var/lib/apt/lists/*
|
||||
|
||||
echo "
|
||||
FROM $BASE_IMAGE
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y gcc python3-dev git cmake
|
||||
RUN pip install --upgrade pip
|
||||
RUN pip install packaging numpy
|
||||
RUN mkdir /src /data
|
||||
|
||||
# Copy resources in increasing likelihood of change, to keep as much as possible cached
|
||||
COPY fastify-requirements.txt /root
|
||||
RUN pip install -r /root/fastify-requirements.txt
|
||||
COPY script-requirements.txt /root
|
||||
RUN pip install -r /root/script-requirements.txt
|
||||
COPY fastify.py /root
|
||||
|
||||
WORKDIR /data
|
||||
# ENTRYPOINT uvicorn fastify:app --reload
|
||||
ENTRYPOINT PYTHONPATH=/src python /root/fastify.py --port=$PORT '/src/$( basename "$script" )'
|
||||
" | docker build "$BUILD_DIR" -f - -t llama.cpp/tools-base
|
||||
|
||||
echo "#"
|
||||
echo "# Binding $script to http://localhost:$PORT/"
|
||||
echo "#"
|
||||
set -x
|
||||
docker run \
|
||||
"$@" \
|
||||
--mount "type=bind,source=$( realpath "$script_folder" ),target=/src,readonly" \
|
||||
--mount "type=bind,source=$( realpath "$DATA_DIR" ),target=/data" \
|
||||
-p "$PORT:$PORT" \
|
||||
-it llama.cpp/tools-base
|
Loading…
Add table
Add a link
Reference in a new issue