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"
Error: {str(e)}
" else: product_names = [line.strip() for line in product_input.split("\n") if line.strip()] if not product_names: return "
No product names provided.
" # 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 = "
" # Add debug info to verify data output_html += f"

Processing {len(product_names)} products.

" 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 += "
" output_html += "
" if not all_similarities: output_html = "
No results found. Please check your input or try different products.
" 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; } """) as demo: gr.Markdown("# Product Categorization Tool\nAnalyze products and find the most similar ingredients using AI embeddings.") gr.Markdown("Use **Cmd+Enter** (Mac) or **Ctrl+Enter** (Windows/Linux) to quickly categorize products.") 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 ) process_btn.click( fn=categorize_products, inputs=[file_input, gr.State(True), file_top_n, file_confidence], outputs=file_output ) # Add keyboard shortcut handler for both tabs text_input.change(None, None, None, js=""" function() { // Set up the keyboard event listener document.removeEventListener('keydown', cmdEnterHandler); document.addEventListener('keydown', cmdEnterHandler); function cmdEnterHandler(e) { if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') { // Find the currently visible tab content const textTab = document.querySelector('.tabitem:nth-child(1)'); const fileTab = document.querySelector('.tabitem:nth-child(2)'); if (textTab && window.getComputedStyle(textTab).display !== 'none') { // Text tab is active const button = textTab.querySelector('button'); if (button) button.click(); } else if (fileTab && window.getComputedStyle(fileTab).display !== 'none') { // File tab is active const button = fileTab.querySelector('button'); if (button) button.click(); } e.preventDefault(); } } return []; } """) gr.Markdown("Powered by Voyage AI embeddings • Built with Gradio") return demo