Skip to content

Commit 6c36b76

Browse files
committed
Add files for building container image for serving LLM apps.
1 parent 8980a90 commit 6c36b76

File tree

4 files changed

+112
-0
lines changed

4 files changed

+112
-0
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
FROM ghcr.io/oracle/oraclelinux:9-slim
2+
3+
RUN curl -L -o ./miniconda.sh -O https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh \
4+
&& chmod +x ./miniconda.sh \
5+
&& ./miniconda.sh -b -p /opt/conda \
6+
&& rm ./miniconda.sh \
7+
&& echo ". /opt/conda/etc/profile.d/conda.sh" >> ~/.bashrc \
8+
&& /opt/conda/bin/conda update python cryptography \
9+
&& /opt/conda/bin/conda clean -ya
10+
11+
RUN microdnf install -y expat-devel which wget && microdnf clean all
12+
13+
ARG USERNAME=uwsgi
14+
RUN useradd -ms /bin/bash $USERNAME
15+
USER $USERNAME
16+
WORKDIR /home/$USERNAME
17+
18+
# Create conda env
19+
RUN /opt/conda/bin/conda create -n conda_env python=3.10 pip -y
20+
SHELL ["/opt/conda/bin/conda", "run", "-n", "conda_env", "/bin/bash", "-c"]
21+
22+
ADD requirements.txt /opt/requirements.txt
23+
RUN pip install -r /opt/requirements.txt && pip cache purge
24+
ADD app.py /opt/app.py
25+
26+
ENV MODEL_DIR="/opt/ds/model/deployed_model"
27+
ENV PATH=/home/$USERNAME/.conda/envs/conda_env/bin:/opt/conda/bin/:$PATH
28+
ENTRYPOINT [ "uwsgi" ]
29+
CMD [ "--http", "0.0.0.0:8080", "--master", "--enable-threads", "--single-interpreter", "-p", "5", "--chdir", "/opt", "--module", "app:app" ]

LLM/deployment/container/Readme.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Container for serving LLM apps with Flask and uWSGI
2+
3+
This directory contains files for building a container image for serving LLM applications on OCI Data Science model deployment service.
4+
* `Dockerfile`: The Dockerfile for building the image based on Oracle Linux 9.
5+
* `requirements.txt`: The Python dependencies for serving the application.
6+
* `app.py`: The Flask application for serving the LLM applications.
7+
8+
This container image will basic LangChain/LangGraph applications, including the LLM/Chat Models for OCI Model Deployment and OCI Generative AI. You may add additional dependencies into `requirements.txt` as needed.

LLM/deployment/container/app.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""Flask app with /health and /predict endpoint."""
2+
3+
import importlib.util
4+
import logging
5+
import os
6+
import sys
7+
import traceback
8+
9+
from flask import Flask, request
10+
11+
12+
# Get logging and debugging settings from environment variables
13+
LOG_LEVEL = os.environ.get("LOG_LEVEL", logging.INFO)
14+
MODEL_DIR = os.environ.get("MODEL_DIR", "/opt/ds/model/deployed_model")
15+
FLASK_DEBUG = os.environ.get("FLASK_DEBUG", False)
16+
17+
18+
def set_log_level(the_logger: logging.Logger, log_level=None):
19+
"""Sets the log level of a logger based on the environment variable.
20+
This will also set the log level of logging.lastResort.
21+
"""
22+
if not log_level:
23+
return the_logger
24+
try:
25+
the_logger.setLevel(log_level)
26+
logging.lastResort.setLevel(log_level)
27+
the_logger.info(f"Log level set to {log_level}")
28+
except Exception:
29+
# Catching all exceptions here
30+
# Setting log level should not interrupt the job run even if there is an exception.
31+
the_logger.warning("Failed to set log level.")
32+
the_logger.debug(traceback.format_exc())
33+
return the_logger
34+
35+
36+
def import_from_path(file_path, module_name="score"):
37+
"""Imports a module from file path."""
38+
spec = importlib.util.spec_from_file_location(module_name, file_path)
39+
module = importlib.util.module_from_spec(spec)
40+
sys.modules[module_name] = module
41+
spec.loader.exec_module(module)
42+
return module
43+
44+
45+
logger = logging.getLogger(__name__)
46+
set_log_level(logger, LOG_LEVEL)
47+
48+
score = import_from_path(os.path.join(MODEL_DIR, "score.py"))
49+
app = Flask(__name__)
50+
51+
52+
@app.route("/health")
53+
def health():
54+
"""Health check."""
55+
return {"status": "success"}
56+
57+
58+
@app.route("/predict", methods=["POST"])
59+
def predict():
60+
"""Make prediction."""
61+
payload = request.get_data()
62+
results = score.predict(payload)
63+
return results
64+
65+
66+
if __name__ == "__main__":
67+
app.run(debug=FLASK_DEBUG, host="0.0.0.0", port=8080)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
flask
2+
langchain>=0.3
3+
langchain-community>=0.3
4+
langchain-openai>=0.2
5+
langchain-experimental>=0.3
6+
langgraph>=0.2
7+
oracle-ads>=2.12
8+
pyuwsgi

0 commit comments

Comments
 (0)