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