# ========================== # Imports # ========================== import os, sys, random import gradio as gr from langchain_openai import ChatOpenAI from langchain.schema import AIMessage, HumanMessage from langchain.prompts import ChatPromptTemplate from langchain.schema.output_parser import StrOutputParser from langchain_core.runnables import RunnableLambda from langchain_core.messages import AIMessage, AIMessageChunk, HumanMessage, HumanMessageChunk from game_info import * # ========================== # Constants / Globals # ========================== API_KEY = os.environ["API_KEY"] # API Key for the LLM - Using NVIDIA's NIM to access an 8 Billion parameter Llama3 model CHATBOT_HEIGHT = 500 # Height of Gradio Chatbot IMAGE_HEIGHT = 500 IMAGE_WIDTH = 500 LINES_PER_BOX = 12 # Number of text lines in a gradio text box SUSPECTS_PER_PAGE = 8 # Number of suspects shown per page game = cMystery() # New mystery game imported from game_info # ========================== # LLM Functions # ========================== # Using NVIDIA's NIM (NVIDIA Inference Microservices) to access an 8 Billion parameter Llama3 model #llm = ChatOpenAI(model = "meta/llama3-8b-instruct",temperature=0.5,max_tokens=1024,timeout=None,max_retries=2, #base_url = "https://integrate.api.nvidia.com/v1", #api_key = NVIDIA_API_KEY) llm = ChatOpenAI(model = "gpt-4o",temperature=1.0,max_tokens=1024,timeout=None,max_retries=2,api_key=API_KEY) # ================================ # Custom LangChain Tools and Pipes # ================================ def output_parser(ai_message: AIMessage) -> str: global game current_suspect = game.get_current_suspect() game.add_note_to_recap(ai_message.content, current_suspect) # Save a record of the conversation with the suspect name in the recap return ai_message.content # ========================== # UI Functions # ========================== # ============================================ # Feed the chat message to the LLM # ============================================ def predict(message, history): global game global llm current_suspect = game.get_current_suspect() if current_suspect is None: return "Please select a suspect." AI_Instructions = f"You are a suspect in an art theft." + \ f"Your name is {game.get_suspect_proper_name(current_suspect)} " + \ f"and your background is {game.get_suspect_background(current_suspect)}. " + \ f"Your motive for stealing the painting is {game.get_suspect_motive(current_suspect)}. " + \ f"If you stole the painting do not admit it. " + \ f"Base all conversations on the following JSON text:{game.create_interview(current_suspect)}" prompt = ChatPromptTemplate.from_messages([ ("system", f"{AI_Instructions}"), ("human", f"{message}"), ]) history_langchain_format = [] history_langchain_format.append(AIMessage(content=AI_Instructions)) history_langchain_format.append(HumanMessage(content=message)) gpt_response = llm(history_langchain_format) output_parser(gpt_response) response = gpt_response.content # LCEL configuration for future game expansion / options # lcel_chain = prompt | llm | output_parser # response = lcel_chain.invoke(message) return response # ========================== # Generate recap of game # ========================== def get_recap(): global game recap = "" recap += "💡 Hints 💡 \n\n" + game.get_hints_recap() + "\n" # Add revealed hints recap += "🕵 Suspect Interview Notes 🕵 \n\n" # Add notes from interviews recap += game.get_crime_recap() return gr.Textbox(lines=LINES_PER_BOX, label="Recap of the Mystery", value=recap) # ========================== # Question suspect # ========================== def question_suspect(image, name): global game game.set_current_suspect(name) current_suspect = game.get_current_suspect() return gr.Textbox(lines = LINES_PER_BOX, label = "About the Suspect", value = game.get_suspect_profile(current_suspect)) # ========================== # Generate game hint # ========================== def get_hint(): global game return gr.Textbox(lines = LINES_PER_BOX, label = "Hint About the Mystery", value = game.give_hint()) # =============================== # Reset game for a new mystery # =============================== def new_crime(): global game game = cMystery() game.print_game() return [gr.Image (value = game.get_stolen_painting(), height=IMAGE_HEIGHT, width=IMAGE_WIDTH, label = game.get_stolen_painting_name(), interactive=False), gr.Textbox(lines = LINES_PER_BOX, label="A Crime has been Committed", value = game.get_crime_text()), gr.Image (value = "Question_Mark.jpg", height=IMAGE_HEIGHT, width=IMAGE_WIDTH, label = "Suspect", interactive=False), gr.Textbox(lines = LINES_PER_BOX, label="About the Suspect", value = ""),] # ============================================ # Arrest attempt - Are you correct ? # ============================================ def arrest(): global game current_suspect = game.get_current_suspect() guilty_suspect = game.get_guilty_suspect_name() if current_suspect is None: arrest_results = "Please select a suspect on the right to arrest." elif current_suspect == guilty_suspect: arrest_results = "👍👍 Congratulations 👍👍 \n\nYou caught " + guilty_suspect + " and recovered the painting.\n\n" + \ guilty_suspect + " confessed why: \n\n'" + game.get_suspect_motive(guilty_suspect) + "'" + \ "and said 'I would have gotten away with it too if it weren't for you meddling detectives'" else: arrest_results = "❌❌❌ You Failed Detective ❌❌❌\n\n" + guilty_suspect + " stole the painting and snuck away while you were arresting the wrong person" return gr.Textbox(lines = LINES_PER_BOX, label = "Results of Your Arrest", value = arrest_results) # ================================ # Main Code - Draw Gradio UI # ================================ game.print_game() # DEBUG - Print out the game settings with gr.Blocks() as demo: image_manor = gr.Image(value = "mystery_manor.jpg") #, width=2400, interactive=False) with gr.Row(): with gr.Column(): painting = gr.Image (value = game.get_stolen_painting(), height=IMAGE_HEIGHT, width=IMAGE_WIDTH, label = game.get_stolen_painting_name(), interactive=False) crime_desc = gr.Textbox(lines = LINES_PER_BOX, label="A Crime has been Committed", value = game.get_crime_text() ) with gr.Row(): btn_new_crime = gr.Button("⚡ New Crime ⚡") btn_recap = gr.Button("💡 Recap 💡") with gr.Column(): suspect_image = gr.Image (value = "Question_Mark.jpg", height=IMAGE_HEIGHT, width=IMAGE_WIDTH, label="Suspect", interactive=False) suspect_desc = gr.Textbox(lines = LINES_PER_BOX, label = "About the Suspect", value="") with gr.Row(): btn_arrest= gr.Button("👮 Arrest 👮") btn_hint = gr.Button("🕵 Clue 🕵") with gr.Column(): suspect_list = gr.Examples(examples = game.get_suspect_images(), inputs = [suspect_image,suspect_desc], outputs=[suspect_desc], examples_per_page=SUSPECTS_PER_PAGE, fn=question_suspect, run_on_click=True, label="Suspect List", cache_examples=False) with gr.Column(): QnA = gr.ChatInterface(fn=predict, examples=game.get_sample_questions(), title="❓"+" Question the Suspect "+"❓", fill_height=False, retry_btn=None, undo_btn=None, cache_examples=False) btn_new_crime.click(new_crime, inputs=None, outputs=[painting, crime_desc, suspect_image, suspect_desc]) # Resets game to new one btn_recap.click (get_recap, inputs=None, outputs=[crime_desc]) # Prints out summary of notes btn_arrest.click (arrest, inputs=None, outputs=[suspect_desc]) # Arrest selected subject btn_hint.click (get_hint, inputs=None, outputs=[suspect_desc]) # Ask for hint/clue if __name__ == "__main__": demo.launch()