From dc218e6bcd13d7f9732d678bf56cc54eac5fca82 Mon Sep 17 00:00:00 2001 From: Hema Veeradhi Date: Thu, 31 Oct 2024 10:02:42 -0700 Subject: [PATCH] Add openshift deployment files for react agent,streamlit and update mlflow logging --- agent-starter.sh | 10 ++++ pyproject.toml | 3 +- react-agent-Containerfile | 66 +++++++++++++++++++++++++++ react_agent/agent/__init__.py | 2 +- react_agent/api.py | 18 +++++--- react_agent/constants.py | 3 ++ react_agent/openshift/README.md | 42 +++++++++++++++++ react_agent/openshift/configmap.yaml | 38 +++++++++++++++ react_agent/openshift/deployment.yaml | 31 +++++++++++++ react_agent/openshift/route.yaml | 14 ++++++ react_agent/openshift/service.yaml | 13 ++++++ sample.env | 3 +- streamlit-Containerfile | 66 +++++++++++++++++++++++++++ streamlit-starter.sh | 10 ++++ streamlit/openshift/README.md | 49 ++++++++++++++++++++ streamlit/openshift/configmap.yaml | 8 ++++ streamlit/openshift/deployment.yaml | 23 ++++++++++ streamlit/openshift/route.yaml | 14 ++++++ streamlit/openshift/service.yaml | 13 ++++++ 19 files changed, 416 insertions(+), 10 deletions(-) create mode 100644 agent-starter.sh create mode 100644 react-agent-Containerfile create mode 100644 react_agent/openshift/README.md create mode 100644 react_agent/openshift/configmap.yaml create mode 100644 react_agent/openshift/deployment.yaml create mode 100644 react_agent/openshift/route.yaml create mode 100644 react_agent/openshift/service.yaml create mode 100644 streamlit-Containerfile create mode 100644 streamlit-starter.sh create mode 100644 streamlit/openshift/README.md create mode 100644 streamlit/openshift/configmap.yaml create mode 100644 streamlit/openshift/deployment.yaml create mode 100644 streamlit/openshift/route.yaml create mode 100644 streamlit/openshift/service.yaml diff --git a/agent-starter.sh b/agent-starter.sh new file mode 100644 index 0000000..c787efe --- /dev/null +++ b/agent-starter.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -x + +# Check if the config.yaml file exists and copy it +if [ -f /opt/app-root/config/config.yaml ]; then + cp /opt/app-root/config/config.yaml /opt/app-root/src/config.yaml +fi + +# Start the agent server +${POETRY_HOME}/bin/poetry run python react_agent/api.py \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index ccb067a..be91516 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,6 @@ version = "0.1.0" description = "" readme = "README.md" authors = ["Red Hat"] -packages = [{include = "llm_agents"}] [tool.poetry.dependencies] python = ">=3.10,<4.0" @@ -50,4 +49,4 @@ per-file-ignores = { "tests/*" = ["D"] } [build-system] requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" \ No newline at end of file +build-backend = "poetry.core.masonry.api" diff --git a/react-agent-Containerfile b/react-agent-Containerfile new file mode 100644 index 0000000..08ae572 --- /dev/null +++ b/react-agent-Containerfile @@ -0,0 +1,66 @@ +# Builder image to install dependencies from pyproject and lock file +FROM quay.io/fedora/python-310:latest AS builder +USER root +ENV POETRY_HOME=/opt/poetry \ + POETRY_VERSION=1.8.2 + +# Install Poetry +RUN wget -P /tmp/ https://certs.corp.redhat.com/certs/Current-IT-Root-CAs.pem && \ + curl -sSL https://install.python-poetry.org | python - && \ + ${POETRY_HOME}/bin/poetry config virtualenvs.in-project true && \ + ${POETRY_HOME}/bin/poetry config certificates.de-cop-nexus.cert /tmp/Current-IT-Root-CAs.pem + +# Copy the source code and install dependencies +COPY . /opt/app-root/src/ +WORKDIR /opt/app-root/src/ +RUN ${POETRY_HOME}/bin/poetry install --only main + +# Final image +FROM quay.io/fedora/python-310:latest +LABEL maintainer="et@redhat.com" \ + io.k8s.description="ReAct Agent" \ + io.k8s.display-name="react_agent" \ + io.openshift.tags="et" + +USER root + +# Update OS and install necessary packages +RUN dnf -y update && \ + dnf -y install java-17-openjdk tini wget && \ + dnf -y autoremove && \ + dnf -y clean all && \ + wget -P /etc/pki/ca-trust/source/anchors https://certs.corp.redhat.com/certs/Current-IT-Root-CAs.pem && \ + keytool -import -noprompt -keystore /etc/pki/java/cacerts -file /etc/pki/ca-trust/source/anchors/Current-IT-Root-CAs.pem -alias RH-IT-Root-CA -storepass changeit && \ + update-ca-trust enable && \ + update-ca-trust extract + +# Install Poetry in the final stage +ENV POETRY_HOME=/opt/poetry \ + PATH="/opt/poetry/bin:$PATH" +RUN curl -sSL https://install.python-poetry.org | python - + +# Copy the source code and set up the environment +COPY . /opt/app-root/src/ +WORKDIR /opt/app-root/src/ +RUN fix-permissions /opt/app-root/src -P && \ + echo "" > /opt/app-root/bin/activate + +# Copy virtual environment from the builder stage +COPY --from=builder --chown=1001:0 /opt/app-root/src/.venv /opt/app-root/src/.venv + +# Install streamlit directly +RUN /opt/app-root/src/.venv/bin/pip install streamlit + +# Copy the starter.sh script and set permissions +COPY agent-starter.sh /opt/app-root/src/ +RUN chmod +x /opt/app-root/src/agent-starter.sh + +USER 1001 + +EXPOSE 2113 8501 + +# Ensure the virtual environment is included in the PATH +ENV PATH="/opt/app-root/src/.venv/bin:$PATH" + +# Start the API server using agent-starter.sh +CMD ["/opt/app-root/src/agent-starter.sh"] \ No newline at end of file diff --git a/react_agent/agent/__init__.py b/react_agent/agent/__init__.py index d7826b4..7a760ed 100644 --- a/react_agent/agent/__init__.py +++ b/react_agent/agent/__init__.py @@ -12,6 +12,6 @@ def react_agent(): response_format = "agent" tools = import_tools(common_tools_kwargs={"response_format": response_format}) prompt = PromptTemplate.from_template(REACT_PROMPT) - agent = create_react_agent(completion_llm, tools, prompt) + agent = create_react_agent(completion_llm, tools, prompt, stop_sequence=["Observation:"]) agent_executor = AgentExecutor(name="ReActAgent", agent=agent, tools=tools, handle_parsing_errors=True) return agent_executor \ No newline at end of file diff --git a/react_agent/api.py b/react_agent/api.py index a058397..bde728b 100644 --- a/react_agent/api.py +++ b/react_agent/api.py @@ -7,22 +7,28 @@ import logging from contextlib import asynccontextmanager -import mlflow import uvicorn from fastapi import FastAPI - +import mlflow from react_agent.agent import react_agent from react_agent.apispec import ReActRequest, ReActResponse -from react_agent.constants import APP_HOST, APP_PORT - +from react_agent.constants import APP_HOST, APP_PORT, MLFLOW_EXPERIMENT_NAME, MLFLOW_TRACKING_URI, MLFLOW_TRACKING_TOKEN logger = logging.getLogger(__name__) +# Connect to a hosted mlflow +mlflow.set_tracking_uri(MLFLOW_TRACKING_URI) +if not mlflow.get_experiment_by_name(MLFLOW_EXPERIMENT_NAME): + mlflow.create_experiment(name=MLFLOW_EXPERIMENT_NAME) + +experiment = mlflow.get_experiment_by_name(MLFLOW_EXPERIMENT_NAME) +experiment_id = experiment.experiment_id + # Start logging -mlflow.langchain.autolog(log_traces=True) +with mlflow.start_run(experiment_id=experiment_id): + mlflow.langchain.autolog(log_traces=True) agents = {} - @asynccontextmanager async def lifespan(app: FastAPI): """Run startup sequence.""" diff --git a/react_agent/constants.py b/react_agent/constants.py index 0d62c46..b7b28bd 100644 --- a/react_agent/constants.py +++ b/react_agent/constants.py @@ -13,6 +13,9 @@ OPENAI_URI = os.environ.get("OPENAI_URI", "http://localhost:11434/v1") OPENAI_MODEL = os.environ.get("OPENAI_MODEL", "mistral") OPENAI_IGNORE_SSL = os.environ.get("OPENAI_IGNORE_SSL", False) +MLFLOW_TRACKING_URI = os.environ.get("MLFLOW_TRACKING_URI") +MLFLOW_TRACKING_TOKEN = os.environ.get("MLFLOW_TRACKING_TOKEN") +MLFLOW_EXPERIMENT_NAME = os.environ.get("MLFLOW_EXPERIMENT_NAME") ERROR_MESSAGE = "Unable to process request, please try again later." diff --git a/react_agent/openshift/README.md b/react_agent/openshift/README.md new file mode 100644 index 0000000..a09da29 --- /dev/null +++ b/react_agent/openshift/README.md @@ -0,0 +1,42 @@ +# OpenShift Deployment Instructions + +To deploy the ReAct agent on an OpenShift cluster, you can follow the below instructions. + +## Prerequisites + +- OpenShift CLI (`oc`) installed and configured +- (**Optional**) `kubectl` and `kustomize` installed and configured + +## Deployment Instructions + +### Step 1: Clone the Repository + +```sh +git clone git@github.com:redhat-et/llm-agents.git +cd llm-agents/react_agent +``` + +### Step 2: Deploy the ReAct Agent API Server + +The agent API server is responsible for handling requests and interacting with the LLM. It exposes endpoints for different functionalities of the tool-based agent. The API server processes incoming requests, sends them to the appropriate tools or models, and returns the results to the clients. This deployment step sets up the necessary backend infrastructure for the ReAct agent. + + +```sh +oc apply -f openshift/deployment.yaml +``` + +```sh +oc apply -f openshift/service.yaml +``` + +```sh +oc apply -f openshift/route.yaml +``` + +```sh +oc apply -f openshift/configmap.yaml +``` + +### Containerfile + +The Containerfiles used to build the deployment images can be found [here](https://github.com/redhat-et/llm-agents/react-agent-Containerfile) diff --git a/react_agent/openshift/configmap.yaml b/react_agent/openshift/configmap.yaml new file mode 100644 index 0000000..34e3dfa --- /dev/null +++ b/react_agent/openshift/configmap.yaml @@ -0,0 +1,38 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: react-agent-config + namespace: react-agent-test +data: + APP_HOST: "0.0.0.0" + APP_PORT: "2113" + OPENAI_URI: "" + OPENAI_MODEL: "" + OPENAI_IGNORE_SSL: "True" + MLFLOW_TRACKING_URI: "" + MLFLOW_TRACKING_TOKEN: "" + MLFLOW_EXPERIMENT_NAME: "ReAct Agent" + config.yaml: | + tools: + - name: "constitution_tool" + description: "Answers questions about the U.S. Constitution." + url: "https://my.app/v1/completions" + config: + method: 'POST' + headers: + 'Content-Type': 'application/json' + 'Authorization': 'Basic 12345' + body: + prompt: '{{prompt}}' + responseParser: 'json.answer' + responseMetadata: + - name: 'sources' + loc: 'json.sources' + responseFormat: + agent: '{{response}}' + json: + - "response" + - "sources" + examples: + - "What is the definition of a citizen in the U.S. Constitution?" + - "What article describes the power of the judiciary branch?" \ No newline at end of file diff --git a/react_agent/openshift/deployment.yaml b/react_agent/openshift/deployment.yaml new file mode 100644 index 0000000..3a6ab90 --- /dev/null +++ b/react_agent/openshift/deployment.yaml @@ -0,0 +1,31 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: react-agent-server + namespace: react-agent-test +spec: + replicas: 1 + selector: + matchLabels: + app: react-agent-server + template: + metadata: + labels: + app: react-agent-server + spec: + containers: + - name: react-agent-server + image: docker.io/hemaveeradhi/llm-agent:latest + ports: + - containerPort: 2113 + envFrom: + - configMapRef: + name: react-agent-config + volumeMounts: + - name: config-volume + mountPath: /opt/app-root/config/config.yaml + subPath: config.yaml + volumes: + - name: config-volume + configMap: + name: react-agent-config \ No newline at end of file diff --git a/react_agent/openshift/route.yaml b/react_agent/openshift/route.yaml new file mode 100644 index 0000000..491de49 --- /dev/null +++ b/react_agent/openshift/route.yaml @@ -0,0 +1,14 @@ +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: react-agent-route + namespace: react-agent-test +spec: + to: + kind: Service + name: react-agent-server + port: + targetPort: react-agent-port + tls: + termination: edge + wildcardPolicy: None \ No newline at end of file diff --git a/react_agent/openshift/service.yaml b/react_agent/openshift/service.yaml new file mode 100644 index 0000000..716a0bf --- /dev/null +++ b/react_agent/openshift/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: react-agent-server + namespace: react-agent-test +spec: + selector: + app: react-agent-server + ports: + - name: react-agent-port + protocol: TCP + port: 2113 + targetPort: 2113 \ No newline at end of file diff --git a/sample.env b/sample.env index 150279e..947761d 100644 --- a/sample.env +++ b/sample.env @@ -3,5 +3,6 @@ APP_PORT=2113 OPENAI_URI="https://localhost:11434/v1" OPENAI_MODEL="mistral:latest" OPENAI_IGNORE_SSL="True" -MLFLOW_TRACKING_URI="http://localhost:5000" +MLFLOW_TRACKING_URI="" MLFLOW_EXPERIMENT_NAME="ReAct Agent" +MLFLOW_TRACKING_TOKEN="" \ No newline at end of file diff --git a/streamlit-Containerfile b/streamlit-Containerfile new file mode 100644 index 0000000..103280b --- /dev/null +++ b/streamlit-Containerfile @@ -0,0 +1,66 @@ +# Builder image to install dependencies from pyproject and lock file +FROM quay.io/fedora/python-310:latest AS builder +USER root +ENV POETRY_HOME=/opt/poetry \ + POETRY_VERSION=1.8.2 + +# Install Poetry +RUN wget -P /tmp/ https://certs.corp.redhat.com/certs/Current-IT-Root-CAs.pem && \ + curl -sSL https://install.python-poetry.org | python - && \ + ${POETRY_HOME}/bin/poetry config virtualenvs.in-project true && \ + ${POETRY_HOME}/bin/poetry config certificates.de-cop-nexus.cert /tmp/Current-IT-Root-CAs.pem + +# Copy the source code and install dependencies +COPY . /opt/app-root/src/ +WORKDIR /opt/app-root/src/ +RUN ${POETRY_HOME}/bin/poetry install --only main + +# Final image +FROM quay.io/fedora/python-310:latest +LABEL maintainer="et@redhat.com" \ + io.k8s.description="Streamlit" \ + io.k8s.display-name="Streamlit" \ + io.openshift.tags="et" + +USER root + +# Update OS and install necessary packages +RUN dnf -y update && \ + dnf -y install java-17-openjdk tini wget && \ + dnf -y autoremove && \ + dnf -y clean all && \ + wget -P /etc/pki/ca-trust/source/anchors https://certs.corp.redhat.com/certs/Current-IT-Root-CAs.pem && \ + keytool -import -noprompt -keystore /etc/pki/java/cacerts -file /etc/pki/ca-trust/source/anchors/Current-IT-Root-CAs.pem -alias RH-IT-Root-CA -storepass changeit && \ + update-ca-trust enable && \ + update-ca-trust extract + +# Install Poetry in the final stage +ENV POETRY_HOME=/opt/poetry \ + PATH="/opt/poetry/bin:$PATH" +RUN curl -sSL https://install.python-poetry.org | python - + +# Copy the source code and set up the environment +COPY . /opt/app-root/src/ +WORKDIR /opt/app-root/src/ +RUN fix-permissions /opt/app-root/src -P && \ + echo "" > /opt/app-root/bin/activate + +# Copy virtual environment from the builder stage +COPY --from=builder --chown=1001:0 /opt/app-root/src/.venv /opt/app-root/src/.venv + +# Install streamlit directly +RUN /opt/app-root/src/.venv/bin/pip install streamlit + +# Copy the starter.sh script and set permissions +COPY streamlit-starter.sh /opt/app-root/src/ +RUN chmod +x /opt/app-root/src/streamlit-starter.sh + +USER 1001 + +EXPOSE 8501 + +# Ensure the virtual environment is included in the PATH +ENV PATH="/opt/app-root/src/.venv/bin:$PATH" + +# Start the Streamlit app using streamlit-starter.sh +CMD ["/opt/app-root/src/streamlit-starter.sh"] \ No newline at end of file diff --git a/streamlit-starter.sh b/streamlit-starter.sh new file mode 100644 index 0000000..9283003 --- /dev/null +++ b/streamlit-starter.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -x + +# Check if the config.yaml file exists and copy it +if [ -f /opt/app-root/config/config.yaml ]; then + cp /opt/app-root/config/config.yaml /opt/app-root/src/config.yaml +fi + +# Start the Streamlit app +${POETRY_HOME}/bin/poetry run streamlit run streamlit/intro.py \ No newline at end of file diff --git a/streamlit/openshift/README.md b/streamlit/openshift/README.md new file mode 100644 index 0000000..aaff3d1 --- /dev/null +++ b/streamlit/openshift/README.md @@ -0,0 +1,49 @@ +# OpenShift Deployment Instructions + +To deploy a Streamlit UI on OpenShift, you can follow the instructions below. + +## Pre-Requisites + +- OpenShift CLI (`oc`) installed and configured +- (**Optional**) `kubectl` and `kustomize` installed and configured + +## Deployment Instructions + +### Step 1: Clone the Repository + +```sh +git clone git@github.com:redhat-et/llm-agents.git +cd llm-agents/streamlit +``` + +### Step 2: Deploy the Streamlit app + +Streamlit is a UI application that serves as the front-end interface for interacting with a LLM application. Users can input queries into the webapp, which sends these queries to the agent API server. The agent API server processes the queries, interacts with the underlying tools or models, and sends the responses back to the webapp. The webapp then displays these responses to the user. This deployment step sets up the user interface for interacting with the tool-based agents. + +```sh +oc apply -f openshift/deployment.yaml +``` + +```sh +oc apply -f openshift/service.yaml +``` + +```sh +oc apply -f openshift/route.yaml +``` + +```sh +oc apply -f openshift/configmap.yaml +``` + +### Step 3: Accessing the Streamlit app + +You can now launch the Streamlit app through the route created. To retrieve the route URL to access the app: + +```sh +oc get route streamlit-route -n kubecon-agent-demo +``` + +### Containerfile + +The Containerfiles used to build the deployment images can be found [here](https://github.com/redhat-et/llm-agents/streamlit-Containerfile) \ No newline at end of file diff --git a/streamlit/openshift/configmap.yaml b/streamlit/openshift/configmap.yaml new file mode 100644 index 0000000..63aa773 --- /dev/null +++ b/streamlit/openshift/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: streamlit-config + namespace: react-agent-test +data: + APP_HOST: "react-agent-server.react-agent-test.svc.cluster.local" + APP_PORT: "2113" \ No newline at end of file diff --git a/streamlit/openshift/deployment.yaml b/streamlit/openshift/deployment.yaml new file mode 100644 index 0000000..342d87b --- /dev/null +++ b/streamlit/openshift/deployment.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: streamlit-server + namespace: react-agent-test +spec: + replicas: 1 + selector: + matchLabels: + app: streamlit-server + template: + metadata: + labels: + app: streamlit-server + spec: + containers: + - name: streamlit-server + image: docker.io/hemaveeradhi/streamlit:latest + ports: + - containerPort: 8501 + envFrom: + - configMapRef: + name: streamlit-config \ No newline at end of file diff --git a/streamlit/openshift/route.yaml b/streamlit/openshift/route.yaml new file mode 100644 index 0000000..ba4ada1 --- /dev/null +++ b/streamlit/openshift/route.yaml @@ -0,0 +1,14 @@ +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: streamlit-route + namespace: react-agent-test +spec: + to: + kind: Service + name: streamlit-server + port: + targetPort: streamlit-port + tls: + termination: edge + wildcardPolicy: None \ No newline at end of file diff --git a/streamlit/openshift/service.yaml b/streamlit/openshift/service.yaml new file mode 100644 index 0000000..197b72e --- /dev/null +++ b/streamlit/openshift/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: streamlit-server + namespace: react-agent-test +spec: + selector: + app: streamlit-server + ports: + - name: streamlit-port + protocol: TCP + port: 8501 + targetPort: 8501 \ No newline at end of file