From 9d3fe171e9dd995552c0160444c099c85108a280 Mon Sep 17 00:00:00 2001 From: jfaldanam Date: Tue, 18 Jun 2024 10:49:05 +0200 Subject: [PATCH 1/4] feat: implement lambda handler for deployments in AWS lambda --- .github/workflows/build.yaml | 11 ++++- Dockerfile.lambda | 22 +++++++++ README.md | 28 ++++++++++- src/eidos/lambda.py | 93 ++++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 Dockerfile.lambda create mode 100644 src/eidos/lambda.py diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 59f765e..79385c0 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -19,7 +19,7 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push Docker images + - name: Build and push the base Docker image uses: docker/build-push-action@v4.1.0 with: context: . @@ -28,3 +28,12 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max tags: ghcr.io/khaosresearch/eidos:latest,ghcr.io/khaosresearch/eidos:${{ github.event.release.tag_name }} + - name: Build and push the lambda Docker image + uses: docker/build-push-action@v4.1.0 + with: + context: . + file: ./Dockerfile.lambda + push: true + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ghcr.io/khaosresearch/eidos-lambda:latest,ghcr.io/khaosresearch/eidos-lambda:${{ github.event.release.tag_name }} diff --git a/Dockerfile.lambda b/Dockerfile.lambda new file mode 100644 index 0000000..271c080 --- /dev/null +++ b/Dockerfile.lambda @@ -0,0 +1,22 @@ +FROM amazon/aws-lambda-python:3.11 + +# When sending request from the UI it crashes as described in this issue, this is a patch for it +# https://github.com/aws/aws-lambda-runtime-interface-emulator/issues/97#issuecomment-1707171018 +RUN curl -Lo /usr/local/bin/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/download/v1.10/aws-lambda-rie \ + && chmod +x /usr/local/bin/aws-lambda-rie + +# Install main dependency +COPY pyproject.toml /tmp/eidos/pyproject.toml +COPY README.md /tmp/eidos/README.md +COPY src /tmp/eidos/src +RUN pip install --no-cache-dir "/tmp/eidos" + +# Copy default functions and provide ENV to override it functions +COPY ./functions /var/task/functions +ENV EIDOS_FUNCTIONS_FOLDER=/var/task/functions + +# Copy lambda's code +#COPY serverless/eidos/src/ /var/task + +# Setup lambda's handler +CMD [ "eidos.lambda.lambda_handler" ] \ No newline at end of file diff --git a/README.md b/README.md index b82615e..0eb0735 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,9 @@ Or directly from GitHub: python -m pip install "eidos @ git+ssh://git@github.com/KhaosResearch/eidos.git" ``` -## Run +## Deployment + +* Development Run the API with the following command: @@ -30,6 +32,8 @@ uvicorn eidos.api:app --host 0.0.0.0 --port 8090 --reload You can override the default configuration by setting [environment variables](src/eidos/settings.py). +* Docker + Alternatively, you can use the provided [Dockerfile](Dockerfile) to build a Docker image and run the API in a container: ```bash @@ -43,8 +47,30 @@ Example: curl -X POST -H "Content-Type: application/json" -d '{"who": "me"}' http://localhost:8090/api/v1/execution/salute ``` +* Kubernetes + To deploy the container in Kubernetes, a reference deployment is available and documented at [deployments](deployments/). +* Serverless in AWS + +# Semantic search of database of documents + +Another docker image to deploy serverless in AWS Lambda is provided in [Dockerfile.lambda](Dockerfile.lambda). The image is based on the official AWS Lambda Python 3.11 image. For extending this image the process is the same as the main image. + +```console +$ docker build -t eidos-lambda -f Dockerfile.lambda . +``` + +Run the container locally with the following command or deploy in AWS Lambda as a docker container image: +```bash +docker run --rm -p 9001:8080 eidos-lambda +``` + +Invoke the function for local testing with sample query +```bash +curl -XPOST "http://localhost:9001/2015-03-31/functions/function/invocations" -d '{"command": "EXECUTE", "parameters": {"function": "salute", "args": {"who": "me, I am executing serverless"}}}' +``` + ## Testing `pytest` is used for testing. You can run the tests with the following command: diff --git a/src/eidos/lambda.py b/src/eidos/lambda.py new file mode 100644 index 0000000..c7bdb8a --- /dev/null +++ b/src/eidos/lambda.py @@ -0,0 +1,93 @@ +from enum import Enum +from typing import Any + +import structlog + +from eidos.execute import ( + execute, + get_function_schema, + get_openai_function_definition, + list_functions_names, + list_functions_openai, +) + +log = structlog.get_logger("eidos.lambda") + + +class ValidationCommands(Enum): + """Enum to hold the different validation commands from eidos.""" + + LIST = "LIST" + LIST_NAMES = "LIST_NAMES" + GET_DEFINITION = "GET_DEFINITION" + GET_SCHEMA = "GET_SCHEMA" + EXECUTE = "EXECUTE" + + def __str__(self): + return self.value + + def __repr__(self): + return self.value + + +def lambda_handler(event: dict[str, Any], context: dict[str, Any]): + try: + log.info("Processing event", command=event["command"]) + command = event["command"] + except KeyError: + raise ValueError( + f"There is a required field 'command' with the function to execute. " + f"Possible values: {ValidationCommands.__members__}" + ) + + try: + validation_function = ValidationCommands[command] + except KeyError: + raise ValueError( + f"Unknown function: {event['command']}. " + f"Possible values: {ValidationCommands.__members__}" + ) + + match validation_function: + case ValidationCommands.LIST: + return list_functions_openai() + case ValidationCommands.LIST_NAMES: + return list_functions_names() + case ValidationCommands.GET_DEFINITION: + if "function" in event.get("parameters", {}): + function = event["parameters"]["function"] + return get_openai_function_definition(function) + else: + return { + "statusCode": 400, + "body": "Missing function. Provide as parameters.function", + } + case ValidationCommands.GET_SCHEMA: + if "function" in event.get("parameters", {}): + function = event["parameters"]["function"] + return get_function_schema(function) + else: + return { + "statusCode": 400, + "body": "Missing function. Provide as parameters.function", + } + case ValidationCommands.EXECUTE: + if "function" in event.get("parameters", {}): + function = event["parameters"]["function"] + else: + return { + "statusCode": 400, + "body": "Missing function. Provide as parameters.function", + } + + args = event["parameters"].get("args", {}) # Default to empty parameters + log.info("Executing function ", function=function, arguments=args) + + result = execute(function, args) + + return result + case _: + raise ValueError( + f"Unknown function: {event['command']}. " + f"Possible values: {ValidationCommands.__members__}" + ) From 71bd78e0d3e116a1800ad47a677c3633dd285451 Mon Sep 17 00:00:00 2001 From: jfaldanam Date: Tue, 18 Jun 2024 15:10:25 +0200 Subject: [PATCH 2/4] chore: rename Kubernetes manifests --- {deployments => manifests}/README.md | 0 {deployments => manifests}/eidos-deployment.yaml | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {deployments => manifests}/README.md (100%) rename {deployments => manifests}/eidos-deployment.yaml (100%) diff --git a/deployments/README.md b/manifests/README.md similarity index 100% rename from deployments/README.md rename to manifests/README.md diff --git a/deployments/eidos-deployment.yaml b/manifests/eidos-deployment.yaml similarity index 100% rename from deployments/eidos-deployment.yaml rename to manifests/eidos-deployment.yaml From 758d6bd3fed2ae67ac9b7f49305ca7529ee0350d Mon Sep 17 00:00:00 2001 From: jfaldanam Date: Tue, 18 Jun 2024 15:11:32 +0200 Subject: [PATCH 3/4] feat: clean up Dockerfiles and improve their documentation --- Dockerfile | 2 +- Dockerfile.lambda | 24 ++++++++++++------------ README.md | 17 ++++++++--------- src/eidos/execute.py | 1 + 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Dockerfile b/Dockerfile index 88d4624..df97f71 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ ENV EIDOS_ENV production ENV EIDOS_FUNCTIONS_FOLDER /functions -RUN pip install "." +RUN pip install --no-cache-dir "." EXPOSE 80 diff --git a/Dockerfile.lambda b/Dockerfile.lambda index 271c080..c97b424 100644 --- a/Dockerfile.lambda +++ b/Dockerfile.lambda @@ -1,22 +1,22 @@ -FROM amazon/aws-lambda-python:3.11 +FROM amazon/aws-lambda-python:3.10 # When sending request from the UI it crashes as described in this issue, this is a patch for it # https://github.com/aws/aws-lambda-runtime-interface-emulator/issues/97#issuecomment-1707171018 RUN curl -Lo /usr/local/bin/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/download/v1.10/aws-lambda-rie \ && chmod +x /usr/local/bin/aws-lambda-rie -# Install main dependency -COPY pyproject.toml /tmp/eidos/pyproject.toml -COPY README.md /tmp/eidos/README.md -COPY src /tmp/eidos/src -RUN pip install --no-cache-dir "/tmp/eidos" -# Copy default functions and provide ENV to override it functions -COPY ./functions /var/task/functions -ENV EIDOS_FUNCTIONS_FOLDER=/var/task/functions +COPY README.md /code/README.md +COPY src /code/src +COPY pyproject.toml /code/pyproject.toml +COPY ./functions /functions -# Copy lambda's code -#COPY serverless/eidos/src/ /var/task +ENV EIDOS_ENV production + +ENV EIDOS_FUNCTIONS_FOLDER=/functions + +RUN pip install --no-cache-dir "/code" + +EXPOSE 8080 -# Setup lambda's handler CMD [ "eidos.lambda.lambda_handler" ] \ No newline at end of file diff --git a/README.md b/README.md index 0eb0735..2cb71b8 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Alternatively, you can use the provided [Dockerfile](Dockerfile) to build a Dock ```bash docker build -t eidos-server:latest . -docker run -v $(pwd)/functions:/functions -p 8090:80 eidos-server:latest +docker run --rm -v $(pwd)/functions:/functions -p 8090:80 eidos-server:latest ``` Example: @@ -49,26 +49,25 @@ curl -X POST -H "Content-Type: application/json" -d '{"who": "me"}' http://local * Kubernetes -To deploy the container in Kubernetes, a reference deployment is available and documented at [deployments](deployments/). +To deploy the container in Kubernetes, a reference deployment is available and documented at [manifests](manifests/). * Serverless in AWS - -# Semantic search of database of documents - Another docker image to deploy serverless in AWS Lambda is provided in [Dockerfile.lambda](Dockerfile.lambda). The image is based on the official AWS Lambda Python 3.11 image. For extending this image the process is the same as the main image. -```console -$ docker build -t eidos-lambda -f Dockerfile.lambda . +```bash +docker build -t eidos-lambda -f Dockerfile.lambda . ``` Run the container locally with the following command or deploy in AWS Lambda as a docker container image: + ```bash -docker run --rm -p 9001:8080 eidos-lambda +docker run --rm -p 8091:8080 eidos-lambda ``` Invoke the function for local testing with sample query + ```bash -curl -XPOST "http://localhost:9001/2015-03-31/functions/function/invocations" -d '{"command": "EXECUTE", "parameters": {"function": "salute", "args": {"who": "me, I am executing serverless"}}}' +curl -XPOST "http://localhost:8091/2015-03-31/functions/function/invocations" -d '{"command": "EXECUTE", "parameters": {"function": "salute", "args": {"who": "me, I am executing serverless"}}}' ``` ## Testing diff --git a/src/eidos/execute.py b/src/eidos/execute.py index 7e6f35f..ffe51d9 100644 --- a/src/eidos/execute.py +++ b/src/eidos/execute.py @@ -94,6 +94,7 @@ def execute(function_name: str, arguments: dict | None) -> dict[str, Any]: try: function_definition = json_load(file_path) except FileNotFoundError: + log.error("Error: function module not found.", function=function_name, file_path=file_path) raise FileNotFoundError("Error: function module not found.") # Validate input arguments against the function's schema. From 93acd08079e2f7ea1ede6f64eeea0b8a9580cde9 Mon Sep 17 00:00:00 2001 From: Antonio Date: Wed, 19 Jun 2024 07:41:35 +0200 Subject: [PATCH 4/4] docs: add badges --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 2cb71b8..48a5b2d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # eidos: Validation and execution of AI functions +[![Linter: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) +![Python Version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2FKhaosResearch%2Feidos%2Fmaster%2Fpyproject.toml) +![GitHub Release](https://img.shields.io/github/v/release/KhaosResearch/eidos) +![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/KhaosResearch/eidos/test.yaml?label=CI) + _eidos_ is an API for validating and executing AI functions. It aims to be a generic API to serve as a common interface to allow execution of functions by LLMs. ![Usage diagram of eidos](assets/eidos.png)