streamlit pages
Browse files- pages/config.py +69 -0
- pages/navigation.py +29 -0
- pages/result.py +63 -0
- pages/welcome.py +27 -0
pages/config.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
|
| 3 |
+
import streamlit as st
|
| 4 |
+
from pydantic import ValidationError
|
| 5 |
+
|
| 6 |
+
from pages.navigation import show_stepper
|
| 7 |
+
from src.schemas.requests import SampleInput
|
| 8 |
+
|
| 9 |
+
show_stepper("Configuration")
|
| 10 |
+
|
| 11 |
+
SAMPLE_INPUT = json.dumps(
|
| 12 |
+
[
|
| 13 |
+
{
|
| 14 |
+
"id": 0,
|
| 15 |
+
"text": "Google had solid Q4 2025 earnings but Microsoft's were not great.",
|
| 16 |
+
"entities": [
|
| 17 |
+
{
|
| 18 |
+
"entity_id": 0,
|
| 19 |
+
"entity_text": "Google",
|
| 20 |
+
"entity_type": "company",
|
| 21 |
+
"positions": [
|
| 22 |
+
{"position_text": "Google", "length": 6, "offset": 0}
|
| 23 |
+
],
|
| 24 |
+
},
|
| 25 |
+
{
|
| 26 |
+
"entity_id": 1,
|
| 27 |
+
"entity_text": "Microsoft",
|
| 28 |
+
"entity_type": "company",
|
| 29 |
+
"positions": [
|
| 30 |
+
{"position_text": "Microsoft", "length": 9, "offset": 40}
|
| 31 |
+
],
|
| 32 |
+
},
|
| 33 |
+
],
|
| 34 |
+
}
|
| 35 |
+
],
|
| 36 |
+
indent=2,
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
st.header("Configuration")
|
| 40 |
+
|
| 41 |
+
endpoint = st.selectbox(
|
| 42 |
+
"Endpoint",
|
| 43 |
+
options=["predict", "predict-all-models"],
|
| 44 |
+
index=0,
|
| 45 |
+
)
|
| 46 |
+
st.session_state.endpoint = endpoint
|
| 47 |
+
|
| 48 |
+
st.subheader("Input samples (JSON)")
|
| 49 |
+
default = st.session_state.get("input_json", SAMPLE_INPUT)
|
| 50 |
+
input_json = st.text_area("Paste your JSON input here:", value=default, height=300)
|
| 51 |
+
st.session_state.input_json = input_json
|
| 52 |
+
|
| 53 |
+
col1, col2 = st.columns(2)
|
| 54 |
+
with col1:
|
| 55 |
+
if st.button("Previous", use_container_width=True):
|
| 56 |
+
st.switch_page("pages/welcome.py")
|
| 57 |
+
with col2:
|
| 58 |
+
if st.button("Run prediction", use_container_width=True):
|
| 59 |
+
try:
|
| 60 |
+
parsed = json.loads(input_json)
|
| 61 |
+
if not isinstance(parsed, list):
|
| 62 |
+
raise ValueError("Input must be a JSON array of samples.")
|
| 63 |
+
for item in parsed:
|
| 64 |
+
SampleInput(**item)
|
| 65 |
+
st.switch_page("pages/result.py")
|
| 66 |
+
except json.JSONDecodeError as e:
|
| 67 |
+
st.error(f"Invalid JSON: {e}")
|
| 68 |
+
except (ValueError, ValidationError) as e:
|
| 69 |
+
st.error(f"Invalid input: {e}")
|
pages/navigation.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
|
| 3 |
+
PAGES = [
|
| 4 |
+
("Welcome", "pages/welcome.py"),
|
| 5 |
+
("Configuration", "pages/config.py"),
|
| 6 |
+
("Results", "pages/result.py"),
|
| 7 |
+
]
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def show_stepper(current: str):
|
| 11 |
+
"""Render a horizontal step indicator. `current` is the page title."""
|
| 12 |
+
cols = st.columns(len(PAGES))
|
| 13 |
+
for i, (title, path) in enumerate(PAGES):
|
| 14 |
+
with cols[i]:
|
| 15 |
+
if title == current:
|
| 16 |
+
st.markdown(
|
| 17 |
+
f"<div style='text-align:center; padding:8px 0; "
|
| 18 |
+
f"border-bottom:3px solid #ff4b4b; font-weight:700;'>"
|
| 19 |
+
f"{i + 1}. {title}</div>",
|
| 20 |
+
unsafe_allow_html=True,
|
| 21 |
+
)
|
| 22 |
+
else:
|
| 23 |
+
st.markdown(
|
| 24 |
+
f"<div style='text-align:center; padding:8px 0; "
|
| 25 |
+
f"border-bottom:3px solid #444; color:#888;'>"
|
| 26 |
+
f"{i + 1}. {title}</div>",
|
| 27 |
+
unsafe_allow_html=True,
|
| 28 |
+
)
|
| 29 |
+
st.write("")
|
pages/result.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
|
| 3 |
+
import streamlit as st
|
| 4 |
+
|
| 5 |
+
from pages.navigation import show_stepper
|
| 6 |
+
from src.fe_handler import call_predict, call_predict_all
|
| 7 |
+
|
| 8 |
+
show_stepper("Results")
|
| 9 |
+
|
| 10 |
+
st.header("Results")
|
| 11 |
+
|
| 12 |
+
input_json = st.session_state.get("input_json")
|
| 13 |
+
|
| 14 |
+
if not input_json:
|
| 15 |
+
st.warning("No input provided. Go back and provide samples.")
|
| 16 |
+
if st.button("Back to config", use_container_width=True):
|
| 17 |
+
st.switch_page("pages/config.py")
|
| 18 |
+
st.stop()
|
| 19 |
+
|
| 20 |
+
try:
|
| 21 |
+
parsed = json.loads(input_json)
|
| 22 |
+
except (json.JSONDecodeError, ValueError) as e:
|
| 23 |
+
st.error(f"Invalid input: {e}")
|
| 24 |
+
if st.button("Back to config", use_container_width=True):
|
| 25 |
+
st.switch_page("pages/config.py")
|
| 26 |
+
st.stop()
|
| 27 |
+
|
| 28 |
+
endpoint = st.session_state.get("endpoint", "predict")
|
| 29 |
+
|
| 30 |
+
try:
|
| 31 |
+
with st.spinner("Running prediction..."):
|
| 32 |
+
if endpoint == "predict":
|
| 33 |
+
results = call_predict(parsed)
|
| 34 |
+
_single = True
|
| 35 |
+
else:
|
| 36 |
+
results = call_predict_all(parsed)
|
| 37 |
+
_single = False
|
| 38 |
+
|
| 39 |
+
if _single:
|
| 40 |
+
for sample in results:
|
| 41 |
+
st.markdown(f"**Sample {sample['id']}**")
|
| 42 |
+
for entity in sample["entities"]:
|
| 43 |
+
color = {"positive": "green", "neutral": "gray", "negative": "red"}.get(
|
| 44 |
+
entity["classification"], "gray"
|
| 45 |
+
)
|
| 46 |
+
st.markdown(f"- {entity['entity_text']}: :{color}[{entity['classification']}]")
|
| 47 |
+
else:
|
| 48 |
+
tabs = st.tabs(list(results.keys()))
|
| 49 |
+
for tab, (mode, preds) in zip(tabs, results.items()):
|
| 50 |
+
with tab:
|
| 51 |
+
for sample in preds:
|
| 52 |
+
st.markdown(f"**Sample {sample['id']}**")
|
| 53 |
+
for entity in sample["entities"]:
|
| 54 |
+
color = {"positive": "green", "neutral": "gray", "negative": "red"}.get(
|
| 55 |
+
entity["classification"], "gray"
|
| 56 |
+
)
|
| 57 |
+
st.markdown(f"- {entity['entity_text']}: :{color}[{entity['classification']}]")
|
| 58 |
+
except Exception as e:
|
| 59 |
+
st.error(f"Prediction failed: {e}")
|
| 60 |
+
|
| 61 |
+
st.write("")
|
| 62 |
+
if st.button("Back to config", use_container_width=True):
|
| 63 |
+
st.switch_page("pages/config.py")
|
pages/welcome.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
|
| 3 |
+
from pages.navigation import show_stepper
|
| 4 |
+
from src.fe_handler import check_health
|
| 5 |
+
|
| 6 |
+
show_stepper("Welcome")
|
| 7 |
+
|
| 8 |
+
st.header("Entity Sentiment Classification")
|
| 9 |
+
st.write(
|
| 10 |
+
"This tool classifies sentiment (positive, neutral, negative) "
|
| 11 |
+
"for specific entities mentioned in text articles."
|
| 12 |
+
)
|
| 13 |
+
st.subheader("Available endpoints")
|
| 14 |
+
st.markdown(
|
| 15 |
+
"- **`/predict`** — classify entities using a single model (marker)\n"
|
| 16 |
+
"- **`/predict-all-models`** — classify entities using all available models "
|
| 17 |
+
"and compare their results side by side"
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
if check_health():
|
| 21 |
+
st.success("Backend is running.")
|
| 22 |
+
else:
|
| 23 |
+
st.error("Backend is not reachable. Make sure the FastAPI server is running.")
|
| 24 |
+
|
| 25 |
+
st.write("")
|
| 26 |
+
if st.button("Next", use_container_width=True):
|
| 27 |
+
st.switch_page("pages/config.py")
|