Spaces:
Sleeping
Sleeping
Stefano Fiorucci
commited on
Commit
·
e20f916
1
Parent(s):
8314602
refactoring
Browse files- app.py +33 -98
- frontend_utils.py +52 -1
app.py
CHANGED
@@ -1,20 +1,19 @@
|
|
|
|
1 |
|
2 |
import time
|
3 |
import streamlit as st
|
4 |
import logging
|
5 |
from json import JSONDecodeError
|
6 |
from markdown import markdown
|
7 |
-
import random
|
8 |
-
from typing import List, Dict, Any, Tuple, Optional
|
9 |
from annotated_text import annotation
|
10 |
from urllib.parse import unquote
|
11 |
|
12 |
from backend_utils import load_questions, query
|
13 |
-
from frontend_utils import set_state_if_absent, reset_results
|
|
|
14 |
from config import RETRIEVER_TOP_K, READER_TOP_K
|
15 |
|
16 |
def main():
|
17 |
-
|
18 |
questions = load_questions()
|
19 |
|
20 |
# Persistent state
|
@@ -24,107 +23,47 @@ def main():
|
|
24 |
set_state_if_absent('raw_json', None)
|
25 |
set_state_if_absent('random_question_requested', False)
|
26 |
|
27 |
-
|
28 |
-
|
29 |
-
st.write("# Who killed Laura Palmer?")
|
30 |
-
st.write("### The first Twin Peaks Question Answering system!")
|
31 |
-
st.markdown("""
|
32 |
-
Ask any question about [Twin Peaks] (https://twinpeaks.fandom.com/wiki/Twin_Peaks)
|
33 |
-
and see if the AI can find an answer...
|
34 |
-
|
35 |
-
*Note: do not use keywords, but full-fledged questions.*
|
36 |
-
""")
|
37 |
-
|
38 |
-
# Sidebar
|
39 |
-
# sidebar style
|
40 |
-
st.markdown(
|
41 |
-
"""
|
42 |
-
<style>
|
43 |
-
[data-testid="stSidebar"][aria-expanded="true"] > div:first-child{
|
44 |
-
width: 350px;
|
45 |
-
}
|
46 |
-
[data-testid="stSidebar"][aria-expanded="false"] > div:first-child{
|
47 |
-
width: 350px;
|
48 |
-
margin-left: -350px;
|
49 |
-
}
|
50 |
-
""",
|
51 |
-
unsafe_allow_html=True,
|
52 |
-
)
|
53 |
st.sidebar.header("Who killed Laura Palmer?")
|
54 |
-
st.sidebar.image(
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
st.sidebar.markdown(f"""
|
59 |
-
<style>
|
60 |
-
a {{
|
61 |
-
text-decoration: none;
|
62 |
-
}}
|
63 |
-
.haystack-footer {{
|
64 |
-
text-align: center;
|
65 |
-
}}
|
66 |
-
.haystack-footer h4 {{
|
67 |
-
margin: 0.1rem;
|
68 |
-
padding:0;
|
69 |
-
}}
|
70 |
-
footer {{
|
71 |
-
opacity: 0;
|
72 |
-
}}
|
73 |
-
.haystack-footer img {{
|
74 |
-
display: block;
|
75 |
-
margin-left: auto;
|
76 |
-
margin-right: auto;
|
77 |
-
width: 85%;
|
78 |
-
}}
|
79 |
-
</style>
|
80 |
-
<div class="haystack-footer">
|
81 |
<p><a href="https://github.com/anakin87/who-killed-laura-palmer">GitHub</a> -
|
82 |
Built with <a href="https://github.com/deepset-ai/haystack/">Haystack</a><br/>
|
83 |
<small>Data crawled from <a href="https://twinpeaks.fandom.com/wiki/Twin_Peaks_Wiki">
|
84 |
Twin Peaks Wiki</a>.</small>
|
85 |
-
|
86 |
-
|
87 |
-
<br/>
|
88 |
-
</div>
|
89 |
-
""", unsafe_allow_html=True)
|
90 |
-
|
91 |
# spotify webplayer
|
92 |
-
st.sidebar.markdown(
|
93 |
-
<p align="center">
|
94 |
-
<iframe style="border-radius:12px" src="https://open.spotify.com/embed/playlist/38rrtWgflrw7grB37aMlsO?utm_source=generator" width="85%" height="380" frameBorder="0" allowfullscreen="" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture"></iframe>
|
95 |
-
</p>""", unsafe_allow_html=True)
|
96 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
# Search bar
|
98 |
-
question = st.text_input("",
|
99 |
-
|
100 |
-
max_chars=100,
|
101 |
-
on_change=reset_results
|
102 |
-
)
|
103 |
col1, col2 = st.columns(2)
|
104 |
col1.markdown(
|
105 |
"<style>.stButton button {width:100%;}</style>", unsafe_allow_html=True)
|
106 |
col2.markdown(
|
107 |
"<style>.stButton button {width:100%;}</style>", unsafe_allow_html=True)
|
108 |
-
|
109 |
# Run button
|
110 |
run_pressed = col1.button("Run")
|
111 |
-
|
112 |
-
# Get next random question from the CSV
|
113 |
if col2.button("Random question"):
|
114 |
-
|
115 |
-
question = random.choice(questions)
|
116 |
-
# Avoid picking the same question twice (the change is not visible on the UI)
|
117 |
-
while question == st.session_state.question:
|
118 |
-
question = random.choice(questions)
|
119 |
-
st.session_state.question = question
|
120 |
-
st.session_state.random_question_requested = True
|
121 |
-
# Re-runs the script setting the random question as the textbox value
|
122 |
-
# Unfortunately necessary as the Random Question button is _below_ the textbox
|
123 |
-
raise st.script_runner.RerunException(
|
124 |
-
st.script_request_queue.RerunData(None))
|
125 |
else:
|
126 |
st.session_state.random_question_requested = False
|
127 |
-
|
128 |
run_query = (run_pressed or question != st.session_state.question) \
|
129 |
and not st.session_state.random_question_requested
|
130 |
|
@@ -133,11 +72,7 @@ and see if the AI can find an answer...
|
|
133 |
time_start = time.time()
|
134 |
reset_results()
|
135 |
st.session_state.question = question
|
136 |
-
|
137 |
-
with st.spinner(
|
138 |
-
"🧠 Performing neural search on documents..."
|
139 |
-
|
140 |
-
):
|
141 |
try:
|
142 |
st.session_state.results = query(
|
143 |
question, RETRIEVER_TOP_K, READER_TOP_K)
|
@@ -152,14 +87,15 @@ and see if the AI can find an answer...
|
|
152 |
st.error("🐞 An error occurred during the request.")
|
153 |
return
|
154 |
|
|
|
155 |
if st.session_state.results:
|
156 |
st.write("## Results:")
|
157 |
-
|
158 |
alert_irrelevance = True
|
159 |
if len(st.session_state.results['answers']) == 0:
|
160 |
-
st.info("🤔 Haystack is unsure whether any of
|
|
|
161 |
|
162 |
-
for
|
163 |
result = result.to_dict()
|
164 |
if result["answer"]:
|
165 |
if alert_irrelevance and result['score'] < 0.50:
|
@@ -174,8 +110,8 @@ and see if the AI can find an answer...
|
|
174 |
end_idx = start_idx + len(answer)
|
175 |
# Hack due to this bug: https://github.com/streamlit/streamlit/issues/3190
|
176 |
st.write(markdown("- ..."+context[:start_idx] +
|
177 |
-
|
178 |
-
|
179 |
source = ""
|
180 |
name = unquote(result['meta']['name']).replace('_', ' ')
|
181 |
url = result['meta']['url']
|
@@ -183,5 +119,4 @@ and see if the AI can find an answer...
|
|
183 |
st.markdown(
|
184 |
f"**Score:** {result['score']:.2f} - **Source:** {source}")
|
185 |
|
186 |
-
|
187 |
main()
|
|
|
1 |
+
# inspired by https://github.com/deepset-ai/haystack/blob/master/ui/webapp.py
|
2 |
|
3 |
import time
|
4 |
import streamlit as st
|
5 |
import logging
|
6 |
from json import JSONDecodeError
|
7 |
from markdown import markdown
|
|
|
|
|
8 |
from annotated_text import annotation
|
9 |
from urllib.parse import unquote
|
10 |
|
11 |
from backend_utils import load_questions, query
|
12 |
+
from frontend_utils import (set_state_if_absent, reset_results, get_random_question,
|
13 |
+
SIDEBAR_STYLE, TWIN_PEAKS_IMG_SRC, LAURA_PALMER_IMG_SRC, SPOTIFY_IFRAME)
|
14 |
from config import RETRIEVER_TOP_K, READER_TOP_K
|
15 |
|
16 |
def main():
|
|
|
17 |
questions = load_questions()
|
18 |
|
19 |
# Persistent state
|
|
|
23 |
set_state_if_absent('raw_json', None)
|
24 |
set_state_if_absent('random_question_requested', False)
|
25 |
|
26 |
+
## SIDEBAR
|
27 |
+
st.markdown(SIDEBAR_STYLE, unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
st.sidebar.header("Who killed Laura Palmer?")
|
29 |
+
st.sidebar.image(TWIN_PEAKS_IMG_SRC)
|
30 |
+
st.sidebar.markdown("""
|
31 |
+
<p align="center"><b>Twin Peaks Question Answering system</b></p>
|
32 |
+
<div class="haystack-footer">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
<p><a href="https://github.com/anakin87/who-killed-laura-palmer">GitHub</a> -
|
34 |
Built with <a href="https://github.com/deepset-ai/haystack/">Haystack</a><br/>
|
35 |
<small>Data crawled from <a href="https://twinpeaks.fandom.com/wiki/Twin_Peaks_Wiki">
|
36 |
Twin Peaks Wiki</a>.</small>
|
37 |
+
</p><img src = '{LAURA_PALMER_IMG_SRC}'/><br/></div>
|
38 |
+
""", unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
39 |
# spotify webplayer
|
40 |
+
st.sidebar.markdown(SPOTIFY_IFRAME, unsafe_allow_html=True)
|
|
|
|
|
|
|
41 |
|
42 |
+
## MAIN CONTAINER
|
43 |
+
st.write("# Who killed Laura Palmer?")
|
44 |
+
st.write("### The first Twin Peaks Question Answering system!")
|
45 |
+
st.markdown("""
|
46 |
+
Ask any question about [Twin Peaks]
|
47 |
+
(https://twinpeaks.fandom.com/wiki/Twin_Peaks)
|
48 |
+
and see if the AI can find an answer...
|
49 |
+
|
50 |
+
*Note: do not use keywords, but full-fledged questions.*
|
51 |
+
""")
|
52 |
# Search bar
|
53 |
+
question = st.text_input("", value=st.session_state.question,
|
54 |
+
max_chars=100, on_change=reset_results)
|
|
|
|
|
|
|
55 |
col1, col2 = st.columns(2)
|
56 |
col1.markdown(
|
57 |
"<style>.stButton button {width:100%;}</style>", unsafe_allow_html=True)
|
58 |
col2.markdown(
|
59 |
"<style>.stButton button {width:100%;}</style>", unsafe_allow_html=True)
|
|
|
60 |
# Run button
|
61 |
run_pressed = col1.button("Run")
|
62 |
+
# Random question button
|
|
|
63 |
if col2.button("Random question"):
|
64 |
+
get_random_question(question)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
else:
|
66 |
st.session_state.random_question_requested = False
|
|
|
67 |
run_query = (run_pressed or question != st.session_state.question) \
|
68 |
and not st.session_state.random_question_requested
|
69 |
|
|
|
72 |
time_start = time.time()
|
73 |
reset_results()
|
74 |
st.session_state.question = question
|
75 |
+
with st.spinner("🧠 Performing neural search on documents..."):
|
|
|
|
|
|
|
|
|
76 |
try:
|
77 |
st.session_state.results = query(
|
78 |
question, RETRIEVER_TOP_K, READER_TOP_K)
|
|
|
87 |
st.error("🐞 An error occurred during the request.")
|
88 |
return
|
89 |
|
90 |
+
# Display results
|
91 |
if st.session_state.results:
|
92 |
st.write("## Results:")
|
|
|
93 |
alert_irrelevance = True
|
94 |
if len(st.session_state.results['answers']) == 0:
|
95 |
+
st.info("""🤔 Haystack is unsure whether any of
|
96 |
+
the documents contain an answer to your question. Try to reformulate it!""")
|
97 |
|
98 |
+
for result in st.session_state.results['answers']:
|
99 |
result = result.to_dict()
|
100 |
if result["answer"]:
|
101 |
if alert_irrelevance and result['score'] < 0.50:
|
|
|
110 |
end_idx = start_idx + len(answer)
|
111 |
# Hack due to this bug: https://github.com/streamlit/streamlit/issues/3190
|
112 |
st.write(markdown("- ..."+context[:start_idx] +
|
113 |
+
str(annotation(answer, "ANSWER", "#3e1c21")) +
|
114 |
+
context[end_idx:]+"..."), unsafe_allow_html=True)
|
115 |
source = ""
|
116 |
name = unquote(result['meta']['name']).replace('_', ' ')
|
117 |
url = result['meta']['url']
|
|
|
119 |
st.markdown(
|
120 |
f"**Score:** {result['score']:.2f} - **Source:** {source}")
|
121 |
|
|
|
122 |
main()
|
frontend_utils.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
import streamlit as st
|
|
|
2 |
|
3 |
def set_state_if_absent(key, value):
|
4 |
if key not in st.session_state:
|
@@ -8,7 +9,57 @@ def set_state_if_absent(key, value):
|
|
8 |
def reset_results(*args):
|
9 |
st.session_state.answer = None
|
10 |
st.session_state.results = None
|
11 |
-
st.session_state.raw_json = None
|
12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
|
|
|
1 |
import streamlit as st
|
2 |
+
import random
|
3 |
|
4 |
def set_state_if_absent(key, value):
|
5 |
if key not in st.session_state:
|
|
|
9 |
def reset_results(*args):
|
10 |
st.session_state.answer = None
|
11 |
st.session_state.results = None
|
12 |
+
st.session_state.raw_json = None
|
13 |
|
14 |
+
def get_random_question(questions):
|
15 |
+
reset_results()
|
16 |
+
question = random.choice(questions)
|
17 |
+
# Avoid picking the same question twice (the change is not visible on the UI)
|
18 |
+
while question == st.session_state.question:
|
19 |
+
question = random.choice(questions)
|
20 |
+
st.session_state.question = question
|
21 |
+
st.session_state.random_question_requested = True
|
22 |
+
# Re-runs the script setting the random question as the textbox value
|
23 |
+
# Unfortunately necessary as the Random Question button is _below_ the textbox
|
24 |
+
raise st.script_runner.RerunException(
|
25 |
+
st.script_request_queue.RerunData(None))
|
26 |
+
|
27 |
+
SIDEBAR_STYLE = """
|
28 |
+
<style>
|
29 |
+
[data-testid="stSidebar"][aria-expanded="true"] > div:first-child{
|
30 |
+
width: 350px;
|
31 |
+
}
|
32 |
+
[data-testid="stSidebar"][aria-expanded="false"] > div:first-child{
|
33 |
+
width: 350px;
|
34 |
+
margin-left: -350px;
|
35 |
+
}
|
36 |
+
a {text-decoration: none;}
|
37 |
+
.haystack-footer {text-align: center;}
|
38 |
+
.haystack-footer h4 {
|
39 |
+
margin: 0.1rem;
|
40 |
+
padding:0;
|
41 |
+
}
|
42 |
+
footer {opacity: 0;}
|
43 |
+
.haystack-footer img {
|
44 |
+
display: block;
|
45 |
+
margin-left: auto;
|
46 |
+
margin-right: auto;
|
47 |
+
width: 85%;
|
48 |
+
}
|
49 |
+
</style>
|
50 |
+
"""
|
51 |
+
|
52 |
+
SPOTIFY_IFRAME = """
|
53 |
+
<p align="center">
|
54 |
+
<iframe style="border-radius:12px"
|
55 |
+
src="https://open.spotify.com/embed/playlist/38rrtWgflrw7grB37aMlsO?utm_source=generator"
|
56 |
+
width="85%" height="380" frameBorder="0" allowfullscreen=""
|
57 |
+
allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture">
|
58 |
+
</iframe>
|
59 |
+
</p>
|
60 |
+
"""
|
61 |
+
|
62 |
+
TWIN_PEAKS_IMG_SRC = "https://upload.wikimedia.org/wikipedia/it/3/39/Twin-peaks-1990.jpg"
|
63 |
+
LAURA_PALMER_IMG_SRC = "https://static.wikia.nocookie.net/twinpeaks/images/e/ef/Laura_Palmer%2C_the_Queen_Of_Hearts.jpg"
|
64 |
|
65 |
|