File size: 7,842 Bytes
ac8a60b
8434471
ac8a60b
 
 
8434471
ac8a60b
8434471
 
 
 
 
ac8a60b
 
 
 
 
 
 
8434471
 
 
 
 
 
 
 
 
 
 
 
 
ac8a60b
8434471
 
 
 
 
 
 
 
 
 
 
ac8a60b
8434471
 
 
 
 
 
 
 
 
 
 
 
 
 
ac8a60b
 
 
8434471
 
 
 
 
ac8a60b
 
 
 
8434471
ac8a60b
 
 
b580d80
8434471
 
 
 
 
b580d80
 
ac8a60b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8434471
 
 
ac8a60b
 
 
 
8434471
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ac8a60b
 
 
8434471
 
 
 
 
 
 
 
 
ac8a60b
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
import sqlite3
import streamlit as st
from pydantic import BaseModel, Field
from llama_index.core.tools import FunctionTool

import time

db_path = "./database/mock_qna.sqlite"
qna_question_description = """
      Use this tool to extract the chapter number from the body of input text, 
      thereafter, chapter number will be used as a filtering criteria for
      extracting the right questions set from database.
      The format of the function argument looks as follow:
        It should be in the format with `Chapter_` as prefix.
        Example 1: `Chapter_1` for first chapter
        Example 2: For chapter 12 of the textbook, you should return `Chapter_12`
        Example 3: `Chapter_5` for fifth chapter
        Thereafter, the chapter_n argument will be passed to the function for Q&A question retrieval.
"""
qna_answer_description = """
      Use this tool to trigger the evaluation of user's provided input with the 
      correct answer of the Q&A question asked. When user provides answer to the
      question asked, they can reply in natural language or giving the alphabet
      symbol of which selected answer they think it's most reasonable.
      The format of the function argument `user_selected_answer` looks as follow:
        It should be in the format with character such as A, B, C and D.
        Example 1: User's answer is `a`, it means choice `A`.
        Example 2: User's answer is contextually closer to 3rd answer choice, it means `C`.
        Example 3: User says last is the answer, it means `D`.
        Thereafter, the `user_selected_answer` argument will be passed to the 
        function for Q&A question evaluation.
"""

class Question_Model(BaseModel):
    chapter_n: str = \
                Field(..., 
                      pattern=r'^Chapter_\d*$',
                      description=(
                          "which chapter to extract, the format of this function argumet"
                          "is with `Chapter_` as prefix concatenated with chapter number"
                          "in integer. For example, `Chapter_2`, `Chapter_10`."
                          "if no chapter number specified or user requested for random question"
                          "or user has no preference over which chapter of textbook to be tested"
                          "return `Chapter_0`"
                          )
                    )
class Answer_Model(BaseModel):
    user_selected_answer: str = \
                          Field(...,
                                pattern=r'^[ABCD]$',
                                description=(
                                    "which answer choice `A`, `B`, `C`, `D`"
                                    "user selected. The return format should be"
                                    "in single character such as A, B, C and D."
                                    "if user's answer is contextually closer to a "
                                    "particular answer choice, return the corresponding"
                                    "alphabet A, B, C or D for the answer "
                                    "is closest."
                                ))

def get_qna_question(chapter_n: str) -> str:
    """
      Use this tool to extract the chapter number from the body of input text, 
      thereafter, chapter number will be used as a filtering criteria for
      extracting the right questions set from database.
      The format of the function argument looks as follow:
        It should be in the format with `Chapter_` as prefix.
        Example 1: `Chapter_1` for first chapter
        Example 2: For chapter 12 of the textbook, you should return `Chapter_12`
        Example 3: `Chapter_5` for fifth chapter
        Thereafter, the chapter_n argument will be passed to the function for Q&A question retrieval.
        Once the question is retrieved from database, be reminded to ask user the question.
    """
    con = sqlite3.connect(db_path)
    cur = con.cursor()
    

    filter_clause = "WHERE a.id IS NULL" if chapter_n == "Chapter_0" else f"WHERE a.id IS NULL AND chapter='{chapter_n}'"
    sql_string = """SELECT q.id, question, option_1, option_2, option_3, option_4, q.correct_answer
                    FROM qna_tbl q LEFT JOIN answer_tbl a
                                   ON q.id = a.id
                 """ + filter_clause
    
    res = cur.execute(sql_string)
    result = res.fetchone()

    id       = result[0]
    question = result[1]
    option_1 = result[2]
    option_2 = result[3]
    option_3 = result[4]
    option_4 = result[5]
    c_answer = result[6]

    qna_str  = "Question: \n" + \
               "========= \n" + \
                question.replace("\\n", "\n") + "\n" + \
               "A) " + option_1 + "\n" + \
               "B) " + option_2 + "\n" + \
               "C) " + option_3 + "\n" + \
               "D) " + option_4
    
    st.session_state.question_id = id
    st.session_state.qna_answer = c_answer
    
    con.close()
    
    return qna_str

def evaluate_qna_answer(user_selected_answer: str) -> str:
    """
      Use this tool to trigger the evaluation of user's provided input with the 
      correct answer of the Q&A question asked. When user provides answer to the
      question asked, they can reply in natural language or giving the alphabet
      symbol of which selected answer they think it's most reasonable.
      The format of the function argument `user_selected_answer` looks as follow:
        It should be in the format with character such as A, B, C and D.
        Example 1: User's answer is `a`, it means choice `A`.
        Example 2: User's answer is contextually closer to 3rd answer choice, it means `C`.
        Example 3: User says last is the answer, it means `D`.
        Thereafter, the `user_selected_answer` argument will be passed to the 
        function for Q&A question evaluation.
    """
    answer_mapping = {
        "A": 1,
        "B": 2,
        "C": 3,
        "D": 4
    }
    num_mapping = dict((v,k) for k,v in answer_mapping.items())

    user_answer_numeric = answer_mapping.get(user_selected_answer, None)
    if user_answer_numeric is None:
        raise Exception(f"User's answer can't be found: {user_selected_answer}") 
    
    question_id = st.session_state.question_id
    qna_answer  = st.session_state.qna_answer
    qna_answer_alphabet = num_mapping[qna_answer]

    con = sqlite3.connect(db_path)
    cur = con.cursor()
    sql_string = f"""INSERT INTO answer_tbl 
                     VALUES ({question_id}, {qna_answer}, {user_answer_numeric})
    """
    
    res = cur.execute(sql_string)
    con.commit()
    con.close()

    if qna_answer == user_answer_numeric:
        st.toast('Hooray!', icon='πŸŽ‰')
        time.sleep(0.3)
        st.toast('Hooray!', icon='πŸŽ‰')
        time.sleep(0.3)
        st.toast('Hooray!', icon='πŸŽ‰')
        st.balloons()
    else:
        st.toast('Omg..', icon='πŸ˜…')
        time.sleep(0.3)
        st.toast('Omg..', icon='πŸ˜…')
        time.sleep(0.3)
        st.toast('Omg..', icon='πŸ˜…')
        st.snow()


    qna_answer_response = (
        f"Your selected answer is `{user_selected_answer}`, "
        f"but the actual answer is `{qna_answer_alphabet}`. "
    )

    return qna_answer_response

get_qna_question_tool = FunctionTool.from_defaults(
                            fn=get_qna_question,
                            name="Extract_Question",
                            description=qna_question_description,
                            fn_schema=Question_Model
)

evaluate_qna_answer_tool = FunctionTool.from_defaults(
                            fn=evaluate_qna_answer,
                            name="Evaluate_Answer",
                            description=qna_answer_description,
                            fn_schema=Answer_Model
)