Project 2: Interactive Machine Learning Predictions with LLM Integration

Orchestrator including LLM integration and user interface in console (CLI)

The final phase focuses on creating a dynamic and interactive LLM-based front-end for user interaction. Key steps include:

  • Using the console for user interaction.
  • Configuring a meaningful system prompt that will ensure proper data input gathering and JSON creation by the LLM.
  • Providing the LLM the model input definition to ensure it will create the JSON in the correct format.
  • Including proper error handling by the Orchestrator based on the LLM responses.
  • Callling the Model Use Layer once the LLM provides the final JSON in the correct format.
  • Sending predictions and errors to the LLM to generate a meaningful response and provide this response to the user.

import requests
import json

conversation = "This is the initial prompt. Please tell the user your purpose and ask for the information you require." #the LLM will receive this message alongside the instructions to know a new session started.
messageToUser = ""
code = ""
currentUserInput = ""
body = ""

#load features from the JSON template file
def load_features_from_template(file_path):
	with open(file_path, 'r') as file:
		template = json.load(file)
	return template["features"]

#function to interact with OpenAI API
def call_openai_api(conversation, features):
	#endpoint
	uri = "https://api.openai.com/v1/chat/completions"
	
	#variables temp
	currentAiResponse = ""
		
	#OpenAI API key to use the OpenAI endpoint
	api_key = "KEY"
	
	#format the system prompt dynamically with features
	system_prompt = f""" INSERT SYSTEM PROMPT
	
	#request payload
	data = {
		"model": "gpt-4",
		"messages": [
			{
				"role": "system",
				"content": system_prompt
			},
			{
				"role": "user",
				"content": conversation
			}
		],
		"temperature": 0.7,
		"max_tokens": 300,
		"n": 1,
		"stop": None
	}

	#HTTP headers
	headers = {
	"Content-Type": "application/json",
	"Authorization": f"Bearer {api_key}"
	}

	#make api call
	response = requests.post(uri, headers=headers, json=data)
	
	
	#handle response
	if response.status_code == 200:
		#parse the API response
		currentAiResponse = response.json()
		content = currentAiResponse["choices"][0]["message"]["content"]

		#safely parse the content into a dictionary
		parsed_content = json.loads(content)

		#extract the `code` and `message` fields
		code = parsed_content["code"]
		message = parsed_content["message"]
		body = parsed_content["body"]

		return code, message, body
		
	else:
		raise Exception(f"OpenAI API Error: {response.status_code} - {response.text}")
	
#call model
def call_model(body):
	#endpoint
	uri = "http://127.0.0.1:5000/predictions"
	
	try:
		#send the POST request
		response = requests.post(uri, json=body)
		
		#check if the request was successful
		if response.status_code == 200:
			return response.json()
		else:
			raise Exception(f"Model API Error: {response.status_code} - {response.text}")
	
	except requests.RequestException as e:
		raise Exception(f"Error calling model API: {e}")
	
#main function
def main():
	global conversation
	
	features = load_features_from_template("model/feature_template.json")
	
	#keep running until the code is not "pending"
	while True:
		try:
			#call OpenAI API
			code, message, body = call_openai_api(conversation, features)
			
			#check the status code
			if code == "pending":
				#append the assistant's message to the conversation
				conversation += f"\nAssistant: {message}"
				#print(f"CODE VARIABLE FOR DEBUGGING: \n\n{code}\n\n")
				print(f"\n\nAI: {message}\n\n")
				
				#get user input
				currentUserInput = input("Your response: ")
				
				#append user input to the conversation
				conversation += f"\nUserResponse: {currentUserInput}"
			else:
				#print(f"CODE VARIABLE FOR DEBUGGING: \n\n{code}\n\n")
				print(f"\n\nAI: {message}.\n\n")
				
				try:
					#call the model API
					model_response = call_model(body)
					#print(f"MODEL RESPONSE FOR DEBUGGING: \n\n{model_response}\n\n")
					
					#extract the `code` and `message` from the response
					code = model_response.get("code", "unknown")
					
					#if code is `prediction ready`, exit the loop
					if code == "prediction ready":
						conversation += f"\n\nPrediction from the machine learning layer {model_response}. Please provide a final response that can be provided to the user. Don't forget to keep your format as described in the system prompt."
						try:
							code, message, body = call_openai_api(conversation, features)
							print(f"AI: {message}\n\n")
							break
						except Exception as e:
							print(f"\nError calling OpenAI API: {e}")
							#print(f"CONVERSATION VARIABLE FOR DEBUGGING: \n\n{conversation}\n\n")
							break
					else:
						print(f"\nError: {model_response}")
						break
					
				except Exception as e:
					print(f"\nError calling Model API: {e}")
					break

		except Exception as e:
			print(f"\nError calling OpenAI API: {e}")
			break
			
#entry point
if __name__ == "__main__":
	main()
						

Following the system prompt that has been used. Note that the conversation variable always includes this string: "This is the initial prompt. Please tell the user your purpose and ask for the information you require.". The LLM will receive this message alongside the instructions to know a new session started.". The prompt below will be appended so the actual prompt to the LLM includes this string as well.

Image