Stefano Fiorucci commited on
Commit
e20f916
β€’
1 Parent(s): 8314602

refactoring

Browse files
Files changed (2) hide show
  1. app.py +33 -98
  2. 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
- # Header
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
- "https://upload.wikimedia.org/wikipedia/it/3/39/Twin-peaks-1990.jpg")
56
- st.sidebar.markdown('<p align="center"><b>Twin Peaks Question Answering system</b></p>',
57
- unsafe_allow_html=True)
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
- </p>
86
- <img src = 'https://static.wikia.nocookie.net/twinpeaks/images/e/ef/Laura_Palmer%2C_the_Queen_Of_Hearts.jpg'/>
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
- value=st.session_state.question,
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
- reset_results()
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
- "🧠 &nbsp;&nbsp; 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("🐞 &nbsp;&nbsp; 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("πŸ€” &nbsp;&nbsp; Haystack is unsure whether any of the documents contain an answer to your question. Try to reformulate it!")
 
161
 
162
- for count, result in enumerate(st.session_state.results['answers']):
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
- str(annotation(answer, "ANSWER", "#3e1c21")) + context[end_idx:]+"..."),
178
- unsafe_allow_html=True)
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("🧠 &nbsp;&nbsp; Performing neural search on documents..."):
 
 
 
 
76
  try:
77
  st.session_state.results = query(
78
  question, RETRIEVER_TOP_K, READER_TOP_K)
87
  st.error("🐞 &nbsp;&nbsp; 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("""πŸ€” &nbsp;&nbsp; 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