Skip to content

Commit

Permalink
feat: implement lambda handler for deployments in AWS lambda
Browse files Browse the repository at this point in the history
  • Loading branch information
jfaldanam committed Jun 18, 2024
1 parent a7326fa commit f12d4f5
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 2 deletions.
11 changes: 10 additions & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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/[email protected]
with:
context: .
Expand All @@ -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/[email protected]
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 }}
22 changes: 22 additions & 0 deletions Dockerfile.lambda
Original file line number Diff line number Diff line change
@@ -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" ]
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ Or directly from GitHub:
pip install "eidos @ git+ssh://[email protected]/KhaosResearch/eidos.git"
```

## Run
## Deployment

* Development

Run the API with the following command:

Expand All @@ -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
Expand All @@ -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:
Expand Down
93 changes: 93 additions & 0 deletions src/eidos/lambda.py
Original file line number Diff line number Diff line change
@@ -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__}"
)

0 comments on commit f12d4f5

Please sign in to comment.