How to create an Application with Code Engine and WatsonX - Exam Solutions with AI
In today’s fast-paced world, leveraging the power of artificial intelligence to assist with exam solutions is no longer a distant dream. In this blog post, we will introduce you to an innovative web application called question-answer, which utilizes cutting-edge AI technologies to transform the way students tackle tests and quizzes.
In this tutorial, we will demonstrate how to develop a web application that uses artificial intelligence to solve questionnaires. The application will be able to read and analyze exam questions, then generate answers using AI.
Our web application will utilize a Language Model (LLM) from IBM WatsonX to process and answer questions from a text document. This can be particularly useful in situations where you are taking a test and do not have enough time to search for answers online. Simply create a document containing the questions, upload it to the application, and let the generative AI provide the answers.
We will begin by setting up our development environment locally, followed by building a container to deploy the application on the IBM Cloud.
Step 1: Setting up the Environment
To begin with, we need to set up our development environment. Make sure you have the following components installed:
- Python
- Streamlit
- IBM Cloud CLI
- IBM Cloud Object Storage (COS) service provisioned
Develop Environment
Our application is built on IBM Code Engine, a powerful platform that simplifies the deployment and management of containerized applications. With the help of WatsonX.ai, we will incorporate a Language Model (LLM) to analyze and generate answers to exam questions. This generative AI technology enables the application to provide accurate and reliable solutions, making it an invaluable tool for students in need of assistance during their exams.
Let us first create our working directory
mkdir Code-Engine-Application
then we clear it prompt Code-Engine-Application$G
or you can revert it by typing prompt
. In particular I like the green color.
prompt $E[32mCode-Engine-Application$E[37m$G
For the development section of this application, we are going to use Python 3.10.11. Visit the Python 3.10.11 download page and download the appropriate installer for your system (Windows installer, 32-bit or 64-bit). Run the installer. In the installation window, check the box for “Add Python 3.10 to PATH” and click on “Install Now.” After the installation is complete, open a new Command Prompt or PowerShell window and verify the installation by running the following command:
python --version
Then, create a virtual environment by running the following command:
python -m venv .venv
You will notice a new directory in your current working directory with the same name as your virtual environment. Activate the virtual environment:
.venv\Scripts\activate.bat
Upgrade pip:
python -m pip install --upgrade pip
then we install the necessary packages
pip install -r requirements.txt
Step 2: Writing the Streamlit App (app.py)
Now we will walk through the process of creating a web application using Streamlit that allows users to upload a document text file. The uploaded file will be stored in an IBM Cloud Object Storage (COS) bucket, and then the text will be sent to IBM WatsonX for processing. The result from WatsonX will be displayed back to the user. Let’s dive into the steps involved in creating this web app.
First, let’s create a file called app.py
and import the necessary libraries and modules. Here’s an overview of the code:
# Import required libraries
import streamlit as st
import ibm_boto3
from ibm_botocore.client import Config, ClientError
from io import BytesIO
import os
from dotenv import load_dotenv
from cloudfunction.watsonx import main as watsonx
# Load environment variables
load_dotenv()
credentials = {
"COS_ENDPOINT": os.environ.get("COS_ENDPOINT", None),
"COS_API_KEY_ID": os.environ.get("COS_API_KEY_ID", None),
"COS_INSTANCE_CRN": os.environ.get("COS_INSTANCE_CRN", None),
"COS_BUCKET": os.environ.get("COS_BUCKET", None),
}
# Create IBM COS client
cos = ibm_boto3.client(
"s3",
ibm_api_key_id=credentials['COS_API_KEY_ID'],
ibm_service_instance_id=credentials['COS_INSTANCE_CRN'],
config=Config(signature_version="oauth"),
endpoint_url=credentials['COS_ENDPOINT']
)
# Function to upload a file to the specified IBM COS bucket
def FileUpload(bucket_name, item_name, file_obj):
print("Creating new item: {0}".format(item_name))
try:
cos.upload_fileobj(
Fileobj=file_obj,
Bucket=bucket_name,
Key=item_name
)
print("Item: {0} created!".format(item_name))
return True
except ClientError as be:
print("CLIENT ERROR: {0}\n".format(be))
return False
except Exception as e:
print("Unable to create text file: {0}".format(e))
return False
# Main function for the Streamlit app
def main():
# Set page configuration
st.set_page_config(
page_title="Questions Solver with IBM WatsonX",
page_icon=":question:",
layout="centered",
initial_sidebar_state="expanded",
)
# Add custom CSS
st.markdown(
"""
<style>
.menu {
background-color: black;
padding: 10px 20px;
border-radius: 5px;
}
.menu h1 {
color: white;
margin: 0;
padding: 0;
font-size: 24px;
font-weight: bold;
}
.title {
font-size: 36px;
font-weight: bold;
color: #0f62fe;
}
.response-container {
padding: 10px;
background-color: white;
border-radius: 5px;
}
body {
background-color: #f0f0f0;
}
</style>
""",
unsafe_allow_html=True,
)
# Display title and menu
st.markdown('<div class="menu"><h1>Questions Solver with IBM WatsonX</h1></div>', unsafe_allow_html=True)
# File uploader
uploaded_file = st.file_uploader("Choose a file to upload", type=["txt"], help="Upload a text with max of 20 questions")
if uploaded_file is not None:
file_name = uploaded_file.name
file_contents = uploaded_file.read()
file_obj = BytesIO(file_contents)
# Upload the file and process the response
if FileUpload(credentials['COS_BUCKET'], file_name, file_obj):
st.markdown(f"<div style='color:#0f62fe'><strong>File '{file_name}' uploaded successfully!</strong></div>", unsafe_allow_html=True)
payload = {
"bucket_name": credentials['COS_BUCKET'],
"object_name": file_name
}
print("Payload", payload)
response = watsonx(payload)
st.markdown(f"<div class='response-container'><strong>Response:</strong> <pre>{response['body']}</pre></div>", unsafe_allow_html=True)
else:
st.markdown(f"<div style='color:#da1e28'><strong>Failed to upload file '{file_name}'</strong></div>", unsafe_allow_html=True)
# Run the Streamlit app
if __name__ == "__main__":
main()
The code above sets up the Streamlit app, creates a function to upload the file to the COS bucket, and calls the WatsonX function after successful upload.
Step 3: Creating the Cloud Functions Folder
Now, let’s create a folder called cloudfunctions
. Inside this folder, we will have two files: solver.py
and watsonx.py
.
In solver.py
, we will have the following code:
# Import necessary libraries
import os
from dotenv import load_dotenv
from ibm_watson_machine_learning.metanames import GenTextParamsMetaNames as GenParams
from ibm_watson_machine_learning.foundation_models.utils.enums import ModelTypes
import requests
# Set parameters for text generation
parameters = {
GenParams.DECODING_METHOD: "greedy",
GenParams.MAX_NEW_TOKENS: 1000
}
# Load environment variables
load_dotenv()
project_id = os.getenv("PROJECT_ID", None)
credentials = {
#"url": "https://us-south.ml.cloud.ibm.com",
"url": "https://eu-de.ml.cloud.ibm.com",
"apikey": os.getenv("API_KEY", None)
}
# Function to get the bearer token
def getBearer(apikey):
form = {'apikey': apikey, 'grant_type': "urn:ibm:params:oauth:grant-type:apikey"}
print("About to create bearer")
response = requests.post("https://iam.cloud.ibm.com/oidc/token", data=form)
if response.status_code != 200:
print("Bad response code retrieving token")
raise Exception("Failed to get token, invalid status")
json = response.json()
if not json:
print("Invalid/no JSON retrieving token")
raise Exception("Failed to get token, invalid response")
print("Bearer retrieved")
return json.get("access_token")
# Get and store bearer token
credentials["token"] = getBearer(credentials["apikey"])
# Import Model class
from ibm_watson_machine_learning.foundation_models import Model
# Function to call the AI model for text generation
def call_model(text):
model_id = ModelTypes.LLAMA_2_70B_CHAT
# Initialize the Watsonx foundation model
llm_model = Model(
model_id=model_id,
params=parameters,
credentials=credentials,
project_id=project_id)
print("OK")
# Create the prompt for the model
prompt = f"""<s>[INST] <<SYS>> You have been given a text containing several questions from a test or exam. Your task is to create a document where each question is followed by its answer, provided by you (the AI). Use your knowledge to answer the questions as accurately as possible. For each question, print the question and write the answer below it, starting with "Answer:". The given text is as follows: {text} <</SYS>>[/INST]
"""
# Generate the result from the model
result = llm_model.generate(prompt)['results'][0]['generated_text']
return result
In watsonx.py
, we will have the following code:
import ibm_boto3
from ibm_botocore.client import Config, ClientError
import os
from dotenv import load_dotenv
#from solver import call_model
from cloudfunction.solver import call_model
# Constants for IBM COS values
load_dotenv()
credentials = {
"COS_ENDPOINT": os.environ.get("COS_ENDPOINT", None),
"COS_API_KEY_ID": os.environ.get("COS_API_KEY_ID", None),
"COS_INSTANCE_CRN": os.environ.get("COS_INSTANCE_CRN", None),
"COS_BUCKET": os.environ.get("COS_BUCKET", None),
}
# Create client
cos = ibm_boto3.client(
"s3",
ibm_api_key_id=credentials['COS_API_KEY_ID'],
ibm_service_instance_id=credentials['COS_INSTANCE_CRN'],
config=Config(signature_version="oauth"),
endpoint_url=credentials['COS_ENDPOINT']
)
def GetFile(bucket_name, object_name):
print("Get item: {0}".format(object_name))
try:
response = cos.get_object(
Bucket=bucket_name,
Key=object_name
)
return response["Body"].read()
except ClientError as be:
print("CLIENT ERROR: {0}\n".format(be))
return False
except Exception as e:
print("Unable to create text file: {0}".format(e))
return False
def main(params):
print("Argument of WatsonX:",params)
bucket_name = params.get("bucket_name","questions" )
object_name = params.get("object_name","questions.txt")
print("bucket_name", bucket_name)
print("object_name", object_name)
response = GetFile(bucket_name, object_name)
# Do answering
answers=call_model(str(response))
return {
"headers": {
"Content-Type": "application/json",
},
"statusCode": 200,
"body": answers
}
Step 4: Setting up the Environment Variables
Create a .env
file in the root folder to store the credentials, for example
COS_ENDPOINT='https://s3.us-south.cloud-object-storage.appdomain.cloud'
COS_API_KEY_ID='1234567890abcdef'
COS_INSTANCE_CRN='crn:v1:bluemix:public:cloud-object-storage:global:a/9f2d81f38387467f857ee00f0bf03c36:c5809310-1f23-4bb9-886c-174dfa44a8::'
COS_BUCKET='fake-bucket'
API_KEY='1234567890abcdef'
PROJECT_ID='1234567890abcdef'
we have to copy the previous .env
file and have secrets.env
without strings. Make sure to replace the values with your own IBM Cloud Object Storage and WatsonX credentials.
Once you have created all the files, you can test your program, you can upload this example of questions.txt
streamlit run app.py
Program Tree:
- app.py
- cloudfunctions
- solver.py
- watsonx.py
- .env
- secrets.env
- Dockerfile
Step 5 - Container Installation
-
Install the IBM Cloud CLI. Visit the IBM Cloud CLI installation page .
-
After the installation is complete, open a new Command Prompt window and verify the installation by running the following command:
ibmcloud --version
- Install the Docker CLI. Visit the Docker Desktop for Windows download page and click on “Get Docker.” Run the installer and follow the on-screen prompts to complete the installation . After the installation is complete, open a new Command Prompt or PowerShell window and verify the installation by running the following command:
docker --version
- Log in to your IBM Cloud account where we want to create the container
ibmcloud login -a https://cloud.ibm.com
If you have a federated ID, use ibmcloud login –sso to log in to the IBM Cloud CLI.
ibmcloud login --sso
Here you should choose fist the account where you will have the Container Registry.
- Install the Container Registry plug-in.
ibmcloud plugin install container-registry
- We create the Dockerfile for the app.py,
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
COPY cloudfunction/ cloudfunction/
RUN pip install -r requirements.txt
COPY app.py .
EXPOSE 8501
CMD ["streamlit", "run", "app.py"]
the cloudfunction folder with the files and the requirements.txt files
Then we open our docker destkop.
- Log in to the IBM Cloud Container Registry:
ibmcloud cr login
- Ensure that you’re targeting the correct IBM Cloud Container Registry region.
ibmcloud cr region-set global
Or if you want another region you can use
ibmcloud cr region-set
- We create the namespace
ibmcloud cr namespace-add question-answer-namespace
Step 6. Run Image Locally
To run a Docker image locally, you need to follow these steps:
- Build the Docker image by running this command in the same directory as your Dockerfile
docker build -t ruslanmv/question-answer:latest .
To load environment variables from a secrets.env
file without copying it into the Docker container, you can use the --env-file
option when running the docker run
command. This way, the environment variables will be available in the container, but the secrets.env
file will not be stored inside the container.
First, make sure your secrets.env
file is in the same directory as your Dockerfile. The file should have key-value pairs, one per line, like this:
API_KEY=my_api_key SECRET=my_secret
- After the image is built successfully, run the Docker container using the following command
docker run -p 8501:8501 --env-file secrets.env ruslanmv/question-answer:latest
This command maps the container’s port 8501 to your local machine’s port 8501.
- Once the container is running, open a web browser and go to http://localhost:8501 to access your app.
If there are any issues in the Dockerfile or the app itself, make sure to check the error messages and fix them accordingly.
Step 7 - Upload Image on IBM Container Registry
- Tag an image to point to the registry. Choose a local image, target namespace, and target repository and an optional tag
docker tag ruslanmv/question-answer:latest icr.io/question-answer-namespace/question-answer:latest
- Push the tagged image to IBM Cloud Container Registry. If you want store the Container in the IBM Cloud you can type:
docker push icr.io/question-answer-namespace/question-answer:latest
If we have different accounts one where we have the container and another with the Code Engine, we requiere to create an API Key for the Container Account
ibmcloud iam api-key-create cliapikey -d "My CLI API key" --file key_file
Docker Container Store
If you want to store also the container outside IBM cloud, for example in Docker site. You can push to Docker by typing the command
docker push ruslanmv/question-answer:latest
Then if you are in an another computer you can pull
docker pull ruslanmv/question-answer:latest
Step 8 - Connect bucket to Code Engine Application
- Login to Code Engine First we need to login to our account that has the Code Engine enabeled.
ibmcloud login -sso
- Install the IBM Cloud Code Engine plugin:
ibmcloud plugin install code-engine
- Choose the resource group targeted. Run ‘ibmcloud target -g RESOURCE_GROUP_NAME’, for example
ibmcloud target -g code-engine-667000nwl8-qlz0g5vj
- Create a Code Engine project
ibmcloud ce project create --name question-answer-project
- Set the current project context
ibmcloud ce project select -n question-answer-project
- We have to create the Registry Container
For IBM Cloud Container
If you want to to load your container from the IBM Cloud you can type the following command
ibmcloud ce secret create --format registry --name question-answer-registry --server icr.io --username iamapikey --password YOUR_API_KEY
In the console you should have something like
with
For Docker Container
If you want to load your Docker Container from Docker site, we setup the Docker credentials in the Code Engine
ibmcloud ce secret create --format registry --name my-docker-registry --server https://index.docker.io/v1/ --username YOUR_USERNAME --password YOUR_PASSWORD
Step 9 - Secrets and configmaps
It is possible to use the ‘Secret’ object in Code Engine to store sensitive information for connection in a secure way.
ibmcloud code-engine secret create --name bucket-secret --from-env-file secrets.env
In the project’s dashboard navigate to ‘Secret and Configmap’ and create a Secret you will have something like
You can see the secret This will be store in a key:value format.
After that it is possible to create the application and, in the environment variable section, connect the application to the secret. The application can now access the data with the standard API for environment variables.
- Create the Code Engine application using the image from IBM Cloud Container Registry. To create an application in IBM Cloud Code Engine with the previously created secrets
Step 10 - Create application from IBM Cloud Container
To create an application in IBM Cloud Code Engine using an image from IBM Cloud Container Registry and the previously created secrets, you can modify the command as follows:
ibmcloud code-engine application create --name question-answer --image icr.io/question-answer-namespace/question-answer:latest --port 8501 --env-from-secret bucket-secret --registry-secret question-answer-registry
In this command:
-
question-answer
is the name you want to give to your application. icr.io/question-answer-namespace/question-answer:latest
is the image reference for your application. Thequestion-answer-namespace
is the namespace with the image taglatest
.-
8501
is the port on which your application will listen. -
--env-from-secret bucket-secret
specifies the secret you previously created (bucket-secret
) to be used as environment variables for your application. --registry-secret question-answer-registry
specifies the name of the registry secretquestion-answer-registry
that contains the credentials for accessing the container registry.
By providing the --registry-secret
flag, you are ensuring that the necessary credentials are available to access the container registry where the image is stored.
Create Application from Docker
If you want to create the application form the Docker Container
ibmcloud code-engine application create --name question-answer --image docker.io/ruslanmv/question-answer:latest --port 8501 --env-from-secret bucket-secret --registry-secret my-docker-registry
After you typed the previous command you will get the following response
In my case when you open your browser with the following url
https://question-answer.1a6j02a995w8.us-south.codeengine.appdomain.cloud
The application will loading, after that you can upload a sample questions.txt, then you upload this file then it is processed and you got the following results: You can check this application in the console in IBM Cloud, in the Code Engine within overview with the following configuration
By following the steps outlined above, you can create an AI-Powered Exam Solver web app using Streamlit, IBM Cloud Object Storage, and WatsonX. Users will be able to upload a document text file with questions, and the app will process it using WatsonX, providing a the answers as the result. Feel free to customize and enhance the app based on your specific requirements.
Congratulations! We hope this blog has been helpful in guiding you through the process of creating this web app. Happy coding!
Leave a comment