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; } """, 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