Spaces:
Sleeping
Sleeping
Maxwell Wang
commited on
Commit
•
3e7a501
1
Parent(s):
94f83b5
initial
Browse files- app.py +93 -0
- app_utils.py +24 -0
- pages/1_Profile.py +72 -0
- pages/2_Query.py +124 -0
app.py
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# source .venv/bin/activate
|
2 |
+
# streamlit run <>.py
|
3 |
+
|
4 |
+
import streamlit as st
|
5 |
+
import app_utils as util
|
6 |
+
from pathlib import Path
|
7 |
+
from PIL import Image
|
8 |
+
from io import BytesIO
|
9 |
+
import requests
|
10 |
+
import time
|
11 |
+
|
12 |
+
st.set_page_config(page_title="SAnDL", page_icon="🩴", layout="wide", initial_sidebar_state="collapsed")
|
13 |
+
|
14 |
+
# Example URL
|
15 |
+
image_url = "https://media.discordapp.net/attachments/1013678935231442944/1175575324210249740/sandll2.png?ex=656bbad6&is=655945d6&hm=d8f4a93a7c3ae90978f820093e2e7c17c18b6125546255afaaeca8ed6716bd4d&=&width=2268&height=486"
|
16 |
+
|
17 |
+
# Open image from URL
|
18 |
+
response = requests.get(image_url)
|
19 |
+
img = Image.open(BytesIO(response.content))
|
20 |
+
|
21 |
+
# close taskbar, page margin, ...
|
22 |
+
st.markdown("""
|
23 |
+
<style>
|
24 |
+
.st-emotion-cache-1yeub2j {
|
25 |
+
display: none;
|
26 |
+
}
|
27 |
+
.st-emotion-cache-79elbk {
|
28 |
+
margin-top: -45px;
|
29 |
+
}
|
30 |
+
.slogan-font {
|
31 |
+
font-size:18px !important;
|
32 |
+
margin-bottom: 40px;
|
33 |
+
margin-top: 0px;
|
34 |
+
text-align: center;
|
35 |
+
}
|
36 |
+
.bolded {
|
37 |
+
font-weight: 1000;
|
38 |
+
}
|
39 |
+
[data-testid="stSidebarNav"] {
|
40 |
+
background-image: url(https://media.discordapp.net/attachments/1013678935231442944/1175577023574454402/sandlsmall_2.png?ex=656bbc6b&is=6559476b&hm=c920463c2062c3c42e555224fb0dc0afc5188e9451b45c9eb62c64f297c1a1f1&=&width=596&height=127);
|
41 |
+
background-repeat: no-repeat;
|
42 |
+
margin-top: 20px;
|
43 |
+
background-position: 20px 20px;
|
44 |
+
}
|
45 |
+
section[data-testid="stSidebar"][aria-expanded="true"]{
|
46 |
+
display: none;
|
47 |
+
}
|
48 |
+
</style>
|
49 |
+
""", unsafe_allow_html=True)
|
50 |
+
|
51 |
+
logincol1, logincol2, logincol3 = st.columns([1, 8, 1])
|
52 |
+
|
53 |
+
with logincol2:
|
54 |
+
|
55 |
+
# logo
|
56 |
+
st.image(img)
|
57 |
+
# caption
|
58 |
+
st.markdown('<p class="slogan-font">' + "Developing <span class = \"bolded\">anomaly detection</span> systems for filtration of <span class = \"bolded\">large language model</span> prompts." + '</p>', unsafe_allow_html=True)
|
59 |
+
|
60 |
+
#login form
|
61 |
+
profilecontainer = st.container()
|
62 |
+
login_form = profilecontainer.form('Login')
|
63 |
+
username = login_form.text_input('Username').lower()
|
64 |
+
st.session_state['username'] = username
|
65 |
+
password = login_form.text_input('Password', type='password')
|
66 |
+
if login_form.form_submit_button('Login'):
|
67 |
+
st.session_state.pop('FormSubmitter:Login-Login', None)
|
68 |
+
|
69 |
+
# deletes all keys from previous logins
|
70 |
+
for key in st.session_state.keys():
|
71 |
+
if (key != 'username'):
|
72 |
+
st.session_state.pop(key)
|
73 |
+
|
74 |
+
res = requests.post("https://sandl-backend-ny3lmzb4dq-uc.a.run.app/login", json={"email": username, "password": password})
|
75 |
+
error = res.status_code
|
76 |
+
|
77 |
+
# login validation
|
78 |
+
if error == 200:
|
79 |
+
st.session_state['userdata'] = res.json()["jwt"]
|
80 |
+
st.success('Login Successful!', icon="✅")
|
81 |
+
util.switch_page("query")
|
82 |
+
# username exists in db, but password is incorrect
|
83 |
+
elif error == 400:
|
84 |
+
st.error('Incorrect Password.', icon="🚨")
|
85 |
+
# makes new account
|
86 |
+
elif error == 500:
|
87 |
+
st.info('Account Not Found: New account created.', icon="ℹ️")
|
88 |
+
res = requests.post("https://sandl-backend-ny3lmzb4dq-uc.a.run.app/sign_up", json={"email": username, "password": password})
|
89 |
+
res = requests.post("https://sandl-backend-ny3lmzb4dq-uc.a.run.app/login", json={"email": username, "password": password})
|
90 |
+
st.session_state['userdata'] = res.json()["jwt"]
|
91 |
+
requests.post("https://sandl-backend-ny3lmzb4dq-uc.a.run.app/add_api_key", json={"jwt": st.session_state['userdata']})
|
92 |
+
time.sleep(.5)
|
93 |
+
util.switch_page("query")
|
app_utils.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# switch page util
|
2 |
+
def switch_page(page_name: str):
|
3 |
+
from streamlit.runtime.scriptrunner import RerunData, RerunException
|
4 |
+
from streamlit.source_util import get_pages
|
5 |
+
|
6 |
+
def standardize_name(name: str) -> str:
|
7 |
+
return name.lower().replace("_", " ")
|
8 |
+
|
9 |
+
page_name = standardize_name(page_name)
|
10 |
+
|
11 |
+
pages = get_pages("app.py")
|
12 |
+
|
13 |
+
for page_hash, config in pages.items():
|
14 |
+
if standardize_name(config["page_name"]) == page_name:
|
15 |
+
raise RerunException(
|
16 |
+
RerunData(
|
17 |
+
page_script_hash=page_hash,
|
18 |
+
page_name=page_name,
|
19 |
+
)
|
20 |
+
)
|
21 |
+
|
22 |
+
page_names = [standardize_name(config["page_name"]) for config in pages.values()]
|
23 |
+
|
24 |
+
raise ValueError(f"Could not find page {page_name}. Must be one of {page_names}")
|
pages/1_Profile.py
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import requests
|
2 |
+
import streamlit as st;
|
3 |
+
import app_utils as util
|
4 |
+
|
5 |
+
st.set_page_config(page_title="sandl", page_icon="🩴", layout="wide", initial_sidebar_state="collapsed")
|
6 |
+
|
7 |
+
# goes to login page if not logged in
|
8 |
+
try:
|
9 |
+
set = st.session_state['username']
|
10 |
+
except:
|
11 |
+
util.switch_page('app')
|
12 |
+
# position: relative;
|
13 |
+
|
14 |
+
st.markdown("""
|
15 |
+
<style>
|
16 |
+
.st-emotion-cache-79elbk {
|
17 |
+
margin-top: -65px;
|
18 |
+
}
|
19 |
+
.st-emotion-cache-1wmy9hl {
|
20 |
+
margin-top: -32px;
|
21 |
+
}
|
22 |
+
|
23 |
+
.circle-image {
|
24 |
+
width: 200px;
|
25 |
+
height: 200px;
|
26 |
+
border-radius: 25px;
|
27 |
+
overflow: hidden;
|
28 |
+
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
|
29 |
+
}
|
30 |
+
|
31 |
+
.circle-image img {
|
32 |
+
width: 100%;
|
33 |
+
height: 100%;
|
34 |
+
object-fit: cover;
|
35 |
+
}
|
36 |
+
[data-testid="stSidebarNav"] {
|
37 |
+
background-image: url(https://media.discordapp.net/attachments/1013678935231442944/1175577023574454402/sandlsmall_2.png?ex=656bbc6b&is=6559476b&hm=c920463c2062c3c42e555224fb0dc0afc5188e9451b45c9eb62c64f297c1a1f1&=&width=596&height=127);
|
38 |
+
background-repeat: no-repeat;
|
39 |
+
margin-top: 20px;
|
40 |
+
background-position: 20px 20px;
|
41 |
+
}
|
42 |
+
</style>
|
43 |
+
""", unsafe_allow_html=True)
|
44 |
+
|
45 |
+
profilecol1, profilecol2, profilecol3 = st.columns([1, 8, 1])
|
46 |
+
|
47 |
+
with profilecol1:
|
48 |
+
# goes to prompt page
|
49 |
+
if st.button('◀ Back'):
|
50 |
+
util.switch_page('query')
|
51 |
+
|
52 |
+
with profilecol2:
|
53 |
+
# pfp
|
54 |
+
st.write('')
|
55 |
+
st.markdown("<div class=\"circle-image\"><img src=\"https://img.pikbest.com/png-images/qiantu/blue-hand-drawn-rounded-socks-cartoon-icon_2687993.png!sw800\"></div>", unsafe_allow_html=True)
|
56 |
+
|
57 |
+
st.header(st.session_state['username'], divider='gray', anchor=False)
|
58 |
+
|
59 |
+
st.write("")
|
60 |
+
if st.button('Show API Key'):
|
61 |
+
# gets the api key
|
62 |
+
res = requests.post("https://sandl-backend-ny3lmzb4dq-uc.a.run.app/get_api_keys", json={"jwt": st.session_state['userdata']})
|
63 |
+
# # test code
|
64 |
+
# print(res.status_code)
|
65 |
+
apikey = res.json()["keys"][0]
|
66 |
+
|
67 |
+
st.text(apikey)
|
68 |
+
|
69 |
+
st.write('')
|
70 |
+
if st.button('Logout'):
|
71 |
+
util.switch_page('app')
|
72 |
+
|
pages/2_Query.py
ADDED
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import app_utils as util
|
2 |
+
import streamlit as st
|
3 |
+
import time
|
4 |
+
from pathlib import Path
|
5 |
+
from datetime import datetime
|
6 |
+
import requests
|
7 |
+
|
8 |
+
st.set_page_config(page_title="sandl", page_icon="🩴", layout="wide", initial_sidebar_state="collapsed")
|
9 |
+
# sidebar, page, sidebar
|
10 |
+
# margin-bottom: 30px;
|
11 |
+
st.markdown("""
|
12 |
+
<style>
|
13 |
+
.prompt-font {
|
14 |
+
font-size:18px !important;
|
15 |
+
margin-bottom: 10px;
|
16 |
+
margin-top: 20px;
|
17 |
+
}
|
18 |
+
.percent-font {
|
19 |
+
font-size:30px !important;
|
20 |
+
margin-top: 50px;
|
21 |
+
}
|
22 |
+
|
23 |
+
.st-emotion-cache-79elbk {
|
24 |
+
margin-top: -65px;
|
25 |
+
}
|
26 |
+
.st-emotion-cache-1wmy9hl {
|
27 |
+
margin-top: -32px;
|
28 |
+
}
|
29 |
+
|
30 |
+
[data-testid="stSidebarNav"] {
|
31 |
+
background-image: url(https://media.discordapp.net/attachments/1013678935231442944/1175577023574454402/sandlsmall_2.png?ex=656bbc6b&is=6559476b&hm=c920463c2062c3c42e555224fb0dc0afc5188e9451b45c9eb62c64f297c1a1f1&=&width=596&height=127);
|
32 |
+
background-repeat: no-repeat;
|
33 |
+
margin-top: 20px;
|
34 |
+
background-position: 20px 20px;
|
35 |
+
}
|
36 |
+
</style>
|
37 |
+
""", unsafe_allow_html=True)
|
38 |
+
|
39 |
+
|
40 |
+
# Create a row with 3 columns
|
41 |
+
col1, col2, col3 = st.columns([1, 8, 1])
|
42 |
+
|
43 |
+
with col3:
|
44 |
+
if st.button('👤 Profile'):
|
45 |
+
util.switch_page('profile')
|
46 |
+
|
47 |
+
with col2:
|
48 |
+
|
49 |
+
# tab bar
|
50 |
+
query = r"$\textsf{\large Query}$"
|
51 |
+
recents = r"$\textsf{\large History}$"
|
52 |
+
q, searches = st.tabs([query, recents])
|
53 |
+
|
54 |
+
# query tab
|
55 |
+
with q:
|
56 |
+
|
57 |
+
st.write('')
|
58 |
+
|
59 |
+
st.markdown('<p class="prompt-font">Prompt:</p>', unsafe_allow_html=True)
|
60 |
+
promptcontainer = st.container()
|
61 |
+
|
62 |
+
user_input = promptcontainer.text_area("", height=50, on_change=None)
|
63 |
+
|
64 |
+
container2 = st.container()
|
65 |
+
|
66 |
+
if (user_input == ''):
|
67 |
+
container2.write('')
|
68 |
+
container2.write('')
|
69 |
+
else:
|
70 |
+
# TO-DO:
|
71 |
+
# call api to get value (this would be added to value put into the dict (prob at first pos. hopefully its a set num of chars))
|
72 |
+
# Verify Login **
|
73 |
+
if 'userdata' not in st.session_state.keys():
|
74 |
+
st.info('Not Logged In: Redirecting to Login page...')
|
75 |
+
time.sleep(.5)
|
76 |
+
util.switch_page('app')
|
77 |
+
else:
|
78 |
+
res = requests.post("https://sandl-backend-ny3lmzb4dq-uc.a.run.app/get_intent", json={"jwt": st.session_state['userdata'], "prompt": user_input})
|
79 |
+
value = round(res.json()["certaintyValue"] * 100)
|
80 |
+
if (value < 10):
|
81 |
+
value = '0' + str(value)
|
82 |
+
else:
|
83 |
+
value = str(value)
|
84 |
+
|
85 |
+
# visualization (loading bar)
|
86 |
+
container2.markdown("<p class='percent-font'>" + value + "%</p>", unsafe_allow_html=True)
|
87 |
+
my_bar = container2.progress(0, text='')
|
88 |
+
for percent_complete in range(int(value)):
|
89 |
+
time.sleep(0.01)
|
90 |
+
my_bar.progress(percent_complete + 1, text='')
|
91 |
+
container2.text('Your prompt has a ' + value + "% chance to be non-malicious.")
|
92 |
+
|
93 |
+
container2.text('')
|
94 |
+
container2.text('')
|
95 |
+
container2.text("SAnDL: Sophisticated Anomaly Detection for LLMs")
|
96 |
+
now = datetime.now()
|
97 |
+
current_time = now.strftime("%H:%M:%S")
|
98 |
+
|
99 |
+
if (user_input != ''):
|
100 |
+
st.session_state[value + user_input + " " + current_time] = value + user_input + " " + current_time
|
101 |
+
|
102 |
+
# Searches Tab:
|
103 |
+
with searches:
|
104 |
+
st.write('')
|
105 |
+
x = st.header('Recent Searches:', divider='grey', anchor=False)
|
106 |
+
|
107 |
+
containerx = st.container()
|
108 |
+
containerx.write('')
|
109 |
+
containerx.write('')
|
110 |
+
|
111 |
+
# sorts dict by reverse time stamp
|
112 |
+
t = dict(sorted(st.session_state.items(), key=lambda item: item[1][len(item[1]) - 8:], reverse = True))
|
113 |
+
for key in t.keys():
|
114 |
+
if (key == st.session_state[key]):
|
115 |
+
# everything besides the time stamp
|
116 |
+
tempstring = st.session_state[key][2:len(st.session_state[key]) - 8]
|
117 |
+
|
118 |
+
percent = st.session_state[key][:2]
|
119 |
+
|
120 |
+
# adds ... if tempstring too long
|
121 |
+
if len(tempstring) > 110:
|
122 |
+
tempstring = st.session_state[key][2:112] + "..."
|
123 |
+
containerx.text(tempstring + ' — ' + percent + '%')
|
124 |
+
|