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 |
+
|