File size: 10,882 Bytes
901c6a4 6f0fc7c 901c6a4 6f0fc7c 901c6a4 ad18eab 901c6a4 3a120e2 901c6a4 6f0fc7c 901c6a4 6f0fc7c 901c6a4 6f0fc7c 901c6a4 6ec8fe5 3a120e2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
import streamlit as st
from socratic import *
from tool import get_tools
from agent import Agent
from common import get_llm
import sys
import time
import random
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
# --- APPLICATION ---
PAGE_TITLE: str = "Socratiq"
PAGE_ICON: str = "🗨️"
N_ROUND: int = 50
st.set_page_config(page_title=PAGE_TITLE, page_icon=PAGE_ICON)
open_api_key = st.secrets["OPENAI_API_KEY"]
st.session_state.key = open_api_key
def init_session() -> None:
tools = get_tools()
st.session_state.agent = Agent(get_llm(model_name='gpt-4o', model_temperature=0, api_key=st.session_state.key),
tools)
st.session_state.socrates = SocraticGPT(role=SOCRATES, tools=st.session_state.agent._tools,
key=st.session_state.key, n_round=N_ROUND)
st.session_state.theaetetus = SocraticGPT(role=THEAETETUS, tools=st.session_state.agent._tools,
key=st.session_state.key, n_round=N_ROUND)
st.session_state.plato = SocraticGPT(role=PLATO, tools=st.session_state.agent._tools, key=st.session_state.key,
n_round=N_ROUND)
st.session_state.dialog_lead = None
st.session_state.dialog_follower = None
st.session_state.messages = []
st.session_state.question = None
st.session_state.user_input = None
st.session_state.in_progress = False
st.session_state.user_question = None
def get_random_question():
return "What is the size of the Moon?" if random.randint(1,10) % 3 == 0 else "What is love?"
def show_intro_screen():
st.header("Socratiq")
st.image("./assets/intro-trio.jpg")
description = """\
You ask, 'What's the meaning of life?' and our trio of digital philosophers fetch real-time
wisdom faster than you can say 'Immanuel Kant.' Whether you’re curious about science, or even the nuances of
modern art, Soratiq has you covered.
"""
st.caption(description)
if st.session_state.question is None:
question = st.text_input(label='Paste your question. E.g. "What is the size of the moon?"',
label_visibility='collapsed',
placeholder='Paste your question. E.g. "What is the size of the moon?"')
col1, _, _, _, col2 = st.columns(5)
if col1.button(label="Ask Away!", help="Push the button and dive into the dialog around your question..."):
if not len(question) > 0:
st.warning(
"Whoops! That question seems to be doing the vanishing act. Could you give it another shot? Magic words: 'Valid question, Please!' 🪄")
if len(question) > 0:
set_user_question(question)
if col2.button(label="QuestionRoll", help="The button generates a random question for the user to ponder or discuss. This should be a fun and engaging experience, sparking curiosity."):
question = get_random_question()
set_user_question(question)
st.divider()
with st.expander("Who are they? What's going on?"):
st.markdown("""\
Imagine you've just stepped into an intellectual arena that's as engaging as a top-tier debate club and as lively as your favorite coffeehouse. Like the brainstorming session you've always dreamed of.
- First up, **you're the Maestro**. With a simple thumbs-up or down, you steer this conversation, helping to sculpt the dialogue into something meaningful. Your feedback is the compass.
- Next, we have **Socrates and Theaetetus**, your co-pilots in this philosophical flight. They serve up questions designed to delve deep into the core of the matter.
- **Plato's on board too**, adding a layer of logical critique to ensure our dialogue doesn't go off the rails.
- Finally, meet the **Agent**, our whiz at rapid information retrieval. This isn't just Googling; think of it as real-time fact-checking with a dash of AI brilliance, you'd think it had a Ph.D. in Searchology.
Try `QuestionRoll` and get a random question to watch how it goes. [More details](https://princeton-nlp.github.io/SocraticAI/)
""")
else:
if st.session_state.question is not None:
st.subheader(f"*{st.session_state.question}*")
st.divider()
def set_user_question(question):
st.session_state.question = question
st.session_state.socrates.set_question(question)
st.session_state.theaetetus.set_question(question)
st.session_state.plato.set_question(question)
st.experimental_rerun()
if 'question' not in st.session_state:
init_session()
def get_avatar(role):
if role == SOCRATES:
return "./assets/socrates-avatar.jpg"
elif role == THEAETETUS:
return "./assets/theatetus-avatar.png"
elif role == PLATO:
return "./assets/plato-avatar.jpg"
elif role == 'agent':
return "ai"
elif role == 'system':
return "./assets/agent-avatar.jpg"
elif role == 'user':
return "user"
def get_role(role):
if role == 'agent':
return 'ai'
elif role == 'system':
return 'ai'
else:
return 'user'
def show_chat() -> None:
if st.session_state.messages:
for message in st.session_state.messages:
role = message['role']
content = message['content']
with st.chat_message(get_role(role), avatar=get_avatar(role)):
if role == 'system':
st.markdown("*" + content + "*")
else:
st.markdown(content)
def add_message(role, content):
st.session_state.messages.append({"role": role, "content": content})
with st.chat_message(get_role(role), avatar=get_avatar(role)):
st.markdown(content)
def chat_input():
if st.session_state.user_question is not None:
prompt = st.chat_input("Share your wisdom...")
if prompt:
st.session_state.user_input = prompt
elif st.session_state.question is not None:
st.chat_input("...", disabled=True)
def main() -> None:
show_intro_screen()
chat_input()
show_chat()
if st.session_state.question is not None and st.session_state.user_question is None:
if not st.session_state.in_progress:
st.session_state.in_progress = True
st.session_state.dialog_lead, st.session_state.dialog_follower = st.session_state.socrates, st.session_state.theaetetus
# add_message(st.session_state.dialog_lead.role,
# f"""Hi {st.session_state.dialog_follower.role}, let's solve this problem together. Please feel free to correct me if I make any logical or mathematical mistakes.\n""")
else:
with st.spinner(f"{st.session_state.dialog_follower.role} is thinking..."):
rep = st.session_state.dialog_follower.get_response()
add_message(st.session_state.dialog_follower.role, f"{st.session_state.dialog_follower.role}: " + rep)
st.session_state.dialog_lead.update_history(rep)
st.session_state.plato.update_history(f"{st.session_state.dialog_follower.role}: " + rep)
# next round the opponent answers
st.session_state.dialog_lead, st.session_state.dialog_follower = st.session_state.dialog_follower, st.session_state.dialog_lead
answer = SocraticGPT.get_answer(rep)
user_question = SocraticGPT.get_user_question(rep)
agent_question = SocraticGPT.get_agent_question(rep)
if st.session_state.dialog_lead.role == st.session_state.theaetetus.role:
if user_question is None and agent_question is None and answer is None:
with st.spinner(f"thinking critically..."):
pr = st.session_state.plato.get_proofread()
if pr:
add_message(st.session_state.plato.role, f"{st.session_state.plato.role}: " + pr)
st.session_state.socrates.add_proofread(pr)
st.session_state.theaetetus.add_proofread(pr)
user_question = SocraticGPT.get_user_question(pr) # Plato can suggest to use agent or get user feedback
agent_question = SocraticGPT.get_agent_question(pr)
if agent_question:
with st.status(f"Consulting the agent: ''' {agent_question} '''"):
agent_msg = st.session_state.agent.run(agent_question) # TODO: agent status callback or status polling in a loop
st.session_state.socrates.add_agent_feedback(agent_question, agent_msg)
st.session_state.theaetetus.add_agent_feedback(agent_question, agent_msg)
st.session_state.plato.add_agent_feedback(agent_question, agent_msg)
add_message('agent', f"Agent: {agent_msg}")
if user_question:
st.session_state.user_question = user_question
add_message('system', f'User feedback is required. The question: **"{" ".join(user_question)}"**')
st.experimental_rerun()
if answer:
st.session_state.user_question = f"Is that correct answer? - {answer}"
add_message('system', f"""User, are you agree with the answer? - **{" ".join(answer)}**""")
st.experimental_rerun()
if st.session_state.user_input is not None:
user_input = st.session_state.user_input
if st.session_state.user_question is not None:
user_question = st.session_state.user_question
st.session_state.socrates.add_user_feedback(user_question, user_input)
st.session_state.theaetetus.add_user_feedback(user_question, user_input)
st.session_state.plato.add_user_feedback(user_question, user_input)
st.session_state.user_question = None
add_message("user", f"{user_input}")
st.session_state.user_input = None
if st.session_state.question is not None and st.session_state.user_question is None:
time.sleep(1)
st.experimental_rerun()
if __name__ == "__main__":
main()
# TODO: publish/access dialog debug logs, so the user can dig into the details
# TODO: possible answers to the question - like 'double check your answer' or 'make the answer sound like a pirate' etc
# TODO: get rid of autogenerated 'Footnote' header in agent's references
# TODO: handle rate limit reject, e.g. Limit: 10000 / min. Please try again in 6ms. - it's easy to wait for 6 ms. explicitly prompt it or ask Plato
# TODO: streaming response https://github.com/langchain-ai/streamlit-agent/blob/main/streamlit_agent/basic_streaming.py |