esilver's picture
compare to chicory-ui
b231a24
raw
history blame
9.11 kB
import gradio as gr
from utils import SafeProgress, format_categories_html
from embeddings import create_product_embeddings
from similarity import compute_similarities
from chicory_api import call_chicory_parser
# Global variable for embeddings
embeddings = {}
# Sample product names for the example button
EXAMPLE_PRODUCTS = """Nature's Promise Spring Water Multipack
Red's Burritos
Nature's Promise Spring Water Multipack
Schweppes Seltzer 12 Pack
Hunt's Pasta Sauce
Buitoni Filled Pasta
Buitoni Filled Pasta
Samuel Adams or Blue Moon 12 Pack
Mrs. T's Pierogies
Buitoni Filled Pasta
Pillsbury Dough
Nature's Promise Organic Celery Hearts
MorningStar Farms Meatless Nuggets, Patties or Crumbles
Nature's Promise Organic Celery Hearts
Boar's Head Mild Provolone Cheese
Athenos Feta Crumbles"""
def categorize_products(product_input, is_file=False, top_n=5, confidence_threshold=0.5, progress=None):
"""Categorize products from text input or file"""
progress_tracker = SafeProgress(progress)
progress_tracker(0, desc="Starting...")
# Parse input
if is_file:
from utils import parse_product_file
try:
product_names = parse_product_file(product_input.name)
except Exception as e:
return f"<div style='color: #d32f2f; font-weight: bold;'>Error: {str(e)}</div>"
else:
product_names = [line.strip() for line in product_input.split("\n") if line.strip()]
if not product_names:
return "<div style='color: #d32f2f;'>No product names provided.</div>"
# Create embeddings
progress_tracker(0.2, desc="Generating product embeddings...")
products_embeddings = create_product_embeddings(product_names)
# Call Chicory Parser API
progress_tracker(0.5, desc="Calling Chicory Parser API...")
chicory_results = call_chicory_parser(product_names)
# Compute similarities
progress_tracker(0.7, desc="Computing similarities...")
all_similarities = compute_similarities(embeddings, products_embeddings)
# Format results
progress_tracker(0.9, desc="Formatting results...")
output_html = "<div style='font-family: Arial, sans-serif; max-width: 100%; overflow-x: auto;'>"
# Add debug info to verify data
output_html += f"<p style='color: #555;'>Processing {len(product_names)} products.</p>"
for product, similarities in all_similarities.items():
filtered_similarities = [(ingredient, score) for ingredient, score in similarities if score >= confidence_threshold]
top_similarities = filtered_similarities[:top_n]
# Debug info for Chicory results
chicory_data = chicory_results.get(product, [])
output_html += format_categories_html(product, top_similarities, chicory_result=chicory_data)
output_html += "<hr style='margin: 15px 0; border: 0; border-top: 1px solid #eee;'>"
output_html += "</div>"
if not all_similarities:
output_html = "<div style='color: #d32f2f; font-weight: bold; padding: 20px;'>No results found. Please check your input or try different products.</div>"
progress_tracker(1.0, desc="Done!")
return output_html
def load_examples():
"""Load example product names into the text input"""
return EXAMPLE_PRODUCTS
def create_demo():
"""Create the Gradio interface"""
with gr.Blocks(css="""
.results-container {
height: 600px !important;
max-height: 600px !important;
overflow-y: auto !important;
overflow-x: hidden !important;
padding: 15px !important;
border: 1px solid #333 !important;
background-color: #121212 !important;
color: #fff !important;
display: block !important;
}
.product-result {
background-color: #1e1e1e !important;
border-radius: 8px !important;
padding: 15px !important;
margin-bottom: 20px !important;
}
.product-result h3 {
margin-top: 5px !important;
border-bottom: 2px solid #333 !important;
padding-bottom: 8px !important;
color: #e0e0e0 !important;
}
.result-section {
margin: 15px 0 !important;
border-radius: 8px !important;
}
.result-section h4 {
color: #e0e0e0 !important;
font-size: 16px !important;
font-weight: 500 !important;
}
hr {
border-color: #333 !important;
}
""", js="""
document.addEventListener('keydown', function(e) {
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
// Find the active tab
const tabs = document.querySelector('.tabs');
if (!tabs) return;
const activeTabIndex = Array.from(tabs.querySelectorAll('button'))
.findIndex(tab => tab.getAttribute('aria-selected') === 'true');
// Find the submit button in the active tab content
const tabContents = document.querySelectorAll('.tabitem');
if (activeTabIndex < 0 || activeTabIndex >= tabContents.length) return;
const activeTab = tabContents[activeTabIndex];
const button = activeTabIndex === 0
? activeTab.querySelector('button[variant="primary"]') // Text Input tab
: activeTab.querySelector('button[variant="primary"]'); // File Upload tab
if (button) {
button.click();
e.preventDefault();
}
}
});
""") as demo:
gr.Markdown("# Product Categorization Tool\nAnalyze products and find the most similar ingredients using AI embeddings.")
gr.Markdown("Use **Cmd+Enter** (or Ctrl+Enter) to quickly categorize products (shortcut works in both tabs).")
with gr.Tabs() as tabs:
with gr.TabItem("Text Input"):
with gr.Row():
with gr.Column(scale=1):
# Input section
text_input = gr.Textbox(
lines=10,
placeholder="Enter product names, one per line",
label="Product Names"
)
input_controls = gr.Row()
with input_controls:
top_n = gr.Slider(1, 25, 5, label="Top N Results")
confidence = gr.Slider(0.1, 0.9, 0.5, label="Confidence Threshold")
with gr.Row():
examples_btn = gr.Button("Load Examples", variant="secondary")
categorize_btn = gr.Button("Categorize", variant="primary")
with gr.Column(scale=1):
# Results section
text_output = gr.HTML(label="Categorization Results", elem_classes="results-container")
with gr.TabItem("File Upload"):
with gr.Row():
with gr.Column(scale=1):
# Input section
file_input = gr.File(label="Upload JSON or text file with products", file_types=[".json", ".txt"])
file_controls = gr.Row()
with file_controls:
file_top_n = gr.Slider(1, 25, 5, label="Top N Results")
file_confidence = gr.Slider(0.1, 0.9, 0.5, label="Confidence Threshold")
process_btn = gr.Button("Process File", variant="primary")
with gr.Column(scale=1):
# Results section
file_output = gr.HTML(label="Categorization Results", elem_classes="results-container")
# Connect button click to the categorize function - more explicit binding
categorize_btn.click(
fn=categorize_products,
inputs=[text_input, gr.State(False), top_n, confidence],
outputs=text_output
)
# Add examples button functionality
examples_btn.click(
fn=load_examples,
inputs=[],
outputs=text_input
)
# Remove the Enter key trigger as we're now using Cmd+Enter/Ctrl+Enter instead
# text_input.submit(
# fn=categorize_products,
# inputs=[text_input, gr.State(False), top_n, confidence],
# outputs=text_output
# )
process_btn.click(
fn=categorize_products,
inputs=[file_input, gr.State(True), file_top_n, file_confidence],
outputs=file_output
)
# Remove the custom JavaScript keyboard handler since we're using Gradio's built-in submit event
gr.Markdown("Powered by Voyage AI embeddings • Built with Gradio")
return demo