File size: 9,539 Bytes
901c6a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6ec8fe5
 
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
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

logging.basicConfig(stream=sys.stdout, level=logging.INFO)

# --- 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-4', 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, model="gpt-4")
    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?"


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. Your feedback shapes the conversation, making it an educational journey 
        tailored just for you. So why settle for small talk? Dive into Soratiq today and elevate your discourse!
    """
    st.caption(description)
    st.divider()

    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)
    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: return sources used by the agent - explicitly publish them in the dialog