diff --git a/.github/workflows/push-image.yml b/.github/workflows/push-image.yml index cb4c0ea..7058398 100644 --- a/.github/workflows/push-image.yml +++ b/.github/workflows/push-image.yml @@ -12,12 +12,15 @@ on: - '05-assistive-chatbot' - '02-household-queries' service_name: - description: 'Name of target AWS service. Leave blank if unsure.' + description: 'Name of target AWS service.' type: choice + default: 'container-service-2' options: - - '' + - 'container-service-3' + - 'container-service-2' - 'chatbot-chainlit-svc' - 'secure-chatbot-svc' + - '' build_image: description: "Build and push image" required: true @@ -28,8 +31,19 @@ on: required: true type: boolean default: 'false' + # image_tag: + # description: 'Tag/Version of the image to push' + # required: true + # type: string + # default: '0.06' + delete_images: + description: 'Delete previous images in AWS Lightsail' + required: true + type: boolean + default: 'false' env: + AWS_REGION: us-east-1 IMAGE_NAME: localimage jobs: @@ -49,25 +63,59 @@ jobs: fi echo "service_name=$service_name" >> $GITHUB_OUTPUT - case "${service_name}" in - # The image_tag is specific to the `*-svc` service - 'secure-chatbot-svc') image_tag='0.01';; - 'chatbot-chainlit-svc') image_tag='chatbot-chainlit';; - *) echo "Unknown service_name: '${service_name}'"; exit 3;; - esac + image_tag="${{ inputs.image_tag }}" + if [ "${image_tag}" = "" ]; then + case "${service_name}" in + # The image_tag is specific to the `*-svc` service + 'secure-chatbot-svc') image_tag='0.01';; + 'chatbot-chainlit-svc') image_tag='chatbot-chainlit';; + container-service-*) image_tag='not-used';; + *) echo "Unknown service_name: '${service_name}'"; exit 3;; + esac + fi echo "image_tag=$image_tag" >> $GITHUB_OUTPUT - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: - aws-region: us-east-1 + aws-region: ${{ env.AWS_REGION }} aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} mask-aws-account-id: true # TODO: secure credentials: https://github.com/aws-actions/amazon-ecr-login?tab=readme-ov-file#ecr-private # https://github.com/docker/login-action?tab=readme-ov-file#aws-elastic-container-registry-ecr + # https://medium.com/@lukhee/automating-aws-lightsail-deployments-with-github-actions-53c73c9a1c1f - - name: Login to Amazon ECR + + - name: "Upgrade AWS CLI version and setup lightsailctl" + run: | + # aws --version + # curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" + # unzip awscliv2.zip + # sudo ./aws/install --bin-dir /usr/local/bin --install-dir /usr/local/aws-cli --update + # which aws + aws --version + sudo curl "https://s3.us-west-2.amazonaws.com/lightsailctl/latest/linux-amd64/lightsailctl" -o "/usr/local/bin/lightsailctl" + sudo chmod +x /usr/local/bin/lightsailctl + aws lightsail push-container-image help + + - name: "Delete previous container images" + if: inputs.delete_images + env: + SERVICE_NAME: ${{ steps.check_inputs.outputs.service_name }} + run: | + AWS_IMAGES=$(aws lightsail get-container-images --region "$AWS_REGION" --service-name "$SERVICE_NAME" --output text) + IMAGE_NAMES=$(echo $AWS_IMAGES | grep -Eo ':"$SERVICE_NAME"\.${{ inputs.image-name }}\.[0-9]+') + echo $IMAGE_NAMES + FIRST=0 + while read LINE; do + if [ "$FIRST" -ne 0 ]; then + aws lightsail delete-container-image --region "$AWS_REGION" --service-name "$SERVICE_NAME" --image $LINE; + fi + FIRST=1; + done <<< $IMAGE_NAMES + + - name: "Login to Amazon ECR" id: login-ecr uses: aws-actions/amazon-ecr-login@v2 with: @@ -84,23 +132,72 @@ jobs: # TODO: make this more easily editable and secure # The DOT_ENV_FILE_CONTENTS contains LITERAL_API_KEY, OPENAI_API_KEY, RETRIEVE_K, LLM_MODEL_NAME, SUMMARIZER_LLM_MODEL_NAME echo "${{secrets.DOT_ENV_FILE_CONTENTS}}" > .env + echo "BUILD_DATE=$(date +%Y-%m-%d-%T)" >> .env + echo "GIT_SHA=${{ github.sha }}" >> .env docker build -t "$IMAGE_NAME" --build-arg GURU_CARDS_URL="https://docs.google.com/uc?export=download&id=${{ secrets.GURU_CARDS_URL_ID }}" . - - name: "Publish image to AWS ECR'" - id: publish_image + + - name: "Publish image to Lightsail" if: inputs.build_image + id: pub_image_to_ls env: - ECR_PATH: ${{ steps.login-ecr.outputs.registry }}/${{ secrets.ECR_REPO }} + ECR_PATH: ${{ steps.login-ecr.outputs.registry }}/${{ secrets.ECR_REPO }} + SERVICE_NAME: ${{ steps.check_inputs.outputs.service_name }} + IMAGE_TAG: ${{ steps.check_inputs.outputs.image_tag }} + # LABEL must match regex ^(?:[a-z0-9]{1,2}|[a-z0-9][a-z0-9-]+[a-z0-9])$ + LABEL: git-push + IMAGE_SHA_TAG: ${{ github.sha }} run: | - image_tag="${{ steps.check_inputs.outputs.image_tag }}" - echo "# Publishing image ${image_tag} to $ECR_PATH" + echo "# Publishing image for $SERVICE_NAME" + aws lightsail push-container-image --region $AWS_REGION --service-name "$SERVICE_NAME" --label "$LABEL" --image "$IMAGE_NAME" - docker tag "$IMAGE_NAME" "$ECR_PATH:${image_tag}" - docker push "$ECR_PATH:${image_tag}" + LS_DOCKER_IMAGE=$(aws lightsail get-container-images --service-name "$SERVICE_NAME" | jq -r .containerImages[0].image) + echo "Image name: '$LS_DOCKER_IMAGE'" + echo "LS_DOCKER_IMAGE='$LS_DOCKER_IMAGE'" >> $GITHUB_OUTPUT - - name: "Update AWS Service" + - name: Deploy container on AWS Lightsail if: inputs.deploy_image env: - CLUSTER_NAME: genai-experiments + SERVICE_NAME: ${{ steps.check_inputs.outputs.service_name }} run: | - aws ecs update-service --force-new-deployment --cluster "$CLUSTER_NAME" --service "${{ steps.check_inputs.outputs.service_name }}" + TEMPLATE='{ + "serviceName": "$SERVICE_NAME", + "containers": { + "chatbot": { + "image": "$LS_DOCKER_IMAGE", + "command": [], + "environment": { + "ENV": "PROD", + "BUILD_DATE": "$BUILD_DATE" + }, + "ports": { + "8000": "HTTP" + } + } + }, + "publicEndpoint": { + "containerName": "chatbot", + "containerPort": 8000, + "healthCheck": { + "healthyThreshold": 2, + "unhealthyThreshold": 4, + "timeoutSeconds": 20, + "intervalSeconds": 60, + "path": "/healthcheck", + "successCodes": "200-499" + } + } + }' + echo "$TEMPLATE" | BUILD_DATE=$(date +%Y-%m-%d-%T) envsubst > config.json + cat config.json + aws lightsail create-container-service-deployment --cli-input-json file://config.json + + # aws lightsail create-container-service-deployment --region ${{ inputs.aws-region }} --cli-input-json '${{ inputs.aws-lightsail-service-config }}' > /dev/null + # aws lightsail update-container-service --service-name "$SERVICE_NAME" --no-is-disabled + + # - name: "Update AWS Service" + # if: inputs.deploy_image + # env: + # CLUSTER_NAME: genai-experiments + # run: | + # aws ecs update-service --force-new-deployment --cluster "$CLUSTER_NAME" --service "${{ steps.check_inputs.outputs.service_name }}" diff --git a/05-assistive-chatbot/.env-DEV b/05-assistive-chatbot/.env-DEV new file mode 100644 index 0000000..fc3af97 --- /dev/null +++ b/05-assistive-chatbot/.env-DEV @@ -0,0 +1,11 @@ + +ENABLE_CHATBOT_API=False + +# Used by SentenceTransformerEmbeddings and HuggingFaceEmbeddings +SENTENCE_TRANSFORMERS_HOME="./.sentence-transformers-cache" + +# CHAT_ENGINE +CHAT_ENGINE='Summaries' +LLM_MODEL_NAME='openai :: gpt-3.5-turbo-instruct' +RETRIEVE_K=4 +SUMMARIZER_LLM_MODEL_NAME='openai :: gpt-3.5-turbo-instruct' diff --git a/05-assistive-chatbot/.env-PROD b/05-assistive-chatbot/.env-PROD new file mode 100644 index 0000000..c4282ad --- /dev/null +++ b/05-assistive-chatbot/.env-PROD @@ -0,0 +1,7 @@ + +CHATBOT_LOG_LEVEL='DEBUG' + +ENABLE_CHATBOT_API=True + +CHAT_ENGINE='Direct' +LLM_MODEL_NAME='openai :: gpt-3.5-turbo' diff --git a/05-assistive-chatbot/chatbot/__init__.py b/05-assistive-chatbot/chatbot/__init__.py index e9f951c..8985ee5 100644 --- a/05-assistive-chatbot/chatbot/__init__.py +++ b/05-assistive-chatbot/chatbot/__init__.py @@ -22,9 +22,11 @@ def configure_logging(): logging.info("Configured logging level: %s", log_level) +env = os.environ.get("ENV", "DEV") +print(f"Loading .env-{env}") +dotenv.load_dotenv(f".env-{env}") dotenv.load_dotenv() configure_logging() - logger = logging.getLogger(__name__) @@ -46,7 +48,7 @@ def _init_settings(): # Remember to update ChatSettings in chatbot-chainlit.py when adding new settings # and update chatbot/engines/__init.py:CHATBOT_SETTING_KEYS return { - "env": os.environ.get("ENV", "DEV"), + "env": env, "enable_api": is_true(os.environ.get("ENABLE_CHATBOT_API", "False")), "chat_engine": os.environ.get("CHAT_ENGINE", "Direct"), "model": os.environ.get("LLM_MODEL_NAME", "mock :: llm"), diff --git a/05-assistive-chatbot/chatbot_api.py b/05-assistive-chatbot/chatbot_api.py index 862c035..688d50a 100755 --- a/05-assistive-chatbot/chatbot_api.py +++ b/05-assistive-chatbot/chatbot_api.py @@ -8,6 +8,7 @@ import logging from functools import cached_property +import os from typing import Dict from fastapi import FastAPI, Request @@ -51,7 +52,10 @@ def query(message: str | Dict): def healthcheck(request: Request): logger.info(request.headers) # TODO: Add a health check - https://pypi.org/project/fastapi-healthchecks/ - return HTMLResponse("Healthy") + + git_sha = os.environ.get("GIT_SHA", "") + logger.info("Returning: Healthy %s", git_sha) + return HTMLResponse("Healthy " + git_sha) if __name__ == "__main__":