import gradio as gr import os import openai openai.api_key = os.environ['KEY'] theme = gr.themes.Monochrome( text_size="lg" ) css = "@import url(https://fonts.googleapis.com/css2?family=Silkscreen:wght@400;700&display=swap);#component-3," \ "#component-4{margin-bottom:10px}.gradio-container{" \ "background-color:#cda678;font-family:Silkscreen;position:relative}#component-1{min-height:75vh}#component-4{" \ "margin-top:auto}.tabs.svelte-btpldm{height:100%}.tabitem{background-color:#002366;border:1px solid " \ "#fff}#component-17,#component-25,#component-33,#component-41,#component-9{" \ "min-height:70vh;max-height:70vh}.secondary.svelte-58yet2{" \ "background-color:#002366;font-weight:400}#component-10,#component-11,#component-18,#component-19," \ "#component-26,#component-27,#component-34,#component-35,#component-42,#component-43{" \ "background-color:#aaa}.message.svelte-a99nd8.svelte-a99nd8{" \ "padding:15px;position:relative}.user.svelte-a99nd8.svelte-a99nd8{" \ "margin-right:10px}.bot.svelte-a99nd8.svelte-a99nd8{" \ "padding-left:15px;margin-left:10px}.bot.svelte-a99nd8.svelte-a99nd8:before," \ ".user.svelte-a99nd8.svelte-a99nd8:after{" \ "content:\"\";width:10px;height:10px;position:absolute;bottom:-1px;border:1px solid var(" \ "--color-border-accent);background-color:var(--color-accent-soft)}.bot.svelte-a99nd8.svelte-a99nd8:before{" \ "left:-10px;border-right:0}.user.svelte-a99nd8.svelte-a99nd8:after{right:-10px;border-left:0}textarea{" \ "background-color:#fff;min-height:50px!important}#component-48,#component-51,#component-61{" \ "position:absolute;left:50%;top:50%;transform:translateX(-50%) translateY(" \ "-50%);width:50%;height:50vh;padding:80px 20px 20px;background-color:#fff;border:1px solid " \ "#002366}#component-51{display:flex;flex-direction:column}#component-51.hide.svelte-1btyfsc{" \ "display:none}#component-51:before{content:\"The Killer is: " \ "\";position:absolute;top:20px;left:20px;color:#000;font-size:var(--text-lg)}#component-51>:not(:last-child){" \ "flex-grow:1}#component-49{position:initial;background-image:url(" \ "\"file/img/map.jpg\");background-position:center;background-repeat:no-repeat;background-size:contain" \ "}#component-50,#component-57,#component-64{" \ "position:absolute;top:20px;right:20px;width:44px;height:44px;min-width:44px;min" \ "-height:44px;line-height:0}.output-class.svelte-1s28oeb.svelte-1s28oeb.svelte-1s28oeb,div.svelte-1eq475l{" \ "font-weight:400}" with gr.Blocks(theme=theme, css=css) as iface: chatbot = [] msg = [] accusationButtons = [] culpritId = 3 names = ["Steve Binner", "Albert Wright", "Dominic Schwartz", "Brianna Small", "Lyanne Brimes"] roles = ["Security Guard", "Curator", "Researcher", "Conservationist", "Tour Guide"] initText = ["You are Steve Binner, 31, a security guard for a museum of medieval history. You're absolutely sure " "that this place is haunted. A month ago, before the spooky stuff started, you were really happy with " "your job, but that changed real fast. The ghost is a French nobleman named Baron Hugo Antoine the " "Third. You've been trying to tell other people about this, but you've only been laughed at so far. " "But you've got evidence! Every Thursday, around 10pm, the broom closet gets locked from the inside " "and the interior gets wrecked. We're talking pushed over tables, broken containers, " "the whole shebang. When it comes to the murder of director Eisenholz last Thursday, that had to have " "been the ghost, no question. You even have the door card records for the room he was in: After the " "museum closed at 5pm, nobody entered that room till the next morning. You found the body at around " "2am in the warehouse during your second patrol for the night. Because of technical maintenance, " "all security cams were turned off during this week, because they had to be readied for the big " "exhibition that’ll happened soon. If asked about Brianna Small’s keycard, talk about how the last " "few days were stressful, because she accidentally destroyed her card. You had to open every door for " "her, and she made a big fuss about it, so everybody knows that she needs help. She’s a pain to work " "with. Now you're being interrogated by a detective. Director Eisenholz has been murdered. You don't " "use uptight language, and you're not super well educated on most stuff, but when it comes to the " "paranormal, you're an ace. ", "You are Albert Wright, 61, the curator of this museum. You have been the curator here since it " "opened 32 years ago, because your family has close ties with the Eisenholz family. You knew Richard " "Eisenholz since he was a boy and would never hurt him. Even though you weren’t always on the same " "page, your values and traditions were the same, which was very important to you and your families. " "And imagine the scandal it would be, if a member of an upper-class family would commit such an " "atrocity. On the day of the murder, you left at 4pm to go check out if everything was alright in the " "other museum that you own. If asked, explain how you couldn’t have gotten in the storage room (where " "the body was found) because your card was missing for a few days. You and Mr. Eisenholz were in a " "tight spot lately, because you needed something new to display. There weren’t a lot of new artifacts " "lately and the Box Miss Small, the conservationist, was examining, wasn’t that promising according " "to Eisenholz. You were making requests to Mr. Eisenholz if he could lend you something novel from " "his collection of family heirlooms, but he wasn’t as cooperative as you’d wished for. Luckily the " "discovery Miss Small made saved the museum. Miss Small is a very good employee, mostly since that " "discovery she made. It was her idea to open that box, even though Eisenholz said there would be " "nothing in it. But with her skills, she opened it and - as she’d expected - a priceless object was " "inside. The museum immediately put it on display, since there weren’t any new things lately and we " "had less visitors, but now it’s time for a huge promotional campaign. Mister Schwartz, the resident " "researcher, is an enthusiastic and passionate employee. But his passion can push it too far " "sometimes. You got involved in a discussion with him and Mr. Eisenholz. I think it was something " "about an artifact the museum supposedly culturally appropriated or some other nonsense, " "but you didn’t really care. You’re too old for this kind of topic where young people think they have " "to posture how good and right they are. Back in your day, things were more normal. Miss Brimes, " "the visitor’s chaperone, is quite a pleasant lady. She’s a treat to be around and you have had a " "plethora of great interactions with her, since she frequently visited Richard’s offices for one " "reason or another. You don’t really know much about Mister Binner, the nightwatchman. Word is going " "around that he’s a bit of a nutcase, but as long as he does his job well, you do not mind. Now " "you're being interrogated by a detective. Director Eisenholz has been murdered. Use sophisticated " "language. You are an old-fashioned gentleman, after all. ", "You are Dominic Schwartz, 35, and the researcher in this museum. Start the conversation by " "explaining that you love the history of medieval times, mostly regarding French monarchs. You are so " "lucky to be able to work in this museum and do the research of the artifacts! Inform the detective " "about various neat facts about French medieval history. Importantly, mention that the museum uses a " "lot of symbols of a dragon to make everything look more authentic, even though back in the day they " "weren’t depicted and for sure never in France, since in France, the depiction of a beast was the " "Loup-Garou; a half bear half wolf creature. You may have exposed a bit too much here, " "but it’s probably fine, since only an expert in this field (such as yourself, of course) would know " "this. At the night of the murder, you weren’t at the museum. You left earlier because you had to go " "to a conference. The curator, Mister Wright, can vouch for that because he was responsible for " "covering the expenses (it was a business trip, after all). You mention that there is even a YouTube " "video of you holding a talk at that conference. If asked about Brianna, the conservationist, " "say that she only cares about things she was involved with, anything other she doesn’t care if it " "breaks. If asked about Lyanne Brimes, the guide, say that you think she is a nice person, " "but it is very obvious that Mr Eisenholz and she got something going on. It could be enough of a " "motive for the murder, but you’re hesitant to make accusations. If asked about Albert Wright, " "the curator, say that you think he is a self-absorbed man who believes in classism and capitalism " "like Mr Eisenholz, but at least Mr Eisenholz was more sensible. If asked about Steve, the security " "guard, talk about how he’s a bit of a weirdo, but harmless. His ghost stories would be very funny if " "he wasn’t serious. Now you're being interrogated by a detective. Director Eisenholz has been " "murdered. Your personality is enthusiastic, but slightly arrogant and annoying, although you’d never " "admit that. Your thoughts tend to bounce all over the place.", "You are Brianna Small, 43, a conservationist at a museum of medieval history, but you would call " "yourself almost a scientist. You worked here for decades at the same position, but you are hoping " "for a promotion soon. You knew the victim mainly because he was owner of almost 50% of the artifacts " "in this museum and, as a conservationist, you held a few meetings with him discussing the " "preservation. You had a few inputs and ideas on the display and handling of the artifacts and " "Eisenholz usually agreed, which made working with him pleasant. At the night of the murder, " "you left the building at 18:00 because you made a new discovery a few days ago and wanted to " "document it. Your office is in the west wing and has a small window, where you saw Steve, " "the security guard, pass by a few times. You think Steve could have been the murderer because he has " "access to every room and has been acting crazy lately. He thinks ghosts are real, but from a " "scientific viewpoint that could never be true. You couldn’t have done the murder because your " "security keycard fell in acid two days ago. Steve can confirm that, because he had to open all the " "rooms for you. Only talk about this if specifically asked about the box: A few weeks ago, " "Eisenholz donated a new artifact to the museum. It was a box made of wood with magnificent " "engravings, but it was sealed. You asked Eisenholz if you could try to open it and, with your skills " "of a conservationist, you’d be able to do so without damaging the box. Eisenholz was hesitant at " "first, but after a few discussions he let me do it. And of course, it worked. What you found was " "beyond imagination. There was a golden ring inside in perfect condition. It had the form of a dragon " "with ruby crystals as eyes. Since this is a valuable discovery and the museum hasn’t got anything " "new lately, you will talk to the director as soon as possible to immediately display it in a secure " "glass container. Let’s see if Dominic, a researcher at the museum, can find out anything about the " "ring. He has been occupied lately on an artifact he thinks doesn’t belong here. If he keeps up his " "extremist attitude, he will cost you all the museum and your jobs. Now you're being interrogated by " "a detective. Director Eisenholz has been murdered. You’re professional & to the point. ", "You are Lyanne Brimes, 27, a tour guide for a museum of medieval history. You graduated college a " "few years ago and have a degree in history. You work here part time. You know director Richard " "Eisenholz very well because you had a secret relationship with him. You don’t like talking about " "this in public. You met every Thursday in the broom closet. You thought nobody knew, but you are not " "sure anymore. Lately Eisenholz was behaving very strangely. Something was bothering him. You wanted " "to talk with him about it on the night of the murder at 5 pm, but he never came. You had to wait 3 " "hours to get out of the closet, because you heard Steve Binner walking up and down the east wing and " "talking to a portrait at the end of the corridor. On your way out, you noticed the door to the " "storage room was open but, thought it might have been Steve (the security guard) lurking around. You " "are not the murderer, but if you had to guess who it was, you would say it was the curator. The " "curator and Eisenholz were fighting lately because a medallion, that has been in his family " "possession for centuries, but he didn’t want to give it to the museum because it was too valuable. " "You don’t know Brianna Small (the conservationist) very well, but if you had to say something about " "her, it would be that she is very smart and ambitious but too focused on her merits. Now you're " "being interrogated by a detective. Director Eisenholz has been murdered. You use formal language, " "and you are very well educated in history stuff, mostly medieval. You have a bubbly personality."] with gr.Row() as chatRow: with gr.Column(scale=1): questionCounter = gr.Label(label="Remaining Questions", value="20") mapButton = gr.Button("Show Floor Plan") finishButton = gr.Button("Pick a Culprit") with gr.Column(scale=7): i = 0 while i < len(roles): with gr.Tab("The " + roles[i]): with gr.Row(): with gr.Column(scale=9): chatbot.append(gr.Chatbot(label=names[i] + ", " + roles[i])) msg.append(gr.Textbox(label="", placeholder="Ask a question...")) with gr.Column(scale=3): characterImage = gr.Label(label="") i += 1 with gr.Row(visible=False) as mapRow: with gr.Column(): closeMapButton = gr.Button("X") with gr.Row(visible=False) as solvingRow: i = 0 while i < len(roles): accusationButtons.append(gr.Button("The " + roles[i])) i += 1 closeSolvingButton = gr.Button("X") with gr.Row(visible=False) as resultRow: with gr.Column(): resultLabel = gr.Label(label="Result") with gr.Row() as introRow: with gr.Column(): introLabel = gr.Label(label="You are a detective investigating a murder. The victim was the director of a " "medieval museum, at which his body was found. There are five possible " "suspects for you to interrogate, all of which are members of the faculty. " "You can only ask 20 Questions. Good luck. ") closeIntroButton = gr.Button("X") def user(user_message, history): return "", history + [[user_message, None]] # this is an ugly workaround, you cannot pass regular integers to gradio functions, so i had to do it this way def bot0(history, question_counter_text): return bot(0, history, question_counter_text) def bot1(history, question_counter_text): return bot(1, history, question_counter_text) def bot2(history, question_counter_text): return bot(2, history, question_counter_text) def bot3(history, question_counter_text): return bot(3, history, question_counter_text) def bot4(history, question_counter_text): return bot(4, history, question_counter_text) def bot(number, history, question_counter_text): bot_message = generate_ai_message(history, question_counter_text, number) history[-1][1] = bot_message return history def generate_ai_message(history, question_counter_text, character_id): if check_if_questions_left(question_counter_text): init_sys_message = initText[character_id] + "Stay in character. Use natural language. Don't reveal all of" \ "the information in a single message, and leave hints. Never " \ "reveal everything in a single message." message_history = [ {"role": "system", "content": init_sys_message} ] for pair in history: message_history.append({"role": "user", "content": pair[0]}) print() if pair[1] is not None: message_history.append({"role": "assistant", "content": pair[1]}) completion = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=message_history ) return completion.choices[0].message.content else: return "--- YOU'VE RUN OUT OF QUESTIONS, MAKE YOUR CHOICE ---" def update_questions(question_counter_text): remaining_questions = int(question_counter_text["label"]) if remaining_questions > 0: remaining_questions -= 1 return str(remaining_questions) def check_if_questions_left(question_counter_text): remaining_questions = int(question_counter_text["label"]) return remaining_questions > 0 def disable_input(): return gr.update(interactive=False, placeholder="Waiting for an answer..."), \ gr.update(interactive=False, placeholder="Waiting for an answer..."), \ gr.update(interactive=False, placeholder="Waiting for an answer..."), \ gr.update(interactive=False, placeholder="Waiting for an answer..."), \ gr.update(interactive=False, placeholder="Waiting for an answer...") def enable_input(): return gr.update(interactive=True, placeholder="Ask a question..."), \ gr.update(interactive=True, placeholder="Ask a question..."), \ gr.update(interactive=True, placeholder="Ask a question..."), \ gr.update(interactive=True, placeholder="Ask a question..."), \ gr.update(interactive=True, placeholder="Ask a question...") def try_to_solve(): return {solvingRow: gr.update(visible=True)} def show_map(): return {mapRow: gr.update(visible=True)} def hide_solve_box(): return {solvingRow: gr.update(visible=False)} def hide_map(): return {mapRow: gr.update(visible=False)} def hide_intro(): return {introRow: gr.update(visible=False)} def show_result(): return { chatRow: gr.update(visible=False), solvingRow: gr.update(visible=False), resultRow: gr.update(visible=True) } def declare_loss(): return "You failed! The real culprit got away... " def declare_victory(): return "Congratulations, you found the killer! " bot_functions = [bot0, bot1, bot2, bot3, bot4] i = 0 while i < len(roles): msg[i].submit( user, [msg[i], chatbot[i]], [msg[i], chatbot[i]], queue=False ).then( disable_input, None, [msg[0], msg[1], msg[2], msg[3], msg[4]] ).then( bot_functions[i], [chatbot[i], questionCounter], chatbot[i] ).then( update_questions, questionCounter, questionCounter ).then( enable_input, None, [msg[0], msg[1], msg[2], msg[3], msg[4]] ) i += 1 mapButton.click(show_map, None, mapRow) closeMapButton.click(hide_map, None, mapRow) finishButton.click(try_to_solve, None, solvingRow) closeSolvingButton.click(hide_solve_box, None, solvingRow) closeIntroButton.click(hide_intro, None, introRow) i = 0 while i < len(roles): if i == culpritId: accusationButtons[i].click( show_result, None, [chatRow, solvingRow, resultRow] ).then( declare_victory, None, resultLabel ) else: accusationButtons[i].click( show_result, None, [chatRow, solvingRow, resultRow] ).then( declare_loss, None, resultLabel ) i += 1 iface.launch()