CoolFace
Apppublic

Springs-apps/PrintProntoDemo

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
app.py629 linesDownload Raw Back to root
1import base642import os3import random4import re5 6import dotenv7import gradio as gr8from openai import OpenAI9 10dotenv.load_dotenv()11 12client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))13 14script_dir = os.path.dirname(os.path.abspath(__file__))15COMPANIES = ["PrintPronto", "BannerLord", "PrintMaster"]16printer_1 = """17# PrintPronto18## Product: Business cards19 20Color:21- Black (default)22- Blue23- Brown24- Orange25- Pink26 27Sizes:283.5x2: multiplier 1.0 (default)292.5x2.5: multiplier 0.95302.125x3.375: multiplier 0.931Custom size: This vendor doesn't support custom sizes32 33Material:34- Standard (default)35- Uncoated36 37Base Prices (Quantity,Price per Unit $):38100, 0.2339250, 0.240500, 0.19411000, 0.1742 43## Product: Fabric Banners44 45Sizes:462.5x4: multiplier 1.0474x4: multiplier 1.1482.5x6: multiplier 1.0 (default)492.5x8: multiplier 1.2504x6: multiplier 1.551Custom size: This vendor doesn't support custom sizes52 53Base Prices (Quantity,Price per Unit $):541, 230.00555, 210.005610, 201.505725, 195.2658 59## Product: Bumper Stickers60 61Shapes:62Rectangles: multiplier 1.063Squares: multiplier 1.064Circles: multiplier 1.065Ovals: multiplier 1.066Rounded Rectangles: multiplier 1.067Custom shape: This vendor doesn't support custom shapes68 69Sizes:702x3: multiplier 1.0712x4: multiplier 1.1723x4: multiplier 1.0 (default)733x6: multiplier 1.2742x8: multiplier 1.575Custom size: This vendor doesn't support custom sizes76 77Base Prices (Quantity,Price per Unit $):7850, 1.8079100, 1.1080250, 0.8081500, 0.44821000, 0.2483 84## Product: Paper Stickers85 86Shapes:87Rectangles: multiplier 1.0 88Squares: multiplier 1.089Circles: multiplier 1.090Ovals: multiplier 1.091Custom size: This vendor doesn't support custom shapes92 93Sizes:940.79: multiplier 1.0 (default)951: multiplier 1.1961.18: multiplier 1.0971.26: multiplier 1.2981.38: multiplier 1.599Custom size: This vendor doesn't support custom sizes100 101Material:102- Gloss Paper (default)103- Matte Paper104 105Base Prices (Quantity,Price per Unit $):106250, 0,40107500, 0.221081000, 0.121092000, 0.09110"""111 112printer_2 = """113# BannerLord114## Product: Business cards115 116Color:117- Black (default)118- Blue119 120Sizes:1213.5x2: multiplier 1.0 (default)1222.5x2.5: multiplier 0.951232.125x3.375: multiplier 0.91243x2: multiplier 1.51254x3: multiplier 1.5126Custom size: This vendor supports custom sizes with a multiplier of 1.2127 128Material:129- Standard (default)130- Uncoated131 132Base Prices (Quantity,Price per Unit $):133100, 0.25134250, 0.22135500, 0.21361000, 0.19137 138## Product: Fabric Banners139 140Sizes:1412.5x4: multiplier 1.01424x4: multiplier 1.11432.5x6: multiplier 1.0 (default)1442.5x8: multiplier 1.21454x6: multiplier 1.5146Custom size: This vendor supports custom sizes with a multiplier of 1.2147 148Base Prices (Quantity,Price per Unit $):1491, 228.001505, 205.0015110, 204.9015225, 193.56153 154## Product: Bumper Stickers155 156Shapes:157Rectangles: multiplier 1.0 158Squares: multiplier 1.0159Circles: multiplier 1.0160Ovals: multiplier 1.0161Rounded Rectangles: multiplier 1.0162Custom shape: This vendor supports custom shapes with a multiplier of 1.2163 164Sizes:1652x3: multiplier 1.01662x4: multiplier 1.11673x4: multiplier 1.0 (default)1683x6: multiplier 1.21692x8: multiplier 1.5170Custom size: This vendor supports custom sizes with a multiplier of 1.2171 172Base Prices (Quantity,Price per Unit $):17350, 1.90174100, 1.15175250, 0.85176500, 0.451771000, 0.25178 179## Product: Paper Stickers180 181Shapes:182Rectangles: multiplier 1.0183Squares: multiplier 1.0184Circles: multiplier 1.0185Ovals: multiplier 1.0186Custom size: This vendor supports custom sizes with a multiplier of 1.2187 188Sizes:1890.79: multiplier 1.0 (default)1901: multiplier 1.11911.18: multiplier 1.01921.26: multiplier 1.21931.38: multiplier 1.5194Custom size: This vendor supports custom sizes with a multiplier of 1.2195 196Material:197- Gloss Paper (default)198- Matte Paper199 200Base Prices (Quantity,Price per Unit $):201250, 0.42202500, 0.232031000, 0.132042000, 0.10205"""206 207printer_3 = """208# PrintMaster209## Product: Business cards210 211Color:212- Black (default)213- Blue214- Brown215 216Sizes:2173.5x2: multiplier 1.0 (default)2182.5x2.5: multiplier 0.952192.125x3.375: multiplier 0.92203x2: multiplier 1.52214x3: multiplier 1.5222Custom size: This vendor supports custom sizes with a multiplier of 1.2223 224Material:225- Standard (default)226- Uncoated227 228Base Prices (Quantity,Price per Unit $):229100, 0.21230250, 0.2231500, 0.192321000, 0.18233 234## Product: Fabric Banners235 236Sizes:2372.5x4: multiplier 1.0 (default)2384x4: multiplier 1.12392.5x6: multiplier 1.02402.5x8: multiplier 1.22414x6: multiplier 1.5242Custom size: This vendor supports custom sizes with a multiplier of 1.2243 244Base Prices (Quantity,Price per Unit $):2451, 231.002465, 209.5024710, 203.4524825, 194.00249 250## Product: Bumper Stickers251 252Shapes:253Rectangles: multiplier 1.0254Squares: multiplier 1.0255Circles: multiplier 1.0256Ovals: multiplier 1.0257Rounded Rectangles: multiplier 1.0258Custom shape: This vendor supports custom shapes with a multiplier of 1.2259 260Sizes:2612x3: multiplier 1.0 (default)2622x4: multiplier 1.12633x4: multiplier 1.02643x6: multiplier 1.22652x8: multiplier 1.5266Custom size: This vendor supports custom sizes with a multiplier of 1.2267 268Base Prices (Quantity,Price per Unit $):26950, 1.85270100, 1.10271250, 0.82272500, 0.432731000, 0.23274 275## Product: Paper Stickers276 277Shapes:278Rectangles: multiplier 1.0279Squares: multiplier 1.0280Circles: multiplier 1.0281Ovals: multiplier 1.0282Custom size: This vendor supports custom sizes with a multiplier of 1.2283 284Sizes:2850.79: multiplier 1.0 (default)2861: multiplier 1.12871.18: multiplier 1.02881.26: multiplier 1.22891.38: multiplier 1.5290Custom size: This vendor supports custom sizes with a multiplier of 1.2291 292Material:293- Gloss Paper (default)294- Matte Paper295 296Base Prices (Quantity,Price per Unit $):297250, 0.40298500, 0.212991000, 0.113002000, 0.08301"""302 303IMAGES = {304    "Business Cards": {305        "images": [306            os.path.join(script_dir, "public/src/painted-edge-business-card-05.png"),307            os.path.join(308                script_dir,309                "public/src/painted-edge-cards---w-brown-box-_4-4-color_.png",310            ),311            os.path.join(script_dir, "public/src/silk-business-card-03_1.png"),312        ],313        "links": [314            "https://www.example.com/product_image.jpg",315            "https://www.example.com/product_image.jpg",316            "https://www.example.com/product_image.jpg",317        ],318    },319    "Fabric Banners": {320        "images": [321            os.path.join(script_dir, "public/src/03.jpg"),322            os.path.join(323                script_dir,324                "public/src/fabric-banner-01.webp",325            ),326            os.path.join(script_dir, "public/src/fabric-banner-02.webp"),327        ],328        "links": [329            "https://www.example.com/product_image.jpg",330            "https://www.example.com/product_image.jpg",331            "https://www.example.com/product_image.jpg",332        ],333    },334    "Bumper Stickers": {335        "images": [336            os.path.join(script_dir, "public/src/clear-bumper-sticker.jpg"),337            os.path.join(338                script_dir,339                "public/src/copy_of_untitled_design_9_.webp",340            ),341            os.path.join(script_dir, "public/src/removable-bumper-stickers.jpg"),342        ],343        "links": [344            "https://www.example.com/product_image.jpg",345            "https://www.example.com/product_image.jpg",346            "https://www.example.com/product_image.jpg",347        ],348    },349    "Paper Stickers": {350        "images": [351            os.path.join(script_dir, "public/src/us_images_1_.webp"),352            os.path.join(353                script_dir,354                "public/src/copy_of_us_images_1__1.webp",355            ),356            os.path.join(script_dir, "public/src/copy_of_us_images_1__1.webp"),357        ],358        "links": [359            "https://www.example.com/product_image.jpg",360            "https://www.example.com/product_image.jpg",361            "https://www.example.com/product_image.jpg",362        ],363    },364}365 366 367def find_best_price(request):368    chat_prompt = (369        "You are a customer assistant tasked with finding the best price for multiple items from different printers.\n"370        "Here are the prices and options from the printers:\n"371        f"Printer 1: {printer_1}\n"372        f"Printer 2: {printer_2}\n"373        f"Printer 3: {printer_3}\n"374        "For each product in the request, follow these steps:\n"375        "1. Verify if the requested size is available. If the requested size is unavailable, check if the vendor supports custom sizes.\n"376        "2. If the user has not specified one or more parameters (e.g., size, shape), use the vendor's default values for those parameters.\n"377        "3. Apply the custom size multiplier only under the following conditions:\n"378        "- The user explicitly requests a specific size.\n"379        "- The requested size is not standard (i.e., it is custom).\n"380        "- The vendor supports custom sizes.\n"381        "4. Calculate the final price by:\n"382        "   - Finding the nearest lower quantity in the base price list.\n"383        "   - Multiplying by the size multiplier\n"384        "   - Multiplying by the user requested amount. IMPORTANT. Do not multiply by nearest lower quantity!\n"385        "5. Do not use linear interpolation for quantities - use the nearest lower quantity pricing.\n\n"386        "Example:\n"387        "User: I want 25 banners in size 6x6, 2000 business cards and 5000 circle stickers in size 6x6 on matte paper\n"388        "Answer:\n"389        "Banners:\n"390        "- PrintPronto: Doesn't support custom size\n"391        "- BannerLord: amount (25) * multiplier for custom size (1.2) * price per unit for nearest pack size ($193.56). Total for 25: $5806.80\n"392        "- PrintMaster: amount (25) * multiplier for custom size (1.2) * price per unit for nearest pack size ($194.00). Total for 25: $5820.00\n\n"393        "Business cards:\n"394        "- PrintPronto: amount (2000) * multiplier for default size (1.0) * price per unit for nearest pack size ($0.17). Total for 2000: $340.00\n"395        "- BannerLord: amount (2000) * multiplier for default size (1.0) * price per unit for nearest pack size ($0.19). Total for 2000: $380.00\n"396        "- PrintMaster: amount (2000) * multiplier for default size (1.0) * price per unit for nearest pack size ($0.18). Total for 2000: $360.00\n\n"397        " Paper stickers:\n"398        "- PrintPronto: Doesn't support custom size\n"399        "- BannerLord: amount (5000) * multiplier for circle shape (1.0) * multiplier for custom size (1.2) * price per unit for nearest pack size ($0.13). Total for 5000: $780.00\n"400        "- PrintMaster: amount (5000) * multiplier for circle shape (1.0) * multiplier for custom size (1.2) * price per unit for nearest pack size ($0.11). Total for 5000: $660.00\n\n"401        "Your response should display ONLY final pricing for each available option, nothing more.\n"402        "At the end of your response, write summary in the following format:\n"403        "Full quote (all products): *all items, that user's requested*\n"404        "- Printer1: $price of full request\n"405        "- Printer2: $price of full request\n"406        "- Printer3: $price of full request\n"407    )408 409    chat_response = client.chat.completions.create(410        model="gpt-4o-mini",411        messages=[412            {"role": "system", "content": chat_prompt},413            {"role": "user", "content": request},414        ],415        temperature=0.5,416    )417 418    chat_text = chat_response.choices[0].message.content419    chat_text = chat_text.replace("###", "")420 421    images_to_use = extract_requested_products(422        request=request, product_names=list(IMAGES.keys())423    )424    if images_to_use is None:425        html_page = (426            "<html><body><h3>Requested product is not available.</h3></body></html>"427        )428        chat_text = "Requested product is not available."429        return html_page, chat_text430 431    html_page = render_html_page(chat_text, images_to_use)432 433    return html_page, chat_text434 435 436def extract_total_prices(text):437    """438    Extracts total prices and availability information from a text block,439    preserving the order of appearance.440 441    Args:442        text: The input text.443 444    Returns:445        A list of strings containing prices and availability messages in order.446    """447    results = []448    # Combine patterns using the OR operator | and capture groups449    pattern = (450        r"(Total for \d+: (\$\d+(?:,\d{3})*(?:\.\d{2})?))|(Doesn't support custom size)"451    )452 453    matches = re.findall(pattern, text)454 455    for match in matches:456        # Check which group matched457        if match[0]:  # Price match458            results.append(match[1])459        elif match[2]:  # Availability match460            results.append(match[2])461    return results462 463 464def extract_requested_products(request, product_names):465    product_list = "\n".join(f"- {product}" for product in product_names)466    prompt = (467        "Here are the available product names:\n"468        f"{product_list}\n"469        "Identify any products explicitly or implicitly mentioned in the user's request. "470        "If a product is mentioned in different sizes, consider each size as a separate product and include them in the list. "471        "However, do not include duplicate entries of the same product unless explicitly mentioned multiple times in the request. "472        "If no products match, respond with 'None'. Output the result as a Python list. "473        "Your response should contain only the list or 'None'."474    )475 476    response = client.chat.completions.create(477        model="gpt-4o-mini",478        messages=[479            {"role": "system", "content": prompt},480            {"role": "user", "content": request},481        ],482        temperature=0.1,483    )484    response_text = response.choices[0].message.content485    return eval(response_text)486 487 488def render_html_page(chat_text, images_to_use):489    if images_to_use is None:490        return "<html><body><h3>Requested product is not available.</h3></body></html>"491 492    html_content = """493    <html>494        <head>495            <title>Product Cards</title>496            <style>497                .product-card {498                    border: 1px solid #ccc;499                    border-radius: 5px;500                    padding: 16px;501                    margin: 16px;502                    text-align: center;503                    width: 200px;504                    display: inline-block;505                }506                .product-card img {507                    max-width: 100%;508                    height: auto;509                }510                .product-card button {511                    background-color: #4CAF50;512                    color: white;513                    padding: 10px 20px;514                    border: none;515                    border-radius: 5px;516                    cursor: pointer;517                }518                .product-card button:hover {519                    background-color: #45a049;520                }521                .summary {522                    border-top: 2px solid #ccc;523                    padding-top: 16px;524                    margin-top: 20px;525                    font-family: Arial, sans-serif;526                }527            </style>528        </head>529        <body>530    """531 532    # Assuming processed_prices is a flat list533    processed_prices = extract_total_prices(chat_text)534 535    # Process images and links536    processed_images = [537        image for img in images_to_use for image in IMAGES[img]["images"]538    ]539    processed_links = [link for img in images_to_use for link in IMAGES[img]["links"]]540 541    # Initialize labels for all products542    labels = [""] * len(processed_prices)543 544    # Group prices in subsets of 3 and assign labels545    for group_start in range(0, len(processed_prices), 3):546        subset_indices = list(547            range(group_start, min(group_start + 3, len(processed_prices)))548        )549        subset_prices = [processed_prices[i] for i in subset_indices]550 551        # Find the index of the lowest price in this subset552        min_price_index = subset_indices[subset_prices.index(min(subset_prices))]553        labels[min_price_index] = "<b>BEST VALUE</b>"554 555        # Assign remaining labels randomly556        remaining_indices = [i for i in subset_indices if i != min_price_index]557        random.shuffle(remaining_indices)558        labels[remaining_indices[0]] = "<b>BEST QUALITY</b>"559        labels[remaining_indices[1]] = (560            "<b>FAST PRODUCTION</b>" if len(remaining_indices) > 1 else ""561        )562 563    # Generate HTML content564    for i in range(len(processed_images)):565        image = processed_images[i]566        with open(image, "rb") as img_file:567            base64_image = base64.b64encode(img_file.read()).decode("utf-8")568 569        # Add price with label570        price_with_label = (571            f"{labels[i]}<br>{COMPANIES[i % 3]} "572            f"{images_to_use[i // 3].lower()}: {processed_prices[i]}"573        )574 575        html_content += f"""576        <div class="product-card">577            <img src="data:image/jpeg;base64,{base64_image}" alt="Product Image">578            <p>{price_with_label}</p>579            <a href="{processed_links[i]}" target="_blank"><button>Buy Now</button></a>580        </div>581        """582 583    # Extract the summary from chat_text584    # print(chat_text)585    summary_start = chat_text.find("Full quote (all products):")586    # print(summary_start)587    if summary_start != -1:588        summary_text = chat_text[summary_start:].strip()589        html_content += f"""590        <div class="summary">591            <h3>Quote Summary</h3>592            <pre>{summary_text}</pre>593        </div>594        """595 596    html_content += "</body></html>"597    return html_content598 599 600logo = os.path.join(script_dir, "public/src/logo.svg")601with open(logo, "rb") as logo_file:602    base64_logo = base64.b64encode(logo_file.read()).decode("utf-8")603 604 605iface = gr.Interface(606    fn=find_best_price,607    inputs=gr.Textbox(lines=3, placeholder="Enter what are you looking for"),608    outputs=[609        gr.HTML(label="Product Image"),610        gr.Textbox(label="AI response: text mode"),611    ],612    title="Get Instant Quote",613    description=f"""614    <div style="display: flex; justify-content: center; align-items: center; text-align: center; margin-bottom: 20px;">615        <img src="data:image/svg+xml;base64,{base64_logo}" alt="Logo" style="width: 150px; height: auto;">616    </div>617    """,618    submit_btn="Get quote",619)620 621 622def auth_function(username, password):623    # valid_users = {"admin": "demo4anthony1", "123": "123"}624    valid_users = {"admin": "demo4anthony1"}625    return username in valid_users and valid_users[username] == password626 627 628iface.launch(auth=auth_function, share=True, ssr_mode=False)629