esilver's picture
UI changes
9b29ced
raw
history blame
18.5 kB
import streamlit as st
import pandas as pd
from comparison import compare_ingredient_methods_ui
from ui_core import embeddings, load_examples
from ui_ingredient_matching import categorize_products
from ui_category_matching import categorize_products_by_category
from ui_hybrid_matching import categorize_products_with_voyage_reranking
from ui_expanded_matching import categorize_products_with_openai_reranking
# Removed unused import: from ui_formatters import format_results_html
# Session state initialization moved into render_ui()
def render_ui():
"""Render the Streamlit interface"""
# Initialize session state keys if they don't exist at the start of rendering
if 'ingredient_input' not in st.session_state:
st.session_state.ingredient_input = ""
if 'category_input' not in st.session_state:
st.session_state.category_input = ""
if 'voyage_input' not in st.session_state:
st.session_state.voyage_input = ""
if 'openai_input' not in st.session_state:
st.session_state.openai_input = ""
if 'compare_input' not in st.session_state:
st.session_state.compare_input = ""
# Add state for results persistence
if 'ingredient_results_html' not in st.session_state:
st.session_state.ingredient_results_html = None
if 'category_results_html' not in st.session_state:
st.session_state.category_results_html = None
if 'voyage_results_html' not in st.session_state:
st.session_state.voyage_results_html = None
if 'openai_results_html' not in st.session_state:
st.session_state.openai_results_html = None
if 'compare_results_html' not in st.session_state:
st.session_state.compare_results_html = None
# Page config is now set in app.py
st.title("Product Categorization Tool")
st.markdown("Analyze products by matching to ingredients or categories using AI embeddings.")
# Use st.tabs for the different sections
tab_ingredient, tab_category, tab_voyage, tab_openai, tab_compare = st.tabs([
"Ingredient Embeddings",
"Category Embeddings",
"Voyage AI Reranking",
"OpenAI Reranking",
"Compare Methods"
])
# --- Ingredient Matching Tab ---
with tab_ingredient:
st.header("Match Products to Ingredients")
col1, col2 = st.columns([1, 2]) # Give more space to results
with col1:
with st.container(border=True):
st.subheader("Input & Options")
# Handle button click *before* rendering the text area
if st.button("Load Examples", key="ingredient_examples", icon="πŸ“„"):
st.session_state.ingredient_input = load_examples() # Update state for next rerun
# Input section - Use the session state value
text_input = st.text_area(
"Product Names (one per line)",
value=st.session_state.ingredient_input, # Use value from state
placeholder="Enter product names, one per line",
height=200, # Reduced height slightly
key="ingredient_input_widget" # Use a different key for the widget itself if needed, or manage via value
)
# Update session state if user types manually
st.session_state.ingredient_input = text_input
st.markdown("---") # Separator
st.caption("Matching Options")
use_expansion = st.checkbox(
"Use Description Expansion (AI)",
value=False,
key="ingredient_expansion",
help="Expand product descriptions using AI before matching"
)
top_n = st.slider("Top N Results", 1, 25, 10, step=1, key="ingredient_top_n")
confidence = st.slider("Similarity Threshold", 0.1, 0.9, 0.5, step=0.05, key="ingredient_confidence")
find_ingredients_btn = st.button("Find Similar Ingredients", type="primary", key="ingredient_find", use_container_width=True, icon="πŸ”")
with col2:
with st.container(border=True):
# Results section
st.subheader("Results")
results_placeholder_ingredient = st.container() # Use container for results area
# --- Results Display Logic (Ingredient Tab) ---
if find_ingredients_btn:
if st.session_state.ingredient_input: # Check state value
with st.spinner("Finding similar ingredients..."):
results_html = categorize_products(
st.session_state.ingredient_input,
False,
use_expansion,
top_n,
confidence
)
st.session_state.ingredient_results_html = results_html # Store results
results_placeholder_ingredient.markdown(results_html, unsafe_allow_html=True) # Display immediately
else:
st.session_state.ingredient_results_html = None # Clear results if input is empty
results_placeholder_ingredient.warning("Please enter product names.")
# Display stored results if button wasn't clicked but results exist
elif 'ingredient_results_html' in st.session_state and st.session_state.ingredient_results_html:
results_placeholder_ingredient.markdown(st.session_state.ingredient_results_html, unsafe_allow_html=True)
else:
# Initial state or after clearing
results_placeholder_ingredient.write("Results will appear here.")
# --- Category Matching Tab ---
with tab_category:
st.header("Match Products to Categories")
col1, col2 = st.columns([1, 2]) # Give more space to results
with col1:
with st.container(border=True):
st.subheader("Input & Options")
if st.button("Load Examples", key="category_examples", icon="πŸ“„"):
st.session_state.category_input = load_examples()
category_text_input = st.text_area(
"Product Names (one per line)",
value=st.session_state.category_input,
placeholder="Enter product names, one per line",
height=200, # Reduced height
key="category_input_widget"
)
st.session_state.category_input = category_text_input
st.markdown("---") # Separator
st.caption("Matching Options")
category_use_expansion = st.checkbox(
"Use Description Expansion (AI)",
value=False,
key="category_expansion",
help="Expand product descriptions using AI before matching"
)
category_top_n = st.slider("Top N Categories", 1, 10, 5, step=1, key="category_top_n")
category_confidence = st.slider("Matching Threshold", 0.1, 0.9, 0.5, step=0.05, key="category_confidence")
match_categories_btn = st.button("Match to Categories", type="primary", key="category_match", use_container_width=True, icon="πŸ”")
with col2:
with st.container(border=True):
st.subheader("Results")
results_placeholder_category = st.container() # Use container
# --- Results Display Logic (Category Tab) ---
if match_categories_btn:
if st.session_state.category_input:
with st.spinner("Matching to categories..."):
results_html = categorize_products_by_category(
st.session_state.category_input,
False,
category_use_expansion,
category_top_n,
category_confidence
)
st.session_state.category_results_html = results_html # Store results
results_placeholder_category.markdown(results_html, unsafe_allow_html=True) # Display immediately
else:
st.session_state.category_results_html = None # Clear results if input is empty
results_placeholder_category.warning("Please enter product names.")
# Display stored results if button wasn't clicked but results exist
elif 'category_results_html' in st.session_state and st.session_state.category_results_html:
results_placeholder_category.markdown(st.session_state.category_results_html, unsafe_allow_html=True)
else:
# Initial state or after clearing
results_placeholder_category.write("Results will appear here.")
# --- Common function for Reranking Tabs ---
def create_reranking_ui(tab, tab_key_prefix, tab_name, backend_function, default_match="categories"):
with tab:
st.header(f"Match using {tab_name}")
col1, col2 = st.columns([1, 2]) # Give more space to results
with col1:
with st.container(border=True):
st.subheader("Input & Options")
if st.button("Load Examples", key=f"{tab_key_prefix}_examples", icon="πŸ“„"):
st.session_state[f"{tab_key_prefix}_input"] = load_examples()
tab_input_value = st.text_area(
"Product Names (one per line)",
value=st.session_state[f"{tab_key_prefix}_input"],
placeholder="Enter product names, one per line",
height=200, # Reduced height
key=f"{tab_key_prefix}_input_widget"
)
st.session_state[f"{tab_key_prefix}_input"] = tab_input_value # Update state
st.markdown("---") # Separator
st.caption("Matching Options")
tab_match_type = st.radio(
"Match Type",
options=["categories", "ingredients"],
index=0 if default_match == "categories" else 1,
key=f"{tab_key_prefix}_match_type",
horizontal=True,
help="Choose whether to match against ingredients or categories"
)
tab_expansion = st.checkbox(
"Use Description Expansion (AI)",
value=False,
key=f"{tab_key_prefix}_expansion",
help="Expand product descriptions using AI before matching"
)
tab_emb_top_n = st.slider("Embedding Top N Results", 1, 50, 20, step=1, key=f"{tab_key_prefix}_emb_top_n", help="How many candidates to fetch initially using embeddings.")
tab_top_n = st.slider("Final Top N Results", 1, 10, 5, step=1, key=f"{tab_key_prefix}_final_top_n", help="How many final results to show after reranking.")
tab_confidence = st.slider("Matching Threshold", 0.1, 0.9, 0.5, step=0.05, key=f"{tab_key_prefix}_confidence")
tab_match_btn = st.button(f"Match using {tab_name}", type="primary", key=f"{tab_key_prefix}_match", use_container_width=True, icon="πŸ”")
with col2:
with st.container(border=True):
st.subheader("Results")
results_placeholder_rerank = st.container() # Use container
# --- Results Display Logic (Reranking Tabs) ---
results_key = f"{tab_key_prefix}_results_html"
input_key = f"{tab_key_prefix}_input"
if tab_match_btn:
if st.session_state[input_key]:
with st.spinner(f"Matching using {tab_name}..."):
results_html = backend_function(
st.session_state[input_key],
False,
tab_expansion,
tab_emb_top_n,
tab_top_n,
tab_confidence,
tab_match_type
)
st.session_state[results_key] = results_html # Store results
results_placeholder_rerank.markdown(results_html, unsafe_allow_html=True) # Display immediately
else:
st.session_state[results_key] = None # Clear results if input is empty
results_placeholder_rerank.warning("Please enter product names.")
# Display stored results if button wasn't clicked but results exist
elif results_key in st.session_state and st.session_state[results_key]:
results_placeholder_rerank.markdown(st.session_state[results_key], unsafe_allow_html=True)
else:
# Initial state or after clearing
results_placeholder_rerank.write("Results will appear here.")
# Create the reranking tabs
create_reranking_ui(tab_voyage, "voyage", "Voyage AI Reranking", categorize_products_with_voyage_reranking, "categories")
create_reranking_ui(tab_openai, "openai", "OpenAI Reranking", categorize_products_with_openai_reranking, "categories")
# --- Compare Methods Tab ---
with tab_compare:
st.header("Compare Matching Methods")
col1, col2 = st.columns([1, 2]) # Give more space to results
with col1:
with st.container(border=True):
st.subheader("Input & Options")
if st.button("Load Examples", key="compare_examples", icon="πŸ“„"):
st.session_state.compare_input = load_examples()
compare_product_input_value = st.text_area(
"Product Names (one per line)",
value=st.session_state.compare_input,
placeholder="4 Tbsp sweet pickle relish\nchocolate chips\nfresh parsley",
height=200, # Consistent height
key="compare_input_widget"
)
st.session_state.compare_input = compare_product_input_value # Update state
st.markdown("---") # Separator
st.caption("Comparison Options")
compare_match_type = st.radio(
"Match Type",
options=["categories", "ingredients"],
index=0,
key="compare_match_type",
horizontal=True,
help="Choose whether to match against ingredients or categories"
)
compare_expansion = st.checkbox(
"Use Description Expansion (AI)",
value=False,
key="compare_expansion",
help="Expand product descriptions using AI before matching"
)
compare_embedding_top_n = st.slider(
"Initial embedding candidates",
min_value=5, max_value=50, value=20, step=5,
key="compare_emb_top_n",
help="How many candidates to fetch initially using embeddings for reranking methods."
)
compare_final_top_n = st.slider(
"Final results per method",
min_value=1, max_value=10, value=3, step=1,
key="compare_final_top_n",
help="How many final results to show per method."
)
compare_confidence_threshold = st.slider(
"Confidence threshold",
min_value=0.0, max_value=1.0, value=0.5, step=0.05,
key="compare_confidence"
)
compare_btn = st.button("Compare Methods", type="primary", key="compare_run", use_container_width=True, icon="πŸ”")
with col2:
with st.container(border=True):
st.subheader("Comparison Results")
results_placeholder_compare = st.container() # Use container
# --- Results Display Logic (Compare Tab) ---
if compare_btn:
if st.session_state.compare_input:
with st.spinner("Comparing methods..."):
results_html = compare_ingredient_methods_ui(
st.session_state.compare_input,
compare_embedding_top_n,
compare_final_top_n,
compare_confidence_threshold,
compare_match_type,
compare_expansion
)
st.session_state.compare_results_html = results_html # Store results
results_placeholder_compare.markdown(results_html, unsafe_allow_html=True) # Display immediately
else:
st.session_state.compare_results_html = None # Clear results if input is empty
results_placeholder_compare.warning("Please enter product names.")
# Display stored results if button wasn't clicked but results exist
elif 'compare_results_html' in st.session_state and st.session_state.compare_results_html:
results_placeholder_compare.markdown(st.session_state.compare_results_html, unsafe_allow_html=True)
else:
# Initial state or after clearing
results_placeholder_compare.write("Results will appear here.")
st.markdown("---")
st.markdown("Powered by Voyage AI embeddings β€’ Built with Streamlit")